
The risk isn't linear. Every agent-to-agent call becomes an unlogged trust delegation nobody designed for.
Here’s the claim making the rounds this week: a fleet of AI agents is harder to secure than a single LLM. True, but lazily stated — and the lazy version leads people to spend money in the wrong place. They go buy another model-level guardrail, another prompt-injection classifier, and congratulate themselves. Meanwhile the actual problem is sitting one layer down, in the wiring.
Let me pressure-test the claim properly, because the why changes what you build.
Isn’t a single LLM already hard to secure?
Yes. One model with tool access is a genuine attack surface. Prompt injection is unsolved. Data exfiltration through a summarise-this-webpage tool is real. Nobody serious thinks a single agent is safe.
But a single agent has one property that makes it tractable: there’s exactly one place where intent becomes action. One request comes in, one identity is attached, one set of tools can fire. You can wrap that boundary. You can log every tool call it makes, diff the arguments, rate-limit the outbound calls, and reason about the blast radius on a whiteboard. It’s a locked room with one door. You don’t like everything that can happen inside, but you know where the door is.
The fleet breaks that. Not because the rooms got more dangerous — because you built a corridor of connecting doors and handed everyone the same master key.
What actually changes when agents call agents?
Think about a building site. One contractor with one key to one unit — you can manage that. Now imagine the general contractor gets a master key, hands a copy to the electrician, who hands a copy to their subcontractor, who leaves it with a guy delivering drywall. Every unit in the building is now reachable by someone three handoffs removed from anyone you actually vetted. Nobody did anything malicious. The topology is the vulnerability.
That’s what happens to authorization in a multi-agent system, and it happens by default. Here’s the pattern in practice:
One bearer token, minted for the user, threaded through every hop. It works on the first demo. It is also a disaster, because that token carries the union of every scope any agent might need — read calendars, read the CRM, send email, hit the payments API. The retrieval agent, whose only job is to fetch documents, is now holding a credential that can send email as the user. If retrieval reads an attacker-controlled document and gets injected, the injection inherits send-mail for free.
This is the compounding part. In a single agent, a successful prompt injection gets you that agent’s tools. In a fleet sharing credentials, a successful injection anywhere gets you the most privileged token in the chain. Least privilege didn’t erode gradually. It collapsed to a single point on the first line of code.
Why doesn’t my observability catch this?
Because your observability was built for services, and it assumes the caller’s identity is the caller. When agent A calls agent B, your traces will faithfully record that B ran. What they won’t record is why B believed it was allowed to. The delegation — A vouching for the user, on behalf of a plan the user never literally approved — has no field in your logs. There’s no span for “trust was transferred here.”
So you get an audit trail that reads: email_agent sent a message. Correct, useless. The question that matters — which upstream decision, triggered by which piece of possibly-poisoned input, caused a message to be sent? — is spread across four services, three prompts, and a token whose scopes nobody inspected at any hop. Distributed tracing shows you the calls. It does not show you the reasoning, and in an agent fleet the reasoning is the control flow. That’s the observability gap, stated precisely: you can see the packets and not the intent, and intent is the thing that went wrong.
So what’s the load-bearing fix?
Stop passing the user’s token down the chain. Give each agent its own identity, and mint a fresh, scoped-down credential at every hop. This is not a new invention — it’s OAuth 2.0 Token Exchange, RFC 8693, sitting in your identity provider right now, mostly unused by the agent crowd.
What comes back is a token that says, in effect: the retrieval agent, acting on behalf of this user, may read documents and nothing else, at this service, for the next few minutes. The act claim in the resulting JWT preserves the delegation chain — user, then agent, in order — so your audit log finally has the field it was missing. Now if retrieval gets injected, the injection holds documents:read. It cannot send email, because there is no email scope in the room it’s standing in.
The corridor still exists. But each connecting door now needs its own key, cut for that door, that expires. Compromise stops propagating.
Is this a solved problem, then?
No, and this is where I part ways with the tidy version of the story. The primitives exist — per-agent identity, token exchange, scoped credentials, a policy engine like OPA in front of every tool call. What doesn’t exist is the framework that wires them up for you. Look at the getting-started guides for agent frameworks. You’ll see the pattern: a single API key passed through the whole graph. Production-grade orchestration — where identity, least-privilege, and centralised policy are defaults rather than a security review finding — is still catching up to the capability demos, and the demos are what got funded.
So here’s the more precise claim to replace the one we started with. A fleet isn’t harder to secure because it has more agents. It’s harder because authorization stopped being a property of a request and became a property of a chain — and almost nobody is logging, scoping, or reasoning about the chain. Fix that layer and the number of agents barely matters. Ignore it, and every agent you add is another copy of the master key, handed to someone you never met.