Multi-backend environment variable and secrets manager with AES-256-GCM encryption
npm install vaulter



Organizes your .env files with structure and best practices.
Powered by dotenv for parsing. Store secrets anywhere: S3, MinIO, R2, or filesystem.
---
``bash`
curl -fsSL https://raw.githubusercontent.com/forattini-dev/vaulter/main/install.sh | shor: npm install -g vaulter
`bash`
vaulter init # Initialize project
vaulter key generate --name master # Generate encryption key
vaulter var set DATABASE_URL="postgres://..." -e dev # Set secret
vaulter var set PORT::3000 -e dev # Set config (plain)
eval $(vaulter export shell -e dev) # Export to shell
---
Vaulter follows a backend-sync workflow where the backend is the source of truth and local overrides are for personal customization.
> Backend is the source of truth. Everything syncs via backend.
| Component | Git Status | Purpose |
|:----------|:-----------|:--------|
| .vaulter/config.yaml | β
Committed | Project configuration |
| .vaulter/local/* | β Gitignored | Personal local overrides |
| *.env files | β Gitignored | Generated outputs |
`
.vaulter/
βββ config.yaml # β
Committed - Project config
βββ local/ # β Gitignored - Personal overrides
β βββ configs.env # Non-sensitive overrides (DEBUG, PORT)
β βββ secrets.env # Sensitive overrides (test API keys)
β βββ services/ # Monorepo per-service overrides
β βββ api/
β βββ configs.env
β βββ secrets.env
βββ dev/ # β Gitignored - Environment data (--dir mode)
βββ configs.env # Shared non-sensitive vars
βββ secrets.env # Shared sensitive vars
βββ services/ # Monorepo service vars
βββ api/
βββ configs.env
βββ secrets.env
apps/web/.env # β Gitignored - Generated output
apps/api/.env # β Gitignored - Generated output
`
Directory modes:
- .vaulter/local/ - Personal overrides (never synced to backend).vaulter/{env}/
- - Environment data (synced with --dir mode)
`gitignore`Vaulter - only commit config.yaml
.vaulter/local/
*.env
.env.*
`bash1. Start: Pull latest from backend + apply your local overrides
vaulter local pull --all
$3
`
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β DEVELOPMENT WORKFLOW β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β LOCAL (.vaulter/local/) βββ Personal only (gitignored) β
β βββ configs.env β
β βββ secrets.env β
β β β
β β merged on vaulter local pull β
β βΌ β
β β
β BACKEND (S3/MinIO) βββ Source of truth (synced) β
β βββ dev/ βββββββββββββββββββββββββββββββββββββββββββββββ β
β β βββ all vars (encrypted) β β
β β β β
β βββ stg/ ββββββββ vaulter clone dev stg βββββββββββββββ€ β
β β βββ all vars (encrypted) β β
β β β β
β βββ prd/ ββββββββ vaulter clone stg prd βββββββββββββββ β
β βββ all vars (encrypted) β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
`$3
New team member setup:
`bash
git clone # Gets .vaulter/config.yaml
export VAULTER_KEY_DEV= # Get key securely from team
vaulter sync pull --dir -e dev # Pull from backend β .vaulter/local/
vaulter local pull --all # Generate .env files (offline)
`Sharing a new variable:
`bash
1. Add locally
vaulter local set NEW_FEATURE::enabled # Shared config2. Push to backend (share with team)
vaulter sync push --dir -e dev3. Notify team
"New var added, run: vaulter sync pull --dir && vaulter local pull --all"
`$3
| Task | Tool |
|:-----|:-----|
| Check health |
vaulter_doctor |
| Pull with overrides | vaulter_local_pull all=true |
| Set shared override | vaulter_local_shared_set key="DEBUG" value="true" |
| Set service override | vaulter_local_set key="PORT" value="3001" service="api" |
| See differences | vaulter_local_diff |
| Clone environment | vaulter_clone_env source="dev" target="stg" |
| Compare environments | vaulter_compare source="dev" target="prd" |---
What is Vaulter?
Vaulter is an opinionated organizer for your environment variables. It uses dotenv under the hood for parsing
.env files - we don't reinvent the wheel, we just add structure.`bash
Install in your project
pnpm add vaulter
or: npm install vaulter
`What vaulter adds on top of dotenv:
| Feature | dotenv | vaulter |
|:--------|:------:|:-------:|
| Parse
.env files | β
| β
(uses dotenv) |
| Organize by environment (dev/stg/prd) | β | β
|
| Separate local vs deploy files | β | β
|
| Auto-detect environment (local/CI/K8s) | β | β
|
| Encrypted remote storage | β | β
|
| Sync between team members | β | β
|
| Export to K8s, Helm, Terraform | β | β
|Philosophy: Your local
.env stays local (gitignored). Configs are committed. Secrets are encrypted in your own storage.---
Quick Start (Local Development)
`typescript
// app.ts
import { config } from 'vaulter'config() // Loads from .vaulter/local/ (configs.env + secrets.env)
``bash
Run commands with env vars loaded
npx vaulter run -- pnpm devOr pull from backend first
vaulter local pull --all
`That's it! For most local development, vaulter is just a structured dotenv.
---
π©Ί Health Check - Doctor
Always start with
vaulter doctor to diagnose your setup:`bash
vaulter doctor -e dev
`Doctor performs 16 comprehensive checks:
| Check | What It Does |
|-------|--------------|
| β
Connection | Tests backend connectivity |
| β
Latency | Measures operation speed |
| β
Permissions | Validates read/write/delete access |
| β
Encryption | Tests encrypt β decrypt round-trip |
| β
Sync Status | Compares local vs remote |
| β
Security | Detects .env in git, weak keys |
| β
Perf Config | Suggests cache/warmup/concurrency tuning |
| β
+9 more | Config, project, environment, backend, keys, etc. |
Example output:
`
β ok: 14 | β warn: 1 | β fail: 1β connection: connected (24 vars in dev)
β latency: read=45ms, list=67ms
β permissions: read/write/delete OK
β encryption: round-trip successful
β sync-status: 5 local-only, 3 remote-only, 2 conflicts
β security: 2 .env files tracked in git
β Add to .gitignore immediately
`When to use:
- π Initial setup - validate configuration
- π Debugging - identify root cause
- π Pre-deploy - ensure everything is synced
- π Routine - weekly health check
For AI Agents: Call
vaulter_doctor once at the start of a new session (or when operations fail / environments change) to understand the current state before performing sensitive operations.See docs/DOCTOR.md for complete guide.
---
Commands
$3
| Command | Description |
|:--------|:------------|
|
init | Initialize project config |
| init --split | Initialize with split mode (configs/secrets dirs) |$3
| Command | Description |
|:--------|:------------|
|
var get | Get a variable |
| var set KEY=val -e | Set secret (encrypted) |
| var set KEY::val -e | Set config (plain text) |
| var set KEY:=123 -e | Set typed secret (number/boolean) |
| var delete | Delete a variable |
| var list -e | List all variables |Set syntax:
= encrypted secret Β· :: plain config Β· := typed secret$3
| Command | Description |
|:--------|:------------|
|
sync merge -e | Bidirectional merge (default) |
| sync pull -e | Download from backend to outputs |
| sync pull --dir -e | Download to .vaulter/{env}/ directory |
| sync push -e | Upload .env file to backend |
| sync push --dir -e | Upload .vaulter/{env}/ directory to backend |
| sync push --prune -e | Upload, delete remote-only vars |
| sync diff -e | Show differences without changes |$3
| Command | Description |
|:--------|:------------|
|
export shell -e | Export for shell eval $(...) |
| export k8s-secret -e | Generate Kubernetes Secret |
| export k8s-configmap -e | Generate Kubernetes ConfigMap |
| export helm -e | Generate Helm values.yaml |
| export terraform -e | Generate Terraform .tfvars |
| export docker -e | Docker env-file format |
| export vercel -e | Vercel environment JSON |
| export github-actions -e | GitHub Actions secrets |$3
| Command | Description |
|:--------|:------------|
|
services list | List discovered services |
| services | Same as services list |$3
| Command | Description |
|:--------|:------------|
|
audit list -e | List audit entries |
| audit stats -e | Show statistics |
| rotation list -e | Check rotation status |
| rotation run -e | CI/CD gate for overdue secrets |$3
| Command | Description |
|:--------|:------------|
|
key generate --name | Generate symmetric key |
| key generate --env | Generate key for specific environment |
| key generate --name | Generate RSA/EC key pair |
| key list | List all keys |
| key export --name | Export encrypted bundle |
| key import -f | Import encrypted bundle |
| key backup -o | Backup keys to encrypted bundle |
| key restore -f | Restore keys from backup bundle |$3
| Command | Description |
|:--------|:------------|
|
run -- | Execute command with auto-loaded env vars |
| run -e prd -- | Execute with specific environment |
| run -s api -- | Execute with service-specific vars (monorepo) |
| run --verbose -- | Show which files were loaded |
| run --dry-run -- | Preview without executing |Examples:
`bash
Local development
npx vaulter run -- pnpm devCI/CD build with production vars
npx vaulter run -e prd -- pnpm buildMonorepo service
npx vaulter run -e dev -s api -- pnpm start
`The
run command auto-detects the environment (local, CI, K8s) and loads the appropriate files before executing your command.> Run
vaulter --help or vaulter for all options.---
Security
Every secret is encrypted before leaving your machine using AES-256-GCM.
$3
`bash
vaulter key generate --name master
`$3
For CI/CD separation: public key encrypts, private key decrypts.
`bash
vaulter key generate --name master --asymmetric # RSA-4096
vaulter key generate --name master --asym --alg ec-p256 # EC P-256
``yaml
.vaulter/config.yaml
encryption:
mode: asymmetric
asymmetric:
algorithm: rsa-4096
key_name: master # ~/.vaulter/projects//keys/master[.pub]
`CI/CD: Give CI only the public key (can write, can't read). Production gets the private key.
$3
Use different encryption keys for each environment (dev, stg, prd). This provides complete isolation - production secrets can't be decrypted with dev keys.
`bash
Generate keys for each environment
vaulter key generate --env dev
vaulter key generate --env stg
vaulter key generate --env prd
`Keys are stored in
~/.vaulter/projects/{project}/keys/{env}.Key Resolution Order (per environment):
| Priority | Source | Example |
|:---------|:-------|:--------|
| 1 | Env var
VAULTER_KEY_{ENV} | VAULTER_KEY_PRD=my-secret |
| 2 | Config encryption.keys.{env} | See below |
| 3 | File keys/{env} | ~/.vaulter/projects/myapp/keys/prd |
| 4 | Env var VAULTER_KEY | Global fallback |
| 5 | Config encryption.key_source | Default config |
| 6 | File keys/master | Default fallback |Per-environment config:
`yaml
.vaulter/config.yaml
encryption:
keys:
dev:
source:
- env: VAULTER_KEY_DEV
- file: ~/.vaulter/projects/myapp/keys/dev
prd:
source:
- env: VAULTER_KEY_PRD
mode: asymmetric # Optional: different mode per env
`Multi-app isolation:
`
~/.vaulter/projects/
βββ app-landing/keys/
β βββ dev # app-landing dev key
β βββ stg # app-landing stg key
β βββ prd # app-landing prd key
βββ app-api/keys/
β βββ dev # app-api dev key (DIFFERENT from app-landing)
β βββ prd
βββ svc-auth/keys/
βββ prd
`Each app has completely isolated secrets -
app-landing/prd keys cannot decrypt app-api/prd secrets.$3
In monorepos, shared variables need a consistent encryption key. Use
shared_key_environment to specify which environment's key encrypts shared vars:`yaml
.vaulter/config.yaml
encryption:
shared_key_environment: dev # Use dev key for all shared vars
keys:
dev:
source:
- env: VAULTER_KEY_DEV
prd:
source:
- env: VAULTER_KEY_PRD
`Why this matters:
- Shared vars (
__shared__ service) need ONE key to encrypt/decrypt
- Without shared_key_environment, vaulter uses the current environment's key
- This can cause issues when different environments have different keysExample flow:
`bash
Set shared var (uses dev key because shared_key_environment: dev)
vaulter var set LOG_LEVEL=debug -e dev --sharedRead shared var from prd (still uses dev key for shared vars)
vaulter var list -e prd --shared # Works! Uses dev key for shared
`---
Configuration
`yaml
.vaulter/config.yaml
version: "1"
project: my-projectbackend:
url: s3://bucket/envs?region=us-east-1
encryption:
key_source:
- env: VAULTER_KEY
- file: .vaulter/.key
rotation:
enabled: true
interval_days: 90
patterns: ["_KEY", "_SECRET", "*_TOKEN"]
environments: [dev, stg, prd]
default_environment: dev
audit:
enabled: true
retention_days: 90
Local development files (see "Local vs Deploy Structure" below)
local: .vaulter/local/
CI/CD deploy files (see "Local vs Deploy Structure" below)
deploy: .vaulter/deploy/
`$3
| Provider | URL |
|:---------|:----|
| AWS S3 |
s3://bucket/path?region=us-east-1 |
| MinIO | http://KEY:SECRET@localhost:9000/bucket |
| Cloudflare R2 | https://KEY:SECRET@ACCOUNT.r2.cloudflarestorage.com/bucket |
| DigitalOcean | https://KEY:SECRET@nyc3.digitaloceanspaces.com/bucket |
| FileSystem | file:///path/to/storage |$3
Vaulter separates local development from deployment configurations:
`
.vaulter/
βββ config.yaml
βββ local/ # Developer machine (gitignored)
β βββ configs.env # Non-sensitive (sensitive=false)
β βββ secrets.env # Sensitive (sensitive=true)
β βββ services/ # Monorepo only
β βββ /
β βββ configs.env
β βββ secrets.env
βββ deploy/ # CI/CD pipelines
βββ configs/ # Committed to git
β βββ dev.env
β βββ stg.env
β βββ prd.env
βββ secrets/ # Gitignored, pulled from backend
βββ dev.env
βββ prd.env
`Why this structure:
| Location | Purpose | Git | Contains |
|:---------|:--------|:----|:---------|
|
local/configs.env | Developer's machine | Ignored | Non-sensitive local vars |
| local/secrets.env | Developer's machine | Ignored | Sensitive local secrets |
| deploy/configs/*.env | CI/CD configs | Committed | Non-sensitive (PORT, HOST, LOG_LEVEL) |
| deploy/secrets/*.env | CI/CD secrets | Ignored | Pulled via vaulter sync pull |Gitignore:
`gitignore
Local development
.vaulter/local/configs.env
.vaulter/local/secrets.env
.vaulter/local/services/Deploy secrets (pulled in CI)
.vaulter/deploy/secrets/
`---
CI/CD
$3
Use the official Vaulter GitHub Action for seamless CI/CD integration:
`yaml
- uses: forattini-dev/vaulter@v1
id: secrets
with:
backend: s3://my-bucket/secrets
project: my-app
environment: prd
outputs: env,k8s-secret
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
VAULTER_PASSPHRASE: ${{ secrets.VAULTER_PASSPHRASE }}
`#### Output Formats
| Output | File | Use Case |
|:-------|:-----|:---------|
|
env | .env | Docker, Node.js |
| json | vaulter-vars.json | Custom scripts |
| k8s-secret | k8s-secret.yaml | kubectl apply |
| k8s-configmap | k8s-configmap.yaml | kubectl apply |
| helm-values | helm-values.yaml | helmfile, helm |
| tfvars | terraform.auto.tfvars | terraform, terragrunt |
| shell | vaulter-env.sh | source in scripts |#### Full Examples
kubectl:
`yaml
- uses: forattini-dev/vaulter@v1
with:
backend: ${{ secrets.VAULTER_BACKEND }}
project: my-app
environment: prd
outputs: k8s-secret,k8s-configmap
k8s-namespace: my-namespace
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
VAULTER_PASSPHRASE: ${{ secrets.VAULTER_PASSPHRASE }}- run: |
kubectl apply -f k8s-secret.yaml
kubectl apply -f k8s-configmap.yaml
`Helmfile:
`yaml
- uses: forattini-dev/vaulter@v1
with:
backend: ${{ secrets.VAULTER_BACKEND }}
project: my-app
environment: prd
outputs: helm-values
helm-values-path: ./helm/secrets.yaml
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
VAULTER_PASSPHRASE: ${{ secrets.VAULTER_PASSPHRASE }}- run: helmfile -e prd apply
`Terraform/Terragrunt:
`yaml
- uses: forattini-dev/vaulter@v1
with:
backend: ${{ secrets.VAULTER_BACKEND }}
project: infra
environment: prd
outputs: tfvars
# .auto.tfvars is loaded automatically by Terraform!
tfvars-path: ./secrets.auto.tfvars
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
VAULTER_PASSPHRASE: ${{ secrets.VAULTER_PASSPHRASE }}- run: terragrunt apply -auto-approve
`Docker Build:
`yaml
- uses: forattini-dev/vaulter@v1
with:
backend: ${{ secrets.VAULTER_BACKEND }}
project: my-app
environment: prd
outputs: env
env-path: .env.production
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
VAULTER_PASSPHRASE: ${{ secrets.VAULTER_PASSPHRASE }}- run: docker build --secret id=env,src=.env.production -t app .
`Export to GITHUB_ENV:
`yaml
- uses: forattini-dev/vaulter@v1
with:
backend: ${{ secrets.VAULTER_BACKEND }}
project: my-app
environment: prd
export-to-env: true # Makes vars available in subsequent steps
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
VAULTER_PASSPHRASE: ${{ secrets.VAULTER_PASSPHRASE }}- run: echo "Database is $DATABASE_URL" # Available!
`#### Action Inputs
| Input | Required | Default | Description |
|:------|:--------:|:--------|:------------|
|
backend | β | - | S3 connection string |
| project | β | - | Project name |
| environment | β | - | Environment (dev/stg/prd) |
| service | | - | Service (monorepo) |
| outputs | | env | Comma-separated outputs |
| k8s-namespace | | default | K8s namespace |
| export-to-env | | false | Export to GITHUB_ENV |
| mask-values | | true | Mask secrets in logs |#### Action Outputs
| Output | Description |
|:-------|:------------|
|
env-file | Path to .env file |
| k8s-secret-file | Path to K8s Secret YAML |
| k8s-configmap-file | Path to K8s ConfigMap YAML |
| helm-values-file | Path to Helm values file |
| tfvars-file | Path to .tfvars file |
| vars-count | Number of variables |
| vars-json | JSON array of variable names |$3
You can also use the CLI directly:
`yaml
- name: Pull and build
env:
VAULTER_PASSPHRASE: ${{ secrets.VAULTER_PASSPHRASE }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
run: |
npx vaulter sync pull -e prd
npx vaulter run -e prd -- pnpm build
`$3
`bash
GitLab CI
npx vaulter run -e ${CI_ENVIRONMENT_NAME} -- pnpm buildDocker (build with secrets)
npx vaulter run -e prd -- docker build -t myapp .Terraform
vaulter export terraform -e prd > secrets.auto.tfvarsHelm
vaulter export helm -e prd | helm upgrade myapp ./chart -f -Direct export
npx vaulter export k8s-secret -e prd | kubectl apply -f -
`$3
When running in Kubernetes, env vars are already injected via ConfigMap/Secret. Vaulter's
config() function automatically skips loading when it detects K8s:`typescript
import { config } from 'vaulter'config() // Skips in K8s, loads files elsewhere
`Detection:
KUBERNETES_SERVICE_HOST environment variable is set by K8s automatically.---
Monorepo Support
Auto-detects NX, Turborepo, Lerna, pnpm, Yarn workspaces, Rush.
`bash
vaulter service list # List discovered services
vaulter sync push -e dev -s api # Push specific service
vaulter sync push -e dev --shared # Push shared variables
vaulter export shell -e dev -s api # Export with shared inheritance
vaulter export shell -e dev --shared # Export only shared variables
`$3
When exporting for a specific service, shared variables are automatically included:
`bash
Shared vars: NODE_ENV=development, LOG_LEVEL=debug
API service vars: PORT=3000, LOG_LEVEL=info
vaulter export shell -e dev -s api
Output: NODE_ENV=development, LOG_LEVEL=info, PORT=3000
(service vars override shared vars with same key)
To export without inheritance:
vaulter export shell -e dev -s api --no-shared
`---
Output Targets (Multi-Framework)
One config β multiple
.env files. Works with any framework: Next.js, NestJS, Express, NX, Turborepo, etc.$3
Different apps need different variables in different places:
`
apps/
βββ web/ # Next.js needs .env.local with NEXT_PUBLIC_*
βββ api/ # NestJS needs .env with DATABASE_, JWT_
βββ admin/ # Needs everything
`$3
Define outputs once, pull everywhere:
`yaml
.vaulter/config.yaml
outputs:
web:
path: apps/web
filename: .env.local
include: [NEXT_PUBLIC_*] # Only public vars api:
path: apps/api
include: [DATABASE_, JWT_] # Only backend vars
exclude: [*_DEV] # No dev-only vars
admin: apps/admin # Shorthand: all vars
Shared across all outputs (inherited automatically)
shared:
include: [NODE_ENV, LOG_LEVEL, SENTRY_*]
`$3
`bash
Pull to all outputs at once
vaulter sync pull --allResult:
β web: apps/web/.env.local (5 vars)
β api: apps/api/.env (12 vars)
β admin: apps/admin/.env (25 vars)
`$3
`bash
Pull only web
vaulter sync pull --output webPreview without writing
vaulter sync pull --all --dry-run
`$3
`
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Backend (S3) β
β DATABASE_URL, JWT_SECRET, NEXT_PUBLIC_API, LOG_LEVEL β
ββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββ
β
vaulter sync pull --all
β
βββββββββββββββββΌββββββββββββββββ
βΌ βΌ βΌ
ββββββββββββ ββββββββββββ ββββββββββββ
β web β β api β β admin β
β .env.localβ β .env β β .env β
β β β β β β
β LOG_LEVELβ β LOG_LEVELβ β LOG_LEVELβ β shared (inherited)
β NEXT_* β β DATABASE_β β ALL VARS β β filtered by include/exclude
ββββββββββββ β JWT_* β ββββββββββββ
ββββββββββββ
`$3
| Pattern | Matches |
|:--------|:--------|
|
NEXT_PUBLIC_* | NEXT_PUBLIC_API_URL, NEXT_PUBLIC_GA_ID |
| *_SECRET | JWT_SECRET, API_SECRET |
| DATABASE_* | DATABASE_URL, DATABASE_HOST |
| *_URL | API_URL, DATABASE_URL, REDIS_URL |$3
By default,
shared.include vars are added to ALL outputs. Override with inherit: false:`yaml
outputs:
isolated-app:
path: apps/isolated
inherit: false # No shared vars
include: [ISOLATED_*]
`---
Local Overrides (Dev Environment) - OFFLINE FIRST
vaulter local pull is 100% OFFLINE - no backend calls!Works entirely from local files in
.vaulter/local/. Perfect for local development where you want to work offline and sync later.$3
| Command | What it does | Backend? |
|---------|--------------|----------|
|
vaulter local pull --all | Generate .env files from local | β OFFLINE |
| vaulter local push --all | Send local β backend | β
Backend |
| vaulter local sync | Download backend β local | β
Backend |$3
`
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β LOCAL DEVELOPMENT β
β 1. Edit .vaulter/local/*.env β
β 2. vaulter local pull --all β Generate .env β
β 3. Develop... β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SHARE WITH TEAM β
β vaulter local push --all β Upload to backend β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β NEW TEAM MEMBER β
β 1. git clone β
β 2. vaulter local sync β Download from backend β
β 3. vaulter local pull --all β Generate .env β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
`$3
`
.vaulter/local/
βββ configs.env # Shared configs (all services)
βββ secrets.env # Shared secrets (all services)
βββ services/ # Monorepo only
βββ web/
β βββ configs.env # web-specific configs
β βββ secrets.env # web-specific secrets
βββ api/
βββ configs.env
βββ secrets.env
`$3
Priority:
shared vars < service-specific varsFor each output target, vaulter merges:
1. Shared vars from
.vaulter/local/{configs,secrets}.env
2. Service-specific vars from .vaulter/local/services/{service}/*.envExample:
- 20 shared vars + 3 service-specific = 23 vars for that service
- NOT all vars from all services merged together!
$3
`bash
=== EDIT LOCALLY ===
vaulter local set --shared DEBUG::true # shared config
vaulter local set --shared API_KEY=xxx # shared secret
vaulter local set PORT::3001 -s web # service config
vaulter local set DB_URL=xxx -s api # service secret=== GENERATE .ENV FILES [OFFLINE] ===
vaulter local pull --all
Output: "svc-auth: 23 vars (21 shared + 2 service)"
=== SHARE WITH TEAM ===
vaulter local push --all # Upload entire structure=== GET TEAM'S CHANGES ===
vaulter local sync # Download from backend
vaulter local pull --all # Generate .env files=== OTHER ===
vaulter local diff # Show differences
vaulter local status # Show summary
`$3
Vaulter uses section-aware mode by default when writing
.env files. This preserves your custom variables while managing backend variables separately.`env
Your variables (NEVER touched by vaulter)
MY_LOCAL_VAR=something
CUSTOM_DEBUG=true
MY_PORT_OVERRIDE=3001--- VAULTER MANAGED (do not edit below) ---
DATABASE_URL=postgres://...
API_KEY=sk-xxx
NODE_ENV=production
--- END VAULTER ---
`How it works:
| Location | Behavior |
|:---------|:---------|
| Above marker | Preserved - your custom vars, never modified |
| Between markers | Managed - vaulter controls this section |
| Below end marker | Preserved - any trailing content |
CLI options:
`bash
Section-aware pull (default)
vaulter local pull --allOverwrite entire file (ignores sections)
vaulter local pull --all --overwrite
`Programmatic API:
`typescript
import {
syncVaulterSection,
getUserVarsFromEnvFile,
setInEnvFile
} from 'vaulter'// Sync only the managed section (preserves user vars)
syncVaulterSection('/app/.env', {
DATABASE_URL: 'postgres://...',
API_KEY: 'sk-xxx'
})
// Read only user-defined vars (above the marker)
const userVars = getUserVarsFromEnvFile('/app/.env')
// { MY_LOCAL_VAR: 'something', CUSTOM_DEBUG: 'true' }
// Add var to user section (above marker)
setInEnvFile('/app/.env', 'MY_VAR', 'value', true)
`Use cases:
- Local debugging: Add
DEBUG=true above the marker, it stays after vaulter local pull
- Port conflicts: Override PORT=3001 locally without affecting teammates
- Feature flags: Test with FEATURE_X_ENABLED=true without touching backend
- Framework vars: Keep .env.local compatible with Next.js/Vite expectations---
API Usage
`typescript
import { VaulterClient, loadConfig } from 'vaulter'const client = new VaulterClient({ config: loadConfig() })
await client.connect()
await client.set({ key: 'API_KEY', value: 'sk-xxx', project: 'my-project', environment: 'prd' })
const value = await client.get('API_KEY', 'my-project', 'prd')
const vars = await client.list({ project: 'my-project', environment: 'prd' })
await client.disconnect()
`$3
The
config() function auto-detects your environment and loads the appropriate files:`typescript
import { config } from 'vaulter'// Auto-detect environment and load appropriate files
const result = config()
// With options
config({
mode: 'auto', // 'auto' | 'local' | 'deploy' | 'skip'
environment: 'dev', // Override environment (dev, stg, prd)
service: 'api', // For monorepo service-specific vars
verbose: true, // Debug output
})
`Environment Detection:
| Environment | Detection | Behavior |
|:------------|:----------|:---------|
| Kubernetes |
KUBERNETES_SERVICE_HOST set | Skip loading (vars injected via ConfigMap/Secret) |
| CI/CD | CI=true, GITHUB_ACTIONS, etc. | Load from .vaulter/deploy/ |
| Local | Default | Load from .vaulter/local/ (configs.env + secrets.env) |Why this matters:
- K8s: Env vars are already injected, no file loading needed
- CI/CD: Uses committed configs + secrets pulled from backend
- Local: Developer's machine-specific
.env file (gitignored)$3
`typescript
import 'vaulter/load' // Auto-loads .env into process.env
`---
Runtime Loader (No .env Files)
Load secrets directly from the backend at application startup - no .env files, no Kubernetes ConfigMaps/Secrets needed.
$3
`typescript
// Option 1: Simple import (like dotenv/config)
import 'vaulter/load'// Option 2: Side-effect import with full path
import 'vaulter/runtime/load'
// Option 3: With options
import { loadRuntime } from 'vaulter'
await loadRuntime({
environment: 'prd',
service: 'api', // Optional: for monorepos
required: true // Default: true in prd, false otherwise
})
// Option 4: Using config() with backend source
import { config } from 'vaulter'
await config({ source: 'backend' })
// Now process.env has all your secrets!
console.log(process.env.DATABASE_URL)
`$3
1. Reads
.vaulter/config.yaml to find backend URL
2. Loads encryption key (per-environment support)
3. Fetches secrets from S3/MinIO backend
4. Populates process.env$3
`yaml
.vaulter/config.yaml
project: my-app
backend:
url: s3://my-bucket/secretsRuntime detects environment from NODE_ENV or config
default_environment: dev
`$3
| Option | Type | Default | Description |
|:-------|:-----|:--------|:------------|
|
environment | string | NODE_ENV or dev | Target environment |
| project | string | from config | Project name |
| service | string | - | Service name (monorepo) |
| required | boolean | true in prd | Throw on failure |
| override | boolean | false | Override existing env vars |
| includeShared | boolean | true | Include shared vars (monorepo) |
| filter.include | string[] | [] | Glob patterns to include |
| filter.exclude | string[] | [] | Glob patterns to exclude |
| verbose | boolean | false | Enable logging |$3
Runtime loader automatically uses per-environment keys:
`bash
Set env-specific keys
export VAULTER_KEY_DEV="dev-secret"
export VAULTER_KEY_PRD="prd-secret"Or generate key files
vaulter key generate --env prd
`$3
Kubernetes without ConfigMaps/Secrets:
`yaml
deployment.yaml - No configMapRef/secretRef needed!
env:
- name: VAULTER_KEY_PRD
valueFrom:
secretKeyRef:
name: vaulter-key
key: prd
- name: VAULTER_BACKEND
value: "s3://my-bucket/secrets"
``typescript
// app.ts - Secrets loaded at startup
import 'vaulter/runtime/load'
// process.env.DATABASE_URL is now available
`Lambda/Serverless:
`typescript
import { loadRuntime } from 'vaulter'export const handler = async (event) => {
await loadRuntime({ environment: 'prd' })
// Use secrets from process.env
}
`$3
`typescript
import { loadRuntime, isRuntimeAvailable, getRuntimeInfo } from 'vaulter'// Check if runtime config exists
if (isRuntimeAvailable()) {
const info = await getRuntimeInfo()
console.log(info.project, info.environment, info.backend)
}
// Load with callbacks
const result = await loadRuntime({
environment: 'prd',
onLoaded: (r) => console.log(
Loaded ${r.varsLoaded} vars in ${r.durationMs}ms),
onError: (e) => console.error('Failed:', e.message)
})
`---
MCP Server
Claude AI integration via Model Context Protocol. 53 tools, 6 resources, 12 prompts.
`bash
vaulter mcp
`$3
`json
{
"mcpServers": {
"vaulter": {
"command": "vaulter",
"args": ["mcp", "--cwd", "/path/to/project"]
}
}
}
`$3
| Category | Tools |
|:---------|:------|
| Core (5) |
vaulter_get, vaulter_set, vaulter_delete, vaulter_list, vaulter_export |
| Batch (3) | vaulter_multi_get, vaulter_multi_set, vaulter_multi_delete |
| Sync (3) | vaulter_sync, vaulter_pull, vaulter_push |
| Analysis (2) | vaulter_compare, vaulter_search |
| Status (2) | vaulter_status, vaulter_audit_list |
| K8s (2) | vaulter_k8s_secret, vaulter_k8s_configmap |
| IaC (2) | vaulter_helm_values, vaulter_tf_vars |
| Keys (6) | vaulter_key_generate, vaulter_key_list, vaulter_key_show, vaulter_key_export, vaulter_key_import, vaulter_key_rotate |
| Monorepo (5) | vaulter_init, vaulter_scan, vaulter_services, vaulter_shared_list, vaulter_inheritance_info |
| Categorization (1) | vaulter_categorize_vars |
| Dangerous (1) | vaulter_nuke_preview |
| Utility (4) | vaulter_copy, vaulter_rename, vaulter_promote_shared, vaulter_demote_shared |
| Local Overrides (8) | vaulter_local_pull, vaulter_local_set, vaulter_local_delete, vaulter_local_diff, vaulter_local_status, vaulter_local_shared_set, vaulter_local_shared_delete, vaulter_local_shared_list |
| Snapshot (3) | vaulter_snapshot_create, vaulter_snapshot_list, vaulter_snapshot_restore |
| Versioning (3) | vaulter_list_versions, vaulter_get_version, vaulter_rollback |
| Diagnostic (3) | vaulter_doctor, vaulter_clone_env, vaulter_diff |$3
Static data views (no input required). For actions with parameters, use tools.
| URI | Description |
|:----|:------------|
|
vaulter://instructions | Read first! How vaulter stores data (s3db.js architecture) |
| vaulter://tools-guide | Which tool to use for each scenario |
| vaulter://monorepo-example | Complete monorepo isolation example with var counts |
| vaulter://mcp-config | MCP settings sources (priority chain) |
| vaulter://config | Project configuration (YAML) |
| vaulter://services | Monorepo services list |$3
Pre-configured workflows for common tasks.
| Prompt | Description |
|:-------|:------------|
|
setup_project | Initialize new vaulter project |
| migrate_dotenv | Migrate existing .env files |
| deploy_secrets | Deploy to Kubernetes |
| compare_environments | Compare dev vs prd |
| security_audit | Audit secrets for issues |
| rotation_workflow | Check/rotate/report on rotation |
| shared_vars_workflow | Manage monorepo shared vars |
| batch_operations | Multi-set/get/delete operations |
| copy_environment | Copy variables between environments |
| sync_workflow | Sync local files with remote backend |
| monorepo_deploy | Complete monorepo setup with isolation |
| local_overrides_workflow | Manage local dev overrides (shared + service) |> Full MCP documentation: See docs/MCP.md for complete tool reference with parameters.
---
Shell (Interactive Terminal)
`bash
vaulter shell # Secrets Explorer (default)
vaulter shell menu # Menu
vaulter shell audit # Audit log viewer
vaulter shell keys # Key manager
`$3
Shows local
.env sync status vs backend:| Icon | Status | Meaning |
|:----:|:-------|:--------|
|
β | synced | Local value = backend value |
| β | modified | Local value differs from backend |
| β | missing | Exists in backend, not in local .env |
| + | local-only | Exists only in local .env |$3
Global:
q quit Β· ESC back Β· ββ navigate| Screen | Shortcuts |
|:-------|:----------|
| Menu |
1 2 3 quick access to screens |
| Explorer | r refresh Β· v toggle values Β· Tab cycle env Β· j/k vim nav |
| Audit | o filter op Β· s filter source Β· / search Β· c clear |
| Keys | r refresh Β· c toggle config |---
Pre-built Binaries
Download from Releases:
| Platform | Binary |
|:---------|:-------|
| Linux x64/ARM64 |
vaulter-linux-x64, vaulter-linux-arm64 |
| macOS x64/ARM64 | vaulter-macos-x64, vaulter-macos-arm64 |
| Windows x64 | vaulter-win-x64.exe` |---
MIT Β© Forattini