Deleting the key did not revoke it

OpenCode told 1.4% of its Console users that provider API keys saved in their workspaces may have been exposed, and the remediation instruction is the finding: go revoke the key in OpenAI's, Anthropic's or Google's own dashboard, because removing it from OpenCode revokes nothing. Bring-your-own-key splits custody from revocation across two companies, and the asset it leaks is metered compute whose only authoritative monitor is somebody else's invoice.

By Agentic AI Wiki 11 min read

OpenCode told 1.4% of its Console users that the OpenAI, Anthropic and Google API keys they had saved in a workspace may have been exposed — and the remediation instruction it had to give them is the real finding: go revoke the key in the provider's own dashboard, because deleting it from OpenCode revokes nothing. Bring-your-own-key integrations split custody from revocation across two companies, and the asset they leak is metered compute whose only authoritative monitor is somebody else's invoice.

At a glance

What is publicly established, and what is still only reported. Reporting places OpenCode's disclosure on 17 October 2026; TechCrunch covered it on 19 October.

ClaimSourceStatus
1.4% of Console users affected; every one of them emailed, and no email meant not affectedOpenCodeCompany statement
Attackers reached a legacy system OpenCode says it was decommissioningOpenCodeCompany statement; entry vector not disclosed
The exposed material was third-party provider keys users had saved in Console workspacesOpenCodeCompany statement; storage design not disclosed
Anthropic and Google disabled keys they believed had been exposedReportedWidely reported, no formal provider notice
OpenAI worked with OpenCode to restore access and reimburse API customers for credits consumedAn OpenAI executive on XNot a formal notice; refund mechanics unpublished
Unauthorised charges in the high hundreds to low thousands of dollars, inside a nightAffected usersSelf-reported, unverified
The incident was a “distillation attack”OpenCode's framingNo published technical evidence, no indicators of compromise

Treat the bottom three rows as reporting rather than fact. The argument below does not need any of them. It needs only the first three and the sentence OpenCode had to put in its remediation advice.

The remediation instruction is the finding

Read the advice affected users were given, in order: replace the key at each provider, update the applications that use it, and review recent usage. Nowhere in that sequence does OpenCode's own product do anything. It cannot. A provider API key is a bearer credential issued by OpenAI, Anthropic or Google and validated by them on every request; the only party that can make it stop working is the party that signed it. OpenCode could delete every copy it holds and the leaked string would keep buying inference until somebody logged into a dashboard belonging to a different company.

Custody and revocation in a bring-your-own-key integration Four rows. The first traces the key from the provider that issued it, through the customer pasting it into a vendor console, to the stored copy sitting in the legacy system that was exposed. The second row shows that deleting the saved key removes only the copy and leaves the credential valid. The third shows that revoking it in the provider's console destroys the grant and stops every copy. The fourth shows the attacker's traffic running from their own machine to the provider endpoint and onto the customer's invoice, never crossing the customer's network. How the key got out Provider issues key You paste it in Console saves a copy Legacy system read Deleting it in the console Delete the saved key The copy is gone The key still works Revoking it at the provider Revoke in provider console The grant is destroyed Every copy stops Where the attacker's traffic goes Attacker's machine Provider endpoint Your invoice No step in the bottom row passes through anything you operate, log or rate-limit.
Three parties, and the one UI control that looks like revocation is not on the revocation path.

This is a different shape from the breaches most incident plans are written for. When a vendor leaks your data, the vendor can contain it: pull the system, rotate its secrets, close the hole. When a vendor leaks your credential to a third party, containment is not available to anyone in the incident. The vendor knows the key leaked and cannot kill it. The provider can kill it and does not know it leaked. You are the only party holding both facts, and you learn the first one by email.

The interface makes this worse rather than better, because the affordance is there and it is a no-op. Disconnecting an integration, deleting a workspace, removing a saved key — all three read as revocation to anyone who has used an OAuth consent screen, where disconnecting really does invalidate the grant. Here the same gesture deletes a copy. The lifetime of the credential was never under the vendor's control, so no button in the vendor's UI can shorten it.

Who holds what

Which party can revoke, log and detect Three rows, one per party, against three capabilities. The customer can revoke at the provider but holds no usage log and learns of the exposure only by email. The agent vendor knows first but can neither revoke nor log. The model provider can revoke and holds the authoritative log but does not know the key leaked until someone tells it. No single row carries all three. Who can revoke it, who can see it, who knows can invalidate the key holds the usage log knows it leaked You yes, at the provider no only once emailed The agent vendor no no yes, first The model provider yes yes, authoritative not until told Accent is a capability the party actually holds, the lighter tint is partial, plain is absent. Every row is missing something another row has, so the incident is actionable inside nobody's own systems. An owned gateway collapses all three onto the top row, because a credential you mint is one you can invalidate.
Custody, revocation and observation land on three different desks, which is why the incident has no owner.

Every row in that matrix is missing something the other rows have, and the gaps compose into a specific operational failure. The provider can revoke but has no reason to until someone tells it. The vendor knows but can only ask. You can act but are not holding the log. There is no single party for whom this incident is actionable within their own systems, which is precisely the definition of a separation that was not designed.

The asset was spend, and its monitor is a bill

The thing that makes a stolen provider key unusual is not how it was stored. It is what it converts into. A leaked database row has to be sold, correlated or reused before it costs anybody anything. A leaked provider key is already liquid: it buys GPU time at a published rate, from the first request, billed to you.

That changes where the detection has to live. The attacker's requests never touch your infrastructure — they go from their machine to the provider's endpoint. Your gateway logs nothing. Your SIEM sees nothing. Your rate limiter is not in the path. The authoritative record of the abuse is the provider's usage dashboard, and the authoritative alarm is a billing threshold, which is the one signal in most organisations that is reviewed monthly by someone in finance rather than continuously by someone on call. The users who caught this caught it because an overnight charge was large enough to generate a provider email.

Priced properly, a long-lived provider key with no cap is an uncapped line of credit issued to whoever holds the string. That is the blast radius to write down, and the numbers reported here — hundreds to a few thousand dollars in a night, per key — are the floor rather than the ceiling, because they describe an attacker spending casually. An attacker spending deliberately against a key with no limit is bounded only by the provider's own rate limits and fraud heuristics, neither of which you configured.

Two spend controls are worth more here than any amount of rotation hygiene, because they bound the loss before you know about it: a hard per-key monthly cap set at your actual expected usage rather than at a comfortable headroom, and a separate key per integration so a cap is attributable. A key with a $50 cap that leaks is an incident report. The same key uncapped is a negotiation with your provider's billing team.

“Distillation attack” is a motive, not an indicator

OpenCode framed the incident as a distillation attack — stealing keys to generate training data from frontier models. It may be right. But as of this writing it has published no technical evidence linking the exposed credentials to distillation activity, and no indicators of compromise at all: not the entry vector, not how the keys were stored, not what the attacker's traffic looked like.

A motive is not actionable and an indicator is. If the campaign had been described by request shape — model, sampling parameters, prompt distribution, concurrency — every other vendor holding customer keys could have gone looking for it this week, and so could the providers. Naming the motive instead does one thing the indicators do not: it moves the story from “a company stored bearer credentials in a legacy system” to “a company was targeted by sophisticated adversaries.” Both can be true. Only one of them generates a detection.

This is the pattern to watch for in agent-tool incident reports generally. The useful question is not who and why; it is what a peer can grep for tomorrow. On that measure this disclosure is, so far, a notification rather than a report — which matters because the vendor is the only party who can produce one.

What to build instead

The design rule falls straight out of the matrix: never hand a long-lived provider key to a third party's web workspace, because doing so transfers custody without transferring the ability to revoke. Three alternatives exist today, in increasing order of how much work they are and how much they buy.

ApproachWho can revokeWhat it costs you
Dedicated key per integration, hard spend cap, calendar rotationStill only the provider, but the loss is bounded and attributableKey sprawl; a rotation you will actually have to run
Delegated authorisation (OAuth-style) where the provider offers itYou, per client, from the provider's own consoleOnly available where the provider supports it for that tool
Your own AI gateway holding the real key, issuing the vendor a key you mintYou, instantly, without touching the providerA service to run, and it is now in the latency path

The gateway is the only one of the three that actually repairs the split, and the reason is structural rather than clever: it makes you the issuer. A credential you minted is a credential you can invalidate, which collapses custody, revocation and observation back onto one desk — yours. It also puts the spend meter somewhere you already page on, which is the half of this incident that no amount of rotation addresses.

If running a gateway is not proportionate for your team, the honest fallback is the first row, taken seriously: one key per tool, a cap that would be uncomfortable rather than roomy, and the knowledge that every such key is a copy you cannot recall. That is not a strong position. It is the position bring-your-own-key puts you in, and the value of this incident is that it was stated out loud in a remediation email.

FAQ

Does deleting an API key from a vendor's dashboard revoke it?

No. It deletes the vendor's stored copy. The credential remains valid until it is revoked in the issuing provider's own console — OpenAI's, Anthropic's or Google's. This is the specific instruction affected OpenCode users were given, and it generalises to every bring-your-own-key integration.

How would I know whether a leaked key had been used?

From the provider's usage dashboard, not from your own logs. The attacker's requests go directly to the provider's endpoint and never traverse your infrastructure, so nothing you operate is in the path. Check usage by key, by day, against your expected pattern; an unfamiliar model, an unfamiliar hour or an unfamiliar concurrency level is the signal.

Is a spend cap a substitute for rotating the key?

No, but it is the control that works while you are asleep. Rotation ends an exposure you already know about; a cap bounds one you do not. Set both, and size the cap against your real usage rather than leaving headroom you would not notice being consumed.

Was this a supply-chain attack on the agent itself?

Not as described. OpenCode says attackers reached a legacy system and read keys users had saved in Console workspaces, which is a credential-at-rest exposure in a web product rather than a compromise of the agent, its model or its distributed packages. The relevant class is how credentials get to an agent at all, not supply-chain integrity.

Should developers stop saving keys in agent tools entirely?

Where a delegated-authorisation option exists, use it instead. Where it does not, the defensible version is a key minted for that one tool, capped, and recorded in an inventory you review — because the thing you are agreeing to is that a string you cannot recall now exists on somebody else's disk.

Further reading

On this wiki:

Sources: