dr.chaos

Your API Key Was Someone Else's Credit Card: The OpenCode Console Breach

Aamir Lakhani12 min read

OpenCode's one-line explanation for its October breach was that it was "related to a large scale distillation attack." It's a catchy phrase, the kind that makes a breach sound like it happened to the frontier labs rather than to the people who got the bill. But the people who woke up to hundreds of dollars of Anthropic and OpenAI charges did not have a distillation problem. They had a key custody problem, and so does anyone who has ever pasted an API key into somebody else's web console. So we set ourselves two goals for this piece, and they pull against each other a little. We wanted to be fair to a small open-source team in the middle of an incident it says is still ongoing, and we wanted to be blunt about the architecture that made this incident cheap for the attacker and expensive for everyone else.

Title card reading Your API Key Was Someone Else's Credit Card, with a terminal line showing a usage dashboard spiking overnight on a key the owner forgot was stored in a hosted console.
Illustration: the breach was quiet. The invoice was not.

What actually happened

The public record is still thin, so here is what we could verify, as far as we could tell, and from whom. OpenCode is the open-source AI coding agent built by the former SST team, now Anomaly Innovations, and it comes in two very different shapes. The local CLI and desktop app run on your machine. The hosted OpenCode Console lets a workspace owner link provider accounts once, store the credential, and route the team's requests through OpenCode's gateway so members can use models without ever seeing the key (OpenCode Console docs).

Screenshot of OpenCode's Console documentation page titled Providers, stating that Console stores the provider credential and serves each provider through its gateway so workspace members use the models without seeing the provider key, with steps to connect a provider by entering its API key.
OpenCode's own Console documentation, captured from opencode.ai on October 19, 2026. "Console stores the credential" is the sentence that matters.

On October 17, affected users received an email titled "Action Required, Potential provider key breach," which reportedly described "unauthorized access to a legacy system" that OpenCode was in the process of decommissioning and said provider keys saved in the workspace "may have been exposed" (The Hacker News). Security researcher Matt Johansen posted a screenshot of it that afternoon (X). A few hours later OpenCode's own account appears to have confirmed the breach, tied it to "a large scale distillation attack," put the affected population at 1.4 percent of Console users (all of them with external providers linked), said the incident was ongoing, and said it was working with Anthropic, OpenAI, and Google (OpenCode on X). Co-founder Dax Raad drew the line that matters most for readers, which is that this was the Console and not OpenCode itself, because keys you add locally "never come to us" (Dax Raad on X).

Timeline of the OpenCode Console breach from October 16 to October 19, 2026, showing the intrusion on the night of October 16, breach emails and the first public screenshot on October 17, OpenCode's confirmation that 1.4 percent of Console users were affected, Anthropic revoking affected keys, attackers continuing to hit OpenCode's systems on October 18, and an OpenAI employee announcing refunds on October 19.
What we could pin down by October 19. Times are UTC; sources are linked in the text.

The providers probably moved faster than the post-mortem will. Lydia Hallie, who works on Claude Code at Anthropic, said Anthropic had revoked Claude API keys it believed were affected (X). Tibo Sottiaux at OpenAI said OpenAI worked with OpenCode to get affected users back in business and to refund credits drawn in the attack (X). That is an employee's post rather than a formal notice, and we found nothing from Google at all. Meanwhile Raad said attackers had been hitting OpenCode's systems non-stop for 48 hours, and that his own accounts were being targeted too (X). There is no root cause, no entry point, no dwell time, and no statement on whether the stored keys were encrypted. As far as we could tell on October 19, those are all still open.

Two products, two very different trust models

Strip away the brand name and this incident is a lesson about where a secret lives. In the local CLI, OpenCode's documentation says keys you add with /connect land in a SQLite database on your own machine, by default under ~/.local/share/opencode/ (OpenCode CLI docs). An attacker who wants those keys has to compromise your laptop, and then the next laptop, and then the one after that. That is most likely slow, noisy, retail work. In the Console, the same keys sit in one service run by one company, so the attacker's unit of work is the whole customer base at once. That is wholesale.

Diagram comparing the blast radius of the two OpenCode trust models. In the local CLI, each developer's key sits in a database on their own laptop, so one compromised laptop exposes one key. In the hosted Console, many workspaces store their provider keys in one service behind one gateway, so one compromised legacy system exposes every stored key at once.
Illustration: same product family, very different blast radius.

None of this makes a hosted console a bad idea. Teams want one place to manage access, they want members to use models without holding raw keys, and they want a budget per person. Those are probably good security goals. But a console that stores customers' long-lived bearer credentials for frontier models has to be built like a vault, and the email's own wording suggests these keys likely lived in a "legacy system" that was being retired. In my experience, systems on their way out are exactly where nobody is watching anymore. The decommissioning ticket is open, the owner has moved on, and the data is still there.

There is a second, quieter detail that I think deserves more attention than it got. Several users on GitHub reported that first-party Console and OpenCode Go keys set to never expire suddenly showed as expired on October 17, including users who had linked no third-party keys and received no breach email (GitHub #56833; GitHub #56703). The most likely explanation is a precautionary mass revocation, which is the right call during an active incident. But OpenCode has not said so, and when a vendor says "if you did not get an email you were not impacted" while also expiring your keys, users are probably going to fill the gap with their own theories.

What "distillation attack" can and cannot mean

Words matter in incident response, so it's worth being precise about this one. Anthropic uses "distillation" for training a weaker model on a stronger model's outputs, and "distillation attacks" for covert, large-scale campaigns that pull those outputs out through fraudulent accounts, proxies, and reseller networks (Anthropic; Anthropic). Anthropic has also described proxy networks controlling hundreds of thousands of fraudulent accounts, and The Hacker News reported in September that those proxy pools are partly built from illegally harvested API keys (The Hacker News). I have not independently validated that last report against Anthropic's own document, so treat it as reported.

Read that way, there are three live explanations for OpenCode's phrase. One is that attackers breached OpenCode specifically to harvest keys for a distillation operation, which would make the stolen keys raw material for someone else's training run. Another is that OpenCode was already fighting distillation traffic through its own gateway, and the breach of the legacy system happened in the middle of that fight. The third, perhaps, is that "distillation" was simply the most dignified word available on a bad afternoon. It could have been any of them. My gut tells me it's the first, because the observed abuse was mass querying of frontier models billed to strangers, and that is exactly what a distillation operator wants. But we should not conclude it from the wording alone, and neither should anyone writing a headline.

Here is the part that doesn't depend on which explanation wins. Whatever the attacker wanted the tokens for, the method was the oldest one in cloud security. They found long-lived credentials stored by a third party and spent them. Sysdig named this pattern LLMjacking in 2024, when stolen cloud credentials were used to resell model access and run up victim costs that Sysdig estimated could exceed $46,000 a day (Sysdig). Two years later the target appears to have moved from cloud accounts to the services that aggregate model keys. That is not a new attack. It's an old attack that found a better wallet.

The bill was the detection

The victims did not learn about this from an alert. Most of them, it seems, learned from their invoice. The user reports are self-reported and none of them are confirmed by OpenCode, but they seem to paint a consistent picture, with roughly $281 on OpenAI and $168 on Anthropic for one developer, $1,700 for another, a Reddit user describing about $400 of Anthropic usage overnight, and another reporting about 200 euros of Google usage before the project was suspended (The Hacker News). The numbers are not huge by enterprise standards. But they are exactly the numbers a developer on a personal card notices at breakfast.

That is a design smell, and it probably isn't OpenCode's alone. For a decade we have told people to set billing alerts on cloud accounts, and most of us learned that lesson the hard way. AI API keys reintroduced the same failure with a faster burn rate, because a stolen key pointed at a reasoning-heavy frontier model might spend in an hour what a stolen compute key spent in a week. If your only detection control is the monthly statement, your detection time is likely a month.

One more uncomfortable point is worth making plainly. Console-side budgets and per-member limits probably do nothing once the raw key has left the building, because the attacker no longer routes through OpenCode's gateway (The Hacker News). The only limits that still apply are the ones on the provider side. So the place to put your hard stop is the provider, not the tool that holds the key.

Lock it down in twenty minutes

If you ever pasted a provider key into OpenCode Console, treat it as compromised whether or not you got the email, because the available evidence does not yet tell you otherwise. Deleting it from Console does not revoke it. Go to each provider directly, rather than following links in any breach email (phishers read the news too), and do the following.

Checklist table for locking down AI provider keys after the OpenCode breach. For Anthropic, delete the key in Console API keys, set a lower organization spend limit on the Billing page, and use workspaces with their own spend and rate limits. For OpenAI, revoke the key in the project's API keys page, set a hard limit per project, and keep one project per tool. For the Google Gemini API, create a replacement key, switch apps to it, disable the old key, restrict keys to the Gemini API, and set a per-project spend cap. For all providers, review usage back to October 16 for spikes, unfamiliar models, odd hours, and batch jobs you did not create.
Provider controls as documented in October 2026. Check each console, since these menus move.

For Anthropic, delete the key from the API keys page in the Console (Anthropic support). Every usage tier carries a monthly spend cap, and you can set your own lower organization limit on the Billing page. Workspaces other than the default one can have their own spend and rate limits, which is how you keep one tool's blast radius away from another (Anthropic rate limits). We could not find a documented spend-alert email, so check the Usage page yourself until you're sure.

For OpenAI, revoke the key from the project's API keys page. Spend alerts only notify. Hard limits are the control that actually stops spend, and they can be enforced per organization or per project; OpenAI notes that enforcement is not instant, so spend can overshoot a little (OpenAI spend limits). Give each tool its own project with its own hard limit, and a stolen key becomes a bounded loss instead of an open tab.

For the Gemini API, create a replacement key, switch your applications to it, then disable the leaked key (Google). Restrict keys to the Gemini API and, where you can, to IPs or apps. Since March, AI Studio has offered per-project monthly spend caps, which Google says take roughly ten minutes to enforce (Google). Vertex AI budgets are standard Cloud Billing budgets, which alert but do not stop spending.

Then review usage back to at least October 16. The things to look for are boring and useful, such as spikes at hours you don't work, models you never call (especially the expensive reasoning tiers), a high ratio of output tokens to input, batch jobs you didn't create, and source IPs where the provider exposes them.

Terminal illustration of a developer searching their own machine for AI provider keys by grepping shell history and config directories for Anthropic, OpenAI, and Google key prefixes, finding a key in a dotenv file and one in shell history, then listing where OpenCode's local database lives.
Illustration of a self-audit. Prefixes shown are the common public formats; your keys may differ.

While you're there, look at your own machine. The keys that leak next may not come from a vendor. They'll come from shell history, a forgotten .env, or a config directory that got synced to a backup you don't think about. The local CLI's model is safer than a hosted vault only as long as your laptop is safer than their legacy system.

What I'd change if I were building a BYOK console

If you run a product that stores customers' model keys (and a lot of AI tooling now does, from coding agents to the automation platforms I wrote about earlier this year), this incident is most likely your cheapest tabletop exercise this year. Encrypt every stored key with envelope encryption and a per-tenant key in a real KMS, so a database dump is not a key dump. Never let a decommissioning system keep live secrets; migrate them, rotate them, and then shut it down. Alert on per-key usage that departs from the customer's normal shape, because you see the traffic first and your customer sees the bill last. And give customers a one-click way to revoke at the provider, rather than only deleting your copy.

The providers have homework too. Long-lived static bearer keys are the root of this whole class of incident. The better pattern is short-lived, scoped credentials issued to a workload identity, with spend limits on by default instead of opt-in. Some of that appears to exist in pieces today. None of it is the default developers actually get when they click "create key."

Who holds the keys

OpenCode will publish a post-mortem eventually, and I hope it's a candid one, because the open-source community will learn more from a detailed failure than from a polished one. But the lesson does not have to wait for it. Every AI tool that offers to "manage your keys for you" is, in most cases, asking to become the most valuable target in your stack, because it holds credentials that turn directly into money and compute for whoever steals them. Convenience moved the keys into someone else's database. The bill stayed with you.

So the question to ask about any AI tool is not how smart its model is. Ask who holds the key, where the hard spending limit lives, and who finds out first when someone else starts using it. If the answer to that last one is "your credit card company," you already know which control to fix this week.

Further reading

Aamir Lakhani

Founder · Dr. Chaos

Aamir Lakhani is a leading senior security strategist responsible for providing IT security solutions to major enterprises and government organizations. He creates technical security strategies and leads security implementation projects for…

~/related

Keep digging

More research along the same attack path.

ATT&CK Finally Has a Name for Prompt Injection. Now Write the Detection.

MITRE ATT&CK v20 adds five AI techniques to the Enterprise matrix: Direct and Indirect Prompt Injection under Execution, AI-Enabled Exfiltration, AI Result Poisoning, and Impair Autonomous Defenses. Why the placement matters more than the count, what the direct versus indirect split means for your trust boundaries, how MITRE's exfiltration analytic actually works, and the detection gaps you should know about before your next coverage report.

Aamir Lakhani12 min read

Meta's Muse Wonder Has Your Calendar. Who Holds the Guardrails?

Meta's Muse Wonder and Muse Wonder Full are real frontier models built for personal agents. What Meta shipped, how to read its benchmark and safety charts, what independent tests and six launch-week videos found, what r/MetaAI users are saying, and why the security that matters lives in the Muse product, not in the model developers get through the API.

Aamir Lakhani18 min read

Open Weights Aren't Just Chinese Anymore: Mistral Large 4, Reflection AI's Beam, and the Dolphin Question

Mistral Large 4 and Reflection AI's Beam are the first Western open-weight frontier models in a long while, and neither has shipped a weight file yet. What the two announcements actually say, what the cyber benchmarks really measure, why a security team should care about weights it can hold, and the straight story on "Mistral Dolphin": what an uncensored fine-tune is, how it is made, how operators use it, and where the guardrail goes once the vendor's is gone.

Aamir Lakhani17 min read