A simple, no-install solution to securely encrypt your repository secrets using AES-256!
Multi-encrypt will encode and decode all of your repo secrets quickly and securely, without requiring complex setup or causing developer compatibility issues.
- AES-256-GCM encryption (industry standard)
- Authenticated encryption to prevent tampering
- Secure key derivation (PBKDF2 with 600,000 iterations - OWASP 2024 recommendation)
- Unique initialization vectors (IVs) for each encryption
- No dependencies on external encryption libraries
- Uses Node.js native crypto module
- Backward compatible with v1 encrypted files
Multi-encrypt uses three critical components for each encrypted file:
-
Initialization Vector (IV): A random value generated for each encryption operation. Even if you encrypt the same file multiple times with the same password, the IV ensures you get different encrypted outputs each time. This prevents pattern analysis and strengthens security.
-
Encrypted Data: Your actual file content, encrypted using AES-256-GCM with the derived key and IV.
-
Authentication Tag: A security feature of GCM (Galois/Counter Mode) encryption that ensures the encrypted data hasn't been tampered with. If someone modifies the encrypted data, the tag verification will fail during decryption.
Example structure in encrypted.json:
{
"version": 2,
"salt": "random-64-byte-salt-base64-encoded",
"files": {
"path/to/secret.js": {
"iv": "random-initialization-vector",
"encrypted": "encrypted-file-content",
"tag": "authentication-tag"
}
}
}All three components are necessary for secure decryption. If any piece is modified or missing, decryption will fail as a security measure.
- Add your secrets to your .gitignore file under a "# Secrets" comment:
.DS_Store
node_modules
# Secrets - Handled by multi-encrypt
./src/secrets.json
./config/api-keys.env
./private/credentials.js
- Install multi-encrypt:
# For your repo
npm i multi-encrypt
# For your machine (if you're managing secrets)
npm i -g multi-encrypt- Add encrypt/decrypt scripts to your package.json:
{
"scripts": {
"enc": "multi-encrypt enc",
"dec": "multi-encrypt dec"
}
}That's it! Your secrets are now ready to be encrypted.
# Using npm script
npm run enc
# Or using global install
multi-encrypt encThis will:
- Prompt you for an encryption password
- Read all files listed under "# Secrets" in .gitignore
- Encrypt them using AES-256-GCM
- Save the encrypted data to encrypted.json
# Using npm script
npm run dec
# Or using global install
multi-encrypt decThis will:
- Prompt you for the decryption password
- Read the encrypted.json file
- Decrypt all files to their original locations
If you're upgrading from multi-encrypt v1.x, your existing encrypted.json files will still work. The v2 release automatically detects the legacy format and decrypts using the original algorithm.
To upgrade your encrypted files to the new, more secure v2 format:
- Decrypt your files:
multi-encrypt dec - Re-encrypt them:
multi-encrypt enc - Commit the new
encrypted.json
The v2 format includes:
- 600,000 PBKDF2 iterations (up from 1,000)
- AES-256-GCM (up from CAST5-CBC)
- Random 64-byte salt (instead of static salt)
- SHA-256 digest (up from SHA-1)
Your team members will need to update their multi-encrypt version before they can decrypt v2 files.
| Tool | Type | Windows | Key Management | File Types | Dependencies | Setup Complexity |
|---|---|---|---|---|---|---|
| multi-encrypt | npm package | Native | Password (memorizable) | Any file | 3 (npm only) | npm i + done |
| git-crypt | C++ binary | Problematic | GPG keys | Any file | GPG + binary | Install GPG, generate keys, configure |
| blackbox | Shell scripts | No | GPG keys | Any file | GPG + bash | Install GPG, manage keyrings |
| SOPS | Go binary | Yes | KMS/GPG/age keys | YAML/JSON/ENV | External binary | Install binary, configure KMS or keys |
| git-secret | Bash tool | No | GPG keys | Any file | GPG + bash | Install GPG, add users to keyring |
| dotenvx | npm package | Yes | Public/private keys | .env only | 9+ npm deps | Generate keys, store private key |
| transcrypt | Shell + OpenSSL | Limited | Shared password | Any file | OpenSSL + bash | Configure git filters |
Choose multi-encrypt if you want:
- Password-based encryption (no keys to manage, rotate, or lose)
- Cross-platform support including Windows (just
npm install) - Zero external dependencies (no GPG, no binaries, no cloud services)
- Encrypt any file type (not just .env files)
- Simple mental model (password → encrypted file)
Choose something else if you need:
- Per-user access control (use git-crypt or blackbox with GPG)
- Cloud KMS integration (use SOPS)
- Transparent git encryption (use git-crypt or transcrypt)
- Enterprise audit logging (use a secrets manager)
We believe in being upfront about limitations. Here's what you should know:
✅ A simple, password-based encryption tool for teams that want to commit encrypted secrets to git without complex key management.
✅ Cross-platform - Works identically on Windows, Mac, and Linux with just npm install.
✅ Zero external dependencies - Uses only Node.js built-in crypto module. No GPG, no cloud services, no native binaries.
✅ Backward compatible - v2 can decrypt files encrypted with v1.
❌ Not a secrets manager - There's no UI, no access control, no audit logging. It's a CLI tool.
❌ Not for per-user permissions - Everyone with the password can decrypt everything. For granular access control, use GPG-based tools.
❌ Not transparent encryption - You manually run enc/dec. For automatic encrypt-on-commit, use git-crypt.
❌ Not for huge files - Files are loaded into memory. Fine for configs, not for gigabyte blobs.
| Choice | What We Use | Why | Alternative Considered |
|---|---|---|---|
| Cipher | AES-256-GCM | Industry standard, authenticated encryption, native Node.js support | ChaCha20-Poly1305 (less universal support) |
| KDF | PBKDF2-SHA256 | OWASP recommended, native Node.js support, well-understood | Argon2id (requires native addon) |
| Iterations | 600,000 | OWASP 2024 recommendation for PBKDF2-SHA256 | Higher = slower UX, lower = less secure |
| Salt | 64 bytes random | Exceeds recommendations, prevents rainbow tables | 32 bytes (sufficient but we prefer overkill) |
| IV | 12 bytes random | Native GCM size, unique per file | 16 bytes (works but triggers extra computation) |
-
Password strength is on you - We enforce 8+ characters, but a weak password undermines everything. Use a passphrase or generated password.
-
No password recovery - Forget your password = lose your secrets. We can't help. Keep a backup somewhere safe.
-
All-or-nothing access - Can't give someone access to just one file. It's the whole encrypted.json or nothing.
-
~1 second delay on encrypt/decrypt - The 600,000 PBKDF2 iterations take time. This is a feature (slows down attackers), not a bug.
-
Secrets visible in memory - During encryption/decryption, plaintext exists in Node.js memory. This is true of all encryption tools.
You might wonder why we use PBKDF2 instead of Argon2 (the "modern" choice). Reasons:
- Native Node.js support - PBKDF2 is built into Node.js. Argon2 requires native addons that can fail to compile on some systems.
- Cross-platform reliability - Native addons are a common source of "works on my machine" issues, especially on Windows.
- PBKDF2 is not broken - With 600,000 iterations, PBKDF2-SHA256 is still OWASP-recommended and secure.
- Zero-dependency philosophy - Adding
argon2npm package adds supply chain risk and build complexity.
If you need Argon2, use a tool like SOPS that's designed around it.
This repository is developed with assistance from Claude Code. The .claude/ directory contains Claude's configuration for this project. We believe in transparency about AI-assisted development.
A: All encrypted data is stored in 'encrypted.json' in the root of your repo. While this file is plain text (base64 encoded), it's securely encrypted using AES-256-GCM, a standard trusted by governments and financial institutions worldwide. Each encrypted file includes an initialization vector (IV), encrypted content, and authentication tag to ensure security and data integrity.
A: Multi-encrypt uses multiple layers of industry-standard security:
-
AES-256-GCM Encryption: The gold standard in symmetric encryption, chosen by the NSA for top secret information. It's the same encryption used by major cloud providers like AWS for their EBS volumes and Google Cloud for their storage. The 256-bit key length means there are 2^256 possible keys - more than the number of atoms in the observable universe.
-
PBKDF2 with 600,000 Iterations: Password-based key derivation that's significantly more secure than basic hashing. While some services use as few as 1,000 iterations (like older versions of LastPass) or 10,000 iterations (like some legacy banking systems), we use 600,000 iterations - matching OWASP's 2024 recommendation for PBKDF2-SHA256. This means breaking the encryption would take about 600,000 times longer than with a simple hash, providing maximum protection against brute-force attacks even with modern GPUs.
-
Authenticated Encryption: Using GCM (Galois/Counter Mode) provides built-in authentication, similar to how Signal and WhatsApp verify message integrity. This prevents sophisticated attacks like bit-flipping that could affect other encryption modes like CBC.
-
Unique IVs: Every file gets its own random initialization vector, preventing pattern analysis. This is the same approach used by TLS 1.3 (the protocol securing HTTPS) to ensure that even if you encrypt the same file twice, the outputs look completely different.
-
64-byte Random Salt: Our salt is twice the size of common implementations (many use 32 bytes), matching the recommendations for high-security systems. This makes pre-computed attacks (rainbow tables) practically impossible, as attackers would need to pre-compute for 2^512 possible salts.
-
Authentication Tags: Each encrypted file includes a unique tag that acts like a digital seal. Similar to how modern banking apps verify transactions, any tampering with the encrypted data will break this seal and cause decryption to fail.
For perspective, breaking AES-256 with current technology would take billions of years even with all of the world's current supercomputers combined. The addition of our high-iteration PBKDF2 and large salt size makes the protection even stronger against password-guessing attacks.
Your security ultimately depends on choosing and protecting a strong password, but the technical implementation provides government-grade protection for your secrets.
A: No! Your original files remain untouched. Since they're listed in .gitignore, they won't be committed to your repository. This prevents accidental data loss if you forget your password or need to reference the original files.
A: Yes, add a postinstall script to your package.json:
{
"scripts": {
"postinstall": "multi-encrypt dec"
}
}Developers will be prompted for the password after npm install completes.
A: To change the password:
- Decrypt all files with the old password
- Re-encrypt them with the new password
- Commit the new encrypted.json file
- Share the new password securely with your team
Remember: Anyone who had the old password has seen the secret contents. Consider updating sensitive data like API keys when rotating passwords.
A: For CI/CD environments, you can pass the password via command line:
multi-encrypt dec -p "your-password"Note: Using passwords in command line arguments may expose them in logs. Consider using your CI/CD system's secret management features to securely provide the password.
multi-encrypt encrypt # Encrypt files (alias: enc)
multi-encrypt decrypt # Decrypt files (alias: dec)multi-encrypt --help # Show all commands and optionsYes. 29 unit tests with 90%+ code coverage. Run npm test yourself or check the test files.
Secrets managers (HashiCorp Vault, AWS Secrets Manager, etc.) are great for large enterprises, but they:
- Add infrastructure complexity and cost
- Require network access to decrypt secrets
- Centralize access (single point of compromise)
- Need their own access management
Multi-encrypt is for teams that want simplicity: one password, one encrypted file, zero infrastructure.
For hybrid approaches, use a secrets manager to store just the decryption password, and multi-encrypt for the actual secrets. Best of both worlds.
The file is:
- Encrypted with AES-256-GCM (government-grade)
- Authenticated (tampering is detected)
- Version-controlled (full history, easy rollback)
- Distributed (every clone has a copy)
Compare to alternatives: secrets in environment variables (leaked in logs), secrets managers (network dependency), or plaintext files (accidentally committed).
- Decrypt secrets with old password
- Re-encrypt with new password
- Commit and push
- Share new password with remaining team (via secure channel)
Old password holders can see old versions (git history), but not new secrets. If you need to invalidate old secrets entirely, rotate your API keys too.
You lose your secrets. There is no recovery mechanism - that's the point of encryption.
Recommendations:
- Store password in a password manager (1Password, Bitwarden)
- Keep offline backup in a secure location
- For teams, ensure multiple people know the password
The -p flag passes the password as a command argument, which may appear in:
- Process listings (
ps aux) - CI/CD logs (if not masked)
- Shell history
Safer alternatives:
# Use environment variable (most CI/CD systems support secrets)
echo "$SECRETS_PASSWORD" | multi-encrypt dec
# Or configure your CI to mask the argument
multi-encrypt dec -p "${{ secrets.DECRYPT_PASSWORD }}"The 600,000 PBKDF2 iterations intentionally slow down key derivation. This means:
- For you: 1 second wait
- For attackers: Billions of years to brute-force
This is a security feature. Fast encryption = fast cracking.
MIT - see LICENSE file for details.