Senthalan Kanagalingam (Technical Lead, WSO2 — 8+ years in enterprise IAM, now core maintainer of ThunderID) gave the most complete identity-and-isolation story of the day. The premise: an autonomous agent is a coworker that never sleeps, acts in microseconds, and is infinitely eager — and it can reach sub-agents, internal APIs, databases, external SaaS, and LLM providers. Under zero trust, "assume breach" stops being a precaution and becomes a description of an agent. The answer is to wrap each agent in a cell — WSO2's 2018 architecture pattern — with a hard boundary and a single gateway that acts as the policy enforcement point. On Kubernetes the cell is a namespace with default-deny network policy, mesh encryption, and an Envoy/Contour/Kgateway gateway. Then every edge — initiator→agent, agent→LLM, agent→tool, agent→agent — is gated with AuthN, AuthZ, and audit, using token exchange, intersection authorization (the user is the ceiling), CIBA for out-of-band human approval, and one trace ID that joins every hop, even the denials.
This is the security keystone of the Day 2 AI block. It builds directly on Day 1's Keycloak federated auth (DD09) and pairs with the agent-building talks DD10 / DD11 — they build the agent; this one contains it.
The newest coworker — and its reach
The framing was deliberately disarming: meet your newest coworker, an autonomous agent that's helpful, tireless, and self-directed — 0 sleep, microsecond speed, infinitely eager — running the classic observe → reason → act loop. Then the unsettling part: the agent's reach. A single agent can be invoked by users, systems, or other agents, and from there it can call sub-agents, internal systems (MCP servers, APIs, directories, databases, apps), external SaaS (Gmail, Maps, Sheets, Slack, GitHub…), and multiple LLM providers.
Zero trust — the lens
Three principles, applied with an agent twist:
| Principle | Meaning |
|---|---|
| Verify explicitly | Every request is authenticated and authorized on its own merits — no free pass for sitting inside the network. |
| Least privilege | Grant the minimum access needed — nothing more, and only for as long as it's needed. |
| Assume breach | Design as if the attacker is already in. Contain what any single compromise can reach. |
As far as the wall — the cell
If the question is "how far can a compromised agent reach?", the answer should be "as far as the wall." That wall is the cell — a pattern WSO2's Asanka Abeysinghe and Paul Fremantle introduced in 2018: a decentralised way to group components into an independently deployable, immutable unit. Three properties:
- A hard boundary — components inside are reached only through the cell, never directly.
- A gateway — all traffic in or out passes one controlled entry point.
- Bounded by domain — it maps to one business capability (DDD), owned by one team.
Fig 1 — the cell built around the agent: a hard boundary, a single gateway as the controlled entry point, bounded by one business domain.
At enterprise scale, cells compose: an Agent Cell talks to an Other Agent Cell (west-bound) and a Tool Cell (east-bound), takes invocations from users (north-bound), and reaches LLM providers and external systems (south-bound) — all inside an enterprise boundary, and every edge is gated with AuthN · AuthZ · Audit.
The cell in Kubernetes
The pattern maps cleanly onto cloud-native primitives — and this is the part most directly reusable:
| Cell concept | Kubernetes implementation |
|---|---|
| The cell | A Kubernetes namespace |
| Hard boundary | NetworkPolicy, default-deny — Cilium, Antrea, or Kube-OVN |
| Confidentiality | Encrypted traffic — Istio, Cilium, or Linkerd (mTLS) |
| The gateway | An ingress/egress gateway as the PEP (policy enforcement point) — Envoy Gateway, Contour, or Kgateway |
Administration — declared up front
The cell only works if its allowances are declarative, set at deploy time, not invented at runtime:
- Connections declared — allowed paths compile to NetworkPolicy.
- Permission map per action — the gate never guesses; each action has an explicit permission.
- Registered in the IDP — the agent has an identity with an owner, an auth mechanism, and a lifecycle kill switch.
- Policies in the engine — dynamic policy evaluation per request.
- Secrets stay in the vault — the agent holds references, never the credentials themselves.
Gating every edge
The heart of the talk: each kind of boundary crossing gets its own gating recipe. The agent never holds secrets or makes the trust decision — the gateway does.
Initiator → Agent
- The gateway trusts an IDP; every request carries a valid token.
- Real-time authorization — the policy engine decides per request.
- The token never reaches the agent — the agent gets only a reference token. (So a compromised agent can't replay the real credential.)
- The gateway can do the OAuth dance, so the agent is security-protocol agnostic.
Agent → AI providers
- Provider & model allow-list — only approved models, declared at setup.
- Vault-injected credentials — the agent never holds a standing API key.
- Usage limits & budgets, plus PII redaction & guardrails — sensitive data filtered at the gate before it leaves to a third-party model.
- An attributed audit trail — who, which model, what.
Agent → Tool: where the confused deputy dies
This is the cleverest section. When an agent calls a tool on a user's behalf, the danger is the confused deputy: the agent has broad privileges and could be tricked into using them for a user who shouldn't have them. The gating:
- Allow-list first — not on the agent's list → deny.
- Token exchange — mint a downscoped token (
sub= the user,act= the agent). - Intersection authorization —
user ∩ agent ∩ policy. The user is the ceiling. - The confused deputy dies here — the check is "does the user have this scope?", not "does the agent?"
- Authorization failure → escalate — insufficient scope or insufficient consent.
sub is the user and act is the agent, so the tool authorises against the user's rights while keeping a record that the agent acted. That's how you kill the confused-deputy problem instead of hoping the agent behaves.Human in the loop — user present
When the tool gate answers "need more scope or consent," the challenge flows back to the user, up the same path it came:
- Tool gateway returns
403 insufficient_scope. - The challenge propagates back through the agent's gateway (another 403).
- The gateway initiates an auth-code flow with the required scope — the gateway drives the screen, not the agent.
- The user consents to the IDP; a new token with the added scope is minted; the call replays.
Crucially, the agent only ever hears "blocked → pending → approved" — it never touches the consent flow or the token.
Human in the loop — user away (CIBA)
But agents often run in the background, with the user offline. There's no browser session to step up. The answer is CIBA (Client-Initiated Backchannel Authentication, an OpenID standard for out-of-band approval):
- The tool gate returns
403 insufficient_scope; the gateway initiates CIBA. - A push goes to the user's device — approve / deny / timeout.
- The gate polls, then decides: approved → new token → retry.
- Reserved for high-stakes actions only — not every call needs a human.
Agent → Agent: delegation that can only narrow
Agent-to-agent is mostly like agent-to-tool, with token exchange for downscoping and act-chaining. The token carries a nested act claim recording the full delegation chain:
{
"sub": "alice",
"act": {
"sub": "analytics_agent",
"act": { "sub": "reporting_agent" }
},
"scope": "write_report"
}
Two rules make this safe: authority can only narrow, never widen (each hop downscopes), and the ingress gateway performs its own check rather than trusting the caller. So a three-agent chain ends with strictly less authority than it started — and every link is attributable to Alice.
Audit — one cross-boundary sweep
Because every edge passes a gateway, the system is observable by default. The deck's audit stack: collect & enrich with OpenTelemetry, Fluentd, Hubble; metrics & traces with Prometheus, Jaeger, Thanos. The key property:
What the pattern buys you
The closing split made the value concrete for two audiences:
| Platform engineers — "security you can operate" | Developers — "security you don't write" |
|---|---|
| Policy lives at the platform, not in agent code | Never touch a token or a credential |
| Blast radius capped by default-deny | On-behalf-of, step-up, CIBA — all from the platform |
| One kill switch — suspend the identity, tokens die | Declare tool → action → scope, get authz |
| One trace — the full forensic chain | Swap models — security isn't in the prompt |
The one-line thesis: developers build the agent — the platform builds the trust.
Concrete threats → which control catches them
The pattern is easier to trust when you map each defence to the attack it stops. None of these rely on the agent behaving correctly — that's the point.
| Attack | What goes wrong | Control that stops it |
|---|---|---|
| Prompt injection → exfiltration | A poisoned input convinces the model to POST data to an attacker endpoint. | Default-deny NetworkPolicy — the connection isn't on the declared allow-list, so the packet is dropped at the cell wall regardless of what the model "decided." |
| Confused deputy | Agent with broad rights is tricked into acting for a user who lacks them. | Token exchange + intersection authz — the tool checks the user's scope (sub=user), not the agent's. User is the ceiling. |
| Stolen agent credential | An attacker pops the agent pod and grabs its token. | The agent only ever holds a reference token; real tokens live at the gateway, secrets in the vault. Nothing replayable to steal. |
| Privilege creep via delegation | A chain of agents accumulates more authority than any single user granted. | Act-chaining where authority can only narrow, plus a fresh ingress check at every hop. The chain ends with strictly less than it started. |
| Silent over-reach | An agent quietly does something high-stakes nobody approved. | CIBA out-of-band approval for high-stakes actions; one trace ID with full attribution makes even denials auditable. |
| Data leak to a third-party model | PII ends up in a prompt sent to an external LLM provider. | PII redaction + guardrails at the agent→AI gate, model allow-list, and an attributed audit trail of who sent what to which model. |
Choosing the pieces
The pattern names categories, not products. The realistic options for each cell primitive:
| Role | Options | How to choose |
|---|---|---|
| Boundary (NetworkPolicy) | Cilium, Antrea, Kube-OVN | Cilium if you also want L7 policy and Hubble flow visibility (and it pairs with the fintech talk's eBPF story); Antrea/Kube-OVN if that's already your CNI. |
| Confidentiality (mTLS) | Istio, Linkerd, Cilium mesh | Linkerd for the lightest operational weight; Istio if you need its richer policy/telemetry; Cilium mesh to avoid a sidecar entirely. |
| Gateway / PEP | Envoy Gateway, Contour, Kgateway | Whichever you can configure to do the OAuth dance and call your policy engine per request — the PEP role matters more than the brand. |
| Identity / token exchange | Keycloak, ThunderID, any RFC 8693 IDP | Must support token exchange (RFC 8693) and CIBA. This is non-negotiable — the whole intersection-authz story depends on it. |
FAQ
What is a "cell" and why use it for agents?
A cell (WSO2, 2018) is an independently deployable unit with a hard boundary and a single gateway — components inside are reachable only through that gateway, and it's bounded by one business domain. For agents it answers "how far can a compromise reach?" with "as far as the wall." On Kubernetes the cell is a namespace with default-deny NetworkPolicy and a gateway acting as the policy enforcement point.
How does this stop the confused-deputy problem?
By intersection authorization with token exchange. When the agent calls a tool for a user, the gateway mints a downscoped token (sub = user, act = agent) and the tool authorises against the user's rights — "the user is the ceiling." The agent's broad privileges can't be borrowed to exceed what the actual user is allowed to do.
What is CIBA and when is it used?
CIBA (Client-Initiated Backchannel Authentication) is an OpenID Connect standard for out-of-band approval. When an agent runs in the background with no user session and hits an insufficient_scope, the gateway pushes an approve/deny prompt to the user's device, polls for the result, and on approval mints a new token and retries. It's reserved for high-stakes actions — not every call needs a human.
Why does the token never reach the agent?
Because the agent is the most likely thing to be compromised (prompt injection, a confused model). The gateway holds the real token and hands the agent only a reference, so a compromised agent can't exfiltrate or replay a live credential. Secrets likewise stay in the vault — the agent holds references, never the keys.
How do agent-to-agent delegation chains stay safe?
Token exchange with a nested act claim records the full chain (e.g. alice → analytics_agent → reporting_agent), authority can only narrow at each hop, and every ingress gateway re-checks rather than trusting the caller. The chain ends with strictly less authority than it began, fully attributable to the original user.
Isn't a gateway in front of every call a performance problem?
It adds a hop, but the work is cheap relative to an agent's actual cost. The expensive part of an agent call is the LLM inference (hundreds of milliseconds to seconds); a token check and policy evaluation at the gateway is sub-millisecond to low-millisecond. The gateway also lets you cache token-exchange results and short-circuit allow-list denials before any network round-trip. The latency budget is dominated by the model, not the PEP.
How is this different from a normal service mesh with mTLS?
A service mesh gives you the transport half — mutual TLS, default-deny, traffic encryption. That's necessary but not sufficient for agents. This pattern adds the identity and intent half: token exchange so calls carry the acting user, intersection authorization so the user is the ceiling, CIBA for human approval, and act-chaining for delegation. The mesh stops an unauthorised connection; the gateway stops an unauthorised action. Agents need both.
Can I retrofit this onto agents already in production?
Mostly yes, because the design deliberately keeps security out of the agent code — so you're adding platform layers, not rewriting agents. The order that usually works: (1) put each agent in its own namespace and flip on default-deny NetworkPolicy; (2) front it with a gateway and move token handling there; (3) register the agent identity in the IDP and pull secrets into a vault (agent holds references); (4) introduce token exchange and intersection authz at the tool gates; (5) wire one trace ID across both layers. Each step shrinks blast radius on its own.
What happens when the IDP or gateway is down?
By design the system fails closed: no valid token or no reachable PEP means the call is denied, not waved through. That's the correct behaviour under "assume breach" — availability of an agent action is less important than not authorising an unverified one. In practice you make the gateway and IDP highly available (they're standard cloud-native components) rather than relaxing the check, and you reserve CIBA/step-up for high-stakes actions so a consent outage doesn't block routine work.
Takeaways
- For agents, "assume breach" is a description, not a precaution. Fast, eager, broadly-connected, model-driven — contain accordingly.
- Wrap each agent in a cell — hard boundary + single gateway PEP, mapped to a Kubernetes namespace with default-deny NetworkPolicy and mesh mTLS.
- Declare everything up front — connections compile to NetworkPolicy, permissions per action, identity in the IDP with a kill switch, secrets in the vault (agent holds references).
- Gate every edge. Reference tokens to the agent, model allow-lists, token exchange + intersection authz (the user is the ceiling) to kill the confused deputy, CIBA for offline human approval, act-chaining that only narrows.
- One trace ID joins app and network layers — even denials — so you get full forensic attribution for what happened and what was blocked.
- Developers build the agent; the platform builds the trust.
Next in Day 2 — Service Networking Within Air-Gapped Environments, on running cloud-native with no internet.
References
- KubeCon Mumbai 2026 — Day 2 index · the rest of Day 2
- RFC 8693 — OAuth 2.0 Token Exchange · the act claim & downscoping
- OpenID CIBA · out-of-band backchannel consent
- Kubernetes NetworkPolicy · default-deny boundaries
- Day 1 DD09 — Keycloak federated auth · the identity foundation