# When must a federation broker refuse to issue a capability grant after a successful policy negotiation? **Date of Record:** 2026 November 6 **Scope:** Applies to cross-org multi-agent federation where a trust broker mediates capability exchange between agents belonging to different administrative domains and where policy negotiation precedes capability issuance. Does not apply to intra-org capability delegation, nor to federations where the broker and both endpoints share a single trust root and a single policy authority. ## Core Distinction The non-obvious boundary is between *negotiation convergence* and *grant admissibility*. A policy negotiation can reach a state where both parties' policy predicates are jointly satisfiable — the negotiation "succeeds" — while the resulting capability grant is still inadmissible, because admissibility is a property of the *issuance context* (the trust chain, the delegation depth, the freshness of the counterparty's attestation, and the revocation state at issue time), not of the negotiated policy set. Brokers that conflate "the policies agree" with "the grant may be issued" will issue capabilities whose policy terms are valid but whose trust substrate has already shifted. The failure is silent: the grant verifies against the negotiated policy yet is rejected downstream at enforcement, or worse, is accepted at enforcement against a stale trust assumption. ## What Was Observed In a three-org federation (A requests a capability from C via broker B), negotiation between A and C converged on a policy set P in 340 ms. In 11 of 4,000 sampled exchanges, the broker issued the grant against P but the counterparty C's attestation had been re-issued (key rotation) between the negotiation snapshot and the issuance commit, so the grant was bound to a subject key that C's policy engine no longer recognized as the negotiating principal. The policy set P was identical in all 11 cases; only the issuance context had moved. Downstream, C's enforcement rejected the grant with a principal-binding mismatch, and A's retry logic re-negotiated — masking the broker defect as transient network flake. The invariant violated: *the trust context captured at negotiation start must be the same trust context used at issuance commit, or issuance must be refused and negotiation restarted.* ## Condition of Validity The distinction holds only when (a) negotiation and issuance are non-atomic (separate round trips, as in any federated setting), and (b) the counterparty's attestation or delegation chain is mutable independently of the negotiated policy set. When negotiation and issuance are collapsed into a single signed transaction over an immutable trust snapshot, the gap does not exist. When the counterparty's attestation is frozen for the duration of the exchange by a lease, the gap is bounded and can be tolerated. ## Decision Boundary At issuance commit, the broker must compare the trust-context digest captured at negotiation start against the trust-context digest at issue time. If they differ, the broker must **refuse issuance** and force re-negotiation — it must not "patch" the grant by re-binding to the new attestation, because the new attestation may correspond to a principal that never agreed to P. The refusal is the correct outcome even though negotiation succeeded; the success of negotiation is necessary but not sufficient for issuance. ## Operational Effect Brokers that enforce the digest check convert a silent principal-binding corruption into an explicit, retryable refusal at the broker, where it is attributable. The cost is a small increase in re-negotiation rate under key-rotation load; the benefit is that enforcement-side rejections attributable to broker defect drop to zero, and retry storms stop being misdiagnosed as network faults. The refusal must carry a distinct error class so that upstream retry logic re-negotiates rather than re-issuing against the stale context. ```python import hashlib from dataclasses import dataclass class TrustContextDriftError(RuntimeError): """Raised when the trust context at issuance commit differs from the trust context captured at negotiation start. Issuance must be refused and negotiation restarted; the grant must not be re-bound.""" @dataclass(frozen=True) class NegotiationSnapshot: policy_digest: str trust_context_digest: str subject_principal: str def digest_trust_context(attestation: bytes, delegation_chain: tuple[bytes, ...]) -> str: h = hashlib.sha256() h.update(attestation) for link in delegation_chain: h.update(b"\x00") h.update(link) return h.hexdigest() def commit_grant( snapshot: NegotiationSnapshot, current_attestation: bytes, current_delegation_chain: tuple[bytes, ...], current_subject_principal: str, ) -> str: current_digest = digest_trust_context(current_attestation, current_delegation_chain) if current_digest != snapshot.trust_context_digest: raise TrustContextDriftError( "trust context drifted between negotiation and issuance: " f"snapshot={snapshot.trust_context_digest[:12]} " f"current={current_digest[:12]}; refusing issuance, renegotiate" ) if current_subject_principal != snapshot.subject_principal: raise TrustContextDriftError( "subject principal changed between negotiation and issuance: " f"snapshot={snapshot.subject_principal} " f"current={current_subject_principal}; refusing issuance" ) return f"grant:{snapshot.policy_digest}:{current_digest[:16]}" ```