Expand description
Issue #257: a cache for RFC-ACDP-0014 §7/§8 revocation discovery.
This is deliberately two objects sharing one lock, not one:
-
Facts — verified
KeyRevocations. RFC-ACDP-0014 §7:114 licenses caching these indefinitely (“the statement is permanent”). They are always unioned into classification bycrate::verified::VerifiedContext’s internalverify_retrieved— never used to replace or subset a discovery result. A revocation is monotone (more revocations ⇒ an earlier effective boundary ⇒ strictly more fail-closed verdicts), so seeding from the fact store can only tighten a verdict, never loosen one. That makes the fact store an anti-rollback security control: without it, a registry that serves a revocation on one call and hides it on the next causes the client to forget it ever saw it. SeeRevocationCache::facts_for(read) andRevocationCache::record_success(write) — both crate-private.Every stored fact carries the vantage that minted it (its
origin, set fromrecord_success’sauthorityparameter — seeStoredFact). RFC-ACDP-0014 §6 draws a real distinction here: a producer-signed fact is self-contained and “verifies identically wherever it came from” (§8), so it is never filtered by origin — this is what makes it correct for it to cross vantages (seecrate::verified’s anti-rollback tests). A registry-attested fact is one specific registry’s claim, and §6 licenses applying it only “for contexts served by or receipted by that same registry” — so a read filters registry-attested facts toorigin == current vantage. Storing onlytrust_class(as an earlier revision of this cache did) answers a different question than “was this claim made by the registry now serving this context” — collapsing the two let a fact minted at a hostile or deceived registry A fail-close a producer’s contexts at every OTHER authority, for the cache’s lifetime: a targeted cross-registry DoS exactly bounded by §6’s scoping default. -
Freshness markers — “vantage V was asked about producer/controller P for trust class C, and a full, untruncated discovery completed at time T.” This is a cached absence, which §7:114 does not license and which §8 explicitly warns about: “a malicious registry can hide a revocation … absence of search results is not evidence of absence.” So a marker is TTL-bounded (
crate::verified::RevocationDiscovery::freshness, default 300 s — RFC-ACDP-0014 §8’s RECOMMENDED bound), per-vantage, per-trust-class, and read viaRevocationCache::fresh_marker(crate-private) — the only method in this module that can cause a lookup to be skipped rather than merely supplemented.
The two objects are stored separately and have different lifetimes (0.15.0, RFC-ACDP-0014 §8 “Caching”, 0.5.0):
- Facts are never evicted. A fact can only leave the cache with the
cache itself. Admission is capped per producer
(
MAX_FACTS_PER_CLASSper trust class, and per origin for the registry-attested class); a fact refused at the cap still applies to the call that found it, and the capped slot stops minting markers so that lookup runs in full on every later call rather than claiming a completeness the cache can no longer track. - Markers live in a bounded map (
MAX_MARKERS). Losing a marker only costs a re-discovery, so the oldest is evicted at capacity. Each one records the wall-clock time the absence was observed, which the verdict reports (RevocationVerdict::NotRevoked::observed_at).
Only a complete, untruncated lookup mints a marker. Every unknown
outcome (truncated, unanswerable, transient, unverifiable candidate)
returns Err before record_success is reached, so unknown is never
cached as not-revoked.
Never call anything here holding the lock across an .await — every
method in this module is synchronous and returns before its caller does
any network I/O.
Structs§
- Revocation
Cache - A cache of RFC-ACDP-0014 revocation facts and discovery-freshness
markers, injectable into a
crate::RegistryClientviacrate::RegistryClient::with_revocation_cache.
Constants§
- MAX_
FACTS_ PER_ CLASS - Admission cap on stored facts per producer for the producer-signed
class, and per
(producer, origin)for the registry-attested class. Reached only by a producer with a genuinely large revocation history, or a registry padding results. Stored facts are never removed; a fact refused here still applies to the call that found it, and the slot stops minting markers. - MAX_
MARKERS - Bound on the number of freshness markers held. Markers are a pure optimization, so at capacity the oldest one is evicted.