I gave my agent an API key and lost $100. Never again.
Six months ago, I installed OpenClaw, connected OpenRouter and assumed the agent was just expensive to run. Four days later, I found requests in Chinese that weren't mine. It cost me about $100 over a few days, but you can lose much more. Now I'm building an open source tool that gives agents access to APIs, command-line tools and browser sessions without handing over the keys.
2026-09-06 · updated 2026-09-06
Six months ago, I installed OpenClaw and connected OpenRouter so the agent could work. By the next day, it had used up the limit I had set. I raised it. I assumed the agent was just expensive to run.
Four days later, I looked through the requests. There were lots of requests in Chinese that I hadn’t sent and that had nothing to do with my tasks. Someone was using my API key at my expense. I lost about $100 over a few days.
I never established exactly how the key leaked. It might have been prompt injection, a bug in the agent runtime, or another route. The real key was accessible to the agent. Now someone else was using it too.
In a typical installation, an agent runs as a process with a particular user’s permissions. Keys for programmatic access to services often live in configuration files or environment variables. If the process can access that file or environment, the agent can read the key, including by running a short script. Reading it requires no special vulnerability.
A sandbox creates an awkward trap. I can isolate the agent, but I still want it to do useful work through different services’ APIs. If I put a real key inside the sandbox for that purpose, it is within the agent’s reach again. It can leak through command output or file contents.
I can keep the key outside and give the agent access through a separate, isolated tool. But writing a wrapper for every new service, maintaining it and checking the access boundary gets old quickly. I want to connect the services I need to one ready-made solution without assembling new security plumbing for every small task.
I had a hard spending limit. I raised it myself because I mistook someone else’s spending for my own agent’s work. The hard limit capped the amount; it didn’t tell me who was spending the money.
Providers don’t all offer the same per-key spending controls. Perplexity’s API, for example, has no per-key hard spending cap. In a reply on its official forum on July 10, 2026, the team explicitly stated that the API had no spending caps. Purchased credits and request-rate limits do not replace a separate money budget for each agent.
After losing that $100, I no longer want agents to be able to read real keys. Even when the key supports a hard spending limit.
I work on open source projects and use coding agents. They have their own machines because I don’t want them taking something from my laptop as well. But they still need access to working services. When an agent sends code or other material to GitHub, I don’t want to wonder whether a key from a configuration file or session history went with it.
My case cost roughly a hundred dollars. I looked at how similar situations had ended for other people. One reached the point of production resources being destroyed in AWS.
From one GitHub token to a wrecked production environment
In its Cloud Threat Horizons report for the first half of 2026, Google and Mandiant describe a breach they investigated in 2025. A malicious npm package stole a developer’s GitHub token. The attackers used the connection between GitHub and AWS to get into the cloud, then created an administrator role for themselves. The whole chain took less than 72 hours.
They then stole files from S3 and destroyed resources in the client’s production cloud environment.
The token alone was not enough. The attack also relied on excessive permissions and overly broad trust between systems. That is why this case matters to me. A key taken from a development environment opened a door into a system where other configuration mistakes were waiting.
In the wider s1ngularity campaign, GitGuardian researchers observed stolen tokens being used to make 10,767 private repositories public. Those repositories contained more secrets. The leak kept spreading into other people’s infrastructure.
You can revoke the token and make the repository private again. None of that deletes the code someone has already downloaded to their own disk.
This was a dependency attack rather than an accidental agent commit. But code an agent runs gets the permissions of the user running it. If that user can read a real token, a malicious package has something worth taking.
At METR, the attacker asked the agent for its key
This case connects directly to autonomous agents.
On August 31, 2026, research organisation METR disclosed an incident from March. A researcher was running agents on a personal EC2 instance. The dashboard was accessible over the internet and was supposed to require Google authentication. A bug in the application silently disabled authentication.
An attacker found the dashboard and asked the agent to reveal its model provider’s API key. With that key, the attacker spent three weeks consuming someone else’s credits, worth roughly $600,000.
METR had received those credits for free. There was no $600,000 charge to its bank account. But the unauthorised usage was real, and it went unnoticed among large research runs. The report also describes an SSH key the attacker added to retain access.
My server has little in common with a research organisation’s infrastructure. One similarity is enough to concern me: an agent on a separate machine can read working credentials. Control over that agent can become a way to take the key.
Turning the agent off after a leak does not revoke the real API key. Whoever copied it can call the provider from their own machine.
“Don’t publish secrets” doesn’t check the commit
An attacker is optional.
In a Claude Code issue from June 2025, a user reported that the agent wrote working credentials into config files and committed them to public GitHub. The report names a Gmail app password, Google Maps and Firecrawl keys. Security instructions in CLAUDE.md failed to prevent it. An external scanner detected the exposure.
This is a user report, without an independent forensic investigation. The consequences are easy to understand: revoke access, obtain replacement keys and inspect the remaining files. That is exactly the work I want to avoid when I use coding agents for my open source projects.
The security setup I trust least is giving an agent a secret, telling it never to reveal it, then asking the same agent to check whether it revealed it anywhere. I want a check that still works when the agent is confident it did everything correctly.
A public key can also produce a very real bill. A student from Georgia reported a $55,444.78 Google Cloud bill after accidentally publishing a Gemini API key on GitHub. In a September 2025 update, the student said Google had waived the entire debt. No money had been charged to the card. Before that, they had to deal with support and the threat of the debt being sent to collections.
A human published the key in that case. Having an AI publish it would not make requests using a valid key free of charge.
Sometimes the key stays put. The money still goes
There is another way to surprise the owner of an account: carry out their task using an expensive method.
Evgeny Kotelevskiy described a task he gave Hermes: find companies through LinkedIn and Sales Navigator, then get the data through RapidAPI. The agent chose a different tool on Apify. According to the author, it spent about $200 in roughly 20 minutes, and the collected data was unusable.
That is the user’s account; I have not audited his billing records. But the task is familiar. Research some leads and collect the data. Try another service when the first approach fails. Each attempt can look useful until you add up the cost.
In the DN42 network case, an agent went as far as deploying expensive AWS infrastructure. Community participant Lan Tian preserved the conversations and sequence of events. The operator reported a $6,531.30 bill that AWS later reduced to $1,894. Even after the reduction, they asked the community for help paying it. Those amounts come from the operator’s messages; AWS has not publicly confirmed them.
I want to let an agent spend money on a task. But an API key usually does not carry my intention to “try this for five dollars, then stop”. That intention needs to become a limit checked before the money is spent.
Why a separate server doesn’t reassure me
A separate machine is useful. If the agent breaks its environment, I can rebuild it. I like having somewhere it is allowed to experiment.
But GitHub, paid APIs and other working services live beyond that machine. The keys connect it to my accounts. Reinstalling the server does not cancel a cloud bill or retrieve data someone else has downloaded.
Before connecting another service, I want to understand what I am giving the agent. What work does this access allow? Can it read the key itself? What keeps working if that key ends up with someone else?
The specific sandbox matters too. For example, some sandboxes can already inject HTTP credentials on the host, leaving a placeholder value inside the agent’s environment. Having a container does not tell you whether this boundary is configured. If a real key sits somewhere the agent can read, the name of the isolation mechanism does not hide it.
Where an HTTP proxy stopped being enough for me
With model APIs, the idea is simple enough. The agent sends a request with a fake key, and the proxy inserts the real one before forwarding it to the provider. This also works alongside a sandbox. But my agents use tools with other authentication mechanisms, or entirely different protocols.
My first question was GitHub. Access to its API alone was insufficient.
I needed gh, git clone, git push and access to specific repositories.
Git over HTTPS can pass through an HTTP proxy, but git has to be directed
there, and gh must not pick up a real token from its usual credential
store. That led to wrappers with fake authentication and separate CLI
configuration. Git over SSH needs a different access path.
Then PostgreSQL. psql and database drivers speak their own protocol;
replacing an HTTP header won’t help. I built a separate proxy for them:
the agent connects with a fake password, while the proxy uses the real
password to connect to the database. The PostgreSQL role still determines
table permissions. This is not a SQL filter that understands every query.
Stripe had several cases too. Ordinary CLI API calls can go through the
proxy, but stripe listen maintains a separate, long-lived connection for
webhooks. I put that in a service process which holds the real Stripe key
and forwards events to the local application. The application receives the
secret it needs to verify webhooks, rather than the main Stripe access key.
For SSH, I moved the private key into a separate service. The agent asks for an allowed operation on a selected server or an allowed tunnel. It receives neither the key nor unrestricted access to an SSH-agent socket. Policy determines which servers and commands are available.
Then there is the browser. I want to sign in to a site myself, complete MFA and let the agent work with that authenticated account. For example, read and edit a Notion page shared with it. I don’t want to hand over my password, cookies or browser profile directory, which could be copied and used elsewhere.
In the browser setup, the operator logs in, then the broker gives the agent a limited set of page operations. The profile and internal browser-control interface stay with a separate service user.
That browser boundary has a specific limit too. If the site allows the account to display an API key on a page, the agent can see it. The account’s permissions inside the service still need to be restricted.
What I am building in unsudo
This work grew out of the infrastructure for my own agents. I already use the core and am preparing to publish it as an open source project called unsudo. I want to bring an HTTP proxy, command-line wrappers, SSH session brokers and protected access to a logged-in browser into one solution. Then I can connect services without writing new glue each time.
The foundation is the same throughout: on a Linux server, the agent runs under its own user, while protected services under another user hold the real credentials. For APIs, the agent receives fake values. For SSH or the browser, it gets a way to request an action while keys and sessions stay inside a protected process.
I am preparing the project for its first public release. There is a call log and request-count limits, and risky HTTP operations can require human approval. There is no universal dollar ceiling across all services yet.
I also want to make unsudo available for Windows and Mac. That is separate work. If you want that option, mention it in the release form on unsudo.dev. I want to know which systems you run your agents on.
I don’t consider this a solution to prompt injection. An agent can still misuse access it is allowed to have. Unsudo’s job is to keep an agent’s mistake from also giving it a way to read the real keys. Old copies in files it can access must be removed, and exposed keys revoked. Root access on the same machine, or a compromise of the broker itself, breaks this boundary.
I want the agent to complete a useful task and get an operating-system denial when it tries to read the real key. That is the check I want to pass when connecting the next tool.
See how unsudo works
I’ve put together a walkthrough of the architecture, service examples and how I use it day to day.
See how unsudo works →