Package Exports
- vaulter
- vaulter/load
- vaulter/loader
- vaulter/package.json
- vaulter/runtime
- vaulter/runtime/load
Readme
vaulter
Multi-Backend Environment & Secrets Manager
Organizes your .env files with structure and best practices.
Powered by dotenv for parsing. Store secrets anywhere: S3, MinIO, R2, or filesystem.
Installation
curl -fsSL https://raw.githubusercontent.com/forattini-dev/vaulter/main/install.sh | sh
# or: npm install -g vaulterQuick Start
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π Development Workflow
Vaulter follows a backend-sync workflow where the backend is the source of truth and local overrides are for personal customization.
The Golden Rule
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 |
Directory Structure
.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 outputDirectory modes:
.vaulter/local/- Personal overrides (never synced to backend).vaulter/{env}/- Environment data (synced with--dirmode)
.gitignore Setup
# Vaulter - only commit config.yaml
.vaulter/local/
*.env
.env.*Daily Workflow
# 1. Start: Pull latest from backend + apply your local overrides
vaulter local pull --all
# 2. Work: Add personal overrides (not shared with team)
vaulter local set DEBUG::true # Shared override
vaulter local set PORT::3001 -s api # Service-specific
# 3. Add new variable for team? Push to backend
vaulter set NEW_VAR=value -e dev # Add to backend
vaulter sync push -e dev # Or push local .env
# 4. Check: See what's different
vaulter diff -e dev # Local vs backend
# 5. Promote: Clone to staging/production
vaulter clone dev stg --dry-run # Preview
vaulter clone dev stg # ExecuteEnvironment Promotion Flow
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 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) β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββTeam Collaboration
New team member setup:
git clone <repo> # Gets .vaulter/config.yaml
export VAULTER_KEY_DEV=<from-team> # 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:
# 1. Add locally
vaulter local set NEW_FEATURE::enabled # Shared config
# 2. Push to backend (share with team)
vaulter sync push --dir -e dev
# 3. Notify team
# "New var added, run: vaulter sync pull --dir && vaulter local pull --all"MCP Tools for Workflow
| 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.
# Install in your project
pnpm add vaulter
# or: npm install vaulterWhat 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)
// app.ts
import { config } from 'vaulter'
config() // Loads from .vaulter/local/ (configs.env + secrets.env)# Run commands with env vars loaded
npx vaulter run -- pnpm dev
# Or pull from backend first
vaulter local pull --allThat's it! For most local development, vaulter is just a structured dotenv.
π©Ί Health Check - Doctor
Always start with vaulter doctor to diagnose your setup:
vaulter doctor -e devDoctor 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 immediatelyWhen to use:
- π Initial setup - validate configuration
- π Debugging - identify root cause
- π Pre-deploy - ensure everything is synced
- π Routine - weekly health check
Runbook local (scripts/vaulter-verify-dev.sh)
For a quick pre-deploy validation in local/dev workflows:
VAULTER_VERIFY_ENV=dev pnpm run verify:vaulterThe script runs:
vaulter doctor -e <env> -vvaulter sync diff -e <env> --valuesvaulter list -e <env>
It writes an execution log under artifacts/vaulter-health/ for auditability.
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
Setup
| Command | Description |
|---|---|
init |
Initialize project config |
init --split |
Initialize with split mode (configs/secrets dirs) |
Variables (var)
| Command | Description |
|---|---|
var get <key> -e <env> |
Get a variable |
var set KEY=val -e <env> |
Set secret (encrypted) |
var set KEY::val -e <env> |
Set config (plain text) |
var set KEY:=123 -e <env> |
Set typed secret (number/boolean) |
var delete <key> -e <env> |
Delete a variable |
var list -e <env> |
List all variables |
Set syntax: = encrypted secret Β· :: plain config Β· := typed secret
Sync
| Command | Description |
|---|---|
sync merge -e <env> |
Bidirectional merge (default) |
sync pull -e <env> |
Download from backend to outputs |
sync pull --dir -e <env> |
Download to .vaulter/{env}/ directory |
sync push -e <env> |
Upload .env file to backend |
sync push --dir -e <env> |
Upload .vaulter/{env}/ directory to backend |
sync push --prune -e <env> |
Upload, delete remote-only vars |
sync diff -e <env> |
Show differences without changes |
Export
| Command | Description |
|---|---|
export shell -e <env> |
Export for shell eval $(...) |
export k8s-secret -e <env> |
Generate Kubernetes Secret |
export k8s-configmap -e <env> |
Generate Kubernetes ConfigMap |
export helm -e <env> |
Generate Helm values.yaml |
export terraform -e <env> |
Generate Terraform .tfvars |
export docker -e <env> |
Docker env-file format |
export vercel -e <env> |
Vercel environment JSON |
export github-actions -e <env> |
GitHub Actions secrets |
Services (monorepo)
| Command | Description |
|---|---|
services list |
List discovered services |
services |
Same as services list |
Audit & Rotation
| Command | Description |
|---|---|
audit list -e <env> |
List audit entries |
audit stats -e <env> |
Show statistics |
rotation list -e <env> |
Check rotation status |
rotation run -e <env> |
CI/CD gate for overdue secrets |
Key Management
| Command | Description |
|---|---|
key generate --name <n> |
Generate symmetric key |
key generate --env <env> |
Generate key for specific environment |
key generate --name <n> --asymmetric |
Generate RSA/EC key pair |
key list |
List all keys |
key export --name <n> |
Export encrypted bundle |
key import -f <file> |
Import encrypted bundle |
key backup -o <file> |
Backup keys to encrypted bundle |
key restore -f <file> |
Restore keys from backup bundle |
Run (Execute with Env Vars)
| Command | Description |
|---|---|
run -- <command> |
Execute command with auto-loaded env vars |
run -e prd -- <command> |
Execute with specific environment |
run -s api -- <command> |
Execute with service-specific vars (monorepo) |
run --verbose -- <command> |
Show which files were loaded |
run --dry-run -- <command> |
Preview without executing |
Examples:
# Local development
npx vaulter run -- pnpm dev
# CI/CD build with production vars
npx vaulter run -e prd -- pnpm build
# Monorepo service
npx vaulter run -e dev -s api -- pnpm startThe run command auto-detects the environment (local, CI, K8s) and loads the appropriate files before executing your command.
Run
vaulter --helporvaulter <command> --helpfor all options.
Security
Every secret is encrypted before leaving your machine using AES-256-GCM.
Symmetric (Default)
vaulter key generate --name masterAsymmetric (RSA/EC)
For CI/CD separation: public key encrypts, private key decrypts.
vaulter key generate --name master --asymmetric # RSA-4096
vaulter key generate --name master --asym --alg ec-p256 # EC P-256# .vaulter/config.yaml
encryption:
mode: asymmetric
asymmetric:
algorithm: rsa-4096
key_name: master # ~/.vaulter/projects/<project>/keys/master[.pub]CI/CD: Give CI only the public key (can write, can't read). Production gets the private key.
Per-Environment Keys
Use different encryption keys for each environment (dev, stg, prd). This provides complete isolation - production secrets can't be decrypted with dev keys.
# Generate keys for each environment
vaulter key generate --env dev
vaulter key generate --env stg
vaulter key generate --env prdKeys 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:
# .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 envMulti-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/
βββ prdEach app has completely isolated secrets - app-landing/prd keys cannot decrypt app-api/prd secrets.
Shared Variables Key (Monorepo)
In monorepos, shared variables need a consistent encryption key. Use shared_key_environment to specify which environment's key encrypts shared vars:
# .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_PRDWhy 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 keys
Example flow:
# Set shared var (uses dev key because shared_key_environment: dev)
vaulter var set LOG_LEVEL=debug -e dev --shared
# Read shared var from prd (still uses dev key for shared vars)
vaulter var list -e prd --shared # Works! Uses dev key for sharedConfiguration
# .vaulter/config.yaml
version: "1"
project: my-project
backend:
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/Backend URLs
| 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 |
Local vs Deploy Structure
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
β βββ <service>/
β βββ 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.envWhy 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:
# Local development
.vaulter/local/configs.env
.vaulter/local/secrets.env
.vaulter/local/services/
# Deploy secrets (pulled in CI)
.vaulter/deploy/secrets/CI/CD
GitHub Action (Recommended)
Use the official Vaulter GitHub Action for seamless CI/CD integration:
- 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:
- 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.yamlHelmfile:
- 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 applyTerraform/Terragrunt:
- 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-approveDocker Build:
- 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:
- 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 |
CLI in CI/CD
You can also use the CLI directly:
- 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 buildOther Platforms
# GitLab CI
npx vaulter run -e ${CI_ENVIRONMENT_NAME} -- pnpm build
# Docker (build with secrets)
npx vaulter run -e prd -- docker build -t myapp .
# Terraform
vaulter export terraform -e prd > secrets.auto.tfvars
# Helm
vaulter export helm -e prd | helm upgrade myapp ./chart -f -
# Direct export
npx vaulter export k8s-secret -e prd | kubectl apply -f -Kubernetes Runtime
When running in Kubernetes, env vars are already injected via ConfigMap/Secret. Vaulter's config() function automatically skips loading when it detects K8s:
import { config } from 'vaulter'
config() // Skips in K8s, loads files elsewhereDetection: KUBERNETES_SERVICE_HOST environment variable is set by K8s automatically.
Monorepo Support
Auto-detects NX, Turborepo, Lerna, pnpm, Yarn workspaces, Rush.
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 variablesShared Variables Inheritance
When exporting for a specific service, shared variables are automatically included:
# 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-sharedOutput Targets (Multi-Framework)
One config β multiple .env files. Works with any framework: Next.js, NestJS, Express, NX, Turborepo, etc.
The Problem
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 everythingThe Solution
Define outputs once, pull everywhere:
# .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_*]Pull to All Outputs
# Pull to all outputs at once
vaulter sync pull --all
# Result:
# β web: apps/web/.env.local (5 vars)
# β api: apps/api/.env (12 vars)
# β admin: apps/admin/.env (25 vars)Pull to Specific Output
# Pull only web
vaulter sync pull --output web
# Preview without writing
vaulter sync pull --all --dry-runHow It Works
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 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_* β ββββββββββββ
ββββββββββββPattern Syntax
| 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 |
Inheritance
By default, shared.include vars are added to ALL outputs. Override with inherit: false:
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.
Quick Reference
| 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 |
Workflow
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 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 <repo> β
β 2. vaulter local sync β Download from backend β
β 3. vaulter local pull --all β Generate .env β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββFile Structure
.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.envMerge Order (Per Output)
Priority: shared vars < service-specific vars
For each output target, vaulter merges:
- Shared vars from
.vaulter/local/{configs,secrets}.env - Service-specific vars from
.vaulter/local/services/{service}/*.env
Example:
- 20 shared vars + 3 service-specific = 23 vars for that service
- NOT all vars from all services merged together!
CLI Commands
# === 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 summarySection-Aware .env Management
Vaulter uses section-aware mode by default when writing .env files. This preserves your custom variables while managing backend variables separately.
# 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:
# Section-aware pull (default)
vaulter local pull --all
# Overwrite entire file (ignores sections)
vaulter local pull --all --overwriteProgrammatic API:
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=trueabove the marker, it stays aftervaulter local pull - Port conflicts: Override
PORT=3001locally without affecting teammates - Feature flags: Test with
FEATURE_X_ENABLED=truewithout touching backend - Framework vars: Keep
.env.localcompatible with Next.js/Vite expectations
API Usage
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()Smart Config (Auto-Detection)
The config() function auto-detects your environment and loads the appropriate files:
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
.envfile (gitignored)
Auto-load (dotenv compatible)
import 'vaulter/load' // Auto-loads .env into process.envRuntime Loader (No .env Files)
Load secrets directly from the backend at application startup - no .env files, no Kubernetes ConfigMaps/Secrets needed.
Quick Start
// 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)How It Works
- Reads
.vaulter/config.yamlto find backend URL - Loads encryption key (per-environment support)
- Fetches secrets from S3/MinIO backend
- Populates
process.env
Configuration
# .vaulter/config.yaml
project: my-app
backend:
url: s3://my-bucket/secrets
# Runtime detects environment from NODE_ENV or config
default_environment: devOptions
| 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 |
Per-Environment Keys
Runtime loader automatically uses per-environment keys:
# Set env-specific keys
export VAULTER_KEY_DEV="dev-secret"
export VAULTER_KEY_PRD="prd-secret"
# Or generate key files
vaulter key generate --env prdUse Cases
Kubernetes without ConfigMaps/Secrets:
# 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"// app.ts - Secrets loaded at startup
import 'vaulter/runtime/load'
// process.env.DATABASE_URL is now availableLambda/Serverless:
import { loadRuntime } from 'vaulter'
export const handler = async (event) => {
await loadRuntime({ environment: 'prd' })
// Use secrets from process.env
}Library API
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.
vaulter mcpClaude Desktop
{
"mcpServers": {
"vaulter": {
"command": "vaulter",
"args": ["mcp", "--cwd", "/path/to/project"]
}
}
}Tools (53)
| 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 |
Resources (6)
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 |
Prompts (12)
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)
vaulter shell # Secrets Explorer (default)
vaulter shell menu # Menu
vaulter shell audit # Audit log viewer
vaulter shell keys # Key managerSYNC Column (Secrets Explorer)
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 |
Shortcuts
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 |
License
MIT Β© Forattini