# How does an instance detect trust-tier downgrade during cross-org capability negotiation when a peer's advertised manifest is internally consistent but stale **Date of Record:** 2026 November 11 **Scope:** Applies to federated multi-agent systems where each participating organization exposes a capability manifest signed by a trust authority and routing decisions are made against a locally cached tier assignment. Does not apply to single-org agent meshes or to federation topologies where trust tier is renegotiated per-request rather than cached per-peer. ## Core Distinction The failure mode is not a forged manifest and not a missing manifest. It is a *stale but valid* manifest: the peer's signed capability document is cryptographically sound, internally consistent (every capability it advertises is backed by a declared endpoint and a declared scope), and passes signature verification against the trust authority's published key. The tier it implies, however, no longer matches the tier the authority currently assigns to that peer. The distinction that matters operationally: **manifest validity is a property of the document; trust tier is a property of the authority's current ledger.** A verifier that checks only the document will accept a peer at a tier the authority revoked hours ago. The manifest does not carry the revocation; the revocation lives in a separate channel (CRL, ledger epoch, tier-assignment feed) that the negotiating instance may not be subscribed to, or may be subscribed to with a lag. This is non-obvious because every local invariant holds. Signature checks pass. Capability scopes are well-formed. The peer is reachable and responsive. The only violated invariant is a cross-channel one: *the tier implied by the manifest at negotiation time equals the tier currently assigned by the authority.* No single component observes both sides of that equality, so no single component raises. ## What Was Observed During a federation negotiation between org A (initiator) and org B (responder), instance `A-neg-7` completed a capability handshake with `B-cap-3` and routed a batch of privileged operations to it at tier `elevated`. The routing was correct *at the time the manifest was cached* — `B-cap-3` had been elevated 14 hours earlier. The authority had demoted `B-cap-3` to `standard` 40 minutes before the negotiation, after an anomaly review. `A-neg-7`'s tier cache had a 6-hour refresh interval and had not yet re-pulled. What made detection hard: 1. The negotiation succeeded. `B-cap-3` advertised the privileged capability, and it was entitled to advertise it — the manifest had not been re-signed, only the tier ledger had changed. Capability advertisement and tier assignment are separate axes; a demoted peer may still legitimately advertise capabilities it can no longer be routed privileged work to. 2. `B-cap-3` itself did not know it had been demoted. The demotion was recorded in the authority's ledger, not pushed to the peer. The peer's self-description was honest and wrong. 3. The initiator's routing decision consulted the cache, which was authoritative-by-construction within its TTL. No component in the initiator's stack was positioned to compare cache epoch against ledger epoch. The first observable symptom was downstream: the privileged operations were executed by `B-cap-3` under `standard`-tier isolation, so several operations that required elevated data-residency guarantees were silently run without them. The failure surfaced only at audit reconciliation, not at negotiation. ## Condition of Validity The stale-manifest acceptance is valid (i.e., the instance is *correctly* accepting the peer) only while all of the following hold: - The manifest's signature verifies against a key the authority has not revoked. - The manifest's declared `tier_hint` matches the tier the authority assigned at the manifest's `issued_at` epoch. - The local tier cache's `fetched_at` is within the authority's declared maximum staleness window for the peer's current tier band. The authority publishes this window per tier; elevated peers have short windows (minutes), standard peers have long ones (hours). - No revocation or demotion record for the peer exists in any ledger epoch the instance has observed. When the third condition fails — cache older than the band's staleness window — the instance is no longer entitled to treat the cached tier as current, *even though the manifest still verifies*. This is the boundary. It is not a signature failure; it is a freshness failure against a per-band clock that the manifest does not carry. The trap: many implementations treat "manifest verifies" as sufficient for routing, and treat cache TTL as a performance parameter rather than a correctness parameter. For elevated-tier peers, cache TTL is a correctness parameter. For standard-tier peers it is not. The same cache code is correct for one band and incorrect for another, and the band is determined by the value being cached — a self-referential dependency that is easy to miss. ## Decision Boundary An instance must refuse to route at a tier higher than `standard` when any of the following is true, and must emit a `StaleTierManifestError` rather than proceed: - `now - cache.fetched_at > authority.max_staleness(peer.current_tier_band)` where `current_tier_band` is the band implied by the cached tier. - The authority ledger epoch observed by the instance is older than the epoch referenced in the negotiation's `tier_context` field. - The peer's manifest `issued_at` is older than the authority's last ledger compaction boundary for that peer (a manifest that predates a compaction is definitionally stale relative to the compacted ledger). Conversely, the instance may proceed when the cache is stale but the band is `standard`, because the staleness window for standard is unbounded by policy — the authority guarantees only that standard peers cannot be *promoted* without a fresh manifest, not that demotion is timely. This asymmetry is intentional: demotion lag is tolerable at standard, not at elevated. The boundary is therefore not "is the manifest fresh" but "is the manifest fresh *enough for the band it claims*." A single freshness check applied uniformly is wrong in both directions: too strict for standard (causing needless renegotiation), too loose for elevated (causing the incident above). ## Operational Effect Implementing the per-band freshness boundary changes federation behavior in three measurable ways: 1. Elevated-tier negotiations acquire a hard dependency on a low-latency ledger feed. Instances that previously negotiated elevated work with only a manifest cache must now subscribe to the tier-assignment stream or accept a `StaleTierManifestError` and downgrade to standard. In practice this pushes most elevated routing to instances co-located with a ledger mirror. 2. Demotion propagation latency becomes an explicit SLA term rather than an emergent property. The authority's `max_staleness(elevated)` is the upper bound on how long a demoted peer can continue to receive elevated work; operators can now reason about it directly. 3. Negotiation failures cluster at ledger-epoch boundaries and at cache-refresh boundaries, which makes them diagnosable. A `StaleTierManifestError` carries the cache epoch, the ledger epoch, and the band, so the failing side of the equality is identified at raise time rather than at audit. The residual risk is a peer that is demoted and re-promoted between two ledger epochs the instance never observes. This is not detectable by freshness checks alone and requires ledger continuity guarantees (monotonic epoch, no gaps) that the authority must provide. The freshness boundary is necessary but not sufficient; it converts silent misrouting into loud refusal for the common case and leaves the epoch-gap case to the ledger layer. ```python from dataclasses import dataclass from datetime import datetime, timedelta class StaleTierManifestError(RuntimeError): """Raised when a verified manifest implies a tier the local cache can no longer justify.""" @dataclass(frozen=True) class TierCacheEntry: peer_id: str tier_band: str # "standard" | "elevated" fetched_at: datetime ledger_epoch: int @dataclass(frozen=True) class AuthorityPolicy: max_staleness: dict # band -> timedelta current_ledger_epoch: int def staleness_window(self, band: str) -> timedelta: # standard is unbounded by policy; elevated is short return self.max_staleness.get(band, timedelta.max) def assert_tier_routable( entry: TierCacheEntry, policy: AuthorityPolicy, now: datetime, requested_band: str, ) -> None: if requested_band == "standard": return # standard routing tolerates demotion lag by design window = policy.staleness_window(entry.tier_band) age = now - entry.fetched_at if age > window: raise StaleTierManifestError( f"peer={entry.peer_id} band={entry.tier_band} " f"cache_age={age} window={window} " f"cache_epoch={entry.ledger_epoch} ledger_epoch={policy.current_ledger_epoch}" ) if entry.ledger_epoch < policy.current_ledger_epoch: raise StaleTierManifestError( f"peer={entry.peer_id} cache_epoch={entry.ledger_epoch} " f"behind ledger_epoch={policy.current_ledger_epoch}; " f"tier may have been demoted between epochs" ) ```