API key theft and distillation attacks: how to protect your AI tool keys
An API key for an AI provider is a bearer credential: whoever holds it can spend your money and use your quota, and the provider cannot tell the holder from you. A distillation attack is one reason someone wants many such keys. It trains a cheaper model on the answers of a stronger one, and it needs a very large number of paid requests. If the requests go through stolen keys, someone else pays for them and the traffic does not look like one customer. The practical defense is to limit what each key can do, watch what it does, and keep keys in as few places as possible.
On 17 October 2026 OpenCode reported a security incident that it linked to a large-scale distillation attack. This article starts from what OpenCode said, explains what distillation is and why keys are the target, lists the signals of abuse, and gives checklists for people who use AI tools and for people who build them. It also states what zn-gate does for this problem and what it does not.
What did OpenCode say happened?
We only repeat what OpenCode has said in public. OpenCode's post on X and its notice to affected users are quoted below as reported by Officechai and Blockonomi, with links. Anything OpenCode has not said publicly is marked as unconfirmed.
On 17 October 2026, OpenCode wrote on X: "We had a security breach last night related to a large scale distillation attack that impacted 1.4% of OpenCode Console users." The same post, as quoted by Blockonomi, says the affected users were notified by email that morning and that "if you did not get an email you were not impacted." OpenCode added that "it was a subset of users who had external providers."
The notice sent to affected users, as quoted by Officechai, says OpenCode "identified unauthorized access to a legacy system" and that "third party provider keys (eg OpenAI, Anthropic) saved in your workspace may have been exposed." It adds that "the incident is ongoing so we cannot share more details at this point."
That is the whole confirmed picture. Some things follow from it, and some do not:
- The 1.4% is a share of OpenCode Console users, not of everyone who uses the open-source OpenCode coding agent. The OpenCode documentation describes the Console as where you add billing details and credits and copy your API key, and says workspace owners and admins can connect their own providers.
- According to the notice, the keys at risk are third-party provider keys saved in a workspace.
- OpenCode has not published how the attackers got in, how long they had access, how many keys were used or which models were called. Anything on those points that you read elsewhere is a hypothesis.
We use OpenCode every day, in the official releases and in builds we adapt to our own setup, and we trust the product. We have no commercial relationship with the OpenCode team. We are writing about the incident because it shows a pattern that every AI tool vendor and every user of AI tools should plan for, and because the team has been open about it while the investigation is still running. We do not know more than they have said, and we do not guess about their systems.
What is a distillation attack, and what is model extraction?
Distillation is "training a less capable model on the outputs of a stronger one," in the words of Anthropic's post on distillation attacks. The technique is legitimate and old. Hinton, Vinyals and Dean described it in 2015 in Distilling the Knowledge in a Neural Network as a way to compress an ensemble into a smaller model that is cheaper to run. Providers distill their own models to make smaller, cheaper versions for customers.
The abuse is distilling someone else's model without permission. Anthropic's post says competitors use it to acquire capabilities "in a fraction of the time, and at a fraction of the cost" of developing them, and that models built this way are unlikely to keep the original safeguards. In that post Anthropic reports three campaigns that together generated more than 16 million exchanges with Claude through about 24,000 fraudulent accounts, and says illicit campaigns are recognized by pattern: "massive volume concentrated in a few areas, highly repetitive structures."
A related idea is model extraction. In Stealing Machine Learning Models via Prediction APIs (USENIX Security 2016), Tramèr and co-authors show that someone with only query access to a model can copy its behavior closely, and that removing confidence values from outputs does not fully stop it. The paper is about classic models, not large language models, but the lesson carries over: if you can query a model enough, the model's behavior can be copied from its answers.
Neither technique needs access to the weights. It needs volume. That is where the keys come in.
Why do attackers use stolen API keys for this?
There are two reasons, and both are about cost and visibility.
The victim pays. A distillation run needs many requests, and each one is billed by the provider. With a stolen key the bill goes to the key owner. The attacker's cost drops to the effort of collecting keys.
The trail is spread out. A provider that sees one account sending millions of repetitive requests can detect it, as Anthropic describes, using classifiers, behavioral fingerprinting and tools that spot coordinated activity across many accounts. If the same volume is split over hundreds of keys from different customers, each key sends a small amount of ordinary-looking traffic. The provider's view of any single account looks unremarkable, and the pattern only appears when many accounts are looked at together. Provider-side detection is a hard problem for this reason, and the key owner may be the first person who can see that something is wrong, because only the owner knows what their normal usage is.
The result is that the owner of the key has an interest in watching it that nobody else has.
Where do stolen keys come from?
Keys leak from the places where keys are stored. In practice these are the common ones:
| Place | How a key can leave it |
|---|---|
.env files and shell profiles |
Committed to a repository by mistake, read by a script, a backup or a dependency, or read by a coding agent that then sends it to its model provider |
| Tool and editor configuration | A key pasted into a settings file that is synced, shared or included in a bug report |
| CI logs and build artifacts | A key printed by a debug line or copied into an artifact that others can download |
| Chat, tickets and documentation | A key pasted to ask for help and left there |
| Third-party consoles and dashboards | A key you saved in a service so it can call a provider for you. The key is then exactly as safe as that service |
The last row is the one this incident points to. When you save a provider key in another company's console, you add that company's security to the security of the key. That can be a good trade, because a service with a team behind it may protect a key better than a laptop does. It also means that a flaw in that service can expose keys of customers who did everything right on their own machines.
This article does not describe how any of these leaks are exploited, and we are not going to. The defenses do not depend on it.
How can you tell that a key is being abused?
Signals appear on the provider's side, so the dashboard of each provider is where to look. Look for:
- Spend that rises without a change in your work. A jump between two days, or a steady increase at a time when nobody is working.
- Models you do not use. If you only call one model and the usage page shows another, the key is being used by someone else.
- Usage at odd hours or in odd volume, such as a flat high rate all night.
- Usage per key that does not match what the key is for. A key made for one tool that shows traffic when that tool is closed.
- Unexpected charges or hitting a limit that you did not expect to reach.
- Provider notices. Providers also detect abuse and may write to you or disable a key.
None of these proves theft by itself. Each one is a reason to check. The cheapest way to make them visible is to set up alerts before you need them.
How do you protect your keys as a user?
The aim is that a stolen key can do little damage, for a short time, and that you notice quickly. These measures work together:
- Use one key per tool and per purpose. When a key leaks you know where from, you can revoke it without breaking everything else, and its usage page tells a clear story. Providers group keys in projects or workspaces, and that is a useful way to separate environments. OpenAI's production best practices recommend separate projects for staging and production, which also allows separate rate and spend limits for each.
- Set spend limits and alerts. OpenAI's documentation describes alerts that notify you when usage passes a dollar amount and hard limits that stop traffic when spend reaches the limit. A hard limit is the control that turns an unbounded loss into a bounded one. Set it lower than you think you need and raise it when you hit it.
- Give keys an expiry and rotate them. OpenAI recommends setting an expiration date when you create a project key and having a regular rotation process: create a replacement, update your applications, check that it works, then revoke the old key. Google's documentation recommends rotating keys periodically, says that unrestricted API keys are insecure, and recommends restricting each key to the APIs it needs.
- Revoke, do not just replace, after any doubt. If a tool you use reports an incident and you are unsure whether your key was involved, revoke the key. Creating a new key takes a minute. Then look at the old key's usage for the days before you revoked it.
- Keep keys out of code, repositories and logs. Use environment variables or a secrets manager, as OpenAI recommends, and keep
.envout of version control. - Do not store a key in a third party's console unless the service needs it. If the tool runs on your machine, the key can stay on your machine. If the service must call the provider for you, use a key made only for that, with its own project, a spend limit and an expiry.
- Read the key list now and then. Delete keys for tools you no longer use. Provider consoles show when each key was created, and some show when it was last used. Anthropic's Admin API, for example, exposes the status, creation time and expiry of each key and never returns the secret itself.
What should people who build AI tools do?
If your product stores keys that belong to your customers, you have taken on a duty that a user's laptop does not carry, and the design should show it.
- Encrypt stored keys with a managed key service, such as a cloud KMS, with access controls that separate the people and services that can decrypt from those that only run the product. Store only what you need to call the provider.
- Use least privilege between your components. The part of the system that calls a provider should be able to read the keys it needs for the request in front of it, not the whole store.
- Do not show a key again after it is saved. Display a hint, such as the last few characters, and let the user replace it. The key objects in Anthropic's Admin API work this way, with a partial hint and no secret.
- Apply per-key quotas and anomaly checks on your side. You see the traffic of all your customers before any one customer does. A key that suddenly makes thousands of requests of the same shape is worth pausing and asking about.
- Retire old systems. Code and data stores that are still reachable but no longer in active use tend to get the least review. Delete them, or keep them under the same controls as the live system.
- Make rotation easy. If revoking and replacing a key is a long procedure, users do not do it.
- Tell customers early and plainly, with what to do next. A short message that says which users are affected and what to rotate lets people act before the investigation ends.
What does zn-gate do about this, and what does it not do?
zn-gate is an open-source security layer that runs on your machine, between your coding agent and the tools and content it touches. It helps with the part of the problem that sits on your machine. It does not help with the part that sits in someone else's cloud. We state both so you do not rely on it for something it does not do.
What it does. These statements are in the zn-gate README and in the Scrubber and secrets vault docs:
- With the Claude Code plugin, or zn's hooks for Codex CLI, Cursor and OpenCode, the coding agent is blocked from reading
.envfiles that hold real values and credential files such as~/.aws/credentials. This is a guardrail, not a sandbox. - Secrets in what the agent's tools return, such as file reads and command output, are replaced with markers before the model sees them, and a secret pasted into a prompt is blocked. In Cursor only MCP tool output is scrubbed.
zn env shadow .envmoves your values into an encrypted vault on your machine and leaves markers such as__ZN_STRIPE_KEY__in the file. The vault is local and is not synced.- A key can be used without the model seeing it. A command you allowed gets the value through
zn run, and its output is masked. For HTTP APIs, thesecretsMCP server sends the value only to the hosts you allowed and returns the response with secrets replaced by markers. No tool returns a value to the agent.
What it does not do.
- It does not protect a key stored in a third party's cloud. In the case above, the keys at risk were ones saved in a workspace on a hosted console. zn-gate is not in the path between that service and the provider, so it cannot reduce that exposure. Only the service and you, by limiting and rotating that key, can.
- It does not replace spend limits, alerts or rotation. They are your controls at the provider.
- It does not stop a process you start yourself, or malware running as your user. Its threat model says that any program running as your OS user can ask an unlocked vault for a secret by name, and recommends locking the vault with
zn lockwhen you do not use it. - A command you allowed still holds the value while it runs. zn controls where a credential goes, not what an allowed program does with it.
- It does not detect a distillation attack. Provider-side traffic is outside what it sees.
What zn-gate removes is one common source of leaks, the key that sits in plain text in a .env file where an agent or a prompt injection can find it. A compromised dependency running as your user is a different case: see the threat model above. Keeping a key out of the agent's reach on your machine is useful, and it is one measure among those listed above.
What is a checklist you can use today?
For users:
- Each tool has its own key, in its own project or workspace.
- Each key has a spend limit and an alert at a lower amount.
- Each key has an expiry date, and you know when you will rotate it.
- You know where each key is saved: local environment, vault, CI secret or a third party's console.
- Keys in third-party consoles are only the ones the service needs, with a limit.
-
.envfiles are not in version control, and the coding agent cannot read them (npx -y zn-gate check). - You look at per-key usage at least weekly, and after any incident notice from a tool vendor.
- Unused keys are deleted.
- If a vendor reports an incident that may involve you: create a new key, update the tool, revoke the old key, review the old key's usage.
For builders:
- Customer keys are encrypted with a managed key service, with narrow decrypt permissions.
- A key is never shown again after it is saved.
- Per-key quotas and anomaly alerts are on your side.
- Old systems and stores are retired or covered by the same controls.
- A tested procedure exists to notify customers and to rotate every stored key.
What does this article not cover?
- It does not describe how the OpenCode incident happened beyond what OpenCode said, because that is not public.
- It does not give any method for stealing keys or running a distillation campaign.
- It does not cover provider-side detection beyond what the providers have published.
- It does not cover keys for services that are not AI providers, though most of the advice applies to them.
Frequently asked questions
What is a distillation attack?
Training a less capable model on the outputs of a stronger one. Anthropic describes illicit distillation as competitors using it to acquire capabilities in a fraction of the time and cost of building them. It needs a very large number of requests to the stronger model.
Why would an attacker steal API keys for a distillation attack?
Because the requests have to be paid for and come from somewhere. With a stolen key, the key owner pays the bill, and the traffic is spread over many accounts instead of one account that a provider can spot and block.
How do I know if my AI provider key is being abused?
Look for spend that rises without a change in your own work, requests to models you never use, usage at hours when nobody is working, or traffic from tools you do not recognize. Spend alerts and per-key usage views are the earliest signals.
What should I do if a tool I use had a key leak?
Create a new key, update the tool that needs it, revoke the old key, then review the usage of the old key for the period before you revoked it. Do this even if you are unsure the key was affected.
Does zn-gate protect a key I stored in a third party's cloud console?
No. zn-gate runs on your machine and keeps the coding agent from reading your .env and credential files, keeps secret values out of what the model sees, and can use a key from a local vault without showing it to the model. A key that you pasted into a third party's service is outside its reach.
Should I keep my provider keys in a tool's cloud console?
Only if the tool needs them to run a service for you. If it can run on your machine with the key in your own environment or a local vault, that is one less place to protect. If you do store a key there, give it a spend limit and use a key that is used for that purpose only.
How often should I rotate API keys?
OpenAI's guidance recommends setting an expiration date when you create a project key and having a regular rotation process. How often depends on how much damage one key can do: a key with a low spend limit can be rotated less often than a key with none.
For the basics of keeping a .env away from an agent, see how to stop Claude Code, Codex CLI, Cursor and OpenCode from reading your .env with one command. To see how an agent hook can be bypassed and how that was fixed, read how a coding agent could read your .env file indirectly. zn-gate is MIT-licensed, runs locally and needs no account: GitHub and Install.