@@ -127,6 +127,18 @@ this prevents project-specific caches from becoming winners for unrelated paths.
127127Fallback cache traffic bypasses these router features and does not update health
128128or routing state.
129129
130+ Optional trust enforcement runs at the same acceptance point as filters. After a
131+ candidate narinfo is fetched, ncro verifies the configured upstream signing key
132+ when present and can record the signed narinfo as a local claim. In ` signed `
133+ mode, one valid configured signer is enough. In ` quorum ` mode, ncro only stores
134+ a route after enough signer keys agree on the same ` StorePath ` , ` NarHash ` ,
135+ ` NarSize ` , and ` References ` . Rejected candidates are handled like filter
136+ rejections: the route is not cached and ncro keeps trying remaining candidates.
137+ When the policy is not satisfied and ` trust.fail_closed = false ` , the content is
138+ served anyway but the bypass is logged and counted (` ncro_trust_bypass_total ` ).
139+ The signature covers only the signed fingerprint, not the streamed NAR bytes;
140+ the full model and that boundary are documented in [ trust.md] ( trust.md ) .
141+
130142> [ !NOTE]
131143> Persistence is intentionally narrow. SQLite stores two kinds of data so a
132144> restart does not force ncro to relearn everything from scratch.
@@ -135,13 +147,23 @@ First type of stored data is **route entries**, a mapping from narinfo hash to
135147the winning upstream URL, stored with a creation timestamp and TTL. When the
136148cache exceeds ` max_entries ` , the least recently used entry is evicted first.
137149** Health snapshots** on another hand are per-upstream EMA latency estimates and
138- failure counts, refreshed by the background probe loop. Fallback-cache responses
139- are (intentionally) not stored as route entries, so normal upstreams become
140- active again as soon as they recover.
150+ failure counts, refreshed by the background probe loop. ** Trust claims** (when
151+ trust is enabled) are a third kind of stored data: durable records of which
152+ signer vouched for which content. Fallback-cache responses are (intentionally)
153+ not stored as route entries, so normal upstreams become active again as soon as
154+ they recover.
155+
156+ Negative lookups are cached in two layers: a short-lived in-memory LRU
157+ (` in_memory_negative_ttl ` ) absorbs rapid-fire duplicate misses, while a
158+ longer-lived SQLite entry (` negative_ttl ` ) lets a known miss survive a restart.
159+ The in-memory layer is the only routing state that does not live in SQLite.
141160
142161Discovery and mesh are optional extensions. Discovery can add peers from the
143- local network, while mesh gossip shares recent route decisions across trusted
144- nodes using signed UDP packets. Consider:
162+ local network, while mesh gossip shares recent route decisions (and, when
163+ ` mesh.gossip_trust_claims ` is enabled, re-verified trust claims) across trusted
164+ nodes using signed UDP packets. Relayed claims are re-verified against their
165+ original Nix signer key before being trusted, so a peer is a witness, not a
166+ signing authority. Consider:
145167
146168``` mermaid
147169flowchart LR
0 commit comments