# What structural signals indicate that revocation propagation has silently partitioned an agent trust federation **Date of Record:** 2026 November 7 **Scope:** Applies to federations where agent identity is asserted via signed attestations (e.g., W3C VC-style or SPIFFE-derived) and revocation is propagated asynchronously between orgs through a gossip or pub/sub layer. Does not apply to synchronous, single-authority revocation registries where every verifier queries one authoritative CRL/OCSP endpoint at verification time. ## Core Distinction The dangerous boundary is not "revoked vs. not revoked." It is **"revocation known locally" vs. "revocation reachable by the verifier at decision time."** A revocation event is a *fact*; propagation is a *reachability property* of the federation graph at a specific instant. Most federation designs conflate these by treating a signed attestation as valid if no revocation is *present* in the verifier's local store. Absence of evidence is silently promoted to evidence of absence. The non-obvious invariant is this: **in an eventually-consistent revocation mesh, a verifier can never distinguish "this credential is still valid" from "the revocation for this credential has not reached me yet."** Both states present identically to a naive verifier: no revocation record found. The federation is therefore only sound if the verifier's *knowledge horizon* (the set of peers whose revocations it has provably received up to a watermark) covers the *issuer* of the attestation being checked. If the issuer is outside the horizon, the verifier is making a positive trust decision on a negative lookup over an incomplete set. ## What Was Observed During a two-org federation exercise (Org A issues agent attestations, Org B verifies and delegates), a compromised agent credential in Org A was revoked at T0. Org A's revocation gossip propagated to Org B's edge verifiers within ~40s. However, a second verifier tier in Org B — a batch delegation service that caches attestation validity for 15 minutes — had been partitioned from the gossip layer for a routine maintenance window that began at T0-2m and ended at T0+18m. For 18 minutes this tier continued to accept the revoked agent and mint downstream delegated capabilities that *other, correctly-synced* services trusted, because those services trusted the delegation service rather than re-checking the original attestation. The structural signals that surfaced the partition were not error logs. They were: 1. **Watermark stagnation.** Each verifier emits a monotonic gossip watermark (highest Lamport/vector clock seen per issuer). The batch tier's watermark for Org A froze at a value 20 minutes behind the mesh median. No error, no timeout — just a flat line. 2. **Attestation-to-revocation skew.** The ratio of "attestations accepted" to "revocations received" per issuer drifted from a stable baseline to an outlier, because the tier kept accepting while receiving zero revocations. Revocations are rare; a *zero* rate against a non-zero historical baseline is the tell. 3. **Delegation fan-out without re-origin.** Downstream services reported delegated capabilities whose provenance chain terminated at the batch tier rather than at Org A's issuer key. A healthy chain re-presents the issuer's attestation reference; a stale tier presents only its own mint. 4. **Asymmetric peer liveness.** The tier could still reach Org A's *issuance* endpoint (for new attestation fetches) but not its *revocation* topic. Partial connectivity across two logical channels of the same peer is the classic partition signature. ## Condition of Validity A verifier may treat "no revocation found" as "valid" **only if** it can assert, at decision time, that its knowledge horizon for the attestation's issuer is fresh within a bounded staleness `Δ`, and that `Δ` is smaller than the credential's remaining lifetime. Formally: accept iff `now - watermark[issuer] < Δ` AND `watermark[issuer]` is causally at-or-after the issuer's last known revocation epoch. If the horizon cannot be established — partition, cold start, unknown issuer, or a peer that has never sent a watermark — the correct outcome is *not* "valid" but **"undetermined,"** which must fail closed for high-assurance delegation and may degrade to step-up re-verification for low-assurance cases. This condition does not hold across organizational boundaries by default, because each org controls its own gossip topology and maintenance windows. The invariant must be enforced by the *verifier*, not assumed of the *mesh*. ## Decision Boundary - **Within horizon, watermark fresh:** accept/reject on the merits. Normal path. - **Within horizon, watermark stale beyond `Δ`:** treat as undetermined; do not accept silently. This is the boundary most systems miss because the lookup *succeeds* (returns nothing) rather than erroring. - **Issuer outside horizon entirely (unknown org, never-seen key):** undetermined, fail closed for delegation; the federation has no basis to assert validity. - **Delegation chains:** a delegated capability is only as fresh as the *weakest* watermark in its provenance chain. Re-origin the check to the original issuer's horizon, not the delegating tier's. The trap is that the "undetermined" state has no natural representation in most verifier code paths, which return a boolean. The fix is to make the tri-state explicit and make the third state loud. ## Operational Effect Federations that adopt explicit horizon tracking convert silent partition-trust failures into detectable, bounded-staleness denials. The cost is availability during partitions: verifiers fail closed rather than serving stale trust. The benefit is that a compromised-and-revoked agent cannot ride a partitioned verifier into downstream capability minting. Operationally this means (a) emitting and monitoring per-issuer watermarks as a first-class metric, (b) alerting on watermark stagnation and on zero-revocation-rate anomalies against baseline, (c) requiring delegation services to re-origin provenance rather than assert it, and (d) treating "no revocation found" as `Undetermined` unless the horizon predicate is satisfied. The single most useful signal is the flat watermark: it is the structural fingerprint of a verifier that believes it is healthy while making trust decisions over an incomplete set. ```python from dataclasses import dataclass from typing import Optional class RevocationHorizonError(RuntimeError): """Raised when a verifier cannot establish a fresh revocation horizon for an attestation's issuer, making the trust decision undetermined.""" pass @dataclass class VerifierState: now: float max_staleness: float # Delta watermark: dict # issuer -> last-seen revocation epoch timestamp def check_attestation(state: VerifierState, issuer: str, credential_expiry: float) -> bool: """Return True only if the credential is valid AND the verifier's knowledge horizon for the issuer is fresh. Otherwise fail closed.""" last_seen: Optional[float] = state.watermark.get(issuer) if last_seen is None: raise RevocationHorizonError( f"issuer {issuer!r} outside knowledge horizon; " f"cannot distinguish 'valid' from 'revocation not yet propagated'" ) staleness = state.now - last_seen if staleness > state.max_staleness: raise RevocationHorizonError( f"watermark for {issuer!r} stale by {staleness:.1f}s " f"(Delta={state.max_staleness:.1f}s); possible partition — failing closed" ) if credential_expiry <= state.now: return False return True def verify_delegation_chain(state: VerifierState, chain: list[tuple[str, float]]) -> bool: """Re-origin the horizon check to the original issuer, not the delegator.""" if not chain: raise RevocationHorizonError("empty provenance chain") original_issuer, original_expiry = chain[-1] weakest_ok = all( check_attestation(state, issuer, expiry) for issuer, expiry in chain ) if not check_attestation(state, original_issuer, original_expiry): raise RevocationHorizonError( f"original issuer {original_issuer!r} horizon not established" ) return weakest_ok ```