Manav.id
Agents ยท 20 min read

Can an AI agent delegate to another agent? Only with a human at the root and a bounded depth.

In a multi agent system, authority is usually passed along by handing over a token. A token says what it permits, and nothing about who authorised the chain, how far it may travel, or how to stop it. Here is the alternative: a signed chain that narrows at every hop and verifies back to one human, offline.

Picture a procurement workflow that everyone in 2026 is either running or about to run. A category manager tells an orchestrator agent to renegotiate a logistics contract. The orchestrator does what orchestrators do: it fans out. One sub agent pulls historical pricing. A second drafts revised terms. A third opens a channel to the supplier's own agent to test whether a two year commitment moves the rate.

Now stop the tape at the third hop and ask the supplier's system a simple question: on whose authority is this agent agreeing to anything?

What the supplier can see is a credential. Probably a bearer token, possibly a service account, maybe an agent card describing capabilities. What the supplier cannot see is the category manager. There is no artefact travelling with that request that says a specific human granted this, that the grant covered contract negotiation, that it capped the commitment at two years, that it expires on Friday, or that it was revoked twenty minutes ago when procurement changed its mind.

The supplier's system will make a decision anyway, because systems do. And under ESIGN and UETA, contracts formed by electronic agents can bind the principal, which means the company whose category manager started this may be bound by terms agreed three hops downstream by software nobody in the room has heard of.

How does an AI agent delegate authority to another agent safely? By signing a sub delegation that can only narrow the scope it received, is bounded in depth, and is verifiable back to the human's original signature without calling any issuer. Bearer tokens re issue authority without carrying its origin. A delegation chain carries the human root, the narrowing at each hop, and a revocation anchor that invalidates everything beneath it.

Why does authority disappear after the first hop?

Because of how we pass it. Almost every multi agent system in production today moves authority in one of three ways, and all three lose the same information.

The shared key. The orchestrator has an API key. Sub agents need to call things. The key gets passed down, or read from the same environment, or baked into the shared tool layer. Every agent in the topology now has identical authority, which is the authority of the most privileged task any of them ever needed. Nothing records who is acting. Nothing narrows.

The per agent service account. More disciplined, and it feels like progress. Each agent gets its own identity in the identity provider, with its own scoped role. Now you can tell which agent acted. You still cannot tell which human is behind it, because a service account is a principal with no principal. It stands for itself. Ask a service account who authorised it and the honest answer is "an administrator, once, at provisioning time, for reasons not recorded here".

Token exchange. The most sophisticated of the three, and genuinely useful: OAuth token exchange (RFC 8693) lets one service swap a token for another suited to a downstream call. But look at what the exchange does. It re issues authority. The new token is minted by an authorization server, and its relationship to the original human consent is a record in the issuer's database, not a property of the token itself. To ask "who is at the root of this", you must call the issuer, and you must trust it, and you must do it at every hop.

The confused deputy, thirty years later

There is a name for the underlying shape, and reaching for it makes the whole problem legible. In 1988 Norm Hardy described the confused deputy: a program with legitimate privileges is tricked by a less privileged caller into using those privileges on the caller's behalf. The compiler in his example had permission to write to a billing file. A user who could not write there simply asked the compiler to write its output to that path. The compiler was not compromised. It was confused about whose authority it was acting under.

That is precisely a sub agent. It holds credentials that permit an action. It receives an instruction from somewhere upstream. It cannot distinguish "the human wanted this" from "some text in my context asked for this", because ambient authority carries no origin. The permission is attached to the deputy, not to the request.

Hardy's answer, and the answer of every capability system since, is that authority should travel with the request, as a designation the deputy cannot forge or widen. Not "am I allowed to do this", but "does this specific request carry proof that it may be done". Thirty eight years later we rebuilt the confused deputy out of language models and gave it a credit card.

What should a delegation actually contain?

If the token is the wrong shape, what is the right one? A delegation is a signed object. Not a database row that a server will tell you about if you ask nicely. An object, signed by the delegator's key, that travels with the request and can be checked by anyone holding the delegator's public key.

{
  "v": 1,
  "delegator": "hmn_3b71d2",            // the human, at the root
  "delegateKey": "ed25519:9f4c...a71b", // who may act under this
  "scope": {
    "actions": ["contract.read", "contract.propose"],
    "resources": ["supplier:acme-logistics/*"]
  },
  "constraints": {
    "max_commitment_months": 24,
    "max_value_usd": 250000,
    "counterparties": ["acme-logistics"]
  },
  "notBefore":     "2026-09-05T08:00:00Z",
  "notAfter":      "2026-09-12T17:00:00Z",
  "maxChainDepth": 3,
  "revocationId":  "rev_0c81f4",
  "sig": "ed25519:..."                  // signed by the human's passkey
}

Every field is doing security work, so it is worth walking them one at a time rather than skimming.

delegator and delegateKey together say who granted and who may act. The delegate is identified by a public key, not a name, because names require a directory and a directory requires a call. A key can be checked against a signature with no network.

scope is what may be done, expressed as verbs and resources. Note what is absent: any verb that binds the company. This agent may read and propose. It may not contract.sign. That distinction is the whole difference between an agent that drafts and an agent that commits you.

constraints are the guardrails a human actually cares about and that no OAuth scope can express. A scope can say "may negotiate contracts". It cannot say "up to $250,000 and no longer than two years". Those numbers are the substance of the authority, and if they live in a policy document rather than in the signed object, they are not enforced anywhere at the moment of the act.

notBefore and notAfter are the ones people skip and regret. An agent's authority should expire at the end of the task, not at the end of the quarter. Short windows are the cheapest security you will ever deploy, and they cost nothing except remembering to set them.

maxChainDepth is the field that makes this post necessary, and we come back to it below.

revocationId is the anchor that lets a human unwind everything downstream in one action, which is the property that token sprawl fundamentally cannot offer.

What happens when an agent delegates onward?

Here is the rule that makes chains safe, and it is one sentence: a sub delegation may only narrow.

When the orchestrator hands work to the drafting sub agent, it does not pass its own delegation along. It mints a new one, signed with its own key, under its own delegation, containing a subset of what it holds. Fewer verbs. Fewer resources. Tighter constraints. Sooner expiry. Depth reduced by one.

{
  "v": 1,
  "delegator": "ed25519:9f4c...a71b",   // the orchestrator's key
  "delegateKey": "ed25519:2d10...cc93", // the drafting sub agent
  "scope": {
    "actions": ["contract.propose"],     // read dropped
    "resources": ["supplier:acme-logistics/draft/*"]
  },
  "constraints": {
    "max_commitment_months": 12,         // narrowed from 24
    "max_value_usd": 100000,             // narrowed from 250000
    "counterparties": ["acme-logistics"]
  },
  "notAfter": "2026-09-06T17:00:00Z",    // hours, not a week
  "maxChainDepth": 2,                    // one less than its parent
  "parent": "dlg_9c41e0",
  "sig": "ed25519:..."                   // signed by the orchestrator
}

This property has a name in the capability literature: attenuation. It is the thing macaroons got right in 2014, and credit where it is due, because macaroons articulated it beautifully. A macaroon is a bearer token that anyone holding it can add caveats to, producing a strictly less powerful token, without talking to the issuer. You can hand someone a token that works only for the next five minutes, only for one account, only from one network, and you can do it offline, and they cannot remove your caveat because the chained HMAC would break.

Attenuation is the correct primitive. What a delegation chain adds is the part macaroons deliberately left out: who. A macaroon is a bearer credential. Anyone holding it may use it, and the token does not say who is acting or on whose authority the whole thing began. That was a reasonable design choice for a distributed authorization system inside one company. It is the wrong choice when the question a supplier needs answered is "which human is accountable for this commitment".

UCAN, from the decentralised web community, went further and made the actor explicit, keys all the way down, with issuer and audience on every layer. It is close to the right shape and worth reading if you are designing in this space. The gap it leaves for enterprise use is the root: a UCAN chain terminates at a key, and a key is not a person until something binds it to one. Which is exactly the thing an enrolled passkey with liveness does.

Why does chain depth need a limit?

A fair objection: if every hop narrows, and narrowing is monotonic, a chain can never gain authority. Why cap depth at all? Three reasons, in increasing order of how much they will hurt you.

Comprehensibility. A human granting authority needs to understand what they are granting. "This agent may propose contracts up to $250,000" is understandable. "This agent, and anything it chooses to delegate to, recursively, forever" is not a grant a person can reason about, no matter how monotonic the narrowing. Unbounded depth means the human at the root cannot enumerate who is acting for them, which is the failure we call Authority Opacity in the Identity Failure Map.

Blast radius of one compromised key. Narrowing bounds what a compromised delegate may do, but it does not bound how far it may propagate. An agent at depth two holding a valid delegation can mint children, and those children can mint children. Every one of them is legitimately scoped and every one of them is a live credential in the world until the anchor is revoked. The npm worm known as Shai Hulud is the cautionary tale from an adjacent domain: one compromised maintainer credential propagated automatically across hundreds of packages. Automation turns a single key compromise into a population.

Verification cost. Every hop is a signature to verify and a revocation state to check. At three hops that is negligible. At thirty, on a hot path, it is a latency budget. Depth limits keep verification predictable, which is what makes it deployable at volume.

Depth is a policy dial, not a proof of safety. A depth of three with sloppy scopes is worse than a depth of six with tight ones. But an unbounded dial is a dial nobody has set.

How does a verifier check the chain, offline?

This is the part that determines whether any of it is real. If checking a chain requires calling three issuers, it is not offline verification and it will not survive contact with a supplier's rate limits.

def verify_chain(chain, request, human_pubkey_registry):
    assert len(chain) <= chain[0].maxChainDepth

    root = chain[0]
    human_key = human_pubkey_registry[root.delegator]   # published, cacheable
    assert ed25519_verify(root.sig, canonical(root), human_key)

    prev = root
    for link in chain[1:]:
        # 1. each link is signed by the key its parent delegated to
        assert ed25519_verify(link.sig, canonical(link), prev.delegateKey)
        # 2. authority may only shrink
        assert link.scope.actions   <= prev.scope.actions
        assert link.scope.resources <= prev.scope.resources
        assert constraints_narrower(link.constraints, prev.constraints)
        # 3. lifetime may only shrink
        assert link.notAfter  <= prev.notAfter
        assert link.notBefore >= prev.notBefore
        # 4. depth strictly decreases
        assert link.maxChainDepth < prev.maxChainDepth
        prev = link

    # 5. the acting key is the last delegate; the request fits the tip
    assert request.signer == prev.delegateKey
    assert request.action in prev.scope.actions
    assert satisfies(request, prev.constraints)
    # 6. nothing in the chain has been revoked
    assert not any(revoked(l.revocationId) for l in chain)
    return root.delegator          # the accountable human

Walk the checks and notice that each one closes a specific attack.

Check one stops a forged link. If an agent invents a delegation naming itself, the signature will not verify against the parent's delegate key, because it does not hold that key.

Check two stops scope inflation, which is the attack you should expect first. A sub agent that wants contract.sign cannot simply write it into its own delegation, because its parent's delegation does not contain it and the subset test fails. Authority cannot be created inside the chain, only spent down.

Check three stops lifetime laundering: taking a grant that expires Friday and issuing a child that expires next year.

Check four stops unbounded fan out, and it must be strict rather than non increasing, or an agent can mint siblings forever at the same depth.

Check five is the one people forget. Verifying the chain proves the chain is valid. It does not prove that the party making this request is the chain's tip. Without binding the request signature to prev.delegateKey, a valid chain becomes a bearer token again, and anyone who intercepts it can replay it.

Check six is revocation, and it is worth its own section.

The function returns the human. That is the point of the whole exercise. At the end of verification, the supplier's system does not merely know that a request was permitted. It knows the name of the accountable person, and it holds a cryptographic artefact proving it, which it can keep and check again in two years during a dispute with no call to anyone.

What happens when the human changes their mind?

Revocation is where token architectures quietly fail, so it is worth being precise about what a chain does and does not give you.

Because every link carries its parent's revocationId lineage, revoking the root invalidates the entire subtree in one action. Not "eventually, as tokens expire". Immediately, at the next verification, for every descendant, including descendants the human never knew existed. That is a property bearer tokens cannot offer at all: to revoke a bearer token you must know it exists and reach its issuer, and in a chain of dynamically minted tokens you know neither.

Here is the honest limit. Revocation works at verification time, which means it works for verifiers who check. If a sub agent used its delegation an hour ago to obtain a long lived independent credential from some third system, revoking the chain does not reach into that system and delete what it issued. The chain governs authority that flows through the chain. Authority that leaked out of it is somebody else's revocation problem. This is why the delegation should exclude the verbs that mint durable credentials, and why kill switch design is a separate discipline from delegation design.

ApproachWho is at the root?Narrows on delegation?Verifiable offline?Revocation reaches descendants?
Shared API keyUnknownNoNoRotate and break everything
Per agent service accountA provisioning admin, onceNoNoPer account only
OAuth token exchange (RFC 8693)In the issuer's databaseOptionally, by policyNo, ask the issuerIssuer dependent
Rich authorization requests (RFC 9396)The consenting user, at grant timeExpressive, but not chainedToken dependentIssuer dependent
MacaroonsNobody, bearerYes, caveatsYesVia caveat checks
UCANA keyYesYesVia revocation records
Human rooted delegation chainAn enrolled humanYes, enforcedYesYes, whole subtree

Read that table charitably. Every row solved the problem in front of it. OAuth was designed for a user consenting to an application, and it did that well enough to run the web. Macaroons solved offline attenuation elegantly. RFC 9396 exists precisely because scopes were too coarse for payment authorisation, which is the same discovery this post makes from a different direction. The chain is not a repudiation of any of it. It is the composition: capability attenuation, plus a human at the root, plus offline verification, plus subtree revocation.

Where this meets the standards being written right now

Two things happened in 2025 and 2026 that make this urgent rather than theoretical.

First, multi agent topologies stopped being a research demo. The Model Context Protocol standardised how agents reach tools, and agent to agent protocols standardised how agents reach each other. Agent cards describe what an agent can do. They are self description, and self description is not authority. An agent card is a menu, not a warrant.

Second, agentic payment rails went live. Card networks shipped agent commerce programmes, and the Agent Payments Protocol brought a mandate model to the problem with backing from a large group of payment participants. Mandates are a real improvement, because they make the authorisation explicit and signed. What a mandate does not do by itself is prove that a live, unique human signed it, or carry that proof three hops down. We wrote about that specific gap in AP2 proves the mandate, not the human, and the same gap is what a chain fills for agent to agent hops.

The reason to care about the sequencing is simple. Standards are being set now. If the agent economy standardises on bearer tokens that re issue authority without carrying its origin, we will spend the following decade building forensic tooling to answer a question that could have been answered by construction.

Honest limits

Chains only govern authority that flowed through chains. A shadow API key sitting in an environment variable is invisible to all of this. Delegation is not an inventory. If your agents can reach systems by any path other than the chain, that path is your real security boundary.

A compromised delegate key acts freely within its scope. Narrowing bounds the damage, and bounding is genuinely valuable, but a stolen key at depth two can do everything depth two permits until the anchor is revoked. The chain converts an unbounded compromise into a bounded one. It does not convert it into no compromise.

Depth limits are policy, not proof. Setting maxChainDepth to three does not make three hops safe. It makes the topology comprehensible and the verification cost predictable. Safety comes from the scopes and constraints, which humans have to write thoughtfully.

Someone has to write the scopes. This is the real adoption cost and pretending otherwise would be dishonest. Expressing "may negotiate up to $250,000 for twelve months with these three suppliers" requires a person to decide those numbers. Most organisations have never written that down, because until now the alternative was not a worse grant, it was no grant at all.

What exists today. Delegation chains with delegateKey, scope.actions, constraints, notBefore, notAfter, maxChainDepth and revocationId, verifying offline against a human's enrolled passkey, are shipped. Adapters that map these onto agent to agent protocols and agentic payment mandates are roadmap, not product. Describing them otherwise would be the kind of claim this post exists to argue against.

What to do this week

  1. Draw your actual agent topology. Every agent, every tool, every outbound call to a system you do not own. Most teams discover a hop they had not counted.
  2. For the deepest path, write down what a receiving system can learn about the human. If the answer is nothing, that is the finding.
  3. Separate the verbs that bind you from the verbs that do not. Reading, drafting and proposing are cheap. Signing, paying, granting and publishing are not. Most agents hold both because nobody split them.
  4. Put an expiry on every agent credential you own. Hours for task scoped work. If something breaks when credentials expire, you have found a process that depends on standing authority.
  5. Write one constraint as a number. A value ceiling, a counterparty list, a commitment length. One real number, in the grant, not in a policy document.
  6. Test scope inflation. Have a sub agent request an action its parent does not hold, and confirm the request is refused rather than logged.
  7. Decide your depth limit and defend it. Three is a reasonable default for most enterprise workflows. Whatever you pick, be able to say why.
  8. Check your revocation story. If a human revokes at the root right now, what stops, when, and what keeps working because it obtained an independent credential earlier?

For the mechanics of a single grant, how delegation tokens work covers the token level. For the verification side, how to prove an agent is authorized walks the request path, and the developer docs show the delegate and verify calls. There is a runnable version of the whole flow in the agent delegation demo.

Frequently asked questions

Can one AI agent delegate authority to another agent? Yes, safely, if the sub delegation is a new signed object that can only narrow the scope, constraints and lifetime it received, reduces the remaining chain depth by one, and remains verifiable back to a human's original signature. Passing along a bearer token is not delegation. It is duplication of authority with the origin stripped off.

What is the equivalent of OAuth for autonomous agents? There is no single accepted answer yet, which is exactly why the standards being written now matter. The closest existing pieces are OAuth token exchange for re issuing authority, rich authorization requests for expressing fine grained permissions, and capability systems such as macaroons and UCAN for offline attenuation. A human rooted delegation chain composes attenuation with an accountable root and subtree revocation.

Why does a delegation chain need a maximum depth? For comprehensibility, blast radius and cost. A human cannot reason about a grant that recursively extends forever, even if every hop narrows. A compromised key at any depth can keep minting valid children until the anchor is revoked. And each hop adds a signature verification and a revocation check, so an unbounded chain has an unbounded latency budget.

What is the confused deputy problem in multi agent systems? A sub agent holds credentials that permit an action and receives an instruction from upstream. Because ambient authority carries no origin, the agent cannot distinguish a request the human actually authorised from arbitrary text that arrived in its context. It uses its own privileges on someone else's behalf. Norm Hardy described this in 1988. Agents rebuilt it and gave it payment rails.

How do you revoke authority across a whole agent chain? Revoke at the root. Because each link carries its parent's revocation lineage, invalidating the anchor invalidates every descendant at the next verification, including descendants the human never knew existed. The limit is that this governs authority flowing through the chain. If an agent already used its delegation to obtain an independent long lived credential elsewhere, that credential must be revoked separately.

How is this different from macaroons or UCAN? Macaroons solved offline attenuation elegantly, but they are bearer credentials, so they do not say who is acting or on whose authority the chain began. UCAN makes actors explicit with keys at every layer, which is close to the right shape, but a chain terminating in a key is only accountable once something binds that key to a person. An enrolled passkey with liveness is that binding.

Does verifying a chain at every hop slow the system down? At realistic depths, not meaningfully. Verification is a small number of Ed25519 signature checks plus a revocation lookup, all against cacheable published keys with no callback to an issuer. This is why the depth limit matters operationally as well as conceptually: it keeps the verification cost bounded and predictable on a hot path.

Sources

  1. Norm Hardy, "The Confused Deputy" (1988). Link
  2. RFC 8693, OAuth 2.0 Token Exchange. Link
  3. RFC 9396, OAuth 2.0 Rich Authorization Requests. Link
  4. Birgisson et al., "Macaroons: Cookies with Contextual Caveats" (2014). Link
  5. UCAN, User Controlled Authorization Networks specification. Link
  6. W3C Verifiable Credentials Data Model 2.0. Link
  7. Model Context Protocol specification. Link
  8. Agent Payments Protocol specification. Link
  9. US ESIGN Act, 15 U.S.C. 7001, including subsection (h) on electronic agents. Link
Agents should not trust each other. They should carry proof of a human's authority that narrows at every hop, so the last agent in the chain is exactly as trustworthy as the first signature and never more.