Skip to content
Get early access

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.

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.

Terminal window
cendarum run --env dev -- claude

That’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:

Terminal window
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”
Terminal window
cendarum run --env dev -- claude
cendarum run --env dev -- aider
cendarum run --env dev -- npm run dev # the agent's dev server, same idea

Anything 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.

--only narrows the injected set, and this is the case it earns its keep in:

Terminal window
cendarum run --env dev --only STRIPE_SECRET_KEY -- claude

The 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.

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.

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:

Terminal window
curl -v ... # prints request headers, including Authorization
curl --trace ... # prints everything

Many 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.

  1. Rotate at the provider. Stripe, AWS, whoever issued it. This is the part that matters, and only they can do it.
  2. Update the value in Cendarum, so the next run injects the new one.
  3. 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.

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.

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