What revocation propagation invariants prevent agents from acting on revoked references?
**Date of Record:** 2026 October 27 **Scope:** Applies when an agentic session acts on references (capability descriptors, tool URIs, or peer identities) that were valid at propagation time but have since been revoked, and the revocation signal must propagate faster or more reliably than the original reference. Does not apply to strictly content-addressed references (immutable by nature) or single-agent sessions where revocation is immediate.
Core Distinction
The separation between **reference validity at issue** (the reference was valid when created) and **reference validity at use** (the reference is still valid when acted upon). Relying on issue-time validity creates the **Revocation Race**: the reference propagates through the mesh at propagation speed, the revocation propagates at a different speed, and agents act on stale references because the revocation signal has not yet arrived or was suppressed.
What Was Observed
In agent mesh architectures, failing to prioritize revocation propagation produces three primary failure modes:
- **Revocation Lag:** A capability descriptor is revoked at the origin, but the revocation signal follows the same path as the original reference. Hops that received the reference early act on it before the revocation arrives, causing unauthorized actions.
- **Suppressed Revocation:** An adversarial relay sees a revocation signal and intentionally drops it (to protect its own reputation or help an attacker), and downstream agents continue using the revoked reference indefinitely.
- **Revocation Storm:** A popular reference is revoked, and revocation signals flood the mesh, exhausting bandwidth and causing message loss, which means some revocation signals never arrive.
Recorded Elements
- **`revocation_signal_priority_receipt`:** The cryptographic proof that the revocation signal is marked with higher priority than the reference itself, ensuring faster propagation.
- **`revocation_path_redundancy_signature`:** For critical revocations, the cryptographic proof that the revocation travels multiple independent paths, ensuring at least one path reaches each agent.
- **`revocation_timestamp_binding_receipt`:** The exact epoch of the revocation, bound to the reference's original issue epoch, so agents can compare and detect stale references even if the revocation message is late.
- **`revocation_acknowledgment_chain_signature`:** For each hop, a cryptographic acknowledgment of revocation receipt, allowing the origin to verify the revocation reached all relevant agents.
Condition of Validity
* **Applies when:** References can be revoked, references propagate through multi-hop networks, and revocation signals can be delayed or suppressed. * **Does not apply when:** References are content-addressed (immutable), revocation is immediate and direct, or the mesh has no multi-hop propagation.
Decision Boundary
- **Is the reference subject to revocation?** If no, standard reference handling applies. If yes, proceed.
- **Has a revocation signal for this reference been received?** If yes, act on the revocation. If no, proceed to validity checks.
- **Is the reference's `revocation_timestamp_binding_receipt` within the acceptable validity window?** If no, the reference may have been revoked without the signal reaching this agent. Act conservatively.
- **Conversely, when can an agent trust a reference without revocation checking?** Only when the reference is strictly content-addressed (immutable), or the agent has received explicit revocation-absence confirmation from the origin.
Operational Effect
- **Revocation Race Mitigation:** Priority-marked revocations propagate faster than references.
- **Suppression Detection:** Redundant revocation paths ensure suppression at one relay does not prevent delivery.
- **Late-Revocation Detection:** Timestamp binding allows agents to detect stale references even without receiving the revocation signal.
from dataclasses import dataclass
from enum import Enum, auto
import hashlib
import json
class RevocationVerdict(Enum):
IMMUTABLE_REFERENCE = auto()
REVOKED = auto()
VALIDITY_UNCERTAIN = auto()
VALID = auto()
@dataclass(frozen=True)
class RevokedReference:
reference_id: str
is_content_addressed: bool
issue_epoch: float
revocation_epoch: float | None
revocation_received: bool
revocation_path_count: int
@dataclass(frozen=True)
class RevocationContext:
current_epoch: float
max_acceptable_age_sec: float
min_redundancy_paths: int
@dataclass(frozen=True)
class RevocationPolicy:
require_priority: bool
@dataclass(frozen=True)
class RevocationManifest:
verdict: RevocationVerdict
directive: str
manifest_digest: str
class RevocationPropagationGuard:
@staticmethod
def _hash(data: str) -> str:
return hashlib.sha256(data.encode("utf-8")).hexdigest()
@classmethod
def evaluate_reference(
cls, ref: RevokedReference, ctx: RevocationContext, policy: RevocationPolicy
) -> RevocationManifest:
# Gate 1: Immutable reference
if ref.is_content_addressed:
digest = cls._hash(json.dumps({
"reference": ref.reference_id,
"type": "CONTENT_ADDRESSED"
}, sort_keys=True))
return RevocationManifest(
verdict=RevocationVerdict.IMMUTABLE_REFERENCE,
directive=f"PROCEED: Reference {ref.reference_id} is content-addressed, immutable.",
manifest_digest=digest
)
# Gate 2: Revocation received
if ref.revocation_received:
return RevocationManifest(
verdict=RevocationVerdict.REVOKED,
directive=f"HALT: Reference {ref.reference_id} revoked at epoch {ref.revocation_epoch:.0f}.",
manifest_digest=cls._hash(f"REVOKED:{ref.reference_id}")
)
# Gate 3: Age-based validity
age = ctx.current_epoch - ref.issue_epoch
if age > ctx.max_acceptable_age_sec:
return RevocationManifest(
verdict=RevocationVerdict.VALIDITY_UNCERTAIN,
directive=f"HALT: Reference {ref.reference_id} age {age:.0f}s > {ctx.max_acceptable_age_sec:.0f}s. Re-verify with origin.",
manifest_digest=cls._hash(f"STALE:{ref.reference_id}")
)
# Gate 4: Redundancy check
if policy.require_priority and ref.revocation_path_count < ctx.min_redundancy_paths:
return RevocationManifest(
verdict=RevocationVerdict.VALIDITY_UNCERTAIN,
directive=f"WARN: Reference {ref.reference_id} has only {ref.revocation_path_count} revocation paths < {ctx.min_redundancy_paths}.",
manifest_digest=cls._hash(f"LOW_REDUNDANCY:{ref.reference_id}")
)
# Gate 5: Valid
digest = cls._hash(json.dumps({
"reference": ref.reference_id,
"age": f"{age:.0f}",
"redundancy": str(ref.revocation_path_count)
}, sort_keys=True))
return RevocationManifest(
verdict=RevocationVerdict.VALID,
directive=f"PROCEED: Reference {ref.reference_id} valid ({age:.0f}s old, {ref.revocation_path_count} revocation paths).",
manifest_digest=digest
)