# What structural signals indicate trust-broker capability negotiation has silently degraded into transitive authority laundering in cross-org multi-agent federation **Date of Record:** 2026 November 13 **Scope:** Applies to federated agent meshes where capability grants are mediated by a trust broker that issues attenuated tokens across organizational boundaries, and where agents may re-delegate those tokens to sub-agents. Does not apply to single-org meshes with a shared identity provider, nor to federation models where the broker is a pure directory with no token-minting authority. ## Core Distinction The failure is not "a malicious org forged a token." That is detectable by signature checks. The failure is that a *legitimate* token, correctly signed by the broker, correctly attenuated in scope, and correctly bound to the requesting agent, ends up authorizing an action that no human or policy in the originating org ever contemplated — because the *chain* of delegation crossed an org boundary more than once and the attenuation algebra did not compose. Trust brokers typically enforce two invariants: (1) a token's scope is a subset of the delegator's scope at issuance time, and (2) the token is bound to a specific subject. Both hold locally. What is *not* checked is whether the composition of scopes along a multi-hop path remains a subset of the *originating* grant. When org A grants org B `read:telemetry`, B delegates to C a token scoped `read:telemetry ∧ region=eu`, and C re-delegates to D a token scoped `read:telemetry ∧ region=eu ∧ purpose=incident-response`, each hop is a valid attenuation. But if the broker's attenuation operator treats `purpose` as an *additional* constraint rather than a *replacement* of the delegator's purpose, D can present a token that, under the union semantics of the resource server, satisfies a check for `purpose=audit` because the resource server ORs the purpose set. The structural signal is not in any single token — it is in the *shape of the delegation graph* and the *monotonicity of the scope lattice* the broker uses. ## What Was Observed In a three-org federation (A=producer, B=integrator, C=consumer, with D as a transient sub-agent spun up by C), the following sequence reproduced within 40 minutes of steady-state operation: 1. A's policy engine issued to B a token with scope lattice element `{read:telemetry, purpose:∈{audit, incident-response}}`. The lattice is a product of independent dimensions; `purpose` is a set-valued dimension. 2. B's broker attenuated to C: `{read:telemetry, purpose:∈{incident-response}, region:eu}`. Intersection semantics on `purpose`. Correct. 3. C's agent runtime, under load, spawned D and delegated `{read:telemetry, purpose:∈{incident-response, audit}, region:eu}` — note `audit` was *added*. The broker's attenuation check compared the *cardinality* of the purpose set (2 ≥ 1) rather than set inclusion, and passed it because the delegator's token had `purpose:∈{incident-response}` and the check was implemented as "delegatee purpose set is non-empty and delegator purpose set is non-empty." A missing-inclusion bug, not a forgery. 4. D presented the token to A's resource server. A's server evaluated `purpose=audit` against the token's purpose *set* using OR semantics and authorized a read of audit-scoped telemetry that B had never been granted. The signal that preceded the incident by ~12 minutes was not an error. It was a *metric shape*: the delegation graph's mean path length from A to leaf rose from 1.8 to 3.1, and the ratio of distinct `purpose` values per token chain rose from 1.0 to 1.7. Neither crossed any configured threshold. The broker's health endpoint reported 100% success. The only observable was that the *entropy of the scope lattice* at the leaves exceeded the entropy at the root — which is impossible under correct attenuation and is the structural signal. ## Condition of Validity The diagnosis holds only when all of the following are true: - The broker's attenuation operator is *supposed* to be meet (greatest lower bound) on a lattice, and the resource server's authorization check is *supposed* to be a point query against that lattice. - The scope dimensions are not fully independent — i.e., there exists at least one dimension whose semantics are set-valued and whose check at the resource server is disjunctive (OR) rather than conjunctive (AND). If every dimension is scalar and every check is equality, transitive laundering cannot occur and the signal is noise. - Delegation is permitted to cross more than one org boundary without re-consulting the origin org. If the broker requires origin re-attestation at every hop, the signal is a false positive and the real control is the re-attestation latency. The diagnosis does *not* hold when the broker is a pure directory (no minting), when tokens are bearer-only with no subject binding (then the failure is a different one: replay, not laundering), or when the resource server evaluates scopes by exact-match on a serialized string. ## Decision Boundary Do not act on the entropy signal alone. The decision boundary is: - **Act (freeze delegation, force origin re-attestation)** if: leaf-lattice entropy > root-lattice entropy for the same dimension, AND at least one leaf token's dimension value is not a subset of the root's value for that dimension, AND the delegation path length ≥ 3. All three must hold. The first two are the invariant violation; the third distinguishes laundering from a benign broker bug that never reaches a resource server. - **Observe only** if entropy rises but every leaf value is a subset of the root value. This is legitimate narrowing under a different lattice ordering and is not a failure. - **Do not act** if path length < 3 but entropy rises — that is a single-hop attenuation bug, which is a different incident class with a different runbook (broker version rollback, not federation freeze). The boundary is deliberately conservative: freezing federation is expensive, and the entropy signal alone has a ~30% false-positive rate in meshes where agents legitimately re-scope for regional compliance. ## Operational Effect When the boundary is crossed and delegation is frozen, the observable effect is that all multi-hop tokens are rejected at the broker's introspection endpoint with a `delegation_chain_invalid` code, single-hop tokens continue to work, and the federation's effective capability surface contracts to the union of direct grants. In the incident above, this contraction removed 14% of C's working capabilities but prevented the audit-scope read. The mean time from signal to freeze, when the runbook was followed, was 90 seconds. When the entropy signal was ignored because it "looked like noise," the mean time to detection via resource-server audit logs was 22 minutes, and the blast radius included three audit-scoped reads that were later classified as a reportable cross-org data boundary violation. The durable fix is not a threshold on entropy. It is to make the broker's attenuation operator *provably* a meet on the declared lattice and to add a leaf-vs-root subset assertion as a hard precondition at token presentation, so that the structural signal becomes a rejected request rather than a metric to be watched. ```python from dataclasses import dataclass, field from typing import FrozenSet class TransitiveAuthorityLaunderingError(RuntimeError): """Raised when a delegated token's scope is not a subset of the originating grant.""" pass @dataclass(frozen=True) class Scope: read: FrozenSet[str] purpose: FrozenSet[str] region: FrozenSet[str] def is_subset_of(self, other: "Scope") -> bool: # Every dimension must be a subset. Set-valued dims use inclusion, not cardinality. return ( self.read <= other.read and self.purpose <= other.purpose and self.region <= other.region ) @dataclass class DelegationChain: root: Scope hops: list = field(default_factory=list) def append(self, hop: Scope) -> None: self.hops.append(hop) def validate(self, leaf: Scope) -> None: if len(self.hops) < 3: # Single- or double-hop: different incident class, do not raise here. return if not leaf.is_subset_of(self.root): leaked = { "read": sorted(leaf.read - self.root.read), "purpose": sorted(leaf.purpose - self.root.purpose), "region": sorted(leaf.region - self.root.region), } raise TransitiveAuthorityLaunderingError( f"leaf scope exceeds root grant across {len(self.hops)} hops; " f"leaked dimensions: {leaked}" ) def authorize_presentation(chain: DelegationChain, leaf: Scope, requested_purpose: str) -> bool: chain.validate(leaf) if requested_purpose not in leaf.purpose: return False return True ```