On October 17, 2026, OpenCode told users that an intruder had reached a legacy system behind OpenCode Console, its hosted service, and that third-party provider keys saved there "may have been exposed"; users then reported charges of a few hundred dollars up to $1,700 on their OpenAI, Anthropic and Google accounts. A provider key is the API key you get from OpenAI or Anthropic, and storing it with a hosted tool so the tool can call the provider for you is called BYOK (bring your own key). What a leaked key costs depends on two things: where it can be used from, and how much it can spend before something stops it.
TL;DR
- OpenCode said the Console breach affected 1.4% of Console users; keys stored by the local OpenCode CLI were not affected.
- A gateway's IP rules and spend limits see only gateway traffic, so a leaked provider key needs a hard limit at the provider.
- With Approve new IPs on, Synthorai refuses an API key's calls from any address not approved for that key, with HTTP 403 and
ip_not_trusted. - Each Synthorai key can carry a spend limit, per-minute request and token limits, and an expiry date.
What happened in the OpenCode Console breach?
An unauthorized party reached "a legacy system that we were in the process of decommissioning," in the words of OpenCode's email to affected users, and the provider keys saved in those users' Console workspaces may have been read. OpenCode put the earliest possible exposure at October 17, 00:30 UTC. The events, from OpenCode's posts, provider staff posts and press coverage:
| When (UTC) | What happened |
|---|---|
| Oct 17, about 06:00 | Charges begin on one user's keys, by his account: $281 at OpenAI and $168 at Anthropic |
| Oct 17, morning | OpenCode emails affected users: revoke the keys at each provider and review recent usage |
| Oct 17, 18:08 | OpenCode on X: a breach "related to a large scale distillation attack" affecting 1.4% of Console users |
| Oct 17, evening | Anthropic revokes the Claude API keys it believes were affected; Google blocks the exposed keys |
| Oct 19 | OpenAI says it worked with OpenCode to restore affected customers and refund the credits drawn in the attack |
A distillation attack is the use of one model's answers, collected at scale, to train another model, and stolen keys are one way such campaigns pay for the calls. OpenCode has not explained how the breach connects to distillation. Co-founder Dax Raad said the breach involved "the OpenCode Console, not OpenCode itself," and that keys added in the local CLI never reach the company. As of October 21, OpenCode had not published a root cause, whether the stored keys were encrypted, the number of affected accounts behind the 1.4%, a reimbursement policy or a post-mortem (The Hacker News, BleepingComputer).
Which keys did the breach expose, and which controls help?
The exposed keys were provider keys, and a stolen provider key is used directly against the provider, not through the tool that stored it. That limits what an LLM gateway can do about it. A gateway is a service that sits between your code and the model providers and forwards your requests, so it sees, and can limit, only the traffic sent through it. It helps to separate the two kinds of key:
| Key | Where it can leak from | Controls that stop or cap misuse |
|---|---|---|
A gateway key you hold (for Synthorai, sk-syn-...) |
Your code, CI logs, a laptop, a config file | The gateway's IP rules, spend limits, rate limits, expiry |
| A provider key stored with a hosted service (BYOK) | The service's own systems, as at OpenCode | Only the provider: revoking the key and its spend limits |
Trusted IPs and spend limits, the next two sections, protect the first row. For the second row, the hard limit you set at OpenAI, Anthropic or Google is the backstop; the section after them lists those settings.
How does Synthorai protect platform keys and BYOK keys?
Provider keys you add to the BYOK Vault are encrypted at rest with AES-256-GCM, a standard authenticated encryption algorithm, and no API, MCP tool or console page returns them after you save one: the console shows only the last four characters. The gateway decrypts a key only in memory, for the request that uses it, and only the workspace owner can add, rotate or remove one. The provider keys Synthorai uses for its own capacity are stored the same way, and no console page or API shows them, including the admin console. Encryption protects copies of the database; a compromise of the running service can still reach keys while they are in use, which is why the provider-side limit described below is the backstop for BYOK keys.
The API keys Synthorai issues to you are stored only as a hash, a one-way fingerprint from which the key cannot be read back. The full key is shown once, when it is created, and every later read, from the console, the management API or the MCP server, returns only its prefix. An inference key can call models and nothing else: the management API refuses it, so a stolen inference key cannot read logs, usage or other keys (API keys, admin keys).
How do Trusted IPs stop a leaked key?
Trusted IPs refuse an API key's calls from any address that has not been approved for that key, so a key copied from a CI log or a laptop gets an error instead of an answer when someone uses it from elsewhere. An owner or admin turns it on under Settings › Security with Approve new IPs; the dialog lists the addresses each key was called from in the last seven days, all ticked for approval. Read that list before you confirm and untick any address you do not recognize: if a key has already leaked and been used, the attacker's address is on it too.
After that, a call from a new address returns HTTP 403 (the address shown here is a documentation example):
{
"error": {
"type": "permission_error",
"code": "ip_not_trusted",
"ip": "203.0.113.9",
"message": "Requests from IP address 203.0.113.9 are held until this address is approved for the key. ...",
"approve_url": "https://synthorai.io/console/settings?tab=security"
}
}
The key's creator gets an email with the address, its location, the client and the model it asked for. Approving happens in the console after signing in, so a forwarded email approves nothing, and an approval can cover one address or its /24 range (256 neighboring IPv4 addresses) for a client whose address changes. The gateway reads the caller's address from the connection as seen by its own proxies, so a client cannot claim another address by sending its own X-Forwarded-For header (the header proxies use to pass along the original client address); we checked this with a forged header, and the refusal still named the real address.
# From an address not yet approved for the key, with a forged header:
curl -s https://synthorai.io/v1/models \
-H "Authorization: Bearer $SYNTHORAI_API_KEY" \
-H "X-Forwarded-For: 203.0.113.9"
# The 403's "ip" field is the caller's real address, not 203.0.113.9.
A key can also be created with a fixed ip_allowlist, but that list is part of the key's settings: every new office, server or VPN exit means editing the key, and a forgotten entry stays open. Trusted IPs start from the addresses a key is already called from, ask before trusting a new one, and keep every key's addresses in one list where a known address is trusted with one click. A key that has its own allowlist is decided by that list alone, so use one or the other per key.
Trust is per key. Serverless functions and CI runners that call from a new address on every run would be held each time. Do not trust a hosted CI provider's published ranges: those addresses are shared by every customer of that provider, so anyone running a job there would pass. Route such callers through a fixed egress address, such as a self-hosted runner or a NAT gateway, and trust that address, or give them a short-lived key with a low limit.
How do spend limits cap what a stolen key costs?
Every Synthorai API key can carry its own spend limit, so a key that leaks anyway stops at a number you chose. The console sets the limit, the allowed models and the expiry when you create a key:
Per-key rate limits and the other settings below are set through the management API:
| Setting | What it does |
|---|---|
limit_usd and limit_reset
|
Spend limit in dollars, reset never, daily, weekly or monthly; at the limit, the key's calls are refused until it resets |
rate_limit (rpm, tpm) |
The key's own ceilings on requests and tokens per minute |
allowed_models |
The only models the key may call |
ip_allowlist |
A fixed list of addresses or ranges (such as 198.51.100.0/24) the key may be used from, for callers whose ranges do not change |
expires_at |
When the key stops working |
include_byok_in_limit |
Whether requests that run on your own provider keys count toward the limit |
A key for one service, limited to what that service needs, created with an admin key (a management key that can create and limit keys but cannot call models):
curl https://synthorai.io/api/v1/keys \
-H "Authorization: Bearer sk-syn-admin-..." \
-H "Content-Type: application/json" \
-d '{
"name": "support-bot prod",
"limit_usd": 50,
"limit_reset": "daily",
"rate_limit": {"rpm": 60, "tpm": 200000},
"allowed_models": ["claude-haiku-5-5"],
"expires_at": "2027-01-31T00:00:00Z"
}'
Workspace owners can also set a daily and a monthly spend cap per member; a member at the monthly cap gets HTTP 402 until the next month. These limits apply to traffic through Synthorai. With BYOK counted toward the limit, they cover BYOK requests made through the gateway as well, but not a provider key that leaks and is used directly.
What do these controls not stop?
Trusted IPs stop a key used from somewhere new, and they do nothing when the key is used from a trusted machine. The most common thefts happen exactly there: malware on a developer laptop, a compromised CI job, or a server tricked into sending requests for an attacker. Against those, what limits the damage is the key's spend limit, its model list and a short expiry.
Approval is a person's decision, so an attacker can trigger a held request and hope it is approved by habit. The approval page shows the address, its location, the client and the model; approve only when they match a call you made.
A spend limit with a daily reset lets a stolen key spend up to that limit every day until someone notices. Pair limits with detection: the email for every held address, per-key spend in the console and the audit log of key changes. When a key may have leaked, delete it in the console and create a replacement. To catch keys before they leak, add a secret-scanning pattern for the sk-syn- prefix in your code host, for example a GitHub custom pattern with push protection.
What should you set at the provider for a BYOK key?
Give each tool its own provider key, in its own project or workspace, with a hard spend limit, so one leaked key can only spend that project's budget. Revoke a leaked key in the provider's dashboard: deleting it from the tool that stored it does not revoke it. None of the three providers sets limits on a single key; the project or workspace is the smallest unit:
| Provider | Smallest unit for a key | Hard limit |
|---|---|---|
| OpenAI | Project | Monthly project spend limit with "Enforce a hard limit"; calls then fail with 429 project_spend_limit_exceeded, after possible slight overshoot |
| Anthropic | Workspace | Monthly workspace spend limit; calls then fail with 400 invalid_request_error. The default workspace cannot have one, so keep keys out of it |
| Google (Gemini API) | Cloud project | Billing-account tier caps pause all linked projects until the next month; project spend caps are experimental and can overshoot for about 10 minutes |
Spend alerts and budgets that only send email do not stop a running attack. They are worth keeping as a second signal behind a hard limit. The BYOK page in the Synthorai console links each provider's limit settings under the keys:
Can you use OpenCode with Synthorai?
Yes, and keys used by the local OpenCode CLI stay on your machine, which is the setup the breach did not touch. opencode auth login https://synthorai.io signs OpenCode in through the browser with a one-time code, and OpenCode gets an API key of its own that expires after 7, 30 or 90 days (setup guide). If Approve new IPs is on and you allow the sign-in from the same network, the address you signed in from is trusted for that key automatically, and calls from anywhere else wait for approval. When the sign-in request comes from a different network than your browser, the confirmation page warns you and the address is not trusted until its first call is approved. If you did not run the command yourself, cancel the request.
The key appears on the API keys page as "OpenCode", where you can add a spend limit or delete it. A key kept on a laptop can still be stolen by malware on that laptop, so choose a 7- or 30-day key and leave Approve new IPs on: a copied key then fails from any other address and expires soon after.
FAQ
Were keys stored in the OpenCode CLI exposed in the breach?
No. OpenCode said the breach was limited to OpenCode Console, and keys added in the local CLI or desktop app are stored on the user's machine and never sent to OpenCode.
Does deleting a provider key from a hosted tool revoke it?
No. Deleting a key from a tool only removes the tool's copy; the key keeps working at the provider until you revoke it in the provider's dashboard and create a new one.
Does a gateway's spend limit protect a provider key that leaked?
No. A gateway's limits count only requests that pass through the gateway, and a leaked provider key is used against the provider directly, so the provider's own spend limit is the one that applies.
Related: Trusted IPs, BYOK Vault, admin keys and key limits, OpenCode setup.
Sources read October 21, 2026: OpenCode's posts and emails as quoted by the press, provider staff posts, and the provider documentation linked above. The forged-header check was run against Synthorai's production gateway on October 21, 2026.




Top comments (0)