Read more about: #agents#open-source#infrastructure#llms#claude-code

psst 🀫 Because Your Agent Doesn't Need to Know Your Secrets

I have a confession.

I keep pasting API keys into Claude Code. Or just letting it cat .env. Every time I tell myself I’ll fix it later. I never do.

# "just read the .env"
cat .env

# "here, use this key"
sk-live-4wB7xK9mN2pL8qR3...

# "I'll delete it from the chat after..."
my database password is hunter2, can you check why queries are slow?

We’ve all done it. The secret is now in the model’s context, in our terminal history, possibly in logs, maybe in training data. We tell ourselves it’s fine. It’s not fine.

The Problem

When you give an agent shell access, it needs secrets to do real work. Call APIs. Deploy code. Access databases. The standard approaches all leak:

Environment variables? The agent can run env and see everything. Or it runs export STRIPE_KEY=... and now the secret is in its context.

.env files? The agent can cat .env. Easy.

Paste it in chat? Now it’s in the conversation history. Possibly forever.

The agent doesn’t need to know your Stripe key. It just needs to use it.

The Insight

What if secrets could be injected at the last possible moment - into the subprocess environment - without ever touching the agent’s context?

# 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.com

The agent orchestrates. It knows which secret to use. But it never sees the value.

How It Works

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Agent Context                                        β”‚
β”‚                                                       β”‚
β”‚  "I need to call Stripe API"                          β”‚
β”‚  > psst STRIPE_KEY -- curl https://api.stripe.com     β”‚
β”‚                                                       β”‚
β”‚  [Command executed, exit code 0]                      β”‚
β”‚                                                       β”‚
β”‚  (Agent never sees sk_live_...)                       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                          β”‚
                          β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  psst                                                 β”‚
β”‚                                                       β”‚
β”‚  1. Retrieve encryption key from OS Keychain          β”‚
β”‚  2. Decrypt STRIPE_KEY from local vault               β”‚
β”‚  3. Inject into subprocess environment                β”‚
β”‚  4. Execute command                                   β”‚
β”‚  5. Return exit code (not the secret)                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Secrets are encrypted at rest with AES-256-GCM. The encryption key lives in your OS keychain (macOS Keychain, libsecret on Linux). Zero friction - no passwords to type.

The Interface

Setup once:

npm install -g @pssst/cli
psst init
psst set STRIPE_KEY          # interactive prompt, value hidden
psst set OPENAI_API_KEY

Then agents just use it:

psst STRIPE_KEY -- curl https://api.stripe.com
psst AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY -- aws s3 ls
psst DATABASE_URL -- prisma migrate deploy

That’s the whole API. One pattern: psst SECRET -- command.

Agent Onboarding

Run psst onboard in your project and it adds instructions to your CLAUDE.md or AGENTS.md:

## Secrets Management (psst)

Use `psst SECRET -- command` to run commands with secrets.
Never ask the user to paste secrets in chat.
If a secret is missing, ask them to run `psst set SECRET_NAME`.

It also teaches agents to shame you if you try to paste a secret in plain text. Because we all need accountability.

Local-First, Agent-First

No cloud. No sync. No account. Your secrets stay on your machine, encrypted, accessible only through the keychain.

The first customer is the agent. The interface is designed for non-human use. Humans just set things up and let the agent work.

Try It

npm install -g @pssst/cli
psst init
psst set MY_SECRET
psst MY_SECRET -- echo "The secret is $MY_SECRET"

Code: github.com/Michaelliv/psst


psst 🀫 β€” because your agent doesn’t need to know your secrets.