Filed from the "when do we use RaptorQ?" review (persist#888 thread / CIRISServer). The answer for files is settled and needs no change: shape is chosen by access pattern (FSD/GROUP_CONTENT_ON_BLOBS.md §4), the only size threshold is the CC 2.6.1.3 1 MiB envelope bound (= persist DEFAULT_INLINE_BYTES_CAP), and RaptorQ is a retention-class mechanism (CC 6.1.5 durability/degradation per layer), never a size one — self/family files never fountain (CC 6.1.5 suppresses FountainHoldingClaim under CC 5.2; a 2–5-node collective cannot place N+K=26 symbols). This issue is the other place CC puts RaptorQ, which edge has not implemented: the scope-native privacy wire profile.
CC 5.4 — what is implemented and what is not
| clause |
rule |
edge |
5.4.1 record-id |
K_record_id/K_symbol from the MLS exporter; record_id = HMAC-SHA3-256(K_record_id, CBOR_dCE({v, epc, iid, typ})) |
primitives ✓ — scope_privacy.rs re-exports verify's derive_record_id / derive_symbol_key with KAT vectors; no producer or consumer calls them on any content path |
5.4.2 symbol |
every fragment of a below-federation record is a RaptorQ symbol sealed under symbol_key = HKDF-SHA3-256(salt=record_id, ikm=K_symbol, info="ciris-edge/scope-privacy/symbol/v1"‖u16_be(i)), XChaCha20-Poly1305; fragment set rebinds on Add/Remove; reassembly opens ≥ K valid symbols or ContentMiss |
✗ — community/self/family bytes ride the chunk DAG (CC 5.3.3.1 SFrame seal), content-addressed by ciphertext sha |
5.4.3 fragmentation |
fixed 1.4 KB envelope, padded; uniform AEAD framing across real and cover; type=cover inside the AEAD; Poisson cover emission |
✗ — scope_privacy.rs lists "§3.1 Poisson emission discipline" as DEFERRED since v6.0.0 |
5.4.4 welcome-wrap |
HPKE mode_base under X-Wing + ML-DSA-65 over the encapsulation |
✓ mls::welcome_wrap |
5.4.5 witness-content |
federation witness chain commits only to federation record_ids + one rate-smoothed counter; per-community chain member-only |
✗ — "§3.4 per-community witness chain split" DEFERRED since v6.0.0 |
5.4.6 announce-suppress |
no announce for group-scoped destinations; directory-cached + per-group HKDF resolution |
✓ #499 ScopeAddressTable / announce_suppression.rs |
What the gap costs today
Confidentiality holds (per-chunk AEAD; a relay never holds a DEK). What CC 5.4 closes and the chunk DAG does not: a holder or on-path observer can (a) link every chunk of one blob to one record (sha-addressed, ChunkManifest pins them), (b) read plaintext-proportional sizes and chunk counts (v2 manifest sizes are plaintext by design), and (c) time the publication (no cover, no fixed envelope). CC 1.13.3.1 concedes comm-graph/traffic-analysis privacy as a base non-goal, but CC 5.4 is the normative profile a conformant substrate uses below federation, and CC 1.13.3.4 makes anonymity-to-outsiders the default — so this is a conformance gap to state, not a threat-model surprise.
Ask
FSD first (FSD/CIRIS_EDGE_TRANSPORT.md, a §5.4-profile section under the link-state / round / content tables), then code:
- Objects:
ScopeRecord { record_id, epc, iid, typ } over the MLS exporter (5.4.1); SymbolEnvelope (5.4.2); the cover envelope and the emission scheduler (5.4.3). Reuse: verify's helpers, raptorq (already the AV wrap in realtime_av_codec/fountain.rs), leviculum Channel for reliable symbol delivery — the machinery we already pay for, not a new plane.
- Where it sits vs the chunk DAG: the DAG stays the storage shape (persist stores exact-payload chunks, sha-pinned); the 5.4 profile is the wire shape between members — chunks are symbol-coded per record on the send side and decoded on the receive side before
put_blob_chunk. State that boundary explicitly so persist's "links zero codec crates" stays true.
- Rows: the rung table in
FSD/CONTENT_TRANSFER.md gains a wire column at R5–R8 (community/self/family) naming the envelope shape, with ContentMiss as the honest reassembly refusal.
- Witness: a holder-side test that two symbols of one record are unlinkable without
K_record_id, and a cover envelope is indistinguishable from a real one under the AEAD; a KAT against verify's vectors for symbol_key.
Open question for the Constitution (not edge's to decide)
CC 5.4.3 fixes the envelope at 1.4 KB, "one MTU" — an Ethernet assumption. Reticulum's MTU is 500 bytes and leviculum's link MDU is smaller still; a 1.4 KB fixed envelope would be fragmented by the transport it rides, re-exposing the size/count channel one layer down. Either the profile pins a Reticulum-native envelope (≤ link MDU) or it states that the fixed size binds at the substrate envelope and the transport's own fragmentation is in-model. I will raise it on CIRISConstitution when this FSD is drafted; until ruled, the FSD parameterizes the envelope size and pins the AEAD shape.
Not blocking any current cut (v29.3.0 / CIRISServer 0.5.214); sequenced after FSD/CONTENT_TRANSFER.md §6.2/§6.3 (#646).
Filed from the "when do we use RaptorQ?" review (persist#888 thread / CIRISServer). The answer for files is settled and needs no change: shape is chosen by access pattern (
FSD/GROUP_CONTENT_ON_BLOBS.md§4), the only size threshold is the CC 2.6.1.3 1 MiB envelope bound (= persistDEFAULT_INLINE_BYTES_CAP), and RaptorQ is a retention-class mechanism (CC 6.1.5 durability/degradation per layer), never a size one — self/family files never fountain (CC 6.1.5 suppressesFountainHoldingClaimunder CC 5.2; a 2–5-node collective cannot place N+K=26 symbols). This issue is the other place CC puts RaptorQ, which edge has not implemented: the scope-native privacy wire profile.CC 5.4 — what is implemented and what is not
record-idK_record_id/K_symbolfrom the MLS exporter;record_id = HMAC-SHA3-256(K_record_id, CBOR_dCE({v, epc, iid, typ}))scope_privacy.rsre-exports verify'sderive_record_id/derive_symbol_keywith KAT vectors; no producer or consumer calls them on any content pathsymbolsymbol_key = HKDF-SHA3-256(salt=record_id, ikm=K_symbol, info="ciris-edge/scope-privacy/symbol/v1"‖u16_be(i)), XChaCha20-Poly1305; fragment set rebinds on Add/Remove; reassembly opens ≥ K valid symbols orContentMissfragmentationtype=coverinside the AEAD; Poisson cover emissionscope_privacy.rslists "§3.1 Poisson emission discipline" as DEFERRED since v6.0.0welcome-wrapmls::welcome_wrapwitness-contentrecord_ids + one rate-smoothed counter; per-community chain member-onlyannounce-suppressScopeAddressTable/announce_suppression.rsWhat the gap costs today
Confidentiality holds (per-chunk AEAD; a relay never holds a DEK). What CC 5.4 closes and the chunk DAG does not: a holder or on-path observer can (a) link every chunk of one blob to one record (sha-addressed,
ChunkManifestpins them), (b) read plaintext-proportional sizes and chunk counts (v2 manifest sizes are plaintext by design), and (c) time the publication (no cover, no fixed envelope). CC 1.13.3.1 concedes comm-graph/traffic-analysis privacy as a base non-goal, but CC 5.4 is the normative profile a conformant substrate uses below federation, and CC 1.13.3.4 makes anonymity-to-outsiders the default — so this is a conformance gap to state, not a threat-model surprise.Ask
FSD first (
FSD/CIRIS_EDGE_TRANSPORT.md, a §5.4-profile section under the link-state / round / content tables), then code:ScopeRecord { record_id, epc, iid, typ }over the MLS exporter (5.4.1);SymbolEnvelope(5.4.2); the cover envelope and the emission scheduler (5.4.3). Reuse: verify's helpers,raptorq(already the AV wrap inrealtime_av_codec/fountain.rs), leviculum Channel for reliable symbol delivery — the machinery we already pay for, not a new plane.put_blob_chunk. State that boundary explicitly so persist's "links zero codec crates" stays true.FSD/CONTENT_TRANSFER.mdgains a wire column at R5–R8 (community/self/family) naming the envelope shape, withContentMissas the honest reassembly refusal.K_record_id, and a cover envelope is indistinguishable from a real one under the AEAD; a KAT against verify's vectors forsymbol_key.Open question for the Constitution (not edge's to decide)
CC 5.4.3 fixes the envelope at 1.4 KB, "one MTU" — an Ethernet assumption. Reticulum's MTU is 500 bytes and leviculum's link MDU is smaller still; a 1.4 KB fixed envelope would be fragmented by the transport it rides, re-exposing the size/count channel one layer down. Either the profile pins a Reticulum-native envelope (≤ link MDU) or it states that the fixed size binds at the substrate envelope and the transport's own fragmentation is in-model. I will raise it on CIRISConstitution when this FSD is drafted; until ruled, the FSD parameterizes the envelope size and pins the AEAD shape.
Not blocking any current cut (v29.3.0 / CIRISServer 0.5.214); sequenced after
FSD/CONTENT_TRANSFER.md§6.2/§6.3 (#646).