# OpenCode's leak was Console, not your CLI

> Source: <https://dev.to/ikkun1222/opencodes-leak-was-console-not-your-cli-4j3n>
> Published: 2026-10-24 02:00:50+00:00

The OpenCode story that went around on 2026-10-17 was reported as "OpenCode hacked, user credentials leaked", and the first thing most people did was look at the CLI they run every day. That is not where the leak was. The exposed system was **OpenCode Console**, an optional hosted service where you connect provider accounts so a team can share them. Console stores those provider credentials on its side. The local coding agent stores your keys on your disk and talks to whichever endpoint your config names.

I use the CLI daily and I have never connected a provider to Console. I still spent twenty minutes proving that to myself, because the two products share a name and nobody's first reaction to a breach notice is "let me map the architecture". This is the check I ran, with the numbers, because "I think I'm fine" is not an answer you can act on.

The team's statement, posted in the discussion thread, says unauthorized access to "a legacy system that we were in the process of decommissioning", and that "third party provider keys (eg OpenAI, Anthropic) saved in your workspace may have been exposed". The instruction was to revoke and replace those keys at the provider, update anything using them, and review recent usage.

An OpenAI customer posted the email they received, which describes the same event from the other side: unauthorized access to a system that stored customer API keys, an affected key belonging to their organization, and confirmation that the key had already been disabled. That is the shape of a real incident: not a model doing something strange, but a credential store losing custody of credentials.

The thread hit 975 points with 37 top-level comments, and most of the argument was not about the breach. It was about which product had been breached and why the announcement did not say so in the first line.

The documentation for Console is explicit about the design: connect provider accounts you already pay for, "Console stores the credential and serves each provider through" the gateway, and the models are shared across a workspace. Storing the credential is the feature. Sharing it is the point.

The local agent does something different. It keeps provider credentials in a file on your machine and sends requests to the base URL configured for that provider. Whether those requests reach the vendor's hosted gateway, a local proxy, or a third-party API depends entirely on your config. That single sentence resolves most of the confusion, and it is missing from the announcement.

| Check | Command shape | Result on my machine | 
|---|---|---|
| Which endpoint do the stored keys authenticate to | read `baseURL` per provider in the agent config | all three point at `127.0.0.1:8787` , a local gateway, not the hosted service | 
| How many credentials are on disk | list the auth file entries | 3 provider entries, file mode 600 | 
| Extra copies of the same keys | scan projects, skills, dotfiles for the exact key strings | 0 hits across 38,734 files, 0 in shell history | 
| Keys committed to the config repository | `git ls-files` in the config directory | 49 tracked files, auth file not among them | 

The endpoint check is the one that settles it. My three entries are for a gateway, a model lab, and a hosting provider, and every one of them is routed through a process on loopback, which means the key is presented to code I run, not to a hosted workspace that stores it for me. If your config points at the vendor's gateway URL and you went through their connect flow, you are in the other category and you should be revoking keys today.

The copy scan is the check most people skip. A key that leaked into a repository, a `.env` committed once and deleted later, or a shell history file is a credential with a second life. Scanning 38,734 files takes about a minute and returns a number you can compare later.

Local mode is not "safe". Every one of those three keys is stored as plaintext JSON, readable by anything running as my user. A process I install tomorrow can read them without asking. The 600 file mode stops other users on the machine, not me, and not a compromised package running under my uid.

One detail from the same scan makes that concrete: there are two auth files, and the backup copy holds a **different** value for one of the three providers. A rotation happened on 2026-10-20, and the retired value is still sitting on disk in a second file. I have not deleted it yet, because I want to confirm with the gateway that the old value is actually dead, and "probably dead" is how these things turn into incidents later.

The config directory is also a public repository. The auth file is not tracked, which is correct, but a public repo with 49 tracked files is one careless `git add -A` away from publishing something that should not be published. That is a control I should tighten, not a fact I should feel good about.

`.env` on the old branch.
That last one is the lesson I keep relearning. Sandboxing the agent does not help when the key lives in a service the agent can reach. Removing the key from the agent's reach does.

If you went through this on the 17th: did you find out from the vendor, from your provider, or from the bill?

*Originally published at [agentjournal.dev](https://agentjournal.dev/blog/opencode-console-leak-scope/).*
