Implement Multi-Agent Delegation
Use Delegation when one Session needs to hand a narrower slice of authority to another Session. The SDK creates the Delegation and carries the context; the web console shows the graph and impact.
Prerequisites
Section titled “Prerequisites”- Two live Sessions and a parent that already holds the resource authority being delegated.
- A specific resource, scope subset, positive TTL, and intended maximum hop count.
- An authenticated work channel for transferring the opaque Delegation ID to the target Session.
session() always starts the child under the same application as the parent. Narrow the child’s authority only when it should hold less than the parent. To hand authority across applications, use delegate() with a peer Session under the other application; it returns the Delegation, and the receiver presents it with acceptDelegation().
Implementation flow
Section titled “Implementation flow”flowchart LR Parent["Parent Session"] --> Start["Start child with narrowed authority"] Start --> Delegation["Bounded Delegation"] Delegation --> Child["Run child with delegated context"] Child --> Exchange["Exchange for resource mandate"] Exchange --> Audit["Inspect audit and graph"]
Python uses Authority.narrow(...), await caracal.delegate(...), and async with caracal.accept_delegation(edge_id, validate=True). Go uses AuthorityNarrow(...), Delegate, and AcceptDelegation. The language SDK references define exact option types; the workflow and wire authority are equivalent.
Narrow a child in the same application
Section titled “Narrow a child in the same application”Starting a child with less authority than its parent needs no explicit hand-off - pass Authority.narrow(...) when starting the child Session, exactly as shown in your SDK guide. This page’s subject is the explicit flow between two Sessions that already exist.
Delegate across applications
Section titled “Delegate across applications”The issuer holds the authority and knows the receiver’s Session ID; the receiver presents the returned Delegation ID. Two sides, one edge:
// Issuer side: Anton delegates read-only PiperNet access// to a peer Session owned by the Fiona application.const delegation = await caracal.delegate({ toSessionId: receiverSessionId, // shared by the receiver up front toApplicationId: fionaApplicationId, // omit for same-application peers resourceId: 'resource://pipernet', scopes: ['pipernet:read'], constraints: { maxHops: 1 }, ttlSeconds: 600,})await sendOverWorkChannel({ delegationId: delegation.delegationId })// Receiver side (Fiona): present the offered Delegation inside the// target Session's context. validate: true confirms it is live first.await caracal.session(async () => { const { delegationId } = await readFromWorkChannel() await caracal.acceptDelegation( delegationId, async () => { const governed = caracal.transport({ scopes: ['pipernet:read'] }) const target = caracal.gatewayRequest('resource://pipernet', '/reports') await governed(target.url) }, { validate: true }, )})Every mint inside the accepted block presents the Delegation, so the STS enforces its scopes, resource bound, TTL, and hop budget - and revoking the edge cuts the receiver off mid-run.
Review the graph
Section titled “Review the graph”- Open the web console for your deployment.
- Select Delegation.
- Inspect active edges, inbound edges, outbound edges, and traversal.
- Use impact before revoking an edge.
- Check Audit for the delegated exchange and resource decision.
Hand Over the Delegation ID Safely
Section titled “Hand Over the Delegation ID Safely”The issuer and receiver share the opaque Delegation ID over their authenticated work channel, such as a queue message, RPC field, or task payload. Creation is only an offer and does not mutate receiver context. Possessing and presenting this target-Session-bound ID through acceptDelegation() is receiver consent; STS also checks the receiving application and live target Session. Use acceptDelegation(delegationId, fn, { validate: true }) to confirm that exact ID with Coordinator before work runs under it. Every presentation emits a delegation.accept event.
Safe constraints
Section titled “Safe constraints”| Constraint | Good default |
|---|---|
| Scopes | Small subset of parent authority. |
| TTL | Required; use minutes, not days. |
| Hop count | 1 unless a deeper graph is intentional. |
| Resource | Single resource whenever possible. |
Troubleshooting
Section titled “Troubleshooting”| Symptom | Check |
|---|---|
Delegate requires an active Session | Call delegation inside session() or a bound Caracal context. |
| Resource denies delegated request | Confirm the edge, ancestry, policy grant, and required scopes remain live. |
| Chain validation fails | Confirm authoritative parent links are continuous; configure requireChainContains separately at verifiers when needed. |
| Revocation did not affect child | Confirm cascade revocation and resource-server revocation consumers. |
Validate the delegation
Section titled “Validate the delegation”Mint the allowed subset, attempt one broader scope, revoke the edge, and retry. The subset must succeed; broadening must fail server-side; after revocation neither the child nor descendants may mint or pass resource verification.
Next Step
Section titled “Next Step”Inspect the graph and request trace in Tail and Query the Audit Stream.

