sealed-env
v0.2.0
Published
Encrypted .env files with optional TOTP unsealing for production deploys. Cross-stack with the Java port. One minimal CLI dependency.
Maintainers
Readme
sealed-env
Encrypted
.envfiles with optional TOTP unsealing for production deploys. Cross-stack with the Java port. Zero runtime dependencies.
npm install sealed-envWhy
In 2025, supply-chain attacks on the JavaScript ecosystem stole thousands of plaintext
secrets from CI/CD pipelines and developer machines. The Shai-Hulud worm (Nov 2025)
compromised over 25,000 repositories by scanning .env files and exfiltrating their
contents to public GitHub repos. The tj-actions/changed-files attack (Mar 2025) read
secrets directly from CI Runner memory.
Plaintext .env is dead. And encryption-at-rest alone isn't enough — when the master
key leaks (and it does), the entire vault opens.
sealed-env solves both halves.
What it does
- Encrypts your secrets with AES-256-GCM at rest.
- For production: requires a fresh TOTP code from a human operator before each deploy.
- Even if your CI key leaks, attackers cannot decrypt without the operator's phone.
- Cross-stack: the same
.env.sealedfile works in Node and in Java/Spring Boot. - Zero runtime dependencies. Only Node's built-in
cryptoandfs.
Quick start
1. Initialize
npx sealed-env init --mode basicThis generates a master key and saves it to .env.local (auto-gitignored).
2. Encrypt your existing .env
npx sealed-env encrypt .envYou now have .env.sealed — commit it to your repo.
3. Use in code (auto-load)
import 'sealed-env/config';
console.log(process.env.STRIPE_API_KEY); // resolved from .env.sealed4. (Optional) Use the API directly
import { loadSealed } from 'sealed-env';
const env = loadSealed({ path: '.env.sealed', populate: true });
console.log(env.STRIPE_API_KEY);Three security modes
npx sealed-env init --mode basic # personal projects
npx sealed-env init --mode team # small teams, staging
npx sealed-env init --mode enterprise # production with TOTP unseal| | basic | team | enterprise | |---|:---:|:---:|:---:| | AES-256-GCM | ✓ | ✓ | ✓ | | HMAC integrity tag | — | ✓ | ✓ | | TOTP unseal required | — | — | ✓ | | Deploy-bound tokens | — | — | ✓ |
Enterprise mode: production deploys
# In CI, the deploy job pauses waiting for an operator. Operator runs:
$ npx sealed-env unseal --deploy-id <commit-sha>
> Enter 6-digit TOTP code: 482914
✓ Unseal token (expires in 60s):
usl_eyJhbGciOiJIUzI1NiIsInR5cCI6InNlYWxlZC1lbnYtdW5zZWFsL3YxIn0...
# Operator pastes the token into CI. The deploy continues with:
SEALED_ENV_KEY=...
SEALED_ENV_SIGNING_KEY=...
SEALED_ENV_UNSEAL_TOKEN=usl_...
SEALED_ENV_DEPLOY_ID=<commit-sha>
# Application starts:
node --import sealed-env/config app.jsIf the master key later leaks, attackers still need a fresh TOTP. If a token is captured, it's only valid for that one deploy and expires within seconds.
Comparison
| | sealed-env | dotenvx | dotenv-vault | jasypt | |---|---|---|---|---| | Node + Java with shared format | ✓ | ✗ | ✗ | ✗ | | Zero deps in core crypto | ✓ (1 lazy CLI dep for QR only) | ✗ | ✗ | n/a | | TOTP unseal for production | ✓ | ✗ | ✗ | ✗ | | Memory wipe after ingestion | ✓ | ✗ | ✗ | ✗ | | Public threat model | ✓ | partial | partial | ✗ | | Vendor-neutral (no service) | ✓ | ✓ | ✗ | ✓ |
Status
v0.1.0-alpha — early. API is stabilizing. The .env.sealed v1 format is
frozen and will remain readable forever.
Documentation
License
MIT — David Almeida, 2026.
