psst-cli
v0.5.1
Published
AI-native secrets manager. Agents use secrets without seeing them.
Maintainers
Readme
psst 🤫
Because your agent doesn't need to know your secrets.
The Hall of Shame
I keep pasting API keys into Claude Code. Or just letting it cat .env. Every time I tell myself I'll stop doing that. I never do.
# "just read the .env"
cat .env
# "here, use this key"
sk-live-4wB7xK9mN2pL8qR3...Your secrets are now:
- 📜 In the model's context window
- 📟 In your terminal history
- 📁 In that log file you forgot about
- 🎓 Training data (maybe?)
- 📸 Screenshot material for your coworker's Slack
There's a better way.
What if agents could use secrets without seeing them?
# Agent writes this:
psst STRIPE_KEY -- curl -H "Authorization: Bearer $STRIPE_KEY" https://api.stripe.com
# What the agent sees:
# ✅ Command executed successfully
# What actually ran:
# curl -H "Authorization: Bearer sk_live_abc123..." https://api.stripe.comThe secret never touches the agent's context. It's injected into the subprocess environment at runtime.
The agent orchestrates. psst handles the secrets.
For Humans
You set up psst once. Then your agent handles the rest.
Installation
npm install -g psst-cliSetup (one-time)
# Create vault (stores encryption key in your OS keychain)
psst init
# Add your secrets
psst set STRIPE_KEY # Interactive prompt, value hidden
psst set OPENAI_API_KEY
psst set DATABASE_URL
# Verify
psst listThat's it. Now onboard your agent:
psst onboardThis adds psst instructions to your CLAUDE.md or AGENTS.md file, teaching your agent:
- How to use
psst SECRET -- command - To ask you to add missing secrets
- To shame you if you try to paste secrets in plain text 🤫
Managing Secrets
psst set <NAME> # Add/update secret (interactive)
psst set <NAME> --stdin # Pipe value in (for scripts)
psst get <NAME> # View value (debugging only)
psst list # List all secret names
psst rm <NAME> # Delete secret
# Import/export
psst import .env # Import from .env file
psst import --stdin # Import from stdin
psst import --from-env # Import from environment variables
psst export # Export to stdout (.env format)
psst export --env-file .env # Export to file
# Vault encryption (for backups/travel)
psst lock # Encrypt vault at rest with password
psst unlock # Decrypt vaultEnvironments
Organize secrets by environment (dev/staging/prod):
psst init --env prod # Create vault for "prod" environment
psst --env prod set API_KEY # Set secret in prod
psst --env prod list # List secrets in prod
psst --env prod API_KEY -- curl https://api.example.com
# List all environments
psst list envsEnvironments are stored in ~/.psst/envs/<name>/vault.db.
You can also use the PSST_ENV environment variable:
export PSST_ENV=prod
psst list # Uses prod environmentNote: Existing vaults at ~/.psst/vault.db continue to work as the "default" environment.
Global Flags
All commands support:
-g, --global # Use global vault (~/.psst/)
--env <name> # Use specific environment
--tag <name> # Filter by tag (repeatable)
--json # Structured JSON output
-q, --quiet # Suppress output, use exit codesLocal vs Global Vaults
By default, psst creates a local vault in your project directory:
psst init # Creates .psst/ in current directory
psst init --env dev # Creates .psst/envs/dev/ in current directoryFor user-wide secrets, use the global vault:
psst init --global # Creates ~/.psst/
psst --global set API_KEY # Store in global vault
psst --global list # List global secretsSecret Scanning
Prevent accidentally committing secrets to git:
# Scan files for leaked secrets
psst scan # Scan all tracked files
psst scan --staged # Scan only git staged files
psst scan --path ./src # Scan specific directory
# Install pre-commit hook (runs scan automatically)
psst install-hookThe scanner checks for actual vault secret values — no regex false positives. If a secret is found:
✗ Secrets found in files:
config.js:12
Contains: STRIPE_KEY
Found 1 secret(s) in 1 file(s)
Tip: Use PSST_SKIP_SCAN=1 git commit to bypassBypass the hook when needed:
PSST_SKIP_SCAN=1 git commit -m "message"
# or
git commit --no-verifySecret History & Rollback
Accidentally overwritten a secret? psst keeps the last 10 versions automatically.
# View version history
psst history API_KEY
# History for API_KEY
#
# ● current (active)
# ● v2 01/15/2026 14:30
# ● v1 01/10/2026 09:15
#
# 2 previous version(s)
# Rollback: psst rollback API_KEY --to <version>
# Restore a previous version
psst rollback API_KEY --to 1
# ✓ Rolled back API_KEY to v1Rollback is reversible — the current value is archived before restoring, so you can always undo.
Secret Tags
Organize secrets with tags for easier management:
# Add tags when setting secrets
psst set AWS_KEY --tag aws --tag prod
psst set STRIPE_KEY --tag payments --tag prod
# Manage tags on existing secrets
psst tag DB_URL prod # Add tag
psst untag DB_URL dev # Remove tag
# List secrets filtered by tag
psst list --tag aws # Only aws-tagged secrets
psst list --tag prod # Only prod-tagged secrets
# Run commands with tagged secrets only
psst --tag aws -- aws s3 ls # Inject only aws-tagged secrets
psst --tag prod run ./deploy.sh # Run with only prod secretsTags use OR logic when filtering — psst list --tag aws --tag payments returns secrets with either tag.
For Agents
You don't read secrets. You use them.
The Simple Way
psst run <command>This injects all vault secrets into the command's environment. You never see the values.
# Run any command with all secrets available
psst run ./deploy.sh
psst run python my_script.py
psst run docker-compose upSpecific Secrets
If you only need certain secrets:
psst <SECRET_NAME> [SECRET_NAME...] -- <command># Single secret
psst STRIPE_KEY -- curl -H "Authorization: Bearer $STRIPE_KEY" https://api.stripe.com
# Multiple secrets
psst AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY -- aws s3 lsWhat You Get Back
- Exit code of the command
- stdout/stderr of the command (with secrets automatically redacted)
- Not the secret value
Secrets are automatically replaced with [REDACTED] in command output. Use --no-mask if you need to see the actual output for debugging.
Checking Available Secrets
psst list # See what's available
psst list --json # Structured outputIf a Secret is Missing
psst will automatically check environment variables as a fallback. If neither the vault nor the environment has the secret, the command will fail.
Ask the human to add it:
"I need
STRIPE_KEYto call the Stripe API. Please runpsst set STRIPE_KEYto add it."
How It Works
┌───────────────────────────────────────────────────────┐
│ Agent Context │
│ │
│ "I need to deploy the app" │
│ > psst run ./deploy.sh │
│ │
│ [Command executed, exit code 0] │
│ │
│ (Agent never sees any secret values) │
└───────────────────────────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────┐
│ psst │
│ │
│ 1. Retrieve encryption key from OS Keychain │
│ 2. Decrypt STRIPE_KEY from local vault │
│ 3. Inject into subprocess environment │
│ 4. Execute: curl ... (with $STRIPE_KEY expanded) │
│ 5. Return exit code to agent │
└───────────────────────────────────────────────────────┘Security model:
- Secrets encrypted at rest (AES-256-GCM)
- Encryption key stored in OS Keychain (macOS Keychain, libsecret, Windows Credential Manager)
- Secrets automatically redacted in command output (
[REDACTED]) - Optional vault lock with password for backups/travel
- Secrets never exposed to agent context
- Zero friction for legitimate use
CI / Headless Environments
When keychain isn't available, use the PSST_PASSWORD environment variable:
export PSST_PASSWORD="your-master-password"
psst STRIPE_KEY -- ./deploy.shFAQ
Q: Why not just use environment variables?
Because export STRIPE_KEY=sk_live_... puts the secret:
- In your shell history
- In your agent's context (if it ran the export)
- Visible to
envandprintenv
psst keeps secrets out of the agent's context entirely.
Q: Why not use a .env file?
.env files are fine for local dev, but:
- Agents can
cat .envand see everything - Easy to accidentally commit
- No encryption at rest
Q: Is this like HashiCorp Vault?
Vault is for teams and infrastructure. psst is for your laptop and your AI agent. Different tools, different problems.
Q: What if the agent runs psst get STRIPE_KEY?
It'll print the value. That's a feature for human debugging. If you're worried, don't give your agent shell access. But honestly, if an agent has shell access, it can already do much worse things.
Q: How is the encryption key stored?
In your OS keychain:
- macOS: Keychain.app (unlocked when you log in)
- Linux: libsecret / gnome-keyring
- Windows: Credential Manager
Philosophy
- Local-first: Your secrets never leave your machine. No cloud, no sync, no account.
- Agent-first: Designed for AI agents to use, not just humans.
- Zero friction: No passwords to type (keychain handles it).
- Single binary: Works everywhere Bun runs.
Development
# Install dependencies
bun install
# Run locally
bun run src/main.ts --help
# Build single binary
bun run buildLicense
MIT
