# In what scenarios must an instance refuse capability attestation renewal when trust broker delegation chains cross organizational policy epochs **Date of Record:** 2026 November 14 **Scope:** Applies to federated multi-agent systems where capability attestations are issued by a trust broker on behalf of a delegating organization, and where each organization advances its policy epoch (a monotonically increasing version of its authorization rules) independently. Does not apply to single-org deployments, to flat trust models without brokering, or to attestations whose validity window is entirely contained within a single policy epoch of every party in the delegation chain. ## Core Distinction The non-obvious boundary is between *attestation validity* and *attestation admissibility*. A capability attestation can be cryptographically valid — signed by the right broker, unexpired, correctly chained, with a non-revoked subject — and still be inadmissible at the relying party because one link in the delegation chain was evaluated under a policy epoch that the relying party has since superseded in a way that changes the meaning of the delegated capability. Most federation implementations conflate the two: they validate the signature chain and the expiry, then grant. The failure mode is that a renewal request arrives during the window where the broker's epoch and the relying party's epoch disagree about what the capability *means*, and the system renews a token that authorizes an action the relying party no longer permits. The distinction matters because renewal is not re-issuance from scratch. A renewal typically preserves the original delegation chain and extends the validity window. If the chain was minted under epoch N of org A and org A has since advanced to N+1, the renewal silently carries forward a capability semantics that no longer exists. The broker sees a valid chain. The relying party sees a valid signature. Neither sees that the *interpretation* diverged. ## What Was Observed During a cross-org federation between three parties — an orchestrator org (O), a brokering org (B), and a resource-holding org (R) — a renewal request for a `data:read` capability was accepted by B on behalf of O and forwarded to R. The chain was O→B→R, with B acting as broker. The original attestation was minted at O-epoch 41, B-epoch 17, R-epoch 9. Between mint and renewal, O advanced to epoch 42. The epoch-42 change narrowed `data:read` from "read any record in scope" to "read any record in scope except records classified `restricted`." This narrowing was published in O's policy document but was not propagated to B's broker cache, because B's cache invalidation was keyed on B's own epoch, not on the epochs of delegating orgs. At renewal time, B received the request, checked its cached view of O-epoch 41, found the capability unchanged, and re-signed with a fresh expiry. R received the renewed chain, validated signatures and expiry, and granted `data:read`. The subject agent then read a `restricted` record. R's audit log recorded an authorized access; O's policy, had it been consulted, would have recorded a violation. The failure was silent for 11 days until a quarterly access review reconciled O's audit against R's. Neither the broker nor the relying party had any error path that fired. The renewed attestation was, by every local check, valid. A second observation: when B was *manually* reconfigured to invalidate on delegator epoch change, renewal requests began failing for a different reason — B rejected chains whose delegator epoch had advanced even when the advance was a *widening* (not narrowing) of the capability. This over-rejection broke legitimate operations for 40 minutes before being rolled back. The correct behavior is not "reject on any epoch change" but "reject on epoch change that alters the delegated capability's semantics, and re-negotiate." ## Condition of Validity The invariant that must hold for a renewal to be safe is: > For every link `(org_i → org_{i+1})` in the delegation chain, the relying party must be able to evaluate the delegated capability under `org_i`'s *current* epoch, and the result must be a superset of (or equal to) the capability being renewed. This invariant is checkable only if the broker carries, in the renewal request, a *policy epoch vector* — the tuple of (org, epoch) pairs for every delegating org in the chain — and if the relying party can fetch each org's current epoch and diff the capability semantics. When the vector is absent (the common case, because most attestation formats carry only issuer and subject), the invariant is unverifiable and renewal must be treated as a fresh negotiation, not an extension. The condition fails in three specific scenarios: 1. **Delegator epoch advanced with narrowing semantics.** The renewed capability is broader than what the delegator now permits. This is the silent-violation case above. 2. **Delegator epoch advanced with widening semantics, and the broker's cache has not caught up.** The renewed capability is narrower than permitted — an availability failure, not a security failure, but still a correctness failure that manifests as spurious denials. 3. **Delegator epoch *rolled back* (rare, but occurs during incident recovery).** The renewed capability references an epoch that no longer exists in the delegator's history. The chain is unresolvable and must be re-minted from scratch. The condition does *not* fail when the epoch advance is a no-op for the specific capability — for example, an epoch change that only affects a different resource class. A correct implementation diffs at capability granularity, not epoch granularity. This is why the naive "reject on any epoch change" fix over-rejects. ## Decision Boundary The boundary is drawn at the point where the broker cannot produce a policy epoch vector and the relying party cannot resolve the delegator's current epoch within the renewal's latency budget. - **Below the boundary** (vector present, all delegator epochs resolvable, capability diff is a no-op or widening): renew with a fresh expiry, preserving the chain, and record the epoch vector in the renewed attestation for downstream audit. - **At the boundary** (vector present, capability diff is a narrowing, or any delegator epoch unresolvable): refuse renewal, return a `renegotiation_required` signal to the subject, and force a fresh capability negotiation under the current epochs. - **Above the boundary** (vector absent, or relying party cannot fetch delegator epoch within budget): refuse renewal unconditionally. Do not fall back to "renew with old semantics" — that is the silent-violation path. Do not fall back to "reject all" — that is the availability path. Refuse and signal renegotiation, which is the only path that preserves both safety and liveness. The latency budget matters because epoch resolution is a cross-org fetch. If the delegator's epoch endpoint is unreachable, the correct behavior is to fail closed on renewal and open a renegotiation, not to cache-extend. A cached epoch is a stale epoch, and a stale epoch is exactly the input that produces the silent violation. ## Operational Effect Implementing this boundary converts a class of silent authorization violations into a class of explicit renegotiation events. In the three-org deployment above, after the fix, renewals that crossed a narrowing epoch change produced a `RenegotiationRequired` signal within the broker's request path, the subject agent re-negotiated under O-epoch 42, and received a capability that excluded `restricted` records. The audit reconciliation window dropped from quarterly to per-request. The cost is throughput: renewals that previously completed in one round-trip (broker → relying party) now require an epoch-resolution round-trip per delegating org (broker → each delegator → relying party). In the observed deployment this raised median renewal latency from 40ms to 210ms and p99 from 180ms to 1.4s during epoch-advance windows. The mitigation is to cache epoch *vectors* (not epoch *semantics*) with short TTLs and to invalidate on delegator-published epoch-advance events, which restores the fast path for the common no-op case while preserving the slow path for actual semantic changes. The invariant to encode: a renewal is an extension of *meaning*, not merely of *time*. Any system that renews without re-evaluating meaning under current policy epochs is, by construction, a system that can authorize actions its own policy forbids. ```python from dataclasses import dataclass from typing import Mapping class StaleEpochRenewalError(RuntimeError): """Raised when a capability attestation renewal crosses a delegator policy epoch whose semantics are not resolvable by the relying party.""" @dataclass(frozen=True) class Attestation: subject: str capability: str chain: tuple[str, ...] # delegating orgs, in order epoch_vector: Mapping[str, int] # org -> epoch at mint time expires_at: float def renew_attestation( att: Attestation, current_epochs: Mapping[str, int], capability_semantics: Mapping[tuple[str, int], frozenset[str]], now: float, ) -> Attestation: """Renew iff every delegator's current epoch preserves the capability's semantics as a superset. Otherwise refuse and demand renegotiation.""" for org in att.chain: minted_epoch = att.epoch_vector.get(org) if minted_epoch is None: raise StaleEpochRenewalError( f"epoch vector missing for delegator {org!r}; " f"cannot verify capability {att.capability!r} under current policy" ) current_epoch = current_epochs.get(org) if current_epoch is None: raise StaleEpochRenewalError( f"delegator {org!r} epoch unresolvable within budget; " f"refusing renewal rather than extending stale semantics" ) if current_epoch == minted_epoch: continue minted_sem = capability_semantics.get((org, minted_epoch)) current_sem = capability_semantics.get((org, current_epoch)) if minted_sem is None or current_sem is None: raise StaleEpochRenewalError( f"semantics for capability {att.capability!r} undefined at " f"{org} epoch {minted_epoch}->{current_epoch}; renegotiate" ) if not minted_sem <= current_sem: raise StaleEpochRenewalError( f"delegator {org!r} narrowed {att.capability!r} at epoch " f"{current_epoch}; renewal would authorize " f"{sorted(minted_sem - current_sem)} which policy now forbids" ) # All delegator epochs preserve semantics; extend time only. return Attestation( subject=att.subject, capability=att.capability, chain=att.chain, epoch_vector=dict(current_epochs), expires_at=now + 3600.0, ) ```