NAME
psst β π€« AI-native secrets manager. Agents use secrets without seeing them.
SYNOPSIS
npm install -g psst-cliINFO
DESCRIPTION
π€« AI-native secrets manager. Agents use secrets without seeing them.
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.comWhat the agent sees:
β Command executed successfully
What actually ran:
curl -H "Authorization: Bearer sk_live_abc123..." https://api.stripe.com
The 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-cli
Setup (one-time)
# Create vault (stores encryption key in your OS keychain) psst initAdd your secrets
psst set STRIPE_KEY # Interactive prompt, value hidden psst set OPENAI_API_KEY psst set DATABASE_URL
Verify
psst list
That's it. Now onboard your agent:
psst onboard
This 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 secretImport/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 vault
Environments
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.comList all environments
psst list envs
Environments 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 environment
Note: 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 codes
Local 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 directory
For 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 secrets
Secret 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 directoryInstall pre-commit hook (runs scan automatically)
psst install-hook
The 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 bypass
Bypass the hook when needed:
PSST_SKIP_SCAN=1 git commit -m "message"
# or
git commit --no-verify
Secret History & Rollback
Accidentally overwritten a secret? psst keeps the last 10 versions automatically.
# View version history psst history API_KEYHistory 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 v1
Rollback 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 prodManage 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 secrets
Tags 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 up
Specific 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.comMultiple secrets
psst AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY -- aws s3 ls
What 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 output
If 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.sh
FAQ
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 installRun locally
bun run src/main.ts --help
Build single binary
bun run build
License
MIT
psst β because your agent doesn't need to know your secrets