Prerequisite for promoting agent/node/fed IDs to federation tier. With promotion opt-out and required to run an agent, the directory will hold essentially every identity in the federation — and today anti-entropy's model is every node in the projection holds every record. That is both the #547 I/O shape at a new scale and, more importantly, a global address book.
The three pieces
Two already exist:
SummaryMessage is a hash list. Anti-entropy already exchanges hashes and moves only the diff.
FetchMessage { kind, want } already exists, documented as "a consumer learns of a hash via a third channel and asks edge to chase it".
- CIRISPersist#780
list_wire_hashes_since (v38.7.0) gives a cheap complete local hash set, index-only.
Missing:
- Hash-first retention per
(peer, kind) — converge the hash set without fetching bodies. Today the want-set is fetched immediately and entirely (session.rs, after diff_refs).
- Holder index — edge discards which peer advertised which hash. That association IS the holder map, free, with nothing new on the wire: a peer that advertised
H holds H.
- Fetch-on-demand resolver over (2), with a typed refusal when no holder answers.
- Fetch rate limiting. This is not a nicety, it is the enumeration control. A peer resolving hashes to bodies faster than a human contact flow requires is enumerating the federation.
Why this is a safety control, not an optimization
A hash is not a mailing address. Learning that a record exists reveals nothing about who it is; resolving it to a contactable identity takes a fetch, which is observable, rate-limitable and refusable. That is the entire thing standing between "safety registry" and "spam list" once promotion is universal.
Write that reason at the seam, not just here. Someone will later widen body caching as an optimization, and the comment needs to tell them what they are undoing.
Constraint
Holdings must stay complete in result. want = remote ∖ holdings; a partial holdings view leaves held rows in want forever (#416). Hash-first changes what we store, never what we know we hold.
Hard carve-out
Revocations and tombstones are excluded — see the companion issue. A node holding the hash of a kill order has not been killed.
Related: #547 · CIRISPersist#780 · #416 · #531
Prerequisite for promoting agent/node/fed IDs to federation tier. With promotion opt-out and required to run an agent, the directory will hold essentially every identity in the federation — and today anti-entropy's model is every node in the projection holds every record. That is both the #547 I/O shape at a new scale and, more importantly, a global address book.
The three pieces
Two already exist:
SummaryMessageis a hash list. Anti-entropy already exchanges hashes and moves only the diff.FetchMessage { kind, want }already exists, documented as "a consumer learns of a hash via a third channel and asks edge to chase it".list_wire_hashes_since(v38.7.0) gives a cheap complete local hash set, index-only.Missing:
(peer, kind)— converge the hash set without fetching bodies. Today the want-set is fetched immediately and entirely (session.rs, afterdiff_refs).HholdsH.Why this is a safety control, not an optimization
A hash is not a mailing address. Learning that a record exists reveals nothing about who it is; resolving it to a contactable identity takes a fetch, which is observable, rate-limitable and refusable. That is the entire thing standing between "safety registry" and "spam list" once promotion is universal.
Write that reason at the seam, not just here. Someone will later widen body caching as an optimization, and the comment needs to tell them what they are undoing.
Constraint
Holdings must stay complete in result.
want = remote ∖ holdings; a partial holdings view leaves held rows inwantforever (#416). Hash-first changes what we store, never what we know we hold.Hard carve-out
Revocations and tombstones are excluded — see the companion issue. A node holding the hash of a kill order has not been killed.
Related: #547 · CIRISPersist#780 · #416 · #531