Delegation that cannot escalate
Delegation lets an agent mint a child identity that carries a subset of its own capabilities, and the toolkit refuses to hand a child any capability the parent does not already hold, so a delegate can never gain more power than its parent.
Last updated: 14 Oct, 2026 · Agent Governance Toolkit 4.1.0
The shop agent from the Signing an action and verifying it lesson holds three capabilities: read, refund and order_status. Suppose it wants a small worker that only issues refunds. delegate makes that child, and it enforces one rule worth proving: a child cannot be granted something the parent lacks.
Delegating a narrower capability
delegate takes a name and a capability list and returns a new AgentIdentity. The child gets its own key pair and DID, records the parent's DID, and counts its distance from the root in delegation_depth.
child = parent.delegate(name, capabilities, description=None) # -> AgentIdentityBefore the honest delegation, the example tries the dangerous one. The shop agent attempts to hand a child the admin capability, which it does not hold. The call raises, and only then does it delegate the narrower refund-only child that it is allowed to make.
# a child may only receive capabilities the parent already holds
try:
rogue = shop.delegate("admin-bot", capabilities=["admin"])
except ValueError as e:
print("escalation blocked:", e)
# the honest delegation: a narrower slice of what the parent has
refund_bot = shop.delegate("refund-bot", capabilities=["refund"])
print("child caps :", refund_bot.capabilities)
print("child depth:", refund_bot.delegation_depth)
print("parent set :", refund_bot.parent_did == str(shop.did))escalation blocked: Cannot delegate capability 'admin' - not in parent's capabilities child caps : ['refund'] child depth: 1 parent set : True
Reading the refusal and the child
- The escalation is refused. Asking for
admin, which the parent does not hold, raises aValueErrorwhose message names the capability that is not in the parent's set. No child is created. - The child carries only what was asked. The refund-only delegate holds
['refund'], a strict subset of the parent's three capabilities. - The child records its parent. Its
parent_didmatches the shop agent's DID, and itsdelegation_depthis 1, one step from the root. - The rule is enforced in code, not in a prompt. There is no way to ask for more than the parent holds and have it granted, which is the whole value of delegation over copying a key.
Delegation vs copying the agent's key
| Delegating a child | Copying the key | |
|---|---|---|
| Capabilities | A subset, enforced | All of them |
| Own identity | Yes, its own DID | No, it is the same agent |
| Traceable to a parent | Yes, via parent_did | No |
| Can over-reach | No, escalation raises | Yes, it holds everything |
Where delegation helps
- A worker sub-agent that should do one job, such as refunds, without the power to close accounts.
- A short-lived task handed a narrow slice of the parent's authority for the length of the task.
- A third party you integrate with, given a child identity whose capabilities you can reason about on their own.
Related
- Previous: Signing an action and verifying it
- Next: Revocation: taking an agent out of service
- Reference: Agent Governance Toolkit documentation
- Delegate a read-only child with
capabilities=["read"]and confirm its depth is 1. - Delegate a grandchild from the refund-only child and watch
delegation_depthbecome 2. - Try to delegate
capabilities=["delete_account"]from the shop agent and read the ValueError in full.
You understood something today that you didn't yesterday.