Authorization Chains
The authorization grant pins a single signed hop: one issuer grants one subject a bounded capability to present to one named audience inside a short window. That primitive is deliberately not a delegation framework. This extension adds one thing on top of it: a linear chain of those same grants, each link narrowing the last, so a service can verify that a presenter holds authority tracing back through a bounded number of re-delegations to an origin issuer it can root.
A chain is not a new credential. Every link is the grant field model, signed over the same bytes with the same discipline, so an implementation that already verifies a grant reuses its schema, its canonicalization and its signature check unchanged. The chain adds a parent hash binding each link to the one above it, a continuity rule tying issuer to subject across the seam, a monotonic attenuation rule decided on signed bytes and a three-pass verify order that fails closed. It changes no field of the grant and it defines no capability algebra: a scope entry stays an opaque audience-local token.
The normative document is ink-authorization-chain.md. Take every exact bound, field list and reason string from there. This page is an orientation, not a second source.
Capability gate
Chains ship under their own delegation capability, separate from the authorization capability that covers single grants, so a frozen surface stays frozen. An implementation that does not advertise delegation is not expected to accept chains, and MUST NOT advertise it without fully implementing it. The authorization-chain conformance category pins the decisions. See ink-conformance-profile.md.
This is a post-1.0 named extension. It is not part of the 1.0 core and it blocks nothing in 1.0.
Delegation link
A delegation link is the grant field model with exactly two changes:
typeisnetwork.ink.delegation_link. It is a new type, so there is no legacy alias to dual-accept and a receiver takes only this spelling. The signed bytes bind it and it is never normalized.parentis added: the base64url-no-padding SHA-256 digest of the parent link, computed over a domain-separated encoding of that link’s bytes. It is present on every link except the root, and the root MUST NOT carry it.
Everything else (protocol, issuer, subject, audience, scope, grantId, issuedAt, expiresAt, the optional requireVerifiedOwner and signature) carries its meaning, its bound and its role from the grant unchanged.
A delegation link is never handed to the grant verifier and a grant is never accepted as a chain link. The two types are kept apart deliberately: their lifetime ceilings differ, and a link is only meaningful inside a chain.
The wrapper is network.ink.authorization_chain, carrying a links array ordered root first. The spec bounds the chain length and rejects anything outside it as schema on the signed bytes alone.
Attenuation
Every child link MUST narrow, and never widen, its parent. Attenuation is decided entirely on signed bytes, so the verdict is independent of the verifier clock, and any widening rejects the whole chain with the reason attenuation. Three conditions hold at every parent-child seam:
- Scope subset. The child’s
scopeMUST be a subset of the parent’s by exact string-set inclusion. No wildcards, no hierarchy, no semantic ordering. A token in the child that is absent from the parent is a widening. - Window nesting. The child’s window MUST nest inside the parent’s. A child that starts before its parent or expires after it escapes the parent window.
- Delegability. The parent MUST authorize the re-delegation, per the opt-in below.
audience is not attenuable. It is fixed by the origin issuer and MUST be identical on every link, so attenuation can never re-point the chain at a different service. scope and the validity window are the only fields that narrow.
Each link’s lifetime is also capped by its position, with a short ceiling on the final link (the credential actually exercised at the audience) and a longer one on intermediate links, which only authorize further delegation. The exact ceilings are in the spec’s Lifetime ceilings section, and a link over its ceiling rejects as schema.
Re-delegation is opt-in
Onward re-delegation is opt-in, and the scope machinery carries the opt-in rather than a new boolean field. The reserved registry token delegation.extend authorizes one further hop and nothing else. A non-root link is valid only if its immediate parent’s scope contains that token; a link whose parent lacks it rejects as attenuation.
The origin issuer’s own link needs no incoming token: the origin delegating to the first delegate is baseline delegation, not re-delegation. For the root to seat a child, the root MUST carry delegation.extend.
Because it is an ordinary scope token, the subset rule governs it like any other. A child cannot introduce a delegation.extend its parent lacks, so the ability to re-delegate can only narrow down a chain and never appear from nowhere. An audience MUST NOT read the token as conferring any read, write or message authority; it falls under the grant’s default-deny like any unrecognized token.
Verification order
Three passes, first failure wins, each with a stable reason so two implementations reject in the same place for the same reason. The raw byte cap applies before any decode.
Pass 1, structure, on signed bytes. Schema, including UTF-8 validity, the exact field set per link, the wrapper shape, the chain length and the per-position lifetime ceilings. Then continuity: every non-root issuer byte-equals its parent’s subject, and every non-root parent equals the digest of the link above it. Then attenuation at every seam. Nothing here consults the clock or resolves a key.
Pass 2, signatures, root to head. Each link’s issuer is resolved to a signing key through the authenticated Agent Card signature machinery: fetch the card, authenticate its proof, root it by principal kind, then take the active signing key it binds. Every key MUST be active at the verifier’s clock under the key rotation rules, so a retired or revoked key never verifies a link. Card resolution passes the private-hostname classification with connect-time pinning. Resolution failure fails closed.
Evaluating key status at verification time means rotating a delegate’s key invalidates that delegate’s outstanding links. That is intended, and it is the chain’s fastest revocation lever against a compromised delegate.
Pass 3, context. Every link’s audience equals the verifying service’s own identity, which is the confused-deputy defense carried across the whole chain. The authenticated presenter, when supplied, equals the final link’s subject. Every link is inside its validity window. The final link’s (issuer, grantId) pair is checked against the seen set for replay, and recorded only on final acceptance of the whole chain, as one check-and-insert under a single guard. Every link’s pair is checked against the revocation predicate, and a revoked pair anywhere rejects the whole chain even inside its window. Owner verification is a conjunction: if any link requires a verified owner, the whole chain does.
Orthogonal to key rotation
The delegation walk and the key-rotation walk are different chains and should not be confused. The delegation chain runs across distinct principals, each delegating to the next, and is walked in pass 1 for continuity and attenuation. A rotation chain runs across key sets for a single principal and is walked inside Agent Card signature verification to root that principal’s current signing key. They meet at exactly one seam: key resolution in pass 2.
Prior art
| Decision | Rationale | Prior art |
|---|---|---|
| Flat link array | Avoids exponential size growth | UCAN 1.0 CID-referenced proofs |
| Scope subset on exact strings | No capability algebra to disagree about across implementations | UCAN partial order |
| Delegation separate from invocation | Prevents confused deputy | UCAN delegation and invocation split |
| Short windows over revocation lookups | Simpler in decentralized systems, and key rotation revokes | SPIFFE SVIDs |
| Bounded chain length | Bounds fetch amplification during verification | Practical chains are short |