git-env-vault
v0.6.2
Published
CLI + TUI for managing encrypted environment variables in monorepos with SOPS + age
Downloads
410
Maintainers
Readme
git-env-vault
Encrypted .env secrets in Git for monorepos. SOPS + age, policy-based access control, CLI, and interactive TUI.
Now includes a JS decrypt backend fallback for smoother onboarding (pull/decrypt) while keeping system sops + age as the primary full-featured workflow.
Why git-env-vault
Managing secrets across multiple services and environments is hard:
- Sharing
.envfiles in chat/email is unsafe. - Keeping secrets in CI settings is hard to review and version.
- Onboarding and offboarding access is often manual and error-prone.
- Access revocation usually requires additional manual cleanup.
git-env-vault keeps encrypted secrets next to code while controlling who can decrypt each environment/service.
Key features
- Encrypted secrets stored in Git (
*.sops.yaml). - Service-to-output mapping (
envvault.config.json). - Policy-based recipients (
envvault.policy.json+.sops.yaml). - Safe-by-default diffs (keys only, no values by default).
- Access lifecycle commands (
grant,revoke,updatekeys,rotate). - Local override promotion (
promote,promote-all). - CI validation (
ci-verify). - Interactive terminal UI (
envvault tui).
Requirements
- Node.js
18+ - Git
2+ - SOPS
3.8+(recommended, required for full feature set) - age
1.1+(recommended, required for full feature set)
Installation
# project dependency (recommended)
npm i -D git-env-vault
# global install
npm i -g git-env-vaultRun without installing globally (npx / bunx)
# one-off command (npm)
npx git-env-vault@latest doctor
npx git-env-vault@latest pull --env dev
# one-off command (Bun)
bunx git-env-vault@latest doctor
bunx git-env-vault@latest pull --env devGlobal usage example:
npm i -g git-env-vault
envvault --version
envvault doctorInstall prerequisites by OS (full mode)
Use this if you want the full feature set (edit, set, grant, revoke, updatekeys, rotate).
Windows (winget)
# Node.js LTS + Git
winget install --id OpenJS.NodeJS.LTS -e
winget install --id Git.Git -e
# SOPS + age
winget install --id Mozilla.SOPS -e
winget install --id FiloSottile.age -emacOS (Homebrew)
# Install Homebrew first if needed: https://brew.sh
# Node.js + Git
brew install node git
# SOPS + age
brew install sops ageLinux (Debian / Ubuntu)
sudo apt-get update
sudo apt-get install -y nodejs npm git sops ageLinux (Fedora / RHEL)
sudo dnf install -y nodejs npm git sops ageLinux (Arch)
sudo pacman -S --needed nodejs npm git sops ageGenerate age key (all OS)
# Linux/macOS
mkdir -p ~/.config/sops/age
age-keygen -o ~/.config/sops/age/keys.txt# Windows (PowerShell)
New-Item -ItemType Directory -Force "$env:APPDATA\\sops\\age" | Out-Null
age-keygen -o "$env:APPDATA\\sops\\age\\keys.txt"Quick start (easy mode, no system SOPS required for pull)
1) Install package
npm i -D git-env-vault
# optional: prefer JS backend for basic usage
# set "cryptoBackend": "js" in envvault.config.json2) Create your age key
age-keygen -o ~/.config/sops/age/keys.txt3) Initialize repository config
envvault initGenerated files:
envvault.config.jsonenvvault.policy.json.sops.yamlsecrets/
4) Decrypt secrets (pull)
envvault pull --env devIf system sops is missing, envvault will try the JS backend automatically in cryptoBackend: "auto" mode.
5) Full mode (recommended for teams / production workflows)
Install system tools to enable editing and key management:
# macOS
brew install sops age
# Windows (winget)
winget install --id Mozilla.SOPS -e
winget install --id FiloSottile.age -e
# Linux examples
sudo apt-get install -y sops age
sudo dnf install sops age
sudo pacman -S sops ageUse built-in guidance:
envvault setup
envvault doctor --fix6) Grant access (full mode)
envvault grant --env dev --service api --recipient age1...7) Work with secrets
# interactive mode
envvault tui
# or direct commands
envvault edit --env dev --service api
envvault pull --env dev
envvault pull --env dev --service api --confirm
envvault push --env dev --service api --confirm
envvault diff --env dev --service api
envvault status --env dev8) CI check
envvault ci-verifyCrypto backends
envvault.config.json supports:
{
"cryptoBackend": "auto",
"placeholderPolicy": {
"preserveExistingOnPlaceholder": true,
"patterns": ["__MISSING__", "CHANGEME*", "*PLACEHOLDER*"]
},
"localProtection": {
"global": ["BOT_TOKEN", "TELEGRAM_BOT_TOKEN"],
"services": {
"core-bot": ["BOT_TOKEN"]
}
}
}auto(default): try systemsopsfirst, then JS fallback for supported commandssystem-sops: require systemsopsbinaryjs: force JS backend (basic decrypt/pull only)placeholderPolicy: prevents generated placeholder values (for missing required keys) from overwriting an existing local valuelocalProtection: preserve selected local-only keys duringpull/ protect duringpush
Backend capability matrix
| Command / capability | JS backend (sops-age) | System SOPS |
| --- | --- | --- |
| pull / decrypt | Yes | Yes |
| doctor capability detection | Yes (reported) | Yes |
| edit | No | Yes |
| set | No | Yes |
| grant | No | Yes |
| revoke | No | Yes |
| updatekeys | No | Yes |
| rotate | No | Yes |
Limitations of JS backend
- Intended for decrypt flows (
pull) only. - Does not replace system
sopsfor write/re-encrypt/key-rotation operations. - Output formatting may differ from
sops -dwhen usingdecryptToString. - Best used for onboarding/local read-only workflows; keep system
sops+agefor production maintenance.
Monorepo DX (v0.5.0)
Gitignore management
envvault gitignore check
envvault gitignore fix
envvault gitignore fix --dry-runRefresh / rescan monorepo env files
# preview changes (config/schema only)
envvault refresh --dry-run
envvault sync --dry-run # alias for refresh
# add extra excludes
envvault refresh --dry-run --exclude "apps/legacy/**"
# smarter service naming (dirname|path|fullpath-slug)
envvault refresh --dry-run --name-strategy dirname
# merge into existing config/schema instead of replacing
envvault refresh --dry-run --merge-config --merge-schema
# write config + schema
envvault refresh
# optional: create encrypted secrets snapshots (requires system SOPS)
envvault refresh --write-secretsName strategies:
dirname(default):apps/core-bot/.env->core-botpath: path-based composite name (safer when many duplicate dir names)fullpath-slug: full relative path slug (most collision-resistant)
What refresh updates:
envvault.config.json(servicesmap)envvault.schema.yaml(keys per service)
What refresh does not do:
- does not overwrite local
.envfiles - preserves existing
secretsDir/cryptoBackendconfig settings
Merge modes:
--merge-config: keep existing services and merge discovered ones--merge-schema: keep existing schema services and merge discovered keys
Safe diff + confirm for pull
# preview key changes before writing local .env
envvault pull --env dev --service api --confirm
# machine-readable preview (no write)
envvault pull --env dev --service api --plan
envvault pull --env dev --service api --json
# keep local developer-only token untouched during pull
envvault pull --env dev --service core-bot --confirm --preserve-local BOT_TOKEN
# non-interactive apply (CI/automation)
envvault pull --env dev --service api --confirm --yes
# apply only selected changed keys (comma-separated)
envvault pull --env dev --service api --confirm --select-keys DATABASE_URL,REDIS_URL
# show secret values in diff (unsafe)
envvault pull --env dev --service api --confirm --unsafe-show-valuesBy default, diffs show only key names (added/removed/changed), not values.
pull --plan and pull --json are preview-only and do not write files.
Batch pull (multiple services)
# default behavior (when --service is omitted): process all configured services for the env
envvault pull --env dev
# explicit form (same behavior as above)
envvault pull --env dev --all-services
# wildcard filter
envvault pull --env dev --service-pattern "core-*"
# batch preview (no write)
envvault pull --env dev --service-pattern "core-*" --plan
envvault pull --env dev --all-services --jsonConstraints:
- use only one of
--service,--service-pattern, or--all-services --json/--planare recommended for CI/review automation
Compare local env vs vault (no write)
envvault diff --env dev --service api
envvault diff --env dev --service api --unsafe-show-values
envvault diff --env dev --service api --plan
envvault diff --env dev --service api --jsonPush local .env to encrypted secret (service-level)
envvault push --env dev --service core-bot --dry-run
envvault push --env dev --service core-bot --confirm
envvault push --env dev --service core-bot --confirm --yes
envvault push --env dev --service core-bot --confirm --preserve-local BOT_TOKEN
envvault push --env dev --service core-bot --exclude-keys DEBUG,DEV_ONLY_FLAG
envvault push --env dev --service core-bot --dry-run --jsonNotes:
pushrequires systemsops(JS backend is decrypt/pull only)--preserve-localand configlocalProtectionprevent selected local keys from being pushed into vault--exclude-keysremoves selected local keys from the sync input before encryption
Status (drift overview)
envvault status --env dev
envvault status --env dev --service core-bot
envvault status --env dev --jsonstatus compares local envOutput files with vault secrets and reports key-level drift counts (+/-/~).
Non-interactive flags
pull --confirm --yesset --confirm --yespush --confirm --yes
CI workflows (special CI key + payloads)
Use a dedicated CI key (for example GitHub Actions secret ENVVAULT_CI_KEY) to create a transport payload that CI can decode without direct SOPS usage in the job step.
Create CI payload (local/dev machine or release bot)
From a vault secret:
envvault ci-seal --env dev --service api > ci-api-dev.payloadFrom a plaintext file:
envvault ci-seal --from-file apps/api/.env > ci-api-dev.payloadJSON output (for automation):
envvault ci-seal --env dev --service api --jsonDecode CI payload in CI
# payload can come from CI secret/variable ENVVAULT_CI_BLOB
envvault ci-unseal --out apps/api/.env --validate-dotenvOr pass payload directly:
envvault ci-unseal --payload "$ENVVAULT_CI_BLOB" --out apps/api/.env --validate-dotenvCI verification
ci-verify now also checks for uncommitted .env* changes (for example .env.local) in git status.
envvault ci-verify --allow-unsigned
# bypass dirty .env check only when intentionally needed
envvault ci-verify --allow-unsigned --allow-dirty-envRecommended CI secrets:
ENVVAULT_CI_KEY: symmetric key used byci-seal/ci-unsealENVVAULT_CI_BLOB: encrypted payload generated byci-seal
localProtection behavior (important for BOT tokens)
If a key is listed in localProtection, then:
- during
pull: local value is kept (vault value does not overwrite it) - during
push: local value is not written into encrypted secret
This is useful for developer-specific tokens like BOT_TOKEN.
Placeholder-safe pull behavior (important for local CI/API tokens)
When schema validation adds placeholders for missing required keys (for example __MISSING__), pull will keep an existing local non-empty value instead of overwriting it with a placeholder.
This is useful when:
- real values are injected only in CI via GitHub Actions secrets
- developers already know and configured a local token manually
- you still want schema-required keys to appear as placeholders for newcomers
Example:
- vault/schema would produce
BOT_TOKEN=__MISSING__ - developer already has
BOT_TOKEN=123...locally envvault pullkeeps the localBOT_TOKEN=123...
Config example:
{
"placeholderPolicy": {
"preserveExistingOnPlaceholder": true,
"patterns": ["__MISSING__", "CHANGEME*", "*PLACEHOLDER*", "TODO_*", "<set-me>*"]
}
}Disable if needed:
{
"placeholderPolicy": {
"preserveExistingOnPlaceholder": false
}
}Documentation
- Getting Started
- CLI Reference
- Workflows
- Security Guide
- Configuration
- Security Model
- Troubleshooting
- Optimization Guide
Command overview
Core commands
envvault initenvvault pull --env <env> [--service <service>]envvault push --env <env> --service <service>envvault diff --env <env> --service <service>envvault status --env <env> [--service <service>]envvault refresh [--dry-run] [--write-secrets]envvault sync [--dry-run] [--write-secrets](alias forrefresh)envvault edit --env <env> --service <service>envvault set --env <env> --service <service> KEY=VALUE...envvault doctorenvvault ci-sealenvvault ci-unseal
Access control
envvault grant --env <env> --service <service> --recipient <age-public-key>envvault revoke --env <env> --service <service> --recipient <age-public-key>envvault updatekeys [--env <env>] [--service <service>]envvault rotate --env <env> [--service <service>]
Local overrides
envvault promote --env <env> --service <service> --key <key>envvault promote-all --env <env> --service <service>
Utilities
envvault hooks install --type pre-push|pre-commitenvvault hooks uninstall --type pre-push|pre-commitenvvault hooks statusenvvault gitignore checkenvvault gitignore fix [--dry-run]envvault setupenvvault wizardenvvault up --env <env>envvault ci-verify [--allow-unsigned]envvault tui
Open source readiness check
Core files are present in this repository:
README.mdLICENSECONTRIBUTING.mdCODE_OF_CONDUCT.mdSECURITY.mdSUPPORT.mdCHANGELOG.md.github/ISSUE_TEMPLATE/*.github/PULL_REQUEST_TEMPLATE.md
Development
npm install
npm run build
npm run test
npm run lintCross-platform notes
.sops.yamlcreation rules use a path-separator-safe regex ([\\/]) for Windows/macOS/Linux compatibility.envvault setupprints OS-specific install commands forsops+age.
Support
Maintainer
Maintained by PAS7 Studio.
Funding
License
MIT. See LICENSE.
