Local-first, encrypted secrets manager. No accounts, no servers, no nonsense.
secrets init
secrets add ANTHROPIC_API_KEY sk-ant-xxxxx
secrets run -- npm run dev
# 1. Initialize (creates encrypted key at ~/.secrets/)
secrets init
# 2. Add a secret
secrets add DATABASE_URL postgres://localhost/mydb
# 3. Run your app with secrets injected
secrets run -- npm run devThat's it. Secrets are encrypted with age and stored locally. No cloud, no sync, no account.
Generate a new age keypair and initialize the secrets store at ~/.secrets/.
secrets initYou'll be prompted to set a passphrase. This protects your private key.
Add or update a secret.
secrets add KEY value # global, default environment
secrets add -p backend KEY value # project-scoped
secrets add -e prod KEY value # specific environment
secrets add -p backend -e prod KEY value # project + environmentPrint a secret's value. Requires an interactive terminal (safety guard against piping values into logs).
secrets get KEY
secrets get -p backend KEY
secrets get KEY --force # bypass TTY checkList secret names (no values shown).
secrets list # global secrets
secrets list -p backend # project secrets
secrets list --all # all projects + globalRemove a secret.
secrets delete KEY
secrets delete -p backend KEYCheck if a secret exists. Exits 0 if yes, 1 if no. No output — safe for scripts and agents.
secrets check API_KEY
secrets check -p backend DATABASE_URLInject secrets as environment variables and run a command.
secrets run -- npm run dev # global secrets
secrets run -p backend -- cargo run # global + project (project wins)
secrets run -p backend -e prod -- npm start # specific environmentWhen using -p, global and project secrets are merged. Project secrets take precedence on collision.
Import secrets from external sources.
secrets import .env # from .env file
secrets import secrets.json # from JSON file
secrets import doppler # from Doppler CLI
secrets import doppler --all # all Doppler projects
secrets import - --format json # from stdinOptions:
--overwrite— replace existing keys--delete-source— delete the source file after import--dry-run— preview what would be imported--format json|env— explicit format (auto-detected from extension)--yes— skip confirmation prompts
Export secrets to stdout.
secrets export # KEY=VALUE format
secrets export --format json # JSON format
secrets export -p backend -e prod # specific scope
secrets export --encrypted > backup.age # raw encrypted fileSecrets are organized by project and environment:
~/.secrets/
key # passphrase-protected age private key
key.pub # age public key
default/ # global secrets (no -p flag)
default.age # default environment
prod.age # -e prod
backend/ # -p backend
default.age
prod.age
- Projects (
-p): isolate secrets per project —backend,frontend,api - Environments (
-e): separate secrets per environment —default,prod,staging - Global (no
-p): secrets available everywhere, merged into everyrun
# Import your .env
secrets import .env
# Import and delete the original
secrets import .env --delete-source
# Scope to a project
secrets import .env -p backend
# Replace your workflow
# Before: source .env && npm run dev
# After:
secrets run -- npm run dev# Import current Doppler project
secrets import doppler
# Import all Doppler projects at once
secrets import doppler --all
# Import a specific Doppler project into a named project
secrets import doppler -p backend
# Preview what would be imported
secrets import doppler --dry-runDoppler internal keys (DOPPLER_PROJECT, DOPPLER_CONFIG, DOPPLER_ENVIRONMENT) are automatically excluded.
Encryption: Secrets are encrypted using age via the rage Rust implementation. Each vault file (e.g., default.age) is an age-encrypted JSON blob containing key-value pairs.
Key management: secrets init generates an X25519 keypair. The private key is encrypted with your passphrase. The public key is stored in plaintext for encryption operations.
Passphrase: Prompted interactively on each operation. For CI/scripts, set SECRETS_PASSPHRASE as an environment variable. The passphrase is scrubbed from the child process environment when using secrets run.
Storage: Everything lives in ~/.secrets/. No network calls, no external services, no telemetry. Copy the directory to back up or migrate.
AI coding agents should use secrets without ever seeing secret values:
# Check if a required secret exists
secrets check ANTHROPIC_API_KEY
# List available secrets (names only)
secrets list
# Run a command with secrets injected
secrets run -- npm run dev
secrets run -p backend -- cargo runRules for agents:
- Use
secrets run -- commandto inject secrets into processes - Use
secrets check KEYto verify secrets exist (exit code only) - Use
secrets listto discover available secrets - Never use
secrets get— it exposes values in the agent context
| Feature | secrets | Doppler | dotenvx | .env files |
|---|---|---|---|---|
| Local-first | ✓ | ✗ (cloud) | ✓ | ✓ |
| Encrypted at rest | ✓ (age) | N/A (cloud) | ✓ (AES) | ✗ |
| No account required | ✓ | ✗ | ✓ | ✓ |
| No external dependencies | ✓ | ✗ | ✗ (npm) | ✓ |
| Project scoping | ✓ | ✓ | ✗ | ✗ |
| Environment support | ✓ | ✓ | ✓ | Manual |
| Secret merging | ✓ | ✗ | ✗ | ✗ |
| Import from Doppler | ✓ | — | ✗ | ✗ |
| Import from .env | ✓ | ✓ | — | — |
| Single binary | ✓ | ✗ | ✗ | N/A |
| Agent-safe commands | ✓ | Partial | ✗ | ✗ |
MIT