EnvSeal v6.3.0

Git for secrets.

One encrypted file for every secret your app refuses to start without — env vars, TLS certificates, service-account JSON, kubeconfigs. Follows your branch. Merges like code. No cloud. No accounts. No shared master password.

Read the docs →
— downloads
— stars
— forks
— releases
~/your-project — envseal
$envseal init --local && envseal git-setup
✓Vault initialized at '.envseal' with recipient: age1…
✓[git] registered merge attributes for '*.envseal'
✓[git] installed pre-commit secret shield
$envseal set DATABASE_URL
value for DATABASE_URL: ••••••••••••••••••••••••
✓sealed 'DATABASE_URL' in group 'project' (local seal)
$envseal set TLS_CERT --file ./certs/server.pem --env-var TLS_CERT_PATH
✓sealed file 'TLS_CERT' in group 'project' (local seal)
$envseal list
# [local]
DATABASE_URL [text]
TLS_CERT [file -> $TLS_CERT_PATH]
$envseal run -- npm start
> app@1.0.0 start
> node server.js
listening on :3000
01 — The problem

If you've built anything with more than one contributor, you've probably lived one of these: a teammate adds a key to their local .env and forgets to send it. Someone pastes a database URL into a Discord DM. You check out a branch and accidentally run it against production. A sleepy git add . scoops up a raw key and it sits in Git history forever.

And the one nobody warns you about: half your secrets aren't strings. The TLS certificate in certs/. The service-account JSON someone sent you at 11pm. The kubeconfig with production cluster access. They don't fit in .env, so they end up on a shared drive, in a wiki attachment, or in a "temporary" commit that quietly became permanent. EnvSeal seals the files too — and injects them at runtime as a private temp path.

None of these are exotic. They're what happens when secrets live outside version control and the code that needs them doesn't.

The fix isn't a better .env file. It's putting your vault somewhere Git already knows how to move. EnvSeal makes your secrets a file in your repo — encrypted for the people who need them, switched by branch, and reviewed in pull requests like anything else you commit.

02 — The workflow

Two columns. That's the whole pitch.

You set up the vault, add your teammate as a recipient, commit, and push. They pull, run their app, and it works. Same keys, same certificates, correct branch, zero DMs.

You Setting up
~/api — bash
$envseal init --local
$envseal git-setup
$envseal set DATABASE_URL
$envseal set TLS_CERT --file ./certs/server.pem
$git add .envseal .gitattributes .gitignore
$git commit -m "seal secrets"
[main 3f9c2a1] seal secrets
$git push
To github.com:you/api.git
a1b2c3d..3f9c2a1 main -> main
# vault travels with the repo.
# no DM. no screenshot. no password.
Teammate Onboarding
~/api — bash
$git clone …
Cloning into 'api'…
$envseal recipient id
age1ql3z7hjy54pw3hyww5ayyf…
# you run, once:
$envseal recipient add @them
$git pull
$envseal run -- npm start
> app@1.0.0 start
> node server.js
listening on :3000
03 — What's inside

Ten decisions that shape everything else.

01
Git-native by design
The vault is a file in your repo. Pull requests, git blame, and history apply to secrets the same way they apply to code.
02
Branch-aware
envseal run reads git rev-parse --abbrev-ref HEAD and applies the tag with the same name, if one exists. No manual swapping.
03
Per-recipient access
The vault is sealed to individual public keys. Adding a teammate is a Git commit, not a shared-key redistribution. Removing one needs a follow-up rotate.
04
Ephemeral runtime
Text secrets are injected into the child process environment and disappear on exit. Sealed files decrypt to a private temp path that is overwritten and deleted when the process ends. No plaintext file is left behind.
05
Pre-commit shield
A hook blocks staged .env files and their variants before they reach Git history. Examples like .env.example and the encrypted vault pass through.
06
Merge driver included
Two people editing secrets on different branches usually resolve silently. Same key, different values? You pick ours or theirs.
07
Interactive editor
envseal edit opens the vault in your $EDITOR, writes only the diff back, and zeroes the temporary plaintext on exit. Bulk edits without dozens of set calls.
08
Global + local vaults
Keep personal tokens in a system-wide vault, then pull them into a project on demand — --override KEY for one key, --with-global for everything.
09
Multiple profiles
Need more than branch tags? Target per-environment vaults with -e staging or -e prod — no extra repos, no extra setup.
10
Files, not just strings
TLS certificates, service-account JSON, .p12 keystores, SSH keys, and kubeconfigs are sealed in the same vault. envseal run decrypts each one to a private temp file, points an env var at it, and overwrites and deletes it when the process exits. envseal get KEY -o PATH writes it back out — new files get 0600 (or 0700 if executable) — when a tool insists on a fixed path. No shared drive. No wiki attachment. No stale copy with production access.
04 — Compare

No secrets tool is perfect for every setup.

Each tool below solves the same problems — accidental commits, environment switching, team access, and the certificate that never fit in a .env — just through a different mechanism. This table shows how each one addresses the same concern, so you can judge which trade-offs fit your team. If you need a dashboard or audit log, use Doppler or Infisical. If you're deep in Kubernetes and cloud KMS, SOPS fits better. EnvSeal is for the team in between.

Plain .env dotenvx Mozilla SOPS EnvSeal Doppler / Infisical
Cost Free Free; Member $5/mo, Business $90/mo Free Free, no account needed Doppler: free for 3 users, $8/mo per additional user. Infisical: free up to 5 identities, Pro $23/identity/mo ($20 annual)
Works offline Yes Yes — local CLI Yes with age. Cloud KMS modes need connectivity Yes, fully Cloud-first; offline use needs a prior cache. Infisical's MIT core is free to self-host; supported air-gapped is Enterprise
Environment / branch switching Manual file swap (.env, .env.staging) Manual -f .env.production at invocation Per-file, driven by CI env vars or shell aliases envseal run reads the current Git branch and applies a matching tag --config flag at runtime, or a project setting in the dashboard
Preventing accidental commits Relies on .gitignore discipline Optional dotenvx precommit hook .gitignore + decrypt only in CI; nothing plaintext is committed Built-in pre-commit shield blocks staged .env files (examples pass) Nothing to commit — secrets never touch the repo
Team onboarding Manual file sharing Share .env.keys (one key per environment) Add each age key to .sops.yaml, run sops updatekeys Add a GitHub handle, or paste an age key Invite via dashboard; SSO available on paid tiers
Merge behavior Git text merge Git text merge on the encrypted file Git text merge on YAML/JSON; conflicts need decrypt → merge → re-encrypt Native merge driver with ours / theirs N/A — cloud-managed, no file to merge
Personal / local overrides .env.local (gitignored) .env.local overrides .env Separate .sops.yaml per developer, or shell env vars Global vault + --override / --with-global Personal configs in dashboard, or --personal flag
Files / certificates Strings only Strings only Encrypts any file, but you wire injection yourself Seals files and injects a private temp path at runtime File/mount support varies by plan and platform
Best suited for Solo hackathon, no real secrets Solo/small teams wanting encrypted .env K8s / GitOps teams on cloud KMS Small-to-mid teams who want env vars and file secrets in Git Orgs wanting GUI, audit logs, and a budget

Verified November 2026. dotenvx does more than a table row can capture — it has real encryption, rotation, and git-diff audit. The architectural difference: dotenvx uses a single shared private key per environment, while EnvSeal seals to individual recipients. SOPS encrypts files well; the gap is that recipient management, merge behavior, and runtime injection are left for you to wire up.

05 — The deal

Read this before you trust any encryption tool.

Most encryption tools hide their limits in the fine print. We put them on the landing page. If any of them is a dealbreaker, EnvSeal is not for you — and that's fine.

Where EnvSeal genuinely wins
  • Git-native by design. The vault is a file in your repo, not a parallel system you have to remember to sync.
  • Free and offline. No accounts, no cloud, no telemetry. Works on a plane or behind a corporate firewall.
  • Branch-aware. Run envseal run on staging and it applies the staging tag automatically — no flag, no copy-paste.
  • Per-recipient access. Adding a teammate is a Git commit, not a shared-key redistribution. Removing one needs a follow-up rotate.
  • Files, not just strings. Certificates, service-account JSON, and kubeconfigs are sealed and injected as private temp paths — not shared as attachments.
  • Global + local vault mixing. Personal tokens stay in a system vault; projects only pull what they need.
Where it's honestly not the best fit
  • No web dashboard, no audit log. If you need to see who accessed what and when, use a cloud tool.
  • No non-technical UI. Everyone on the team touches a terminal. If that's a dealbreaker, look at Doppler or Infisical.
  • You can lock yourself out permanently. Zero-trust means zero recovery. Back up your key.
  • Kubernetes / cloud-KMS shops. SOPS fits your existing pipeline better than bolting EnvSeal on.
06 — Reference

The whole CLI, at a glance.

The commands and flags you'll actually use. If you've used Git, this will feel familiar — every command is a single verb, and most have a natural Git analogue you already know.

Core

envseal init [--local] [--git] [-r RECIPIENT]
Initialize a project-local vault or a global vault. -r adds an initial recipient.
envseal git-setup [--init]
Register the merge driver and pre-commit shield in the current repo.
envseal set [-g GROUP] [-t TAG] [--file PATH] [--env-var VAR] KEY
Store a secret without echoing it, or seal an entire file with --file.
envseal get [-g GROUP] [-t TAG] [--token TOK] [-o PATH] KEY
Print one decrypted value, or write a sealed file back to disk with -o.
envseal run [-g GROUP] [-t TAG] [--token TOK] [--clear] [--override K1,K2 | --with-global] -- CMD (alias: exec)
Run a command with secrets injected into the child process. Mix in global-vault keys with --override or --with-global.
envseal load [-g GROUP] [-t TAG] [--token TOK] [--override K1,K2 | --with-global] [KEYS...]
Print shell export statements for eval. Prefer run.
envseal list [-g GROUP] [-t TAG] (alias: ls)
List key names without revealing values.
envseal edit [-g GROUP] [-t TAG]
Edit key/value pairs in your default editor. Temp file is zeroed on exit.
envseal remove [-g GROUP] [-t TAG] [--force] [KEY] (alias: rm)
Delete a key, tag, or group. --force skips confirmation.
envseal link GROUP
Bind a global vault group to the current working directory.
envseal clear
Wipe cached master keys from the OS keyring session.

Sealed files

envseal set KEY --file PATH [--env-var VAR]
Seal any file up to 10 MB. run injects $VAR as a private temp path; the variable defaults to KEY.
envseal get KEY -o PATH
Write a sealed file back to disk. New files get 0600 (or 0700 if executable); overwriting keeps the existing file's mode. Pipe stdout for raw bytes.

Global vault override

--override KEY1,KEY2
Pull only the named keys from the global vault. Conflicts with --with-global.
--with-global
Overlay all keys from the global vault. Global wins on collision. Conflicts with --override.
--global-group GROUP
Which group inside the global vault to use. Defaults to the directory's linked group.
--global-tag TAG
Optional tag inside that global group. Defaults to base.

Access & CI

envseal recipient id
Print your local public identity key. Share it once with the vault owner.
envseal recipient add <@user | KEY | FILE>
@user fetches GitHub SSH keys. Falls back to a manual age key if they have none.
envseal recipient ls
List all authorized recipient keys.
envseal recipient rm <NAME | INDEX | KEY>
Remove a recipient and re-wrap the vault key. Run rotate to change the data key.
envseal token [-t TAG] [-o PATH] [--exp S] [KEYS...]
Mint a scoped, offline CI token. Limit by tag, by key, and by expiry.
envseal rotate
Rotate the encryption key. Every existing token becomes unreadable.
envseal import [-t TAG] PATH
Import variables from an existing .env file. Original left in place.
envseal export [-t TAG] [-o PATH] [--override K1,K2 | --with-global] [KEYS...]
Decrypt to a .env file with strict 0600 permissions.

Global flags

-e, --env PROFILE
Target a specific profile file. -e staging loads .staging.envseal.
-G, --global
Target the system-wide vault. Comes before the subcommand: envseal --global init.
--no-env
Disable reading fallback identity keys from ENVSEAL_IDENTITY and ENVSEAL_IDENTITY_FILE.

Your secrets already live in the repo.
They're just not encrypted yet.

One vault. One command. No more "does anyone have the new key — or the certificate?"