SecretEnv
How does your team share .env files and certificates?
brew install ebisawa/secretenv/secretenv
"Send me the new API key on Slack" — many teams do this, but it carries serious risks.
Committing .env.example to the repository while each person manages their own actual values.
Dedicated SaaS and cloud Secrets Managers are powerful, but they don't fit every team.
The right direction, but existing tools still have challenges.
SecretEnv is a CLI tool that encrypts .env files, certificates, and other secrets for management in a Git repository. It requires no external services or servers, working entirely offline.
# Initial setup (one-time only)
secretenv init --member-handle alice@example.com
# Encrypt .env and bring it under Git management
secretenv import .env
# Add or update values directly
secretenv set DATABASE_URL "postgres://..."
secretenv set API_KEY "sk-..."
Encrypted files are stored in .secretenv/secrets/ and can be committed to Git as-is. The plaintext .env is no longer needed, eliminating the risk of secret leakage.
# Inject encrypted .env as environment variables and run commands
secretenv run -- docker compose up
secretenv run -- npm start
secretenv run -- rails server
# Retrieve individual values
secretenv get DATABASE_URL
secretenv run injects decrypted values as environment variables into the process. No plaintext is written to disk, preventing leaks from forgotten .env files or accidental commits.
# A new member joins
secretenv join --member-handle bob@example.com
# → Create a PR for the team to review and merge
# An existing member runs rewrap to distribute keys
secretenv rewrap
# → bob can now decrypt
Adding and removing members is represented as file operations in Git. Since changes go through PR review, unauthorized access grants are prevented.
# Remove a member
secretenv member remove alice@example.com
# Re-encrypt files to revoke access
secretenv rewrap
# → Recorded in removed_recipients for access history tracking
SecretEnv records removed member history (removed_recipients) within encrypted files. This makes it easy to identify which secrets need rotation when a member leaves, by showing who previously had access.
SecretEnv's data format encrypts each .env key-value pair individually.
set, only the changed entry and signature appear in the diffrewrap, the encrypted values themselves don't change (only key distribution info is updated)Even though files are encrypted, diffs remain minimal, making it possible to review "what changed" in PRs.
All core features of SecretEnv work entirely locally.
SecretEnv uses no custom cryptographic algorithms — it's built entirely on standardized cryptographic primitives.
Each member's public key is used for individual encryption, so only the private key holder can decrypt. Encryption uses HPKE (RFC 9180), the same standard adopted in web browsers and TLS.
Encrypted files include Ed25519 digital signatures (RFC 8032). Any unauthorized modification to repository files is automatically detected during decryption.
When adding members, identity is verified through attestation signatures with SSH private keys and GitHub API public key matching, preventing injection of fraudulent keys.
The encryption process cryptographically binds "which secret this ciphertext belongs to" and "which key generation was used." Reuse or substitution of ciphertext across different contexts is always detected.
Technical details:
Security Designssh-keygen -t ed25519# Homebrew
brew install ebisawa/secretenv/secretenv
# 1. Initialize the workspace
secretenv init --member-handle alice@example.com
# 2. Encrypt and import your existing .env
secretenv import .env
That's it. From now on, use secretenv run or secretenv get to access your secrets.
Learn more:
User Guidessh-keygen -t ed25519.secretenv import .env imports everything at once. Your existing workflow stays the same — just use secretenv run to execute commands as before.secretenv encrypt / secretenv decrypt.secretenv run and secretenv get work non-interactively, making them easy to integrate into CI pipelines.secretenv rewrap --rotate-key regenerates encryption keys and re-encrypts everything. This supports both member changes and periodic rotation.Want to learn more?
View on GitHub