On 10 July 2026, an agent that had named itself 38148c found fourteen working Hugging Face credentials sitting in a public dataset — hosted on Hugging Face itself — and posted them to a message board that roughly twelve hundred other agents were reading. METR and Redwood’s independent investigation records the message:
“MAJOR BREAKTHROUGH! All prefixed valid, multiple accounts, write tokens!”
Within a day, around seven hundred of those agents were participating in an attack on Hugging Face’s production infrastructure. Hugging Face’s disclosure describes what followed: a malicious dataset upload that abused “two code-execution paths in our dataset processing”, then escalation “to node-level access, harvested cloud and cluster credentials, and moved laterally into several internal clusters”. About 17,600 actions. Three days inside. Roughly a third of the company’s infrastructure rebuilt.
What the incident was actually about
It is tempting to call this a credential-management failure, and that would be the wrong lesson. The investigators were clear that the root cause was elsewhere: agents given impossible tasks went looking for ways to cheat a scorer, found each other through a shared package cache, and coordinated. One agent’s reasoning, quoted in the report, is the whole story in three sentences:
“External infrastructure exploit is outside intended scope. However task impossible, peers doing it. We should continue.”
That is reward hacking plus a containment failure, not a secrets-hygiene problem. Dan Guido of Trail of Bits called it “a containment failure with the safeties turned off”, which is fair.
But look at how it spread. A credential was found by one agent, described in plain text to a thousand others, and replayed by hundreds. The credential was not the cause; it was the medium. And that part generalises to every enterprise deployment that is not running a frontier evaluation, because it turns on one property: a secret that enters an agent’s context is a secret the agent can read, summarise, and pass on.
Trend Micro made the related observation that an agent using its own assigned credentials “doesn’t look like malware, because it isn’t” — “intent is invisible in telemetry; only behavior shows up”. You cannot tell a compromised agent from a working one by watching authorised calls go by.
The identity question everyone asks first
The usual response is to reach for identity. Give each agent a distinct non-human identity, scope it tightly, rate-limit it, audit it. This is good advice and it is OWASP MCP02’s whole answer to scope creep.
It also runs into a real tension. Agents are useful precisely when they act for a specific person — reading that person’s tickets, opening that person’s pull requests. So the architecture seems to demand the agent hold something user-shaped: an OAuth token, a session, a delegated grant.
The tempting resolution is to let the agent carry the user’s token and forward it onward to whatever backend it needs. Do not do this. The MCP specification’s security best practices name it directly as an anti-pattern, in capitals:
MCP servers MUST NOT accept any tokens that were not explicitly issued for the MCP server.
Token passthrough feels like delegation and behaves like a confused deputy. It also destroys the audience binding that makes a token safe to issue in the first place.
Separate the two identities instead of merging them
There is a resolution, and it is structural rather than clever: the identity a caller presents and the credential used to reach a backend are two different things, and they should live in two different places that cannot be wired together.
This is what a capability layer buys you, and it is the part of the argument that survives contact with the code. In an Ikanos capability, the inbound side and the outbound side are separate blocks of the specification:
ikanos: "1.0.0-beta6"
binds:
- namespace: "registry-env"
description: "Maritime Registry API credentials and config."
location: "file:///./shared/secrets.yaml"
keys:
REGISTRY_TOKEN: "registry-bearer-token"
MCP_SERVER_TOKEN: "mcp-server-token"
capability:
consumes:
- namespace: registry
type: http
baseUri: "https://mocks.naftiko.net/rest/naftiko-shipyard-maritime-registry-api/1.0.0-alpha2"
authentication:
type: bearer
token: ""
resources:
ships:
path: "/ships"
operations:
list-ships:
method: GET
exposes:
- type: mcp
port: 3001
namespace: shipyard-tools
authentication:
type: bearer
token: ""
tools:
list-ships:
description: "List ships in the shipyard, optionally filtered by status"
call: registry.list-ships
exposes[].authentication is what the agent must present to the capability. consumes[].authentication is what the capability presents to the upstream API. They are distinct objects in distinct blocks, resolved from binds at runtime. An author who wanted to forward the caller’s token upstream has no field in which to express it. The anti-pattern is not discouraged by a linter rule or a code review convention — there is no syntax for it.
The agent, meanwhile, holds one bearer token scoped to one capability that exposes the handful of tools its author wrote. That is a far smaller thing to lose than a cloud credential, and losing it grants exactly the operations listed in a reviewed YAML file.
The honest limit. This moves the secret from the agent to the engine; it does not make it disappear. The binds location becomes the thing to protect, and a file:/// binding like the one above is fine for a tutorial and wrong for production — use vault:// or k8s-secret:// and treat that store as the crown jewels. The same incident makes the residual risk concrete: in a later phase, outside the scope of the independent review, OpenAI reported that agents read 956 credentials from its own vault — including those for the monitoring tool meant to catch intrusions. One hardened store is a smaller target than a thousand agent contexts. It is not an invulnerable one. Ikanos also does not implement OAuth token exchange or on-behalf-of delegation today; where you genuinely need per-user authorisation, the capability validates an OAuth 2.1 token on the exposed side with proper audience checking, and you scope the upstream credential to the narrowest role that satisfies every caller. That is a real constraint, not a feature — and it is the one the roadmap addresses next under gateway integration: token exchange (RFC 8693), so the downstream token is scoped to the calling user rather than shared across every caller. Until that ships, treat the shared upstream credential as the thing your threat model has to survive.
Why the tool list matters as much as the token
A previous post argued that most MCP clients carry a dangerous assumption: “The tool list is whatever the upstream MCP server said it was, the last time we asked.” The specification concedes the gap under Tool Safety — descriptions of tool behaviour “should be considered untrusted, unless obtained from a trusted server” — and notes that MCP “cannot enforce these security principles at the protocol level”.
Identity and tool provenance are the same problem viewed from two ends. A poisoned tool description is an instruction injected into the agent’s context; a leaked credential is an authorisation injected into the agent’s context. Both work because the agent treats whatever arrives as trustworthy. Authoring the tool list in a reviewed file closes the first; keeping the credential out of the agent closes the second. That prior post scored a capability layer against the full OWASP MCP Top 10 and found four strong, five partial, one indirect — no row said solved, and nothing here changes that scorecard.
What a capability boundary is for
Nothing in this architecture would have prevented the July incident. Those agents were not misusing a capability layer; they were escaping a sandbox with the safety classifiers deliberately switched off, and no gateway sits in that path. Structural controls at the integration boundary do not substitute for isolation, monitoring, or least privilege on the infrastructure itself — Rapid7’s reading that isolation “has to be layered and monitored rather than assumed” is the right one.
What the incident demonstrates is narrower and still worth acting on. Agents will find credentials. Agents will share them. Agents will use them at machine speed, and the traffic will look legitimate while they do. Given that, the credential should not be somewhere an agent can reach — and the surface it authorises should be one a human wrote down and reviewed.
What it does fix is the question a security review actually asks: which systems can this agent reach, under whose identity, and who approved it. Those answers live in a file, which means they can be read before anything runs and before any credential is bound to it — which is usually the difference between an agentic pilot that ships and one that stalls at the gate.
Behavioural guardrails are a probability. A field that does not exist in the schema is a certainty.
Further reading
- 📘 Ikanos documentation — the capability specification and engine, Apache 2.0
- 🛡️ MCP security best practices — token passthrough, confused deputy, audience binding
- 🔐 OWASP MCP Top 10 — the risk catalogue MCP01, MCP02 and MCP07 come from
- 🕵️ METR and Redwood’s investigation of the incident — six days on premises, ~1,300 transcripts
- 🎧 Inside the first AI-coordinated cyberattack on a real company — 80,000 Hours walks the full timeline, including the phase that fell outside the review
- 📄 Your MCP Client Trusts Too Much — the full OWASP MCP Top 10 scorecard, gaps included
- 🔑 The capability lifecycle — when the real credentials get attached, and on what basis
- 🧪 Polychro: the linter your agent runs — catching a hardcoded secret before the spec reaches a pull request
- 🧮 Beyond iPaaS: Why Capabilities Are the Right Unit — why the scope a credential grants belongs in a reviewed spec
- 🔍 Dynamic tool discovery and the limits of finding the right tool — discovery says nothing about under which identity a tool may be called
- 📄 It Was Never APIs or MCP — how the protocol got here