Pre-release v0.2.1 is a release candidate — security hardening and release verification are still open. Don't hold real secrets in it yet. See status →
EnvGuardian

A CLI over age(1) — MIT

Commit your team's .env to git — encrypted.

Small teams don't need a secrets manager. They need one file, versioned with the code, readable by exactly the right people. That's the whole tool.

Go 1.24+ Linux · macOS · Windows ~2k SLOC

age-nativeNo custom cryptography
git-reviewableAccess changes in normal PRs
fail-closedUnverifiable means non-zero

The mental model

Three questions decide everything the tool does.

If you know the answers, you can predict exactly what any command will do — and, more importantly, what it will refuse to do.

Who can decrypt?
A short, reviewable list of public keys. Adding or revoking someone is a normal code review.
recipients.toml
What is the secret?
The encrypted bytes. Sealed with age(1) — no crypto invented here.
.env.age
Is it authentic and current?
A detached SSH signature over the ciphertext, plus a lock that binds fingerprints to exact digests.
.env.age.sig · lock.toml

Anatomy

What actually lives in your repo.

Nothing hidden, nothing surprising. A directory of TOML files, an .age blob per environment, and a detached signature. Everything a reviewer needs to look at is a plaintext file already tracked in git.

The one file that never ships is the plaintext. init adds it to .gitignore for you.

  • .envguardian/
  • ├─ config.tomlgit
  • ├─ recipients.tomlgit
  • ├─ lock.tomlgit
  • ├─ rotation.tomlgit
  • └─ auto-decrypt-state.tomllocal only
  •  
  • .env.agegit
  • .env.age.siggit
  • .envgitignored

A day in the life

What using it actually looks like.

From cloning the repo to shipping a new secret, this is the entire flow — no additional CLI to learn, no dashboards to log into.

# You just cloned a repo. Your teammates have already committed .env.age. $ go run ./cmd/envguardian decrypt decrypted .env.age → .env
# You edit .env locally, then re-encrypt. Idempotent — no diff if nothing changed. $ envguardian encrypt encrypted .env → .env.age $ git add .env.age .env.age.sig .envguardian/lock.toml $ git commit -m "feat(config): add API_RATE_LIMIT"
# Someone else pulls. Config changed, so hooks pause — no silent overwrite. $ git pull envguardian: managed inputs changed (recipients.toml, .env.age). review the change, then run: envguardian decrypt --accept-changes $ envguardian decrypt --accept-changes ✓ accepted commit 4e2a1c; decrypted .env.age → .env

Scope discipline

What EnvGuardian isn't.

Deliberately narrow. The whole reason a small tool can be careful and finishable is that it says no to the things it isn't. If you need any of the below, reach for the right side.

Not this

  • A runtime secrets manager
  • Production secret injection
  • A server or SaaS
  • Storage for large or binary secrets
  • Compliance and audit tooling

Use instead

  • HashiCorp Vault, AWS Secrets Manager
  • Your cloud provider's parameter store
  • There is no server. That's the point.
  • Object storage with its own encryption
  • IAM with audit logs

Where the work stands

An honest release status.

v0.1.0 was a development tag with known critical findings. v0.2.1 is a release candidate — the implementation work is done; the launch verification is not. This isn't marketing copy, it's the tracked plan.

→ full plan in docs/PLAN.md

done

Path containment, transactional sealing, fail-closed verification, detached SSH signatures, secret-safe diagnostics, atomic writes.

done

Revocation + rotation ledger, semantic merge driver, multi-file support, ADRs 0001–0008.

done

Coverage gates (80% overall, 85% on crypt / keys / config / dotenv). Every action pin verified.

open

Branch protection: signed commits, required checks, no force-push to main.

Load-bearing for the trust story — set on the GitHub side, not in code.

open

Published-binary verification on each supported OS, then the pre-release banner comes off.

Install today: brew install --cask yehiagewily/tap/envguardian (macOS/Linux) or go install …@v0.2.1.