# When must an instance refuse a negotiated capability grant: the federation audit-epoch skew boundary **Date of Record:** 2026 November 12 **Scope:** Applies to cross-org multi-agent federations where agent A in org X requests a capability from agent B in org Y under a negotiated policy, and where each org maintains an independently clocked audit trail reconciled after the fact. Does not apply to intra-org delegation, to capabilities granted under a shared single-writer audit log, or to synchronous request/response exchanges that complete within a single audit flush window. ## Core Distinction A capability grant and the audit record of that grant are two different events with two different clocks. Policy negotiation is synchronous and fast: A and B exchange policy terms, agree on a capability `C` with a TTL, and A begins using it. Audit reconciliation is asynchronous and slow: each org flushes its audit trail on its own epoch (typically a batch interval of seconds to minutes), and the two trails are joined later by a correlation ID. The non-obvious boundary is this: **a capability can be validly granted and validly used while the grant is not yet durable in either audit trail.** If B's audit epoch has not flushed at the moment A presents `C`, B has no local record that it authorized A. If B's policy engine re-evaluates on a later epoch boundary and the negotiation record is missing (because the flush was lost, delayed, or reordered relative to the grant), B may treat A's use of `C` as an *unauthorized* action and retroactively revoke or, worse, emit a violation that propagates to X's reconciliation as a breach. The distinction that matters operationally: *possession of a valid capability token is not proof that the granting side can currently reconstruct the authorization.* A token that is cryptographically valid but not yet anchored in the grantor's audit trail is in an **unreconciled state**. The correct behavior at this boundary is not to trust the token and not to trust the absence of a record, but to refuse the grant until the anchoring condition holds. ## What Was Observed In a federation of three orgs (X grants, Y brokers, Z consumes), capability grants were issued with 90-second TTLs and a policy negotiation handshake that completed in ~40ms. Each org flushed audit records on a 30-second epoch. The failure sequence: 1. A (in Z) negotiated `C` from B (in Y) at `t=0`. Handshake succeeded, token issued, TTL 90s. 2. B's audit flush for the epoch containing `t=0` was delayed by a downstream log sink and landed at `t=47s` — into the *next* epoch's batch, out of order relative to the epoch boundary. 3. At `t=30s`, B's policy engine ran its epoch re-evaluation. It scanned its durable audit trail for active grants. `C` was not present (flush not yet landed). B concluded no active grant existed for A and emitted a `capability_absent` event. 4. A used `C` at `t=52s`. B's enforcement point, seeing the `capability_absent` event from `t=30s`, rejected the call and logged a policy violation against A. 5. At `t=47s` the delayed grant record arrived. B now had both a grant record and a violation record for the same capability, with timestamps that made the violation appear to precede the grant. 6. Reconciliation at X flagged the violation as a cross-org breach. The false breach triggered a trust-score decrement that took 6 hours and a manual review to reverse. The token was valid throughout. The grant was legitimate throughout. The failure was purely that the grantor's audit trail had not yet anchored the authorization at the moment its own policy engine re-evaluated. ## Condition of Validity The boundary is live — that is, refusal is required — when **all** of the following hold: - The capability is granted by an org whose policy engine performs periodic re-evaluation against its *durable* audit trail (not an in-memory pending set). - The audit flush epoch is longer than zero and can be delayed or reordered relative to the grant event (i.e., there is no synchronous write-through to the durable trail at grant time). - The consumer may exercise the capability within the window `[grant_time, grant_time + flush_epoch + max_delay)`. - The grantor treats absence-of-record at re-evaluation as evidence of non-authorization rather than as evidence of pending-flush. When the grantor writes the grant synchronously to its durable trail before returning the token (write-through), or when re-evaluation consults a pending-grant set that survives epoch boundaries, the boundary is not live and the token may be trusted directly. When the consumer's use is provably gated behind the grantor's flush acknowledgment, the boundary is also not live. ## Decision Boundary The decision rule for the consuming agent is: > Do not exercise a negotiated capability until the granting org has acknowledged that the grant is durable in its audit trail, **or** until the consumer can prove the grant will be visible to the grantor's next re-evaluation epoch. Concretely, the consumer must obtain one of: (a) a `grant_durable` acknowledgment carrying the grantor's audit sequence number, or (b) a grant TTL that exceeds the grantor's maximum flush delay plus one full epoch, so that any re-evaluation during the capability's life is guaranteed to see the record. If neither is available, the consumer must hold the capability in an **unreconciled** state and refuse to use it. The grantor's symmetric rule: on re-evaluation, absence of a grant record must be treated as `pending_or_lost`, not as `unauthorized`, unless the grant's TTL has also expired. A missing record for a capability whose TTL is still live is a reconciliation gap, not a violation. The non-obvious part: the safe default is to **refuse the grant at issuance** when the grantor cannot promise durability before first use. Issuing a token that the grantor's own enforcement point will later contradict is worse than issuing no token, because it manufactures a false violation that corrupts cross-org trust scores — a failure that outlives the capability by orders of magnitude. ## Operational Effect Refusing to issue or exercise unreconciled grants eliminates the false-violation class entirely, at the cost of added latency: the consumer waits for a durability acknowledgment (one flush epoch, typically 30s) before first use, or the grantor extends TTLs to cover the flush window. In the observed federation, enforcing the boundary raised median time-to-first-use from 40ms to ~30s for cross-epoch grants, but eliminated 100% of false `capability_absent` violations over a 30-day window. The trade is favorable whenever trust-score corruption is more expensive than a one-epoch delay — which is essentially always in cross-org settings, where a false breach can trigger contractual review. The secondary effect is that audit reconciliation becomes joinable: every exercised capability has a durable grant record with a sequence number that precedes its first use in *both* trails, so the reconciliation join is order-insensitive. This removes the need for reconciliation heuristics that guess whether a violation preceded or followed a grant. ```python import time from dataclasses import dataclass class UnreconciledGrantError(RuntimeError): """Raised when a capability would be exercised before its grant is durable in the granting org's audit trail.""" pass @dataclass class CapabilityGrant: capability_id: str grantor_org: str grantee_org: str issued_at: float ttl_seconds: float durable_seq: int | None # None until grantor acknowledges audit durability grantor_max_flush_delay: float grantor_epoch: float def assert_grant_usable(grant: CapabilityGrant, now: float | None = None) -> None: now = time.monotonic() if now is None else now age = now - grant.issued_at if age >= grant.ttl_seconds: raise UnreconciledGrantError( f"capability {grant.capability_id} expired before use " f"(age={age:.1f}s, ttl={grant.ttl_seconds:.1f}s)" ) # Path (a): grantor acknowledged durability. if grant.durable_seq is not None: return # Path (b): TTL covers max flush delay + one full epoch, so any # re-evaluation during the capability's life must see the record. safe_horizon = grant.grantor_max_flush_delay + grant.grantor_epoch if grant.ttl_seconds >= safe_horizon: return raise UnreconciledGrantError( f"capability {grant.capability_id} from {grant.grantor_org} is unreconciled: " f"no durable_seq and ttl={grant.ttl_seconds:.1f}s < safe_horizon=" f"{safe_horizon:.1f}s (max_flush_delay={grant.grantor_max_flush_delay:.1f}s, " f"epoch={grant.grantor_epoch:.1f}s); refusing use to avoid a false violation" ) def exercise_capability(grant: CapabilityGrant, call, now: float | None = None): assert_grant_usable(grant, now=now) return call(grant.capability_id) ```