AI coding agents
You want Claude Code, Cursor or aider to actually hit an API — a real request, a real response, a real error you can debug. So it needs a real key.
The obvious move is pasting the key into the chat. The model then refuses, or warns you, or both.
The refusal is correct
Section titled “The refusal is correct”It is worth being clear about this, because the instinct is to treat it as an obstacle to argue around.
Pasting a live credential into a conversation is a leak. It goes into the transcript. The transcript goes to the model provider, usually into logs, often into a retention window you do not control, and possibly into a support ticket later. A model declining to handle that is the model doing its job.
So don’t talk it round. Make the pasting unnecessary.
The pattern
Section titled “The pattern”cendarum run --env dev -- claudeThat’s it. run decrypts the environment on your machine and spawns the agent
with those values in its process environment — the same mechanism that gets them
into a dev server.
Now when the agent needs the key, it writes:
curl -s https://api.stripe.com/v1/customers \ -H "Authorization: Bearer $STRIPE_SECRET_KEY"It references the key by name. Your shell resolves $STRIPE_SECRET_KEY at
execution time, inside a process the model never sees the inside of.
What lands in the transcript is the string $STRIPE_SECRET_KEY. The value
appears in your prompt: never. In the model’s reply: never. In the conversation
you might paste into an issue next week: never.
Nothing to refuse, because nothing was pasted.
It works for any agent you launch from a terminal
Section titled “It works for any agent you launch from a terminal”cendarum run --env dev -- claudecendarum run --env dev -- aidercendarum run --env dev -- npm run dev # the agent's dev server, same ideaAnything you start from a shell inherits the environment, so anything you start from a shell works. There is no integration, no plugin, and nothing for us to maintain per agent — which is the same reason there is no per-language SDK.
Give it only the key it needs
Section titled “Give it only the key it needs”--only narrows the injected set, and this is the case it earns its keep in:
cendarum run --env dev --only STRIPE_SECRET_KEY -- claudeThe agent can now reach exactly one credential. Not your database URL, not your
production SMTP password, not the twelve other things in dev.
An agent session is a long-running process doing things you did not individually approve. Handing it your whole environment because that was the default is worth one extra flag to avoid.
Use a test key
Section titled “Use a test key”The strongest thing you can do here has nothing to do with Cendarum: make the
key the agent can reach a test key. sk_test_…, a sandbox tenant, a staging
project.
Environment injection keeps a credential out of a transcript. It is not a reason to let an agent hold a production credential — and if you are reaching for production because test mode does not reproduce the bug, that is worth a moment’s thought before it is worth a flag.
What this does not do
Section titled “What this does not do”The honest part, and please read it before relying on the pattern.
It is not a boundary against the agent. The agent runs inside your
environment. If it runs printenv STRIPE_SECRET_KEY, the value prints, and then
it is in the transcript after all. What the pattern removes is the need to
paste, and the value’s presence in the conversation by default. It does not
make the value unreachable — nothing that also makes it usable could.
Verbose HTTP output prints the header. This is the most likely way it leaks in practice, and it is very likely, because it is exactly what an agent does when a request fails:
curl -v ... # prints request headers, including Authorizationcurl --trace ... # prints everythingMany HTTP libraries do the same at debug log level. If you are debugging a failing
request with an agent, assume the header can end up on screen. Ask for -s and a
status code rather than a full trace.
Error messages can reflect it. Some APIs echo the presented key, or a prefix of it, in an authentication error. Some frameworks print the whole environment in a stack trace.
If it does leak, it has been transmitted. It is not just on your screen; it went to the model provider along with the rest of the turn. Treat it as disclosed and rotate — see below.
Web chat interfaces get no help from this. The pattern needs a process you spawn. If you are pasting into a browser, there is no process for us to inject into, and pasting the key is still pasting the key.
This is a real residual and it is in the threat model: once a value is in a child process’s environment, what that child does with it is beyond our boundary.
If you think it leaked
Section titled “If you think it leaked”- Rotate at the provider. Stripe, AWS, whoever issued it. This is the part that matters, and only they can do it.
- Update the value in Cendarum, so the next
runinjects the new one. - Rotate the environment if a teammate’s access is also in question. Note this re-keys the environment; it does not change the credential itself. Step 1 is not optional.
Two things about long-running sessions
Section titled “Two things about long-running sessions”The vault can lock while the agent keeps working. run hands the values over
at spawn and immediately drops its own copy, so Cendarum does not need to stay
unlocked. Your agent session survives the 15-minute idle lock.
The agent’s copy is a snapshot. Rotating a key or revoking a grant does not reach a process that is already running. If you rotate mid-session, restart the agent.
The short version
Section titled “The short version”| Do | cendarum run --env dev --only THE_ONE_KEY -- claude |
| Do | use a test key |
| Do | ask for curl -s and a status code |
| Don’t | paste the value into the chat |
| Don’t | ask for curl -v on an authenticated request |
| Don’t | assume the agent cannot read the value — it can |