Sharing secrets with agents
Sharing API keys, passwords, and other secrets with coding agents is a bad idea from a security perspective. However, if you are not doing it, you are handicapping your agentic workflows. That is the premise of this entire text. There are plenty of arguments against both, and against the whole idea of using LLMs for coding, but the focus here is on how to share secrets with an agent while minimising the security footprint. I would like to say safely, but there is no entirely safe way to work with a model that can reach the outside world.
The seatbelt argument
I would imagine that anyone who has been using LLM-assisted coding actively, and wants to get the most out of it, has committed the sin of sharing secrets with the models, either consciously or unconsciously. Most of us have run agents in our main OS, with plenty of things like SSH keys sitting around that a stupid or malicious agent could post somewhere. Now, of course this is not happening, and the likelihood is very small. But because it’s a non-zero value, I would suggest everyone minimise their use of coding agents directly in their main system.
It is nice that they can do everything you can do, and use git on your behalf, but some people will get burned. It’s a bit like seatbelts. If you are running coding agents in your main OS, you are sitting in a car without one.
How this actually leaks
The potential paths are quite many, and I don’t think we need to go through all of them. But as one example, this blog post about an attacker impersonating Cloudflare to pass an identity check, and getting the agent to leak personal details that were never even given to it, tells you something about the shape of the problem.
Then there is the whole possibility of rogue employees at the LLM vendors who can access the logs. I wouldn’t trust that secrets are reliably sanitised from all outputs, and even where a vendor claims to clean or not persist logs at all, our industry is full of negligence, so I wouldn’t lean on that either. I think it’s good to be slightly paranoid here, and to go through the small inconvenience of setting up a development environment that minimises the risk.
By the way, secrets here are mostly software secrets, but trade secrets are of course the same problem. Those are actually much harder to hide, so let’s focus on the easier part: access keys and other credentials that open external systems.
Two moves
So what does minimising actually mean in practice? For me it comes down to two moves, and neither of them is about trusting the model more.
Assume the leak
The first is to assume the leak. Not to plan for it, or to add a policy about it, but to actually build on the assumption that everything the agent can see is already gone. Once that is the starting point, the interesting question stops being “how do I stop it leaking” and becomes “what exactly did it have to leak”. That is a much easier question, and unlike the first one, it has an answer you can engineer.
Make the secret worthless
The second move follows from the first: make the thing it can leak worthless to anyone else. This is the part I find genuinely elegant. A secret is not dangerous because it is a secret, it is dangerous because of what it opens and from where. Take either of those away and the secret becomes an inconvenience rather than an incident.
So instead of handing the agent the real credentials, you hand it something that only works from inside a box you control, and you keep the real thing outside that box. The agent still gets its work done, because it can still ask for the operation. It just can’t carry the means to do it anywhere else.
That is the whole idea, and it looks roughly like this:
The sandbox is the “from where” and the expendable token is the “what it opens”. Neither half is much use alone — a sandbox with your real SSH keys in it is just a slower way to lose them, and an expendable token on your main machine is protecting the wrong thing. Together they mean the worst realistic outcome is that someone gets a credential that does nothing unless they are already sitting at my laptop, at which point I have considerably bigger problems.
What it looks like in practice
My rule, then, is that every secret we give to an agent must be expendable. Right now I’m using this to limit access to GitHub.
I start a local Lima session with Colima and run a local proxy, so when my agent wants to talk to GitHub it goes through host.lima.internal. It also runs on a separate file system. If my agent goes rogue, or pulls in a malicious NPM package that ships all my secrets somewhere, what the attacker gets is a local token that only works on my internal network, on my machine. It’s useless to anyone else unless they get full control of my laptop. And it should be quite hard to break out of the Lima VM. Hopefully, at least.
I use a lighter version of the same idea with Fly.io and Doppler, where the agent gets a very limited temporary API key because it needs those only rarely. Though writing this out, I think those should get a proxy too. On it.
The same pattern in CI
I’ve leaned on this pattern in other places as well, like our Git runner in our internal Forgejo. The runner is an ephemeral Fly.io machine that gets tokens for releases, migrations, and so on, and all of those tokens are only usable inside the Fly 6pn network. So if the runner is compromised, the credentials it leaks have a very limited blast radius and don’t work from anywhere else.
I don’t always follow my own advice
I’m not sure any of this is sufficient, but I feel like I’m miles ahead of most people I talk to. And sometimes I’m miles ahead of myself, in other projects. I have repositories where I’m happily running claude --dangerously-skip-permissions inside my main system and waiting for the disaster, and then I have entire development environments that are completely sandboxed, where I’m proxying every outbound call.