In the transcript
You paste the key into the chat so the agent can run one command. The provider stores that conversation, it sits in your history, and months later you can still search for it, and so can anyone who gets into your account.
Claude Code, Cursor, Aider, Codex: sooner or later the agent needs a real API key to run a command. Today you paste it into the chat or let the agent read your .env, and from then on the plaintext sits in the transcript, in the model's context, and in whatever logs sit behind it. With envmanager run the agent writes a placeholder instead, we fill in the real value when the command starts, and we scrub it out of the output again.
$ envmanager run --only STRIPE_KEY -- \
curl -H "Authorization: Bearer {{STRIPE_KEY}}" https://api.stripe.com/v1/charges
{ "object": "list", "data": [ ... ] }
$ envmanager run --only STRIPE_KEY -- sh -c 'echo $STRIPE_KEY'
***
You paste the key into the chat so the agent can run one command. The provider stores that conversation, it sits in your history, and months later you can still search for it, and so can anyone who gets into your account.
You tell the agent to "check the config", it cats the .env, and every value in the file is now in the model's context for the rest of the session, whether the task needed it or not.
Agent frameworks log every tool call, and a curl with a bearer token in the header is a tool call like any other. That log gets shipped somewhere, and in our experience rarely with the header redacted.
The agent helpfully hardcodes the key it just saw into a script to make the test pass. Git history keeps it long after the line is removed, and bots scan public repos for exactly this within minutes.
Three things happen together, every time.
Every run names the variables it may use: --only DATABASE_URL,STRIPE_KEY, or an allow-list in envmanager.json. No scope means the command refuses to start rather than quietly exposing everything.
The values go into the child process environment and replace any {{VAR}} placeholder inside the command, right before it starts. The command runs directly, not through a shell, so a value can never be read as a shell instruction.
Anything the command prints is filtered and every secret value is replaced with ***. An accidental echo, a verbose log, a stack trace: none of it hands the value back to the agent.
Drop this in your project's CLAUDE.md and the agent stops asking for keys. It asks for a placeholder instead.
## Secrets
Never ask for or read secret values. Never read .env.
To run anything that needs a secret, use:
envmanager run --only KEY1,KEY2 -- <command>
Refer to values as {{KEY}} inside the command.
Allowed keys are listed in envmanager.json under agent.allowed_keys.With an allow-list in place the agent doesn't need --only every time, and it can never widen the scope on its own.
{
"project_id": "your-project-id",
"environment": "development",
"agent": {
"allowed_keys": ["DATABASE_URL", "STRIPE_KEY"]
}
}This stops accidental leaks, which is the everyday problem: a cooperative agent or tool surfacing a value into its context, its logs, or a commit. It is not a sandbox though. The value is still decrypted to the CLI on your machine, so a program that is actively trying to get it out and holds your CLI credential could fetch it another way. That is why we make you scope every run: give the command the two keys it needs instead of --all, and the worst case stays limited to those two.
One more edge case: values shorter than six characters are not scrubbed, because masking those would garble normal output. So don't rely on run to hide a four-digit PIN.
run is one command in a secrets manager we built for teams, so the human side is covered by the same tool.
Secret values are AES-256-GCM encrypted in Supabase Vault and decrypted server-side only for people and tokens you gave access to. Every reveal is logged.
Developers see staging, leads see production. An agent gets a CLI token scoped the same way, so the blast radius of a bad run is whatever that token can see.
Vercel, Railway, Render, Dokploy, Coolify, GitHub Actions, AWS, Azure and GCP. Change a value once and the next sync pushes it everywhere it belongs.
Every pull, run and change lands in the same log with who, what and when, so "which agent used the Stripe key on Tuesday" has an answer.
Yes. run is a normal CLI command, so anything that can execute a shell command can use it. The CLAUDE.md snippet above is the Claude Code version; the same instruction works in .cursorrules or any agent instruction file.
Your own shell expands $KEY before EnvManager ever sees the command, usually to nothing. The double-brace form passes through the shell untouched, and we fill it in. Programs that read the environment themselves, like npm or psql, just work with plain injection and need no placeholder at all.
If it holds your CLI credential, technically yes, which is why this is not a sandbox. In practice you tell the agent not to, you scope the token to the environment it needs, and every pull shows up in the audit log so you would see it.
run is included in Pro, which is one flat price for the whole team, no per-seat or per-agent charges. There is a 14-day trial with no card.
Import your .env, install the CLI, and your agent runs with placeholders from the next command on. 14 days free, no card. 49 founding spots left at $9 a month for the whole team.