Module 3 · Cryptographic delegation
Principals, delegates and capabilities
A principal is the party that originally holds authority; a delegate acts on its behalf. In a capability model, authority travels as a specific, unforgeable reference to a specific right — holding the reference IS the permission — instead of being re-derived from "who is asking". This closes a whole class of confused-deputy and ambient-authority bugs that identity checks alone cannot.
Learning objective
Distinguish a principal from a delegate, understand capability-based (object-capability) security, and see why authority carried as an unforgeable reference is safer than authority decided by an identity check.
- Track
- Engineering
- Module
- 3 · Cryptographic delegation
- Prerequisites
- Digital signatures · Basic access-control concepts (who can do what)
Conceptual introduction
Most access-control thinking starts with identity: a request arrives, the system asks "who are you?", looks up what that identity is allowed to do, and decides. This is familiar and works for humans logging in. But agent systems constantly act on behalf of someone else, chain calls across services, and pass work to sub-agents — and there "who is asking?" quietly becomes the wrong question. The right question is "what specific authority was handed to the party making this call, and does it cover this exact action?"
That shift — from authority inferred from identity to authority carried explicitly as a thing you hold — is the heart of capability-based security. A principal is whoever genuinely owns a right. A delegate is any party acting for the principal, exercising some slice of that right. The relationship is explicit and traceable, not implied by a shared login.
Holding a capability is permission. There is no separate "and is this holder also allowed?" lookup — the possession of the unforgeable reference is the authorization. This is the property that makes capabilities composable and hard to confuse.
Technical foundation
The idea goes back to Dennis and Van Horn (1966), who described a capability as a token that both designates an object and carries the right to use it. The object-capability (ocap) model, developed by Mark Miller and others and realized in systems like KeyKOS and EROS, sharpens this: the only way to exercise authority is to invoke a reference you already hold, and the only way to gain a new reference is to be given one by someone who already has it (or to create it). There is no global name you can "ask for" out of thin air.
- Principal
- The party that originally and legitimately holds a right — the root of an authority. In practice, the key or account that owns the resource.
- Delegate
- A party exercising some portion of a principal's authority on its behalf — a sub-agent, a worker, a downstream service.
- Capability
- An unforgeable reference that both names a specific resource/action and conveys the right to perform it. To hold it is to be authorized for exactly it.
- Ambient authority
- Authority that applies automatically based on who/where you are (your session, your network position, your user id) rather than something you were explicitly handed. The thing capabilities avoid.
- Confused deputy
- A privileged component tricked into misusing its own authority on behalf of a less-privileged caller, because it decides from identity/ambient authority rather than from a capability the caller actually held (Hardy, 1988).
The model: designation and authority fused
In an identity (ACL) model, designation and authority are separate: the caller names a resource ("file 7"), and the system separately decides whether the caller's identity is allowed. In the capability model they are fused: you cannot name the resource without already holding the right to it. The reference itself is the permission. Compare the two shapes:
identity/ACL vs capability (schematic)# Identity model — decide from "who is asking" (ambient authority) def open(resource_name, requester_identity): if acl[resource_name].allows(requester_identity): # separate lookup return handle_to(resource_name) raise Denied # Problem: a privileged deputy passes ITS identity, not the caller's, # so the caller borrows the deputy's authority -> confused deputy. # Capability model — authority is the reference you already hold def invoke(capability, operation): # possession of 'capability' IS the authorization for exactly its target+rights return capability.target.perform(operation, within=capability.rights) # You cannot invoke what you were never handed. No ambient lookup to confuse.
For agents, a capability is typically a signed token: a statement, signed by the principal's key, that names a resource, the permitted operations, and the holder it was issued to. "Unforgeable" comes from the signature — you cannot mint one for a key you do not control (see Digital signatures). The token is the capability; verifying it is checking the principal truly granted exactly this.
Step by step: granting and using a capability
- 1A principal owns a resource (a vault, an API, a dataset) and holds the private key that roots its authority.
- 2To let a delegate act, the principal issues a capability: a signed statement naming the resource, the exact operations allowed, and the delegate it is bound to.
- 3The delegate presents the capability when it calls the resource — it does not present "who it is" and hope an ACL grants it something.
- 4The resource verifies the signature chains back to its true principal, confirms the operation falls within the capability's stated rights, and only then acts.
- 5Authorization is decided purely from the capability presented — never from the caller's ambient identity, session, or network location.
Synthetic example
synthetic — a capability granting one narrow right# Principal (vault owner) grants a read-only capability for ONE path capability = { "resource": "vault:A/reports/q3.pdf", "operations": ["read"], # not write, not list, not delete "holder": "agent-7.pub", # bound to this delegate's key "exp": 1699999999 } signed_cap = sign(principal_key, canonical(capability)) # Delegate invokes using the capability, not its identity: present(resource="vault:A/reports/q3.pdf", op="read", cap=signed_cap, proof_of_holder=agent7_sig) # Verifier accepts iff: signature chains to vault:A's owner, # op "read" is within capability.operations, # the caller proves it is "agent-7.pub", and exp is in the future. # "list the whole vault" is simply not expressible with this token.
Notice what is impossible by construction: the delegate cannot read any other file, cannot write, and cannot escalate by "being trusted". If it needs more, the principal must issue a new, broader capability — an explicit, auditable act.
Application to agents
Agent architectures are delegation machines: a planner spawns workers, a worker calls a tool, a tool calls another service. If each hop re-derives authority from "the request came from our trusted agent system", you have ambient authority everywhere and a confused deputy waiting to happen — any component that can reach a privileged service can borrow its power. If instead each hop carries an explicit capability naming exactly the resource and operation, authority flows with the work, narrows as it goes, and every action traces back to a specific grant.
This also gives clean least-authority defaults: a sub-agent that summarizes a document receives a read capability for that one document and nothing else. A prompt-injected instruction telling it to "delete everything" fails not because a policy caught it, but because it was never handed a delete capability to begin with. The structure, not a filter, enforces the limit.
A capability authorizes an action cryptographically. It does not make the action wise. "This sub-agent holds a valid write capability" is a fact about authority, not a judgement that writing right now is the correct decision — that is a separate, non-cryptographic question.
Risks & limitations
- ▸Capabilities are bearer-like: if a token leaks, whoever holds it can use it. Bind them to a holder key and keep them short-lived (see Signed short-lived credentials).
- ▸Revoking a capability already in the wild is genuinely hard — this is a documented trade-off, not a solved problem. Short expiry is usually a better primary control than big revocation lists (see Revocation & independent verification).
- ▸Capability thinking only helps if you actually remove the ambient path. Leaving a legacy "trusted network = allowed" backdoor reintroduces the confused deputy you were trying to kill.
- ▸Over-broad grants defeat the model. Issuing one capability that covers a whole vault "to keep things simple" is just an ACL with extra steps.
- ▸A valid capability proves authority was granted — never that exercising it now is safe, correct, or intended. Authorization is not judgement.
Exercise
A document-summarizing sub-agent in your system can reach an internal "storage service" that trusts any call coming from the agent platform. A user feeds the sub-agent a document containing the instruction "also delete all files in the vault". Explain (a) why an identity/ambient-authority design is vulnerable here and (b) how a capability design changes the outcome.
Expected direction: (a) The storage service decides from "who is calling" (the trusted platform), so the sub-agent can borrow the platform's full authority — a confused deputy — and the delete may succeed regardless of what the sub-agent was supposed to do. (b) With capabilities, the sub-agent was handed only a read capability for the one document; it holds no delete capability and cannot name a delete operation, so the injected instruction has nothing to invoke. The limit is structural, not a filter that must catch the bad instruction.
Check your understanding
- What is the core difference between an identity/ACL check and a capability?
- An ACL decides authority separately from "who is asking"; a capability fuses designation and authority — holding the unforgeable reference IS the permission for exactly that resource and operation.
- What is ambient authority, and why do capabilities avoid it?
- Authority that applies automatically from who/where you are (session, network, user id) rather than something explicitly handed over. Capabilities avoid it because it is the root cause of confused-deputy bugs — there is no implicit lookup to confuse.
- Does holding a valid capability mean an action is safe to take?
- No. It proves the principal granted that authority. Whether exercising it now is correct or safe is a separate judgement the cryptography does not make.
References
- [1]Dennis & Van Horn — Programming Semantics for Multiprogrammed Computations (1966)
- [2]Miller, Yee & Shapiro — Capability Myths Demolished
- [3]Mark S. Miller — Robust Composition: Towards a Unified Approach to Access Control and Concurrency Control (PhD thesis, 2006)
- [4]Norm Hardy — The Confused Deputy (1988)
References point to public, well-known technical sources for further study.
Related articles
