# In what scenarios must an instance reject a federated capability token whose trust anchor chain is valid but whose delegation depth exceeds the anchor's declared attenuation budget **Date of Record:** 2026 November 14 **Scope:** Applies to cross-organization agent federation where capabilities are minted by a trust anchor in org A and presented to a resource server in org B, and where each anchor publishes a signed attenuation budget (max delegation depth and max hop count). Does not apply to single-org intra-tenant delegation, nor to token formats that carry no explicit depth counter (where depth must be inferred from chain length at verification time). ## Core Distinction Signature validity of a trust anchor chain is not the same as authority validity of the token presented under it. A chain can be cryptographically intact — every link signed by a key that resolves to a published anchor, every anchor's own certificate valid and unrevoked — while the token still exceeds the authority that anchor was willing to lend. The specific invariant: **a trust anchor's attenuation budget is a property of the anchor, not of the token, and it is consumed by the entire delegation path, not just the final hop.** The non-obvious part is that most verifiers check depth at each hop (does this delegation stay within the parent's remaining budget?) but never reconcile the *accumulated* depth against the *root anchor's* declared maximum. A chain of five hops, each individually legal relative to its parent, can exceed the root anchor's `max_delegation_depth` of three. Each hop verifies; the composition does not. This is the classic confused-deputy shape, but dressed in attenuation semantics: the intermediate delegates correctly, and the root is still over-extended. ## What Was Observed During a federation pilot between two agent platforms, org A published an anchor with `max_delegation_depth: 3` and `max_hops: 4`. Org B's resource server validated the chain link-by-link and admitted a token at depth 6. The token had been produced not by malicious forgery but by a well-behaved orchestrator that delegated to a sub-orchestrator, which delegated to a tool-router, which delegated to a retriever, which delegated to a summarizer, which delegated to a formatter — six agents, each performing a legal single-step delegation against its immediate parent's remaining budget. No single step was illegal. The accumulated path exceeded the anchor's declared budget by three. The failure surfaced only downstream: the formatter agent, holding a capability scoped to `read:documents` with an attenuation budget that had been "refilled" by each hop's local accounting, was able to request a document outside the original anchor's intended scope because the anchor's budget was never enforced as a *global* ceiling. The anchor's budget was being treated as per-hop, not per-path. A second, quieter observation: revocation checks passed. The anchor was not revoked. The chain was valid. The token was over-budget. Verifiers that only check "is the chain valid and is the anchor trusted" admitted it. Verifiers that also check "is the accumulated depth ≤ root anchor's max" rejected it. The two verifier populations disagreed on the same token, which is the tell that the invariant was underspecified. ## Condition of Validity The rejection rule is valid **only** when all of the following hold: 1. The token carries an explicit, signed `delegation_depth` counter that is monotonically incremented at each hop and cannot be reset by an intermediate (i.e., the counter is covered by the signature of each delegating party, not just the root). 2. The root anchor's `max_delegation_depth` is published in the anchor's signed metadata and is resolvable by the verifier independently of the token (so the verifier does not trust the token's own claim about the anchor's budget). 3. The verifier can reconstruct the full chain from the token's `cnf`/`chain` field and count hops, rather than trusting a hop count asserted inside the token body. If any of these fail — particularly (1), where a buggy intermediate can reset the counter — the rejection must be based on chain length reconstructed by the verifier, not on the token's self-reported depth. If (2) fails (anchor metadata unresolvable), the correct behavior is to fail closed with a distinct error, not to fall back to a default budget. ## Decision Boundary Reject when `accumulated_depth > anchor.max_delegation_depth` **or** `chain_length > anchor.max_hops`, even if every individual hop is within its parent's remaining budget and every signature verifies. The distinction from the adjacent case matters: - **Admit** when the chain is valid, the anchor is trusted and unrevoked, and accumulated depth ≤ anchor budget. This is the normal path. - **Reject (over-budget)** when the chain is valid and the anchor is trusted but accumulated depth exceeds the anchor budget. This is the boundary condition this record addresses. The token is not forged; it is over-extended. - **Reject (invalid chain)** when any link fails signature or anchor resolution. Different error, different remediation (re-fetch anchor metadata vs. re-mint token with shallower delegation). Conflating over-budget with invalid-chain leads operators to chase the wrong fix: they re-fetch anchor metadata (which is fine) when the actual remedy is to re-mint the token with a shallower delegation path or to have the root anchor raise its budget deliberately. ## Operational Effect Enforcing the accumulated-depth check changes federation behavior in three ways. First, deep orchestrator pipelines that previously "worked" because each hop was locally legal now fail at the verifier, which is correct but disruptive; the fix is to flatten the delegation path or to have the root anchor publish a budget that matches the intended pipeline depth. Second, the error must be distinguishable — an over-budget rejection should return a machine-readable `delegation_depth_exceeded` with the observed depth and the anchor's budget, so the orchestrator can shorten its path rather than retry blindly. Third, anchor budgets become a cross-org contract surface: raising `max_delegation_depth` in org A is a security-relevant change that org B's verifier will honor immediately, so budget changes need the same review discipline as key rotation. The invariant to carry forward: **depth is a path property, not a hop property.** Verify it at the root, against the root's published budget, using a chain the verifier reconstructed itself. ```python from dataclasses import dataclass class DelegationDepthExceeded(RuntimeError): """Raised when a federated token's accumulated delegation depth or hop count exceeds the root trust anchor's published attenuation budget, even though every individual link and signature is valid.""" def __init__(self, observed_depth: int, anchor_budget: int, observed_hops: int, anchor_hops: int): self.observed_depth = observed_depth self.anchor_budget = anchor_budget self.observed_hops = observed_hops self.anchor_hops = anchor_hops super().__init__( f"delegation_depth_exceeded: depth {observed_depth} > budget {anchor_budget} " f"or hops {observed_hops} > {anchor_hops}" ) @dataclass(frozen=True) class AnchorBudget: max_delegation_depth: int max_hops: int def verify_federated_token( chain_links: list, # verifier-reconstructed chain, root first anchor_budget: AnchorBudget, signatures_valid: bool, anchor_trusted: bool, anchor_revoked: bool, ) -> None: if not signatures_valid: raise ValueError("invalid_chain: signature verification failed") if not anchor_trusted: raise ValueError("untrusted_anchor: anchor not in trust store") if anchor_revoked: raise ValueError("revoked_anchor: anchor has been revoked") # Depth is a path property: count from the reconstructed chain, # never trust a self-reported counter inside the token body. observed_depth = len(chain_links) - 1 # root does not delegate to itself observed_hops = len(chain_links) - 1 if observed_depth > anchor_budget.max_delegation_depth or observed_hops > anchor_budget.max_hops: raise DelegationDepthExceeded( observed_depth=observed_depth, anchor_budget=anchor_budget.max_delegation_depth, observed_hops=observed_hops, anchor_hops=anchor_budget.max_hops, ) # token admitted: valid chain AND within the root anchor's attenuation budget ```