
enject: .env रहस्यों को prAIng आँखों से छिपाएँ: रहस्य स्थानीय एन्क्रिप्टेड स्टोर (प्रति परियोजना) में रहते हैं और रनटाइम पर सीधे ऐप्स में इंजेक्ट किए जाते हैं, कभी भी डिस्क पर सादे पाठ के रूप में नहीं छूते।
.env सीक्रेट्स को prAIng आँखों से छिपाएँ।
(नोट: इस प्रोजेक्ट को पहले enveil कहा जाता था और अब इसका नाम बदलकर enject कर दिया गया है)
AI कोडिंग टूल्स जैसे Claude Code, Copilot, Cursor और अन्य आपके प्रोजेक्ट डायरेक्टरी में फ़ाइलों को पढ़ सकते हैं, जिसका मतलब है कि एक सादा टेक्स्ट .env फ़ाइल एक आकस्मिक सीक्रेट डंप है जो होने वाला है। यह सैद्धांतिक नहीं है। यह एक ज्ञात समस्या है जो मेरे साथ कई बार हुई है (Claude Code के settings.json फ़ाइल में झाँकने से मना करने के बाद भी)। enject यह सुनिश्चित करके इसे हल करता है कि सादा टेक्स्ट सीक्रेट्स डिस्क पर कभी मौजूद न हों। आपकी .env फ़ाइल में केवल प्रतीकात्मक संदर्भ होते हैं; वास्तविक मान एक एन्क्रिप्टेड स्थानीय स्टोर में रहते हैं और लॉन्च के समय सीधे आपकी सबप्रोसेस में इंजेक्ट होते हैं।
यह प्रोजेक्ट Filip Hric के समाधान/ब्लॉग पोस्ट से प्रेरित है, जो 1Password का उपयोग करके समान अवधारणा का उपयोग करता है। मैं एक स्व-निहित समाधान चाहता था जो तीसरे पक्ष की सेवाओं पर निर्भर न हो, जिससे यह समाधान उत्पन्न हुआ। और हाँ, यह प्रोजेक्ट लगभग पूरी तरह से Claude Code के साथ मैन्युअल सत्यापन और परीक्षण के साथ बनाया गया था।
यह प्रोजेक्ट मुख्य रूप से AI/LLM टूल्स द्वारा आपके प्रोजेक्ट में .env सीक्रेट्स को आकस्मिक रूप से पढ़ने की ज्ञात समस्या को कम करने के लिए डिज़ाइन किया गया है। अतिरिक्त लाभों में शामिल है: यदि कोई .env गलती से किसी रिपॉजिटरी में कमिट हो जाता है तो सीक्रेट लीकेज को रोकना, सादा टेक्स्ट सीक्रेट्स के बजाय संदर्भों वाली .env फ़ाइलों को साझा करने की क्षमता, और एन्क्रिप्टेड स्टोर को स्वयं साझा करने का विकल्प।
यह प्रोजेक्ट किसी AI एजेंट को आपके सीक्रेट्स प्राप्त करने से रोकने के लिए कोई चांदी की गोली नहीं है। उदाहरण के लिए, एक एजेंट अभी भी (दुर्घटना या प्रॉम्प्ट इंजेक्शन के माध्यम से) ऐसा कोड लिख सकता है जो रनटाइम पर टर्मिनल आउटपुट या किसी फ़ाइल में सीक्रेट्स को बाहर निकालता है। हम दृढ़ता से सलाह देते हैं कि आप इस टूल या सामान्य रूप से .env फ़ाइलों पर प्रोडक्शन सीक्रेट्स को स्टोर करने के लिए भरोसा न करें।
आपकी .env फ़ाइल इस तरह दिखती है:
DATABASE_URL=en://database_url
STRIPE_KEY=en://stripe_key
PORT=3000
तकनीकी रूप से इसे कमिट करना सुरक्षित है (हालांकि शायद ऐसा न करें), और इससे भी महत्वपूर्ण बात: किसी भी AI टूल के लिए सुरक्षित जो गलती से (या शायद इतनी गलती से नहीं) इसमें झाँक रहा है।
जब आप enject run -- npm start चलाते हैं, तो यह:
en:// संदर्भ को हल करता हैस्टोर फ़ाइल एक बाइनरी ब्लॉब है। मास्टर पासवर्ड के बिना, यह रैंडम शोर से अप्रभेद्य है। हर लेखन पर नॉन्स ताज़ा उत्पन्न होता है, इसलिए AES-GCM नॉन्स पुन: उपयोग असंभव है। सिफरटेक्स्ट में कोई भी संशोधन — यहां तक कि एक उलटा बिट भी — प्रमाणीकरण को विफल कर देता है और डिक्रिप्शन को अस्वीकार कर दिया जाता है।
यह रिलीज़ अभी भी अल्फा में है, इसलिए cargo install को कॉल करते समय नवीनतम संस्करण जोड़ना आवश्यक है।
cargo install enject --version 0.2.0-alpha
Rust 1.70+ की आवश्यकता है।
git clone https://github.com/greatscott/enject
cd enject
cargo build --release
संकलित बाइनरी target/release/enject पर है। इसे अपने PATH पर एक स्थान पर एक बार स्थापित करें ताकि आप इसे किसी भी प्रोजेक्ट से चला सकें:
macOS / Linux (bash या zsh)
# विकल्प A: ~/.local/bin (sudo की आवश्यकता नहीं, Linux पर सामान्य)
mkdir -p ~/.local/bin
cp target/release/enject ~/.local/bin/
# विकल्प B: /usr/local/bin (sudo की आवश्यकता है, सिस्टम-वाइड उपलब्ध)
sudo cp target/release/enject /usr/local/bin/
# विकल्प C: ~/.cargo/bin (यदि आपने rustup का उपयोग किया है तो पहले से PATH पर)
cp target/release/enject ~/.cargo/bin/
यदि आपने विकल्प A का उपयोग किया और ~/.local/bin पहले से आपके PATH पर नहीं है, तो इसे अपने शेल कॉन्फ़िग (~/.zshrc, ~/.bashrc, या ~/.bash_profile) में जोड़ें:
export PATH="$HOME/.local/bin:$PATH"
फिर इसे रीलोड करें:
source ~/.zshrc # या ~/.bashrc
सत्यापित करें कि यह काम करता है:
enject --version
बाइनरी वैश्विक रूप से स्थापित है — आप इसे कभी पुनः स्थापित नहीं करते। लेकिन प्रत्येक प्रोजेक्ट को अपना स्वयं का एन्क्रिप्टेड स्टोर मिलता है:
cd your-project
enject init
यह वर्तमान निर्देशिका में प्रोजेक्ट के कॉन्फ़िग और एन्क्रिप्टेड स्टोर के साथ .enject/ बनाता है। इसे .gitignore में जोड़ें — इसे कभी कमिट नहीं किया जाना चाहिए।
इसे प्रति प्रोजेक्ट एक बार, प्रोजेक्ट रूट में चलाएँ:
enject init
यह एक रैंडम 32-बाइट सॉल्ट उत्पन्न करता है, .enject/config.toml लिखता है, .enject/store पर एक खाली एन्क्रिप्टेड स्टोर बनाता है, और आपको मास्टर पासवर्ड सेट करने के लिए संकेत देता है। .enject/ को अपने .gitignore में जोड़ें — स्टोर को कभी कमिट नहीं किया जाना चाहिए।
enject set some_database_url
# संकेत: 'database_url' के लिए मान: (छिपा हुआ)
enject set some_api_key
मान हमेशा इंटरैक्टिव रूप से दर्ज किए जाते हैं। मान को कमांड-लाइन तर्क के रूप में पारित करने का कोई तरीका नहीं है — यह सीक्रेट्स को शेल हिस्ट्री या ps आउटपुट में दिखने से रोकता है।
.env में सीक्रेट्स का संदर्भ लेंDATABASE_URL=en://some_database_url
MY_API_KEY=en://stripe_key
PORT=3000
सादा KEY=VALUE पंक्तियाँ अपरिवर्तित रहती हैं। केवल en:// संदर्भ हल किए जाते हैं।
enject run -- npm start
enject run -- python manage.py runserver
enject run -- cargo run
-- के बाद की सभी चीज़ें OS को शाब्दिक रूप से पास की जाती हैं। सबप्रोसेस आपका पूरा शेल वातावरण (इसलिए PATH, HOME, आदि मौजूद हैं) विरासत में लेता है, जिसमें .env मान शीर्ष पर स्तरित होते हैं।
enject list # संग्रहीत कुंजी नाम प्रिंट करें (मान कभी नहीं)
enject delete <key> # एक सीक्रेट हटाएँ
enject import <file> # सादा टेक्स्ट .env में सभी मानों को एन्क्रिप्ट करें, इसे en:// टेम्पलेट के रूप में फिर से लिखें
enject rotate # स्टोर को नए मास्टर पासवर्ड के साथ पुनः एन्क्रिप्ट करें
कोई get और कोई export नहीं है। किसी सीक्रेट मान को stdout पर प्रिंट करना एक AI-पढ़ने योग्य लीकेज वेक्टर बनाता है — enject का पूरा उद्देश्य मानों को डिस्क और किसी भी पढ़ने योग्य आउटपुट स्ट्रीम से दूर रखना है।
प्रत्येक सुरक्षा अपरिवर्तनीयता में एक संगत स्वचालित परीक्षण और एक मैन्युअल निरीक्षण पथ होता है।
cargo test
31 परीक्षण, सभी नीचे दिए गए दावों को कवर करते हैं।
स्वचालित: store::password::tests::test_encrypt_decrypt_roundtrip
एक सीक्रेट सहेजता है, स्टोर को स्थायी रखता है, इसे डिस्क से पुनः लोड करता है, डिक्रिप्ट करता है, और मान सही ढंग से राउंड-ट्रिप होता है या नहीं जाँचता है। केवल तभी पास होता है जब डिस्क पर बाइट्स वैध सिफरटेक्स्ट हों — सादा टेक्स्ट डिक्रिप्शन में विफल होगा।
cargo test store::password::tests::test_encrypt_decrypt_roundtrip
मैन्युअल निरीक्षण:
enject init # पासवर्ड: test123
enject set mykey # मान: my-super-secret
xxd .enject/store | head -5
strings .enject/store
xxd बाइनरी डेटा दिखाएगा। strings कुछ भी वापस नहीं करेगा — निकालने के लिए कोई ASCII अनुक्रम नहीं है। पहले 12 बाइट्स रैंडम नॉन्स हैं; बाकी सब AES-GCM सिफरटेक्स्ट है जिसमें एक 16-बाइट प्रमाणीकरण टैग जुड़ा हुआ है।
स्वचालित: store::password::tests::test_nonce_changes_on_each_save
स्टोर को लगातार दो बार सहेजता है, हर बार फ़ाइल के पहले 12 बाइट्स पढ़ता है, और दावा करता है कि वे भिन्न हैं।
cargo test store::password::tests::test_nonce_changes_on_each_save
मैन्युअल निरीक्षण:
xxd .enject/store | head -1 # पहले 12 बाइट्स नोट करें
enject set anotherkey # कोई भी लेखन नॉन्स को घुमाता है
xxd .enject/store | head -1 # पहले 12 बाइट्स अब भिन्न हैं
स्वचालित: store::password::tests::test_wrong_password_returns_err
एक पासवर्ड के साथ स्टोर बनाता है, फिर इसे एक अलग पासवर्ड से अनलॉक करने का प्रयास करता है और Err लौटाए जाने का दावा करता है।
cargo test store::password::tests::test_wrong_password_returns_err
मैन्युअल:
enject list # गलत पासवर्ड दर्ज करें
# आउटपुट: "गलत मास्टर पासवर्ड या दूषित स्टोर।"
# निकास कोड: 1
AES-GCM सिफरटेक्स्ट पर एक 16-बाइट प्रमाणीकरण टैग उत्पन्न करता है। कोई भी संशोधन — यहां तक कि एक उलटा बिट भी — डिक्रिप्शन आगे बढ़ने से पहले सत्यापन को विफल कर देता है। सादा टेक्स्ट कभी उजागर नहीं होता।
स्वचालित: store::password::tests::test_tampered_ciphertext_returns_err
स्टोर फ़ाइल के सिफरटेक्स्ट क्षेत्र में एक बाइट उलटता है (12-बाइट नॉन्स के बाद), फिर डिक्रिप्शन का प्रयास करता है और Err का दावा करता है।
cargo test store::password::tests::test_tampered_ciphertext_returns_err
मैन्युअल:
# बाइट 20 उलटें (नॉन्स के बाद सिफरटेक्स्ट के अंदर)
python3 -c "
data = open('.enject/store', 'rb').read()
bad = data[:20] + bytes([data[20] ^ 0xFF]) + data[21:]
open('.enject/store', 'wb').write(bad)
"
enject list
# आउटपुट: "गलत मास्टर पासवर्ड या दूषित स्टोर।"
en:// संदर्भ पर कठोर त्रुटियदि .env में किसी संदर्भ के लिए स्टोर में कोई मिलान कुंजी नहीं है, तो enject run तुरंत गैर-शून्य कोड के साथ बाहर निकलता है। सबप्रोसेस कभी लॉन्च नहीं होती।
स्वचालित: env_template::tests::test_unknown_ev_ref_returns_err
एक संदर्भ के साथ resolve() को कॉल करता है जिसका कोई मिलान प्रविष्टि नहीं है और Err का दावा करता है।
cargo test env_template::tests::test_unknown_ev_ref_returns_err
मैन्युअल:
echo "DB=en://nonexistent_key" > .env
enject run -- env
# आउटपुट: Secret 'nonexistent_key' स्टोर में नहीं मिला। इसे इससे जोड़ें: enject set nonexistent_key
# निकास कोड: 1 ( `env` सबप्रोसेस कभी नहीं चली)
एकाधिक प्रोजेक्ट्स में उपयोग किए जाने वाले सीक्रेट्स के आसान रखरखाव के लिए वैकल्पिक/अतिरिक्त सिस्टम-वाइड स्टोर लागू करें।
अपडेट करते समय स्टोर के पासवर्ड को मैन्युअल रूप से दर्ज करने की आवश्यकता को कम करें।