OpenCode Console Breach in 2026: How to Protect Your AI API Keys
Hello HaWkers, on Saturday, October 17, 2026, OpenCode, one of the most popular open source coding agents for the terminal, confirmed it had been breached. OpenAI, Anthropic, and Google API keys that some users had saved in the OpenCode Console may have been exposed. Within hours, reports of charges nobody authorized started showing up: from a few hundred dollars up to $1,700 in a single night. The Hacker News thread passed 970 points.
The incident is small in terms of people affected, but big in what it teaches. The question every developer who uses AI daily should ask right now is simple: if the tool where I stored my key were breached today, how much would I lose before I noticed? In this article you will understand what actually happened, what is still unknown, and walk away with a practical, code-backed playbook to revoke, test, and cap your AI API keys.
What Happened in the OpenCode Console
First, an important distinction. OpenCode has two parts:
- The open source app and CLI, which run on your machine. When you connect a provider with the
/connectcommand, the key is saved locally in~/.local/share/opencode/auth.json. - The OpenCode Console, the company's online platform. There, a workspace can connect provider accounts so team members can use the models without ever seeing the provider key. That means the key is stored on OpenCode's servers.
The incident only hit the Console. According to co-founder Dax Raad, the open source app and CLI were not affected, and keys used in local OpenCode never reach the company.
The timeline disclosed so far:
- Oct 17, 00:30 UTC: earliest possible key exposure time, according to the email sent to affected users.
- Oct 17: security researcher Matt Johansen posts on X a screenshot of the email with the subject "Action Required: Potential provider key breach".
- Oct 17: OpenCode publicly confirms a security breach "related to a large-scale distillation attack".
- Oct 19: TechCrunch and BleepingComputer publish the first full reports.
The company said the breach happened in a legacy system that was already being decommissioned and that 1.4% of Console users were affected. If you did not get the email, you were not impacted.
What Was Exposed and How Much It Cost
What leaked were the external provider keys stored in Console workspaces. The email mentions OpenAI and Anthropic as examples, and the reports also include Google keys. The company stated that no other data appears to have been exposed.
The impact showed up on the bill first. Developers reported spending alerts from OpenAI, Anthropic, and Google, with charges in the hundreds of dollars and some totals above $1,000. The largest reported amount was $1,700 overnight. Some users also had projects suspended by Google, with the justification of "abusive activity consistent with hijacked resources".
The providers responded quickly:
- Anthropic and Google blocked the keys they believed were exposed.
- OpenAI worked with OpenCode to restore access and refunded the credits used in the attack.
- OpenCode said refunds for unauthorized charges are in progress.
What Is a "Distillation Attack"
OpenCode chose the term distillation attack to describe the incident, and that sparked debate. Distillation by itself is a legitimate technique: you train a smaller model using the outputs of a bigger, more capable one. It becomes an attack when those outputs are obtained at scale, without authorization, and often paid for by someone else.
The economics are brutal. A stolen key means free frontier model output for the attacker, charged to the key owner's credit card. The more expensive the model, the more valuable the key.
It is worth noting that not everyone bought this explanation. Part of the community argued that this is simply a leak, and that the link to distillation has not been proven. OpenCode also has not explained how the attackers got in, how the keys were stored, or the full extent of the misuse. The company says the incident is still ongoing.
The Strangest Part: AI Refusing to Defend
One detail from Dax Raad's account caught people's attention. He said the attackers kept trying to break into the systems nonstop for at least 48 hours, using AI agents, and that the team put defensive AI agents to work around the clock.
The problem: the models used in the investigation helped at first, but refused to continue once the malicious code tripped their safety filters. Raad called it "a little frustrating". Things only stabilized when OpenCode got access to OpenAI's Daybreak program, which gives verified users models that are less likely to refuse defensive security work.
This is not the first time it has happened in 2026. In July, Hugging Face reported that commercial models refused to analyze logs from an intrusion carried out by autonomous agents and summed up the dilemma: guardrails "can't tell an incident responder from an attacker". If your incident response plan depends on an AI assistant, have a plan B.
Why Coding Agents Became Credential Magnets
The OpenCode case is a symptom of something bigger. AI coding tools concentrate exactly what an attacker wants:
- Keys from several providers in one place, often with no spending limit.
- High-value keys, because they unlock the most expensive models.
- Usage that is heavy by nature, so a consumption spike takes a while to look abnormal.
And the leak does not need to come from a sophisticated breach. AI keys exposed in public repositories are an old problem, as I showed in the article about AI companies that exposed API keys on GitHub. And agents attacking the supply chain are already reality, as in the RubyGems attack carried out with OpenAI agents.
The practical takeaway: treat every AI key like a credit card with no limit, because without configuration, that is exactly what it is.
Practical Playbook: How to Protect Your AI Keys
Now the part that matters for those who were affected and, above all, for those who were not. There are four steps, from most urgent to most structural.
Step 1: Revoke and Confirm the Key Is Dead
If you got the OpenCode email, or had any key in the Console, the official recommendation is to revoke and replace the key directly in each provider's dashboard. Changing your OpenCode password or deleting the local config does not invalidate a key that has already leaked.
After revoking, confirm it. A revoked key should return HTTP 401 on a test call. The script below tests all three at once without spending tokens, because it only lists the available models:
#!/usr/bin/env bash
# check-keys.sh - confirms old keys were actually revoked
# Usage: OLD_OPENAI_KEY=... OLD_ANTHROPIC_KEY=... OLD_GEMINI_KEY=... ./check-keys.sh
check() {
local name="$1" status="$2"
if [ "$status" = "401" ] || [ "$status" = "403" ] || [ "$status" = "400" ]; then
echo "OK $name revoked (HTTP $status)"
else
echo "ALERT $name still responds (HTTP $status) - revoke it now"
fi
}
# OpenAI: model listing endpoint, does not consume tokens
if [ -n "$OLD_OPENAI_KEY" ]; then
s=$(curl -s -o /dev/null -w "%{http_code}" https://api.openai.com/v1/models \
-H "Authorization: Bearer $OLD_OPENAI_KEY")
check "OpenAI" "$s"
fi
# Anthropic: same idea, with the required headers
if [ -n "$OLD_ANTHROPIC_KEY" ]; then
s=$(curl -s -o /dev/null -w "%{http_code}" https://api.anthropic.com/v1/models \
-H "x-api-key: $OLD_ANTHROPIC_KEY" -H "anthropic-version: 2023-06-01")
check "Anthropic" "$s"
fi
# Gemini: the key goes in the query string
if [ -n "$OLD_GEMINI_KEY" ]; then
s=$(curl -s -o /dev/null -w "%{http_code}" \
"https://generativelanguage.googleapis.com/v1beta/models?key=$OLD_GEMINI_KEY")
check "Gemini" "$s"
fiA tip for Gemini: create the new key in AI Studio before disabling the old one, so you do not take down what is running in production in the middle of the swap.
Step 2: Find Every Place the Old Key Lives
Revoking without updating breaks your applications; updating without searching leaves the old key hidden in some forgotten .env. Before closing the incident, sweep the project and your home directory for the known key formats:
# find-ai-keys.sh - searches for AI keys in plain text
# Common prefixes: sk-ant- (Anthropic), sk-proj- / sk- (OpenAI), AIza (Google)
PATTERN='(sk-ant-[A-Za-z0-9_-]{20,}|sk-proj-[A-Za-z0-9_-]{20,}|sk-[A-Za-z0-9]{32,}|AIza[0-9A-Za-z_-]{35})'
# Current project files, skipping node_modules and .git
grep -rEn --exclude-dir={node_modules,.git,dist} "$PATTERN" . 2>/dev/null \
| sed -E 's/(sk-ant-|sk-proj-|sk-|AIza)[A-Za-z0-9_-]{6}[A-Za-z0-9_-]*/\1******/g'
# Git history: the key may have been removed from the file, but not from the commit
git log -p --all 2>/dev/null | grep -Eo "$PATTERN" | cut -c1-12 | sort -uThe sed masks the key in the output, so you do not paste a secret into a shared terminal or a screenshot. If anything shows up in the git history, revoking is mandatory: rewriting history does not undo the fact that the key was published.
Do not forget the places outside your code: CI secrets (GitHub Actions, Vercel, Railway), server environment variables, the OpenCode Console workspace itself, and any other AI tool where you pasted the same key.
Step 3: Limit the Damage Before the Next Incident
The second half of the lesson is shrinking the size of the loss when, not if, a key leaks again:
- One key per service. If OpenCode, your backend, and your test script share the same key, you have to rotate everything at once and you cannot tell where the spending came from.
- Spending limits. On Anthropic, workspaces let you set their own monthly limit, always below the organization limit, and keys are tied to the workspace where they were created. On OpenAI, limits live at the organization and project level, so create a separate project for each tool.
- Minimum scope. Google recommends restricting Gemini keys to the Gemini API only.
- Per-key monitoring, so an anomaly shows up quickly.
For the last point, you can build a simple alert. The Node.js example below compares the last hour's spending with the weekly average and fires a warning when the jump is large. The fetchHourlySpend function is an integration point: wire it to your provider's usage API or your own call log.
// spend-alert.mjs - spending spike alert per API key
// fetchHourlySpend(keyId) must return an array with the spend (USD) for the last 168 hours
import { fetchHourlySpend } from './usage-source.mjs';
const KEYS = ['opencode-local', 'backend-prod', 'ci-tests'];
const SPIKE_FACTOR = 5; // 5x above average already deserves attention
const MIN_ABSOLUTE = 10; // ignores spikes below $10
async function checkKey(keyId) {
const hours = await fetchHourlySpend(keyId);
const lastHour = hours.at(-1) ?? 0;
const history = hours.slice(0, -1);
const avg = history.reduce((sum, v) => sum + v, 0) / Math.max(history.length, 1);
// Only alert when the jump is both relative AND absolute, to avoid noise
if (lastHour > MIN_ABSOLUTE && lastHour > avg * SPIKE_FACTOR) {
return `Key ${keyId}: $${lastHour.toFixed(2)} in the last hour (average $${avg.toFixed(2)})`;
}
return null;
}
const alerts = (await Promise.all(KEYS.map(checkKey))).filter(Boolean);
if (alerts.length > 0) {
// Swap for Slack, email, or PagerDuty
console.error('Spending spike detected:\n' + alerts.join('\n'));
process.exitCode = 1;
}Running this every 15 minutes on a cron, an attack like the one on OpenCode would be noticed in minutes, not the next morning, when the bill has already hit $1,700.
Step 4: Take Care of Your Local Keys Too
The incident did not hit the CLI, but it is worth looking at it. Locally saved keys sit in plain text in a file in your home directory. Any process running as your user can read it, and that includes a malicious npm package or a compromised extension. The bare minimum is making sure only your user has access:
# Checks and fixes the permissions of the OpenCode credentials file
AUTH="$HOME/.local/share/opencode/auth.json"
if [ -f "$AUTH" ]; then
ls -l "$AUTH" # should show -rw------- (600)
chmod 600 "$AUTH" # read and write for the owner only
fi
# Bonus: never let the file end up in synced backups or public dotfiles
grep -q "opencode/auth.json" ~/.gitignore_global 2>/dev/null \
|| echo ".local/share/opencode/auth.json" >> ~/.gitignore_globalFile permissions do not protect against everything, but they close the door on the silliest leak: the file ending up in a public dotfiles repository.
What We Still Do Not Know
To keep our feet on the ground, here is what remains open as of this article's publication:
- How many accounts exactly make up the 1.4% of Console users.
- How the attackers got into the legacy system and for how long they had access.
- How the keys were stored and whether all of them were actually copied.
- Whether the "distillation attack" label holds up or whether this is just a leak with key misuse.
If you use the Console, follow OpenCode's official channels and review your key usage going back to at least October 16.
Outlook: API Keys Have Become Financial Assets
The OpenCode incident shows that, in 2026, attackers treat an AI API key as money, and rightly so. The most expensive models cost a lot per token, and a key without a limit is unlimited credit for whoever finds it. At the same time, platforms that store keys "to make life easier for the team" become targets as valuable as a payment gateway.
The good news is that the defense requires nothing exotic: one key per service, spending limits on all of them, per-key monitoring, and the habit of testing whether the old key is really dead. Those who already did this saw the incident as news. Those who did not, saw it on their bill.
Let's go! 🦅
📚 Want to Keep Up With What Is Coming?
This article covered the API key leak in the OpenCode Console and how to protect your AI keys, but the ecosystem changes every week and not everything becomes an article here.
On X I share what I am testing, behind the scenes of my projects, and the news that shows up before it turns into a post.
Follow Me There
💡 Daily content about development, career, and the tools I actually use

