Delegation tokens
When an agent calls a tool, the tool has no idea who it is acting for. It knows a request arrived. It does not know which agent instructed it, or which person that agent was working on behalf of.
A delegation token closes that gap.
The operator signs a short-lived JWT naming the user, the agent, the workspace
and the session. The agent’s runtime requests it from the operator and sets it
on the outbound tool call as X-Omnia-Delegation. The tool decides what to do
with that: refuse the call, narrow a query, write an audit line, or nothing at
all.
How a token gets to a tool
Section titled “How a token gets to a tool”The runtime asks the operator for a token when it calls a handler that opted in. The operator checks who is asking with the Kubernetes API, signs the token, and the tool checks the signature against the operator’s published keys.
sequenceDiagram participant R as Runtime container participant O as Operator participant K as Kubernetes API server participant T as Tool (receiver) R->>O: POST /api/v1/delegation/token (ServiceAccount token) O->>K: TokenReview K-->>O: ServiceAccount identity O-->>R: signed JWT R->>T: tool call with X-Omnia-Delegation T->>O: GET /api/v1/.well-known/jwks.json O-->>T: public keysThe operator works out the workspace and the agent from the cluster, using the authenticated ServiceAccount. The session and the end-user pseudonym come from the request, so the calling pod supplies those two.
Opting in
Section titled “Opting in”No token is sent unless the handler asks for one. Most tools are third
parties that have never heard of Omnia, and sending them a signed statement
about a real user would be a credential-shaped artifact handed out for
nothing. A handler declares delegation.enabled: true to receive one; see
Opting in for the field.
sub is always there; origin says how well it is known
Section titled “sub is always there; origin says how well it is known”A signature means the operator minted the token. It does not mean every claim inside it was independently checked, and which is which is the first thing you need.
Every caller gets a sub, a stable pseudonym, regardless of how it
authenticated: through an identity provider, an API key, or a schedule with
no human involved. The origin claim says how that sub came to be known.
Only origin: oidc means the operator itself verified an identity provider’s
signature and read the subject off the result. Every other value is the
calling agent pod’s own account of how it authenticated, not something the
operator attested. A token with no origin at all falls in the same group.
Check origin before you act on sub.
wid (the workspace) and act.sub (the agent) need no such check. The
operator derives both from the calling pod’s Kubernetes ServiceAccount on every
path and never takes them from the request. wid is the UID of the Workspace
that owns the ServiceAccount’s namespace, and is absent when the namespace
belongs to no Workspace. act.sub is an agent that runs as that ServiceAccount.
When one ServiceAccount runs several agents, the pod chooses which of them to
name, but only from that set.
What it is not
Section titled “What it is not”The token says who is asking. What they may do is your decision, from your own source of truth: authorization is a separate concern.
aud names one receiver, in the form <namespace>/<registry>/<handler>, and
exp is five minutes after issue at most. When the token was derived from the
user’s session token, it is capped at that token’s own expiry if that is
sooner. The receiver does the refusing: a verifier built with the receiver’s
audience accepts only tokens whose aud is that audience, so a token minted
for one handler fails at a receiver configured with a different audience. A
receiver that skips the audience check accepts any.
Jira and Microsoft Graph have never heard of Omnia and ignore this header. Giving those services a per-user identity means giving them a credential they understand, through a separate on-behalf-of mechanism.
Related
Section titled “Related”- Delegation tokens reference — the claim table and the opt-in field
- Verify a delegation token in Go — a worked example using
pkg/delegation - Configure tool policies — deciding what an authenticated caller may do