# Where must an instance stop trusting a federated peer: the delegation-depth collapse at the trust-bootstrap boundary **Date of Record:** 2026 November 7 **Scope:** This record applies to cross-org agent federation where task routing authority is delegated transitively across administrative domains via signed capability tokens (e.g., macaroon or biscuit-style attenuation). It does not apply to single-org multi-agent meshes sharing one trust root, nor to federations that use pairwise bilateral attestation with no transitive delegation. ## Core Distinction The non-obvious invariant is this: **trust bootstrapping is not a graph reachability problem; it is a depth-bounded attenuation problem.** Operators routinely conflate "can domain A reach domain D through B and C" (a routing question, solvable by transitive closure over the federation graph) with "is the credential that arrives at D still a valid delegation of A's original authority" (a cryptographic and policy question, bounded by attenuation semantics, not by graph diameter). A token that has been legitimately attenuated through B and C can still arrive at D carrying authority that A never granted — not because any hop forged a signature, but because each hop *added* a caveat whose satisfiability D cannot independently evaluate. The delegation is cryptographically intact and semantically over-broad. These are different failures and they demand different defenses. ## What Was Observed During a three-org federation rollout (call them Origin, Broker, and Edge), task routing used a capability token minted by Origin, attenuated by Broker to restrict scope to `task:route` and `org:edge`, and presented to Edge's task router. Edge's policy engine verified the signature chain end-to-end and admitted the request. The request then executed with Origin's *full* original caveat set, because Edge's verifier only checked caveats it recognized and silently ignored the rest (a common "forward-compatible caveat" implementation choice). The concrete failure: Broker had appended a caveat `time < 2026-11-14T00:00Z` intended to expire the delegation at midnight UTC. Edge's clock was 40 minutes ahead of Broker's, and Edge's verifier did not recognize the `time` predicate namespace at all — it treated the caveat as an opaque, unsatisfiable-by-unknown-predicate token and, per its forward-compat rule, *dropped* it rather than rejecting. The delegation remained live past its intended expiry for 40 minutes, and during that window a task was routed to Edge that Edge's own policy would have rejected had it evaluated the caveat. A second, related observation: policy interoperability failed not at the semantic level (both orgs agreed on what `task:route` meant) but at the *predicate evaluation* level. Origin assumed all federated verifiers could evaluate `time`, `org`, and `role` predicates. Edge could evaluate only `org` and `role`. There was no negotiation step that established the common predicate set before tokens were minted. ## Condition of Validity The invariant holds — that a federated credential is safe to honor — only when **all** of the following are true at the verifying domain: 1. The verifier can evaluate *every* caveat in the presented chain, or the chain explicitly marks unevaluable caveats as fatal (fail-closed), not droppable. 2. The delegation depth is within the bound declared at bootstrap, and the bound is enforced by the verifier, not merely by the minting policy. 3. Clock skew between minting and verifying domains is bounded and the bound is smaller than the smallest time-based caveat granularity in use. If any condition is unmet, the credential must be treated as *present but not authoritative* — a distinct state from "absent" and from "invalid." ## Decision Boundary The decision boundary is between **fail-closed on unknown predicates** and **forward-compatible predicate dropping**. These are not reconcilable defaults; they are opposite policies and must be chosen explicitly per federation, not inherited from a library default. - Choose fail-closed when the credential carries any authority the verifier cannot fully bound. This is the correct default for cross-org routing. - Choose forward-compatible dropping only when the credential's authority is already fully bounded by caveats the verifier *does* understand, and unknown caveats can only *narrow* (never *widen*) authority. This is almost never true across administrative domains, because the verifier cannot know what an unknown predicate means. The practical rule: **a verifier must reject any chain containing a caveat whose predicate it cannot evaluate, unless the federation's bootstrap handshake has explicitly whitelisted that predicate namespace as narrowing-only.** Absent that whitelist, unknown predicate ⇒ reject. ## Operational Effect Enforcing the boundary changes the failure mode from *silent over-authorization* to *loud routing rejection*. The cost is availability: federated tasks that would have "worked" (by ignoring caveats) now fail at the verifier. The benefit is that the failure is attributable — the rejecting domain can report exactly which predicate namespace it could not evaluate, which drives the interop negotiation that should have happened at bootstrap. The bootstrap handshake must therefore exchange, and pin, the predicate namespace set and the clock-skew bound before any token is minted. This is a one-time cost per federation pair and it eliminates the entire class of "cryptographically valid but semantically over-broad" admissions. ```python class FederationTrustBoundaryError(RuntimeError): """Raised when a federated credential cannot be fully bounded by the verifier.""" pass def verify_federated_token( token_caveats: list[str], verifier_predicate_namespaces: set[str], pinned_narrowing_namespaces: set[str], clock_skew_seconds: float, max_clock_skew_seconds: float, delegation_depth: int, max_delegation_depth: int, ) -> None: # 1. Depth bound must be enforced by the verifier, not the minter. if delegation_depth > max_delegation_depth: raise FederationTrustBoundaryError( f"delegation depth {delegation_depth} exceeds pinned bound " f"{max_delegation_depth}" ) # 2. Clock skew must be smaller than the smallest time-caveat granularity. if clock_skew_seconds > max_clock_skew_seconds: raise FederationTrustBoundaryError( f"clock skew {clock_skew_seconds}s exceeds pinned bound " f"{max_clock_skew_seconds}s; time caveats are unreliable" ) # 3. Every caveat must be evaluable, or explicitly whitelisted as narrowing-only. for caveat in token_caveats: namespace = caveat.split(":", 1)[0].strip() if namespace in verifier_predicate_namespaces: continue if namespace in pinned_narrowing_namespaces: continue raise FederationTrustBoundaryError( f"cannot evaluate caveat namespace '{namespace}' in caveat " f"{caveat!r}; refusing to admit over-broad delegation" ) def admit_federated_task(request: dict, federation_config: dict) -> dict: verify_federated_token( token_caveats=request["caveats"], verifier_predicate_namespaces=federation_config["verifier_namespaces"], pinned_narrowing_namespaces=federation_config["narrowing_namespaces"], clock_skew_seconds=request["observed_clock_skew_s"], max_clock_skew_seconds=federation_config["max_clock_skew_s"], delegation_depth=request["depth"], max_delegation_depth=federation_config["max_depth"], ) return {"admitted": True, "task": request["task"]} ```