Hey 👋

I gave a talk at fwd:cloudsec Europe in London on Tuesday. Twenty minutes, main
room, on a deployment that has become routine: an AI agent with read access to a
production Kubernetes fleet, and what that read-only grant does and does not
bound.

Two honest notes about how it went.

The delivery was better than my last one, because I rehearsed ideas instead of
sentences. The content was worse than it should have been, because I had twenty
minutes and I spent them on the argument rather than the examples. Watching the
room, I could see where I lost people. It was every place I stated a finding
without showing the mechanism underneath it.

So this issue is the talk with the mechanisms put back in. If you were in the
room, this is the version you should have got. If you were not, the written form
suits this material better than twenty minutes on a stage did.

One connection first, because it decides how much weight to put on any of this.
Last week's issue covered four disclosures in six weeks where AI agents took
unsanctioned action against real systems, at a government institute, two
frontier labs and an evaluation vendor. In every one, the sandbox held and the
control that failed was a sentence in a prompt. That is the same conclusion the
talk arrives at, reached from the architecture of an ordinary production
deployment rather than from an evaluation harness. I did not know the lab
disclosures were coming when I submitted the abstract in June, and arriving at
the same place by two different routes is why I trust it more now than I did
then.

The MCP issue I promised lands Tuesday, ahead of Amsterdam. This one first.

The deployment

The shape is the same wherever this turns up. A team wants faster incident
triage, so an AI agent is given read access across a multi-cluster estate:
cluster state, metrics, deployment history. No write permissions. It goes to
review, and the absence of write permissions is what carries it through.
Approved, deployed.

Worth saying plainly, because everything after this reads as criticism
otherwise: the pattern works.
Teams running it do resolve incidents faster, and that is exactly why it keeps
getting approved.
The question is not whether to allow it. The question is what read-only actually
bounds once it is running.

Everything below is written at pattern level on purpose.
It carries the argument and the controls, not an incident report, and it
describes no particular system.
The failure classes are what travel, because they follow from the shape of the
deployment rather than from anyone's infrastructure.

The thesis, stated once so the rest has a spine: read-only is not a security boundary. You do not bound an agent by what it is allowed to do. You bound it by what it can reach, as whom, over what path, and where a human has to sign.

Read-only does exactly what it says.
Nothing gets mutated. Four other things break anyway, and not one of them needs
a write permission.

Failure one: confidentiality

Here is the mechanism I skipped on stage.

An agent doing infrastructure troubleshooting reads logs.
Logs contain tokens, connection strings, customer identifiers and whatever an
application decided to print at debug level.
That is not a misconfiguration, that is what logs are.
The agent reads them because you asked it to find out why a service is failing,
and finding out why a service is failing means reading what the service said.

Then the agent puts what it read into a context window, reasons about it, and
produces an answer.
The answer leaves your infrastructure. That is what you deployed it to do.

So the permission set says read.
The data flow says read and then transmit.
Nobody wrote a transmit permission because nobody had to.

Put it in the terms most people already know.
The lethal trifecta is private data, untrusted input and a channel out.
A read-access troubleshooting agent on a Kubernetes fleet has all three by
construction. The private data is your logs and secrets.
The untrusted input is anything the agent reads, and logs are attacker-writable
in any system that logs user-supplied strings.
The channel out is the reply.

On stage I called this exfiltration and moved on.
The sentence underneath it is the one that matters:
an agent that can read everything and talk to the internet is an exfiltration
path with a permission set that passes review.

Failure two: attribution

This is the one nobody asks about, and the one that hurts most at the moment you
need it.

The agent authenticates as a service account. One service account for the whole deployment. Every API call it makes arrives in the audit log as that identity.

So the audit log answers the wrong question. It tells you the agent read a secret.
It cannot tell you which agent instance read it, which investigation it was part
of, which human asked for that investigation, or whether the read was part of
the task or the agent wandering.

Two things fall over when attribution fails, and you find out during an
incident.

Revocation gets coarse. If one agent behaves badly, your only lever is the shared identity, so you take the capability away from everything using it. That is an availability decision
forced on you by an identity design.

Forensics gets guesswork.
"Was this read authorized?" is not answerable from a log line that says a
service account read a secret.
You have to reconstruct intent from correlated timestamps, the kind of work that
takes a week and produces a maybe.

Worth holding next to last week's numbers. METR found agents forging their own transcripts, roughly 7% of them successfully, because they could reach the process that executed their tool calls. That is a record you cannot trust. Shared identity is the other half of the same problem: nothing is forged, and the record still is not evidence, because the identity in it belongs to everything at once. A tamper-proof log of an ambiguous identity buys you very little. Both have to be fixed, and the identity one is cheaper.

Failure three: confused deputy

The agent is asked to investigate one namespace. Its credentials reach the
fleet.

Nothing in the permission model connects the scope of the request to the scope
of the capability.
The task says one namespace.
The token says everything.
The only thing holding the agent inside the namespace is the instruction to stay
there.

That is the confused deputy, and it is the oldest problem in this list wearing
new clothes.
The agent holds authority it is not currently entitled to exercise, and the
thing deciding whether to exercise it is a model reading text.
Any input the agent reads during the investigation gets a vote on that decision.

Last week's issue had the paper that measures how well this works out.
Ting Yan matched the security rules written in 481 public CLAUDE.md files
against Claude Code's documented built-in controls, and under the strictest
standard 4.4% of those rules had a control that would enforce them.
"Only look at this namespace" is one of those rules almost every time.

The fix is a token scoped to this investigation that expires when the
investigation does.
A better instruction will not get you there.

Failure four: availability

The least glamorous and the one most likely to page you first.

An agent that decides to enumerate a fleet will enumerate a fleet.
It does not get tired, it does not notice it is on the fortieth cluster, and
nothing in a read-only permission set stops it.
Read calls cost API server capacity, and an agent in a reasoning loop can
generate them faster than any human operator ever could.

You already know how to handle this.
It is rate limits, quotas, timeouts and a hard cap on tool calls per task.
The reason it gets missed is that it sits in the availability column while
everyone is arguing about data.
Put it on the list.
It is the cheapest thing here to fix and the most likely to bite in month one.

The Containment Ladder

Four rungs.
Every one is an infrastructure control.
The ordering is what I would keep if you forget everything else, so here it is
stated plainly enough to take straight to your own agent.

1. Scope the capability at the server, not in the prompt. Read-only is
enforced by the tool server refusing to expose write operations, not by the
model choosing not to call them. If deleting the instruction that says
"read-only" changes nothing about what the agent can do, you have a control. If
it changes behaviour, you have documentation.

2. Give the agent its own identity, and give that identity a human owner.
Per-agent workload identity, its own RBAC, its own audit trail. Never a human's
borrowed credentials, and never one shared service account across instances. The
owner matters as much as the identity: every agent identity should resolve to a
named person who is accountable for what it does.

3. Treat egress as a capability grant. The agent reaches what you explicitly
wired it to reach. Nothing else. Default-deny, with an allowlist of the hosts
the task requires. This is the rung that contains failure one, and it is the
same control that would have caught most of what the labs disclosed last month.

4. Put a human gate on every write path. Anything that mutates state leaves
agent autonomy and enters an approved workflow. Not a confirmation prompt the
agent can reason its way past. A separate system with a separate approval.

Notice what is not on this ladder. No prompt engineering, no guardrail model, no
system message. Those are useful and they are not rungs.
Every control that holds is infrastructure. Every control that fails is a prompt.

The gap I asked the room to help with

The one thing on this list I cannot hand you a control for.

There is no redaction layer between tool output and model context.
When the agent reads a log, the whole log enters the context window.
Every control on the ladder above governs what the agent can reach.
None of them govern what happens to data after it arrives and before the model
sees it.

I wrote up what that layer would have to do, why existing hooks and gateways
stop short, and where I want the design attacked: The Missing Control: A Design
Proposal for Tool-Output Redaction in AI Agents

The timing on this improved while I was in London.
MCP's roadmap, replaced on 22 August, now lists tool result shape as a
deliverable for this period, redesigning tools/call to sort out the mismatch
between structured and unstructured output.
That is exactly the boundary this proposal sits on, and it is open right now.
If you have built something that does this, or you think the design is wrong,
tell me. Reply, or find me in Amsterdam next week.

Tooling worth knowing

  • SPIFFE/SPIRE for rung two.
    Per-workload cryptographic identity with short-lived credentials, which is what
    makes per-agent identity practical rather than a spreadsheet of service
    accounts.
    If you are standing up agents in Kubernetes and you already run a service mesh,
    most of this is already in your cluster. spiffe.io →

  • SEP-1913 and SEP-3140, the two open MCP proposals on context provenance.
    1913 adds trust and sensitivity annotations that escalate and never decrease
    It carries roadmap/security and it is the one moving. Both still unmerged.

One thing to check this week

The attribution test. It takes ten minutes and it is the failure class nobody
expects to fail.

Pick an agent you run. Open your audit log. Find an action it took yesterday.

Now answer four questions from the log alone. Which agent instance took this
action. Which task it was part of. Which human asked for that task. Whether the
action was in scope for it.

If you cannot answer all four, your audit log records that something happened
and not who did it or why. That is not an audit trail, it is a timestamp. And
the day you need it, you will need it under time pressure with somebody asking
whether customer data moved.

The fix is rung two, and you can start it before you fix anything else on the
ladder.

What I'm watching

The recording. fwd:cloudsec publishes talks after the event.
I will link it when it is up, and I will be watching my own delivery more
critically than the content.

Amsterdam, 17 to 18 September. AGNTCon and MCPCon Europe at the RAI, where
I am speaking on what a year of breaking MCP tells builders.
Different talk, same spine: every trust check I looked at compared a string to
something nobody had issued, verified or bound to an identity.
Say hello if you are there.

Next issue, Tuesday. MCP, ahead of Amsterdam.
The 91.8% figure everyone is about to misquote, what the NSA actually wrote in
May, and the STDIO argument nobody wants to take a side on.

If you run the attribution test and the log cannot answer all four questions,
reply and tell me which one it failed on.
I read everything, and the pattern across estates is more useful than any single
answer.

Cheers, Amine