Encrypted .env Files
Export .env files whose values are encrypted per environment — safe to commit to git, decrypted transparently by varsafe run.
varsafe encrypts .env exports by default, so the file can be committed to git or stored on disk without exposing secrets. varsafe run --env-file decrypts it transparently at injection time.
How It Works
Each environment has a keypair managed by varsafe. When you run varsafe export, each value is individually encrypted with that environment’s public key. The private key is stored server-side, encrypted by KMS — it never exists in plaintext in the database.
Cryptography: ECIES with X25519 key exchange, HKDF-SHA256 key derivation, and AES-256-GCM symmetric encryption. Each value gets its own ephemeral keypair, and each ciphertext is cryptographically bound to the key id and the variable name it sits under.
Quick Start
# Export an encrypted .env file
varsafe export -p my-api -e development -o .env
# The file is safe to commit
git add .env
# Decrypt and inject into a command
varsafe run --env-file .env -- npm run dev
File Format
An encrypted .env file looks like this:
#@varsafe/v2/ek_a1b2c3d4
DATABASE_URL="varsafe:v2:base64url-encoded-ciphertext..."
API_KEY="varsafe:v2:base64url-encoded-ciphertext..."
- The header comment (
#@varsafe/v2/ek_...) identifies the format version and the keypair used for encryption - Each value is prefixed with
varsafe:v2:followed by the encrypted payload - A file whose header names any other version is not read as a sealed file
- The public key is not stored in the file — it’s fetched from the API and cached in the CLI’s encrypted local store
Only the default .env export is encrypted. --format docker, json, and yaml are always plaintext, and --plain disables encryption for .env output.
Workflows
Local Development
# One-time: export encrypted .env
varsafe export -p my-api -e development -o .env
# Daily: run with decryption
varsafe run --env-file .env -- npm run dev
Commit the encrypted .env — new team members clone and run without configuring secrets manually.
CI/CD
In CI, prefer skipping .env files entirely and injecting straight from the API:
varsafe run -i "$VARSAFE_PROJECT_ID" -e production -- ./deploy.sh
When a consumer requires a plaintext file (e.g. docker compose --env-file), export with --plain to RAM-backed tmpfs and delete it in the same command chain:
varsafe export --plain --tmpfs -o app.env
docker compose --env-file /dev/shm/app.env up -d
rm -f /dev/shm/app.env
See CI/CD Pipelines for the full pipeline setup.
Multiple Environments
varsafe export -p my-api -e development -o .env
varsafe export -p my-api -e staging -o .env.staging
# Run with a specific file — pass the environment it was sealed to
varsafe run -e staging --env-file .env.staging -- npm run dev
A sealed file can only be opened by a run that resolves the same environment: the key it names belongs to that environment, and varsafe will not hand it over under another one. Mismatch it and the run fails rather than silently injecting nothing.
Merging Sources
API secrets and env-file secrets can be combined; file values take priority over API values:
varsafe run -p my-api -e development --env-file .env -- npm run dev
Key Rotation
Auto-Rotation on Loss of Access
When someone loses the ability to export environment keys — they are removed from the team, they leave, or they are downgraded out of owner/admin — varsafe automatically rotates the keypair of every environment whose private key that person accessed. Private-key access is tracked in the audit trail, which is what makes this targeted rotation possible.
Rotation does not undo past exposure: files sealed before it still open with the old key, which the departing member may have copied. Treat the secrets in those files as compromised and rotate the secrets themselves, not just the key.
The behavior is a team setting (Key rotation on loss of access in team settings):
- auto (default) — rotate exposed environment keys when someone loses key-export access
- manual — keys stay in place; rotate them yourself
Transparent Re-Encryption
After a rotation, existing encrypted .env files still decrypt: on the next varsafe run --env-file, the CLI detects the stale key, decrypts with the old key, and silently rewrites the file encrypted with the current key (you’ll see Re-encrypted <file> with current key on stderr). No manual migration needed.
Plaintext Export
For cases where encryption isn’t wanted:
# Plaintext .env
varsafe export --plain -o .env
# JSON and YAML are always plaintext
varsafe export -f json -o secrets.json
varsafe export -f yaml -o config.yaml
Security Properties
- At rest: values encrypted with AES-256-GCM; the private key is KMS-encrypted in the database
- Value isolation: each value uses its own ephemeral keypair, so recovering the plaintext of one value tells you nothing about the others
- Context binding: each ciphertext is authenticated against its key id and variable name, so blobs cannot be shuffled between variables or replayed across key generations
- No silent downgrade:
--env-filerequires the file to actually be sealed, so stripping the markers off a committed file fails the run instead of turning it into unauthenticated input - Key exposure tracking: the audit trail records every private-key access, enabling targeted rotation
- Transparent re-encryption: stale files are silently upgraded on next use after a keypair rotates
What it does not give you
- Not forward secrecy. The environment’s long-lived private key decrypts every value ever sealed to it, including files already in git history. The ephemeral keypair per value gives value isolation, not forward secrecy — whoever obtains the environment private key can open every past export sealed under it.
- Not sender authentication. See the warning at the top: sealing needs only the public key.
- Rotation is prospective. Rotating a keypair stops future files from being readable with the old key, but the old key is retained so existing files keep opening — and anyone who already exported the old private key keeps whatever they copied. Rotation limits ongoing exposure; it does not revoke access to files someone already holds.