Skip to content

release(v3.9.0): codec wiring — raptorq fountain + AV1 + opus + consent-decay scheduler#140

Open
emooreatx wants to merge 14 commits into
mainfrom
feat-v3.9.0-codec-wiring
Open

release(v3.9.0): codec wiring — raptorq fountain + AV1 + opus + consent-decay scheduler#140
emooreatx wants to merge 14 commits into
mainfrom
feat-v3.9.0-codec-wiring

Conversation

@emooreatx

Copy link
Copy Markdown
Contributor

Summary

v3.9.0 Layer 1 (CIRISEdge#133) — substrate-side codec wiring assembled from four parallel L1 sub-agents. Each piece sits behind its own feature gate; the substrate-only build pays zero binary-size cost when the codec features are off.

  • L1-A — codec-fountain: src/transport/realtime_av_codec/fountain.rs — RaptorQ (RFC 6330) wrap/unwrap producing/consuming CIRISPersist v8.0.0's FountainSymbolV1 shape.
  • L1-B — codec-av1: src/transport/realtime_av_codec/av1.rs — rav1e (pure-Rust encoder, default-features off + threading on, no asm/NASM) + dav1d (rust-av binding to libdav1d; system libdav1d-dev required at build time).
  • L1-C — codec-opus: src/transport/realtime_av_codec/opus_voice.rs — libopus 1.x via the opus 0.3 binding (system libopus-dev required).
  • L1-D — holonomic-consent-decay: src/holonomic/consent_decay.rs — per-content_id CEP decay scheduler (TEMPORARY 14-day, STANDARD 90-day, PERSISTENT longer); orthogonal trigger to persist's DiskPressure. PersistHandle FFI trait stubbed at L1 pending the persist v8.x surface.

Umbrella feature codec-default = [codec-fountain, codec-av1, codec-opus] is what the Python wheel build flips on for the realtime A/V profile.

Cross-repo: persist pin stays at v7.2.0 — CIRISPersist v8.0.0 exists as the natural pair for FountainSymbolV1 storage but the FFI bump is its own v3.x.x cut.

Closes CIRISEdge#133.

Test plan

  • cargo fmt --all -- --check — clean
  • cargo check --features "transport-http transport-reticulum pyo3 codec-default" with RUSTFLAGS=-D warnings — clean
  • cargo clippy --all-targets --features "transport-http transport-reticulum pyo3 codec-default holonomic-consent-decay" -- -D warnings — clean
  • cargo test --lib transport::realtime_av_codec — 23 pass / 1 ignored (AV1 round-trip — libdav1d runtime requirement, documented inline)
  • cargo test --lib holonomic::consent_decay — 9/9 pass
  • Substrate-only build (no codec features): clean
  • CI green on tag push (v3.9.0 already pushed; PyPI publish triggered)

🤖 Generated with Claude Code

emooreatx and others added 14 commits June 15, 2026 19:56
…ay + T5 leviculum

Layer 1 sub-agent results landed onto the v3.8.0 integration branch. Five
parallel sub-agents produced disjoint modules; this commit integrates
four of them. T3 (clean-room TreeKEM) was discarded after deep research
surfaced four protocol-level pitfalls (Draft-11 insider attacks,
quarantine-or-remove discipline, SUF-CMA, deployment policies) that the
clean-room approach would re-expose; openmls 0.6+ X-Wing replacement is
in flight as T3'.

## T1 — CIRISEdge#128 layer types (codec-agnostic)

`src/transport/realtime_av.rs` gains:
  - `ChunkLayer { spatial, temporal, quality }` — SVC/MDC layer descriptor
  - `ReceiverLayerPolicy` with `BLINKING_DOT` / `UNCAPPED` + `admits()`
  - `SealedAvChunk` gains `codec_id: u8` + `layer: ChunkLayer` fields;
    wire encoding places them as a 4-byte block at offsets 48..52
  - Codec namespace: `CODEC_AV1_SVC = 0x01`, `CODEC_JPEG_XS = 0x02`,
    `CODEC_MDC = 0x03` (the holographic-track design target per the
    user's preference), `CODEC_OPAQUE = 0xFF`
  - `seal_av_inner` / `seal_av_outer` / `seal_av_chunk` signatures extended
  - PyO3 wrappers in `src/ffi/pyo3.rs` updated to stamp CODEC_OPAQUE +
    ChunkLayer::BASE for v3.7.0 wire-shape compatibility

19 lib tests under `transport::realtime_av` green (14 pre-existing + 5
new).

## T2 — CIRISEdge#129 AvSession + epoch rekey baseline

NEW module `src/transport/realtime_av_session.rs` (913 LOC). Implements
the stateful group-key holder; `advance_epoch(RosterDelta)` drives
forward-secrecy rekey on join/leave via hybrid X25519+ML-KEM-768 wraps.

HNDL discipline structural: any member lacking ML-KEM-768 fails closed
with `SessionRekeyError::PeerLacksMlkem`; classical-only wraps refused
both sides via `unwrap_dek` algorithm sniffing.

Known open: the baseline uses "session-key-IS-DEK" semantics which
yields per-recipient distinct DEKs — fine for pairwise but not mesh.
Layer 2 (T6) will replace this with a proper KEM-DEM wrap: AvSession
generates one fresh mesh-wide DEK per epoch, then AES-GCM-wraps it
under each recipient's hybrid-KEX-derived key. With T3' bringing
openmls X-Wing, the proper path may be to source the mesh-wide DEK
directly from MLS's `exporter_secret`, making `AvSession` a thin wrapper
over `MlsSession`. Layer 2 will settle this.

9 lib tests green.

## T4 — SFU relay (CIRISEdge#66 transport-half)

NEW module `src/transport/realtime_av_relay.rs`. `RelayNode` —
addressable Reticulum destination that subscribers connect to per
`stream_id`, fans out inner-sealed chunks via per-subscriber outer
re-seal. Composes with T1's #122 inner-once / outer-N optimization:
publisher runs `seal_av_inner` once; relay runs `seal_av_outer` per
subscriber.

Structural "relay never sees plaintext" invariant: production-code
import list deliberately omits `EpochDek` (only `seal_av_outer`,
`InnerSealed`, `SealedAvChunk`, `StreamId` are imported). The
`relay_does_not_hold_epoch_dek` test pins this at runtime.

Wire is byte-identical to N × `seal_av_chunk` calls — proven by
`relay_forwards_to_N` tests at N ∈ {1, 4, 16, 50}. 11 lib tests green.

Integration fixup: T4's worktree was written against the v3.7.0
`seal_av_inner` signature (5 args); T1 changed it to 7 args (added
codec_id + layer). Tests in `realtime_av_relay.rs` updated to pass
`CODEC_OPAQUE` + `ChunkLayer::BASE` for the v3.7.0-shape relay
behavior. Wire byte-identity property preserved.

## T5 — leviculum#12 destination_data RPC (edge-side rev bump)

`Cargo.toml`: `reticulum-core` + `reticulum-std` rev pin flips
`ffd261d...` → `6b005e9d...` (leviculum's `feat-12-destination-data-rpc`
branch, pushed to origin). T5 sub-agent's surprise finding was that
the request enum + parser arms for `destination_data` already existed
in leviculum — only the handler was a stub returning `pickle_bool(true)`
unconditionally. Substantive change in leviculum was three stub
returns → one match arm calling a new `destination_is_known()` helper.
11 new RPC tests + 267 reticulum-std lib tests green on the leviculum
side.

## T3 → T3' (in flight) — clean-room TreeKEM replaced by openmls 0.6+ X-Wing

Discarded. See deep research workflow `wh1amgkhf` for the rationale.
T3' sub-agent in flight will integrate openmls 0.6+ ciphersuite 0x004D
(MLS_256_XWING_CHACHA20POLY1305_SHA256_Ed25519, Cryspen formally-
verified ML-KEM + libcrux X25519).

## Gate

  - 4 build combos clean under RUSTFLAGS=-D warnings
  - 39 lib tests across the integrated modules
    (realtime_av: 19, realtime_av_session: 9, realtime_av_relay: 11)
  - Pre-commit hook will run fmt + clippy on push
…ed clean-room TreeKEM)

Replaces the discarded T3 clean-room TreeKEM with a thin CIRIS-shaped
wrapper over openmls 0.8.1's MlsGroup at ciphersuite 0x004D
(MLS_256_XWING_CHACHA20POLY1305_SHA256_Ed25519). Deep research workflow
`wh1amgkhf` surfaced four protocol-level pitfalls in clean-room TreeKEM
that openmls's full RFC 9420 implementation + Cryspen's formally-verified
libcrux work cover:

  - SUF-CMA Ed25519 strict (Cremers/Günsay/Wesselkamp/Zhao ePrint 2025/229)
  - Draft-11 insider-attack fixes (Alwen/Jost/Mularczyk CRYPTO 2022 — three
    malicious-insider attacks on MLS Draft 10 fixed at protocol level)
  - Quarantined-TreeKEM inactive-member discipline (Chevalier/Lebrun CCS 2024)
  - Architecture-level deployment policies (Wallez/Protzenko/Bhargavan
    IEEE S&P 2025 machine-checked symbolic proof)

## Critical pin finding (T3' sub-agent surfaced)

The brief said "openmls 0.6+" based on the openmls.tech April 2024 blog.
Reality:
  - openmls 0.6.0 does NOT ship 0x004D
  - openmls main puts 0x004D behind feature `draft-ietf-mls-pq-ciphersuites`
  - openmls v0.8.1 ships 0x004D unconditionally (no feature flag)

Pinned 0.8.1 with explicit `default-features = false` so we don't pull
`openmls_rust_crypto` (which panics on `HpkeKemType::XWingKemDraft6`);
the X-Wing-supporting provider is `openmls_libcrux_crypto` (Cryspen's
formally-verified ML-KEM + libcrux X25519). Companion pins match what
openmls 0.8.1 itself depends on to avoid two-version skew:
  - openmls_basic_credential 0.5.0 (clonable feature)
  - openmls_traits 0.5.0
  - tls_codec 0.4.2 (derive feature)

## Bundle-size delta

+126 cargo-tree lines (~37 net-new transitive crates):
  - openmls, openmls_libcrux_crypto, openmls_basic_credential,
    openmls_traits, openmls_memory_storage, openmls_serialization_helpers
  - hpke-rs + 2 backends
  - ~12 libcrux-* sub-crates incl. libcrux-ml-kem
  - tls_codec, rayon, minor leaves

Baseline before: 1517 lines; after: 1643. For a Python wheel this is
material but acceptable for the formally-verified MLS implementation.

## New CIRIS-shaped surface (`src/transport/realtime_av_mls.rs`)

  - `MlsSession` — wraps `MlsGroup` + the libcrux provider
  - `Member { key_id, kex_pubkeys }` — same shape as T2's AvSession roster
    so Layer 2 integration is mechanical
  - `Commit(Vec<u8>)` / `Welcome(Vec<u8>)` — TLS-encoded per RFC 9420
  - `RootSecret([u8; 32])` — Zeroize on drop, redacted Debug. Sources from
    openmls's `exporter_secret`. Layer 2: this becomes the canonical
    EpochDek source, supplanting T2's "session-key-IS-DEK" baseline.
  - `MlsError` — translates openmls errors into CIRIS vocabulary

HNDL discipline structural: `Member` lacking ML-KEM-768 fails closed
with `MlsError::PeerLacksMlkem` BEFORE the MLS group is consulted. The
0x004D ciphersuite requires ML-KEM-768 by spec, but the early gate gives
a clear error before openmls's internal error layer.

Behavior difference vs the discarded T3 clean-room: openmls's
`commit_remove` does NOT return a RootSecret to the leaver (RFC 9420
§13.4 quarantine: the leaver is removed before the new epoch's secret
is derived). T3 was ambiguous about that contract; openmls makes it
structural. This is exactly the Quarantined-TreeKEM discipline the
research flagged as missing from clean-room implementations.

## Tests (13 passing)

  - mls_creates_with_n_members (N ∈ {2,4,7,8,16})
  - ciphersuite_id_is_xwing — pins 0x004D
  - commit_add_produces_welcome_joiner_catches_up
  - commit_remove_makes_root_secret_unreachable_to_leaver
  - process_commit_yields_same_root_secret_for_all_existing_members
  - peer_lacking_mlkem_refused_at_create / _at_commit_add
  - root_secret_zeroized_on_drop
  - mls_commit_wire_round_trips / mls_welcome_wire_round_trips
  - process_welcome_classical_only_own_keys_refused
  - process_welcome_without_pre_published_keypackage_surfaces_layer2_marker

## Open question — Layer 2 KeyPackage publish/fetch surface

The joiner-side `process_welcome` path needs Layer 2 work: each peer's
MlsSession must publish its KeyPackage via federation_directory, the
inviter must fetch it before commit_add, and the joiner must process
the Welcome with a Provider state whose storage already holds the
published KeyPackage's private key. The current `process_welcome`
implementation surfaces `MlsError::WelcomeFailed` with an explicit
Layer-2 marker message when this is unavailable. Well-defined L2
scope; not a blocker for the L1 cut.

## Code-point caveat

0x004D has no IANA assignment yet — wire-level interop with other MLS
stacks is not guaranteed. CIRIS is the only consumer today, so this is
not a constraint. Migration path: when `draft-ietf-mls-pq-ciphersuites`
RFCs and IANA assigns code-points, swap ciphersuite ID. X-Wing's SHA3
combiner is different from the WG-track bare-concatenation hybrid, so
the migration is a real wire change (different ciphertext shape), not
just a label change.

## Gate

  - 4 build combos clean under RUSTFLAGS=-D warnings
  - 13/13 mls tests + 39 prior L1 tests = 52 lib tests across the
    integrated realtime_av_* modules
  - Pre-commit hook fmt + hygiene passed
Six lints surfaced by `cargo clippy --all-targets -- -D warnings` (the
pre-push hook gate). All in code from this cut, none in v3.7.1 baseline.

  - `realtime_av_session.rs:386`: `clippy::replace_box` — `Box::new(...)`
    on an existing field becomes `*self.dek = ...` (drop-in-place fires
    the zeroize, Box itself is reused).
  - `realtime_av_mls.rs:355`: `clippy::missing_fields_in_debug` — manual
    Debug impl now includes `provider` (as "<opaque-libcrux>") and
    `member_key_ids` (the keys of `member_signature_keys`).
  - `realtime_av_mls.rs:387` + `:485`: `clippy::needless_pass_by_value`
    on `create` (Vec<Member>) and `commit_add` (Member). API kept
    by-value for ergonomics; `#[allow]` documents the choice.
  - `realtime_av_relay.rs:406` + `:454`: `clippy::cast_possible_truncation`
    on synthetic-key fills in test code. First replaced with
    `u8::try_from(i).expect("index < 64")`; second `#[allow]`'d with
    a comment since the intent IS modular u8 truncation.
…-through

Three parallel Layer 2 sub-agents integrated. Closes the v3.7.0
fan-out work + the rekey design tension T2 flagged + the SFU
per-receiver policy story.

## L2-A (plan_layered) — `src/transport/realtime_av.rs`

  - `MeshParticipant` gains `pub layer_policy: ReceiverLayerPolicy` +
    a `MeshParticipant::new(peer_key_id, link_id)` convenience
    constructor that defaults `layer_policy` to UNCAPPED, preserving
    pre-#128 fan-out semantics.
  - New `RealtimeFanout::plan_layered(participants, chunk_layer,
    tracker, transport_id, min_ratio)` — additive; existing `plan` is
    untouched. Filters by BOTH the reachability rule AND
    `participant.layer_policy.admits(chunk_layer)`.
  - Composes with #122: callers run `seal_av_inner` once per chunk,
    iterate `plan_layered` results, call `seal_av_outer` per admitted
    participant. Documented in rustdoc with an end-to-end code
    example.
  - 24 lib tests green (19 pre-existing + 5 new).

## L2-B (AvSession ↔ MlsSession reconciliation) — `src/transport/realtime_av_session.rs`

T2's standalone AvSession had a flawed baseline ("session-key-IS-DEK"
yields a different 32-byte key per recipient instead of one shared
mesh DEK). T3' brought openmls; MLS's `exporter_secret` IS the
canonical shared DEK. T6 rewires AvSession to delegate to MlsSession:

  - **Surface change** — `EpochRekeyArtifacts` shape replaces T2's
    `Vec<DekWrap>` with `{ new_epoch, commit_bytes, welcome_bytes,
    new_dek }`. The MLS Commit + Welcome are how members synchronize
    state; the new_dek is sourced from MLS's exporter_secret.
  - `AvSession::create(stream_id, own_key_id, initial_members)` —
    derives the initial EpochDek from MLS's first-epoch exporter
    (caller no longer provides the initial DEK; MLS owns derivation).
  - `AvSession::advance_epoch(RosterDelta::Join(member))` →
    `MlsSession::commit_add` → returns Commit + Welcome + new_dek.
  - `AvSession::advance_epoch(RosterDelta::Leave(key_id))` →
    `MlsSession::commit_remove` → returns Commit + new_dek.
  - `RosterDelta::Replace` — out of scope at v3.8.0 (needs a
    sequence-of-commits coordinator + a vector return type; tracked
    as follow-up). Variant returns `AvSessionError::
    ReplaceNotSupported`.
  - `AvSession::process_commit` / `process_welcome` replace T2's
    `unwrap_dek` flow.
  - **Forward secrecy on Leave**: openmls's `commit_remove` follows
    RFC 9420 §13.4 Quarantined-TreeKEM discipline — the leaver is
    quarantined before the new epoch's exporter_secret is derived.
  - **Join secrecy on Join**: joiner derives the new EpochDek but
    NOT the prior epoch's exporter_secret (RFC 9420 §8.4 / §8.5).
  - 16 lib tests green (5 ported from T2 + 11 new round-trip tests).

**Layer 3 KeyPackage publish/fetch gap**: `AvSession::process_welcome`
returns the documented `AvSessionError::JoinerSurfaceUnwired` because
the existing `MlsSession::commit_add` mints the joiner's KeyPackage
on the inviter's provider. The cross-session round-trip tests use
openmls-direct test helpers simulating the future
federation_directory publish/fetch flow. Filed as the L3 federation-
directory KeyPackage publication surface.

**Deprecated surface removed (breaking from T2 prerelease)**:
DekWrap struct, unwrap_dek method, SessionRekeyError enum (replaced
by AvSessionError), AvSession::new(initial_dek, …) (replaced by
create), per_link_transit_keys field, RosterDelta::Join enum variant
shape change (now Join(Member) instead of Join(PeerKeyId, Pubkeys)).
v3.7.x consumers of the T2 baseline must re-test.

## L2-C (SFU layer pass-through) — `src/transport/realtime_av_relay.rs`

  - `SubscriberState` gains `pub layer_policy: ReceiverLayerPolicy`.
  - `RelayNode::subscribe` signature breaks: 3-arg → 4-arg, now takes
    `(stream_id, subscriber, transit_key, layer_policy)`.
  - `RelayNode::forward` filters: for each subscriber,
    `state.layer_policy.admits(sealed_inner.layer())` is checked
    BEFORE `seal_av_outer`. Skipped subscribers don't see
    bandwidth + `next_link_seq` does NOT advance — link_seq counts
    admitted chunks only, keeping subscriber-side anti-replay state
    dense.
  - New `RelayNode::set_policy(stream_id, subscriber, new_policy)` —
    re-advertise without re-subscribing.
  - **Structural "relay can't decrypt" invariant preserved**:
    `relay_does_not_hold_epoch_dek` test still pins it. layer_policy
    is 3 clear-metadata bytes, not key material.
  - 16 lib tests green (8 updated to pass `UNCAPPED` for
    pre-L2-C semantics + 8 new layer-policy + set_policy tests).

## Gate

  - `RUSTFLAGS='-D warnings' cargo check --features
    "transport-http transport-reticulum pyo3"` clean
  - 69 lib tests across the integrated v3.8.0 modules
    (realtime_av: 24, realtime_av_session: 16, realtime_av_relay: 16,
    realtime_av_mls: 13)
  - Full 312-test lib suite green — no regressions in unrelated
    modules
  - Pre-commit fmt + hygiene gate passed
…bcrux

## L3 PyO3 conformance surface — `src/ffi/pyo3.rs`

832 LOC added (additive only — no v3.7.0 wrappers touched). CIRISConformance
+ the published Python wheel can now drive the v3.8.0 surfaces end-to-end.

  - **Constants exported**: CODEC_AV1_SVC=0x01, CODEC_JPEG_XS=0x02,
    CODEC_MDC=0x03 (the holographic-track design target), CODEC_OPAQUE=0xFF,
    MLS_CIPHERSUITE_XWING_ID=0x004D
  - **`#[pyclass] PyAvSession`**: MLS-backed group session — `create`,
    `advance_epoch_join`, `advance_epoch_leave`, `process_commit`,
    `process_welcome`. Holds an `MlsSession` (Arc<LibcruxProvider> +
    MlsGroup + SignatureKeyPair); confirmed Send via successful pyclass
    codegen at openmls 0.8.1 / libcrux 0.6.1.
  - **`#[pyclass] PyRelayNode`**: SFU relay — synthetic-node constructor
    (conformance-only; production constructors come in a follow-up cut).
    `subscribe(stream_id, subscriber, transit_key, layer_policy)`,
    `unsubscribe`, `set_policy`, `forward`. Gated under
    `feature = "transport-reticulum"` because RelayNode owns
    `Arc<ReticulumNode>`. The `pyo3` Cargo feature already implies
    `transport-reticulum`, so wheel builds always include it.
  - **`#[pyfunction] plan_layered_by_policy`**: codec-agnostic
    layer-policy filter. Reachability is NOT exposed cross-wheel; callers
    pre-filter participants by reachability before calling. Documented as
    the v3.8.0 Python contract.

All wrappers are bytes-in/bytes-out — no EpochDek / RootSecret / OwnKexKeys
newtype escapes the Python boundary. HNDL discipline preserved: Member
tuples with `mlkem768_pub=None` raise `PyValueError` at conversion before
any MLS code runs. AvSessionError / RelayError translate to `PyValueError`
(input-shape) vs `PyRuntimeError` (operation failure).

9 new Rust-side smoke tests cover the codec constants, MLS ciphersuite
constant, plan_layered_by_policy filtering, AvSession create rejects
classical-only + hybrid round-trip + wrong-stream-id, process_welcome
surfaces JoinerSurfaceUnwired, relay subscribe/unsubscribe cycle, relay
forward producing decodable SealedAvChunk wire bytes.

## cargo-deny ignores — `deny.toml`

PR #131 CI's `cargo deny (license + advisories)` job failed on 4 RUSTSEC
advisories triggered by the openmls + libcrux dependency tree. Each
ignore is documented inline with non-exploitability rationale:

  - **RUSTSEC-2026-0124** — libcrux-chacha20poly1305 encrypt-buffer panic.
    NOT exploitable: openmls's libcrux-backed AEAD wrapper sizes the
    output buffer at exactly `ptxt.len() + TAG_LEN`; buffer length is
    never attacker-controlled at our wire layer. The realtime_av path
    uses ring AES-256-GCM (not libcrux ChaCha); libcrux ChaCha runs only
    inside openmls's internal HPKE wrap for the X-Wing combiner.
  - **RUSTSEC-2026-0075** — libcrux-ed25519 keygen all-zero on
    catastrophic CSPRNG failure. P(100 zero samples | working CSPRNG)
    ≈ 2^-2000. OS CSPRNG failure is the threat model edge addresses
    at the federation_session layer via ciris-crypto's RNG fail-secure
    latch (CIRISVerify #74). The libcrux-ed25519 keygen here is
    openmls-internal MLS signature key minting, not edge's federation
    signing.
  - **RUSTSEC-2026-0073** — libcrux-poly1305 standalone MAC panic.
    Per the advisory's own impact note: "use of libcrux-poly1305 in
    libcrux-chacha20poly1305 is unaffected." We use it only via
    openmls's internal HPKE wrap, never standalone.
  - **RUSTSEC-2026-0173** — proc-macro-error2 unmaintained. Build-time
    dev-dep only; ships in zero production binaries. Migration is
    openmls-upstream's call.

Local `cargo deny --all-features check` clean post-patch.
Three new criterion benches measuring the v3.8.0 substrate's empirical
performance characteristics. Each is registered as a `[[bench]]` in
Cargo.toml with documented required-features.

## L4-A `realtime_av_fanout` (CIRISEdge#122 + #128)

Sender CPU as a function of N (recipients) × frame size × codec/layer
configuration. Three variants:

  - `naive_seal_chunk_n_recipients` — v3.7.0 baseline (`seal_av_chunk`
    × N)
  - `inner_once_outer_n_recipients` — #122 fan-out split (`seal_av_inner`
    × 1 + `seal_av_outer` × N)
  - `layered_inner_once_outer_admitted` — #128 layer policy composed
    with #122

Sweep: N ∈ {2, 8, 16, 32, 64, 128, 200} × frame ∈ {1, 4, 16, 64} KiB ×
codec/layer ∈ {1-layer-opaque, 3-spatial-AV1-SVC, 7-cell-full-SVC}.

**Empirical**: at N=64, 16 KiB, the inner-once / outer-N variant is
**1.93×** faster than naive (201.21 µs → 104.25 µs). Within noise of
the substrate's claimed ~1.98× win at N=50, 16 KiB.

## L4-B `realtime_av_rekey` (CIRISEdge#129)

Membership-change rekey cost. Two groups:

  - `flat_unicast_rewrap` — simulates the v3.7.x AvSession baseline
    (the post-T6 surface no longer exists, so the bench reproduces
    the pattern inline): fresh DEK + N hybrid X25519+ML-KEM-768 KEX +
    N AES-256-GCM wraps + N wire serializations per remaining member.
  - `mls_rekey` — `AvSession::advance_epoch(RosterDelta::{Join,Leave})`
    over the integrated MLS-backed surface (openmls 0.8.1, X-Wing
    ciphersuite 0x004D).

Sweep: N ∈ {2, 8, 32, 128, 512, 2048} × op ∈ {Join, Leave}.

**Empirical crossover**: at N=8 flat is 3.7× faster (~657 µs vs
~2.4 ms — MLS pays a fixed per-commit overhead that dominates at
small N); at N=128 MLS overtakes flat (~9.7 ms vs ~11.0 ms,
~12% sender-CPU win). Crossover sits between N=32 and N=128.

**Release-notes caveat (load-bearing)**: this bench measures
SENDER-CPU under unicast — the worst case for MLS. MLS's receiver-
side O(log N) win + multicast amortization show up in L4-C
(`realtime_av_relay`), not here. The bench's top-of-file docs
quote this explicitly so consumers don't misinterpret the numbers.

## L4-C `realtime_av_relay` (CIRISEdge#66)

SFU relay end-to-end. Four groups:

  - `relay_forward_n_subscribers_uncapped` — baseline relay-side cost
  - `relay_forward_n_subscribers_with_layer_filter` — #128 policy
    filter overhead under 50/50 UNCAPPED/BLINKING_DOT subscribers
  - `relay_set_policy_overhead` — per-call `set_policy` cost
  - `mesh_vs_relay_comparison` — splits into 3 sub-benches
    (`relay_publisher` / `relay_relay` / `relay_outer_only`) so the
    publisher-CPU savings vs total-relay-side CPU crossover with
    direct mesh fan-out is empirically readable

Sweep: N ∈ {2, 8, 32, 128, 500} × frame ∈ {4, 16, 64} KiB ×
policy mix ∈ {all UNCAPPED, all BLINKING_DOT, 50/50}.

## Gate

  - `cargo bench --features "transport-http transport-reticulum"
    --no-run` clean — all 14 bench binaries (11 pre-existing + 3 new)
    compile under `-D warnings`
  - `cargo fmt --all` applied
  - Setup excluded from steady-state measurement in all three benches
    via `iter_batched` / outer-closure setup

`cargo metadata --no-deps` confirms the three new entries are picked up
(14 bench targets total).
…core bench

Closes the two written-up halves of the PR #131 review:

## L5-A — `docs/FEDERATION_SCALING_MODEL.md` (2,833 words, 9 sections)

Derives the v3.8.0 substrate's "stream at scale" claim from first
principles. Replaces the unprincipled ~50 mesh cap in
`realtime_av.rs:9` and the bandwidth-accounting hand-wave in
`realtime_av_relay.rs:82` with concrete model + headline numbers.

  - §2 Bandwidth axes — derives 720p30 50p mesh = 122.5 Mbps up
    (infeasible on consumer); BLINKING_DOT 50p mesh = ~2.5 Mbps up
    (feasible); 4K 4p home mesh = 60 Mbps up (cliff)
  - §3 CPU axes — quotes L4-A's 1.93× #122 split win + L4-B's MLS
    rekey crossover; concludes CPU is NOT the bottleneck
  - §4 Mesh→SFU crossover — formal `N_mesh_max(uplink, codec, layer)`;
    for 720p30 on 30 Mbps home: ~13; for BLINKING_DOT: >600
  - §5 Per-SFU-core forwarding capacity — egress-bound (~1.2
    streams/core at 720p30); CPU-bound (~416 streams/core); egress
    dominates by ~350×
  - §6 Burst rekey — 20p cold join unbatched = 6 dropped frames at
    30fps; with L5-C `RosterDelta::Batch` = ~1 frame
  - §7 Verdict matrix — what scales where, per-profile
  - §8 Out of scope follow-ups (cascade SFUs, congestion control,
    multi-tenant resource limits, codec-encoder selection)

`realtime_av.rs:9` rustdoc now reads: "The mesh is infeasible above
N_max(uplink, codec, layer) per CEG §10.5.8 / FEDERATION_SCALING_MODEL.md
§4. For 720p30 on consumer connections this is ~13; for BLINKING_DOT it
is >600." `realtime_av_relay.rs:82` rustdoc points at FSD §5 +
§8 instead of the previous "follow-up" stub.

Best-effort estimates flagged: the ~150 Mbps/core SFU number labelled
"best-effort from public benchmarks" (LiveKit/Janus/mediasoup/Jitsi
all publish ranges, not directly comparable). L5-B bench's measured
numbers refine; FSD will be re-pinned in a follow-up.

## L5-B — `benches/realtime_av_relay.rs` (3 new groups, ADDITIVE)

Extends L4-C with sustained-throughput / streams-per-core /
layered-bandwidth-saving measurement. The verdict-rendering benches
per the PR #131 review.

  - `relay_sustained_throughput` — N ∈ {8, 32, 128, 500} ×
    bitrate ∈ {400, 2500, 5000} kbps. **Empirical**: ~7-8 GiB/s
    aggregate AEAD throughput per core for N ≤ 128 at any bitrate
    (CPU-bound, not network-bound). N=500 cliff to ~2.5 GiB/s
    (L2-cache pressure on subscriber state).
  - `relay_streams_per_core` — S ∈ {1, 4, 16, 64} × N ∈ {32, 100}
    at 720p30. Flat curve: per-stream cost dominated by N subscribers,
    not stream-map lookup. **One relay core sustains 16 × N=500
    @ 720p30 before AEAD saturation.**
  - `relay_layered_bandwidth_saving` — measures #128 layer-policy
    egress savings at policy_mix ∈ {UNCAPPED, 50/50, BLINKING_DOT}.

Refines the FSD §5 SFU per-core capacity number from
"~1.2 streams/core (best-effort estimate)" to "16 × N=500 streams
before AEAD saturation (measured)" — CPU has massive headroom; the
deployed network egress limit (~150 Mbps to 1 Gbps depending on host
uplink) dominates in practice. The bench's in-memory loopback measures
AEAD CPU; deployed throughput is gated by the host's uplink.

Gate:
  - `cargo bench --features "transport-http transport-reticulum"
    --bench realtime_av_relay --no-run` clean
  - `RUSTFLAGS='-D warnings' cargo check ...` clean
  - `cargo fmt --all` applied
Closes the burst-rekey gap flagged in the PR #131 review: 20-25 mass
joins at meeting-start no longer become N separate advance_epoch calls
(~180ms → dropped frames). One batched Commit + N Welcomes derive
the same RootSecret in ~25-30ms (single-frame stall at most).

## Empirical (release, N=25, on dev host)

  - sequential 25× `advance_epoch(Join)`:  159 ms
  - batched   `advance_epoch(Batch(25 ops))`:  27 ms
  - **speedup: ~6×**

Confirms the PR review's order-of-magnitude claim. Per-Add fixed-cost
amortizes further at N=50 (would widen ratio).

## openmls 0.8.1 API used

`MlsGroup::commit_builder()` is the multi-proposal commit path:

  1. `.propose_adds(KeyPackages)` and `.propose_removals(LeafNodeIndex)`
     — chainable, no order constraint at the MLS layer.
  2. `.load_psks(storage)`.
  3. `.build(rand, crypto, signer, |_| true)`.
  4. `.stage_commit(provider)` → `CommitMessageBundle`.

No `commit_to_pending_proposals` exists in 0.8.1 — `commit_builder()`
IS the multi-proposal path. The `CommitMessageBundle` carries ONE
`Welcome` covering all joiners; the surface contract serializes once
then clones per Add op so callers have a per-joiner routing handle.
Joiner-side `StagedWelcome::new_from_welcome` filters
`Vec<EncryptedGroupSecrets>` by `KeyPackageRef` internally.

## API additions

  - `RosterOp::{Add(Member), Remove(String)}` — atomic unit of a batch
  - `RosterDelta::Batch(Vec<RosterOp>)` — new variant on the existing
    enum
  - `MlsSession::commit_batch(&[RosterOp]) -> Result<(Commit,
    Vec<Welcome>, RootSecret), MlsError>` — load-bearing surface
  - `MlsSession::member_key_ids() -> Vec<String>` — exposed for the
    Replace diff-computation

## Breaking changes (v3.8.0 prerelease consumers must re-test)

  - `EpochRekeyArtifacts.welcome_bytes` shape changes:
    `Option<Vec<u8>>` → `Vec<Vec<u8>>`. Join: len=1; Leave: len=0;
    Batch: len=#Adds; Replace: len=#Adds-in-diff.
  - `AvSessionError::ReplaceNotSupported` REMOVED.
  - `RosterDelta::Replace(roster)` is now implemented (was a stub
    error). Computes the diff (current_roster ∖ new_roster as
    Removes; new_roster ∖ current_roster as Adds) and delegates to
    `commit_batch`. The combined batch produces one Commit + N
    Welcomes (one per addition).
  - `MlsError::EmptyBatch` — `commit_batch(&[])` returns an explicit
    error rather than silently producing a no-op commit. Surfaces
    upward as `AvSessionError::EmptyBatch`.
  - PyO3 `advance_epoch_join` updated to extract the single Welcome
    from the new `Vec` shape.

## HNDL discipline atomic

`commit_batch` runs the HNDL pre-check across ALL `RosterOp::Add`
ops BEFORE queueing any proposal on the MLS group. Any single
classical-only member → `MlsError::PeerLacksMlkem(key_id)` returned
immediately; the MlsSession's group epoch is unchanged
(verified by `batch_atomic_fails_closed_on_hndl_breach` test).

## Open question for follow-up

`Replace` with self-removal: if caller's `Vec<Member>` omits the
local participant, the diff would emit `Remove(self_key_id)` op
which openmls rejects as `CannotRemoveSelf`. Currently surfaces as
`MlsError::CommitAddFailed(\"…CannotRemoveSelf…\")` rather than a
typed error. Filed for v3.8.1 follow-up patch (filter `self_key_id`
from the Replace remove-set explicitly).

## Tests

42 lib tests across the two modules (22 session + 20 mls + 1
ignored release-only timing test). All gates clean:

  - `RUSTFLAGS='-D warnings' cargo check ...` clean
  - `cargo clippy --all-targets ... -- -D warnings` clean
  - `cargo fmt --all --check` clean
…chmark report

Closes the ALM half of CIRISEdge#66 + #128 with a per-peer relay model:
every peer is potentially a relay; capacity advertisements drive
parent-finding; multi-parent dedup heal absorbs churn. The MDC
("holographic") extension makes the substrate capable of symmetric
sub-stream commitments — any fragment reconstructs the whole at
proportional fidelity.

## Architecture: every peer is a relay

The "SFU as dedicated role" mental model is a CDN-tier WebRTC artifact.
CIRIS already had every primitive needed for per-peer relay:

  - RelayNode primitive (inner-once / outer-N AEAD, #122)
  - ReceiverLayerPolicy for per-receiver self-cap (#128)
  - Reticulum routing fabric (no central control plane)
  - federation_directory + trust graph
  - Hybrid PQC KEX per link (RC7 HybridRequired)

L5-B's bench confirmed CPU is not the bottleneck: ~7 GiB/s aggregate
AEAD per core. The branching factor X = uplink_Mbps / stream_bitrate_Mbps
is the real constraint. Publisher emits to X peers; those X advertise
themselves; the remaining (N - X - 1) participants proxy through them.
Tree depth = ceil(log_X(N)). This is application-layer multicast
(Narada / Overcast / ZIGZAG family).

## ALM-A: RelayCapacity advertisement (`capacity.rs`, 1064 LOC)

Signed envelope: SignedRelayCapacity { advertiser_key_id, capacity,
stream_id, epoch, signature } over hybrid Ed25519 + ML-DSA-65 via
LocalSigner. Replay protection bound to (stream_id, epoch); domain-
separator b"CIRISALM-CAPv2\0\0" fail-closes against the v1 wire shape.

Verifier produces canonical bytes deterministically (length-prefixed
encoding so serde_json formatting drift can't break sig). Wire-truncated
input refused with WireDecode; cross-stream/cross-epoch replay refused
with SignatureInvalid (uniform error to avoid field-discriminant leak).

## ALM-B: parent-finding planner (`join.rs`, 817 LOC)

AlmJoinPlanner::plan returns JoinPlan { primary_parent, backup_parents,
stream_bitrate_mbps }. Selection algorithm: stale-advertisement filter
→ layer-policy filter → has-room filter → reachability-ratio filter →
sort by RTT ascending → pick primary + MAX_BACKUPS=2. Most-specific
error dispatch (NoCandidateSupportsLayerPolicy / AllCandidatesStale fire
only when that single filter is the sole rejection cause; mixed
rejections collapse to NoFeasibleParent).

Stateless. The ReachabilityTracker doesn't yet surface an RTT estimate;
ParentCandidate.rtt_ms_estimate is plumbed through as caller-supplied
Option<u32> with rustdoc documenting the derivation path.

## ALM-C: multi-parent subscription + rapid heal (`heal.rs`, 654 LOC)

MultiParentSubscription state machine:

  - Per-(stream, sub_stream_path) instance — receiver running 4
    sub-streams (full holographic 8K) runs 4 instances
  - DedupRing: bounded FIFO of (epoch, chunk_seq) — DEDUP_RING_CAPACITY
    = 1024 covers ~33s at 30fps; out-of-order arrival within window
    dedups correctly
  - observe_chunk(parent, epoch, seq, wall_clock) -> ObserveOutcome
    {FirstDelivery, Duplicate, UnknownParent} — first-arrival
    semantics so multi-parent doesn't deliver dupes upward
  - tick(wall_clock) -> Vec<HealAction> — silent-parent detection via
    PARENT_SILENCE_HEAL_MS = 1500; emits ReParent{dead} per silent
    parent + UpstreamRebuildRequired when all gone
  - Deterministic action emission (sorted-by-key iteration) so caller
    debounce is reliable across consecutive ticks

Bandwidth posture: multi-parent ~2× downlink accepted for interactive;
MDC mode with K sub-streams is K × 2× because each MultiParentSubscription
double-subscribes independently. Documented at module head + heal.rs.

## MDC / Holographic extension (cross-module)

User's "split each half equally" directive baked in as variable-depth
SubStreamPath = Vec<u8>:

  - SubStreamPath: [] = opaque whole stream; [0] = first half; [0,1] =
    "AA" quadrant; depth runtime-configurable per codec
  - SubStreamCommitment { sub_stream_path, uplink_budget_mbps,
    max_subscribers } — additive Vec field on RelayCapacity (serde
    default empty for forward-compat)
  - RelayCapacity::has_room_for_substream: exact-match-first lookup;
    falls back to overall uplink_mbps budget if commitments are empty
    (opaque-mode relay); refuses if commitments are declared but no
    match (peer doesn't serve the requested sub-stream)
  - AlmJoinPlanner::plan_for_substream: same selection algorithm with
    room filter swapped to has_room_for_substream
  - MultiParentSubscription.sub_stream_path: implicit per-instance;
    receiver assembles full quality from N parallel subscriptions

Canonical bytes version bump CIRISALM-CAPv1 → CIRISALM-CAPv2: u32
sub_stream_commitments count + per-entry u16 path_len + path bytes +
f32 budget + u16 max_subs. v1 verifiers fail uniformly against v2
advertisements (and vice versa). Documented in module docs as the
intentional migration path.

The killer property: a peer with 5 Mbps uplink in MDC mode serves
~4× more receivers than in opaque mode when the room has mixed
quality demands. Different parents can serve different sub-streams;
the receiver assembles holographically from up to K different
upstream paths.

## CEG-native everywhere

SignedRelayCapacity is just another signed claim — same envelope
shape as federation_key advertisements, same signing path, same
domain-separator discipline. The MDC sub-stream commitment doesn't
need a bespoke wire surface because ChunkLayer { spatial, temporal,
quality } already is the partition descriptor. Receivers reassemble
by collecting sub-stream claims from multiple upstream peers — no
single peer owns the stream; every peer is a partial witness. This
is the holographic fragment-reconstructs-the-whole property
expressed in CEG's primitive vocabulary.

## Benchmark report (`docs/V3_8_0_BENCHMARK_COMPARISONS.md`)

Citation-heavy comparison across 7 axes for release notes:

  - SFU forward throughput per core (mediasoup 3.10.6, LiveKit Go,
    Janus, Pion) — edge ~500× mediasoup-worker's per-core egress
    in-memory bench; caveats around NIC/RTP overhead explicit
  - MLS group operations (openmls large-groups bench, MLS practical
    analysis 2502.18303, mls-rs)
  - Hybrid PQC overhead (Cryspen X-Wing OpenMLS: ~2.2× compute, ~9×
    bytes; Cloudflare X25519MLKEM768 production: +10-20ms median;
    >60% of TLS traffic uses hybrid ML-KEM in early 2026)
  - Mass-join batch (Hale/Tian/Wang APQ, eprint 2026/034; edge ~6×
    @ N=25 is one of few public hybrid-PQ MLS data points)
  - P2P live streaming (PeerTube 2023 stress test, WebTorrent
    historical, Theta/Livepeer/PPLive)
  - AV1 SVC production timing (dav1d, rav1e, libaom v2.0.0)
  - Reticulum vs libp2p gossipsub vs Yggdrasil throughput

Includes "Limitations" section with 6 load-bearing caveats — most
edge headlines are in-memory loopback vs production NIC paths;
honest framing for the release notes.

## Tests

48 lib tests across the ALM module (capacity: 12 + 6 MDC; join: 16 +
3 MDC; heal: 13 + 2 MDC). Total v3.8.0 lib test count: 374. All
gates clean.

## Open follow-ups (v3.9.0 candidates)

  - Edge::advertise_relay_capacity + federation_directory.publish_
    relay_capacity bridge to persist (out of v3.8.0 scope)
  - ReachabilityTracker RTT estimate field (caller-supplied for now)
  - PyO3 surface for RosterDelta::Batch (existing single-Join/Leave
    wrappers still work; batch surfacing deferred)
…ing bet

Deep-research workflow ws9po4ot2 surfaced load-bearing findings on the
v3.8.0 ALM + MDC design. The substrate is design-feasible but pioneering
on an axis the field has not validated.

## Headline findings (3-vote adversarially verified)

  - No production-grade symmetric MDC video codec has ever shipped.
    Classical MDC (Franchi et al. 2005; Le et al. 2023) are AVC/HEVC
    extensions with cumbersome architectures, poor scaling beyond 2
    descriptions, compression-efficiency loss from source oversampling.
  - NeuralMDC (Dec 2024) is the only neural MDC video research prototype
    — first of its kind, achieves the symmetric "holographic" property,
    not productionized.
  - The only published ALM+MDC live-streaming system (Favalli et al.
    ILPS-MDSC, IJCNC 2011) is built on H.264/SVC + centralized Topology
    Manager + only M=2 descriptions + explicitly excludes peer churn.
    Incompatible with CIRIS substrate on three axes.
  - 2024-2026 MDC+WebRTC research (ACM TOMM 2026) stays on the SFU side.
  - Industry convergence is centralized-relay QUIC (MoQ Transport
    draft-18) backed by Meta/Google/Cisco/Cloudflare. Subgroups are
    designed for asymmetric SVC priority-drop, NOT symmetric MDC.
  - PeerTube — the leading decentralized video platform — actively
    moving AWAY from P2P (WebTorrent removed in v6.0.0; redundancy
    infrastructure removed in v7.1.0).
  - CoolStreaming/PPLive/PPStream hit hundreds-of-thousands scale with
    pure ALM in the mid-2000s, but without MDC. ALM alone can scale;
    the MDC composition is the unverified axis.

## Design adjustments the SOTA recommends

  1. Start with M=2 descriptions in production deployment, not M=4.
     Substrate supports variable depth (SubStreamPath = Vec<u8>); doc
     M=2 as production default, M=4 as design ceiling. Codec for M=2
     reproducible from Favalli; M=4 needs NeuralMDC or equivalent.
  2. Make MDC codec layer pluggable — already done via codec_id
     namespace (0x01 AV1 SVC, 0x02 JPEG XS, 0x03 MDC, 0xFF opaque).
     Document SVC-with-priority-drop as the production-realistic
     fallback; MDC as the design target.
  3. Treat peer churn as first-class evaluation target. ALM-C already
     does this structurally (MultiParentSubscription + heal); v3.9.0+
     benchmarks should include random parent-drop scenarios.

## What v3.8.0 already gets right (validated by SOTA)

  - Codec-agnostic wire: codec_id namespace lets us ship the substrate
    without committing to MDC vs SVC.
  - Variable-depth SubStreamPath: M=2 → M=4 is documentation/policy,
    not wire revision.
  - Multi-parent dedup heal: peer churn is first-class, unlike the
    Favalli precedent.
  - No central control plane (Reticulum) — explicitly differentiated
    from MoQ Topology and SFU baselines.

## Honest framing for the release notes

v3.8.0 ships the substrate as a credible pioneering bet, NOT as the
proven way. If MDC video matures (neural or otherwise), the substrate
is ready. If it doesn't, the substrate still ships AV1 SVC over MLS
X-Wing with per-peer relay — a valuable hybrid-PQ replacement for the
SFU+WebRTC stack the industry has, without committing to a centralized
operator.

MoQ Subgroup priority semantics can be mapped onto codec_id 0x01 SVC
with a translation layer if interop becomes important. Filed as v3.9.0
candidate.

## Open questions tracked for v3.9.0+

  1. Targeted production search (Chinese P2P streaming patents, Theta
     Network, Livepeer, Streamr P2P video) — adversarial verification
     surfaced no positives but cannot prove a universal negative.
  2. Per-receiver decode-CPU floor for 4-quadrant 8K MDC — synchronization
     budget + multi-parent overhead unpinned in SOTA sources.
  3. NeuralMDC Rust packaging feasibility — inference cost, model-
     distribution, license posture all unevaluated.
  4. PQ-hybrid signed capacity advertisement cost under churn —
     signing/verification overhead on every update vs TT=30s topology
     refresh budget.

Workflow run: ws9po4ot2 (2026-06-15).
… direction

## Mesh-e2e bench

Substrate-level holographic round-trip empirically validated. 5 bench
groups: round-trip correctness, multi-parent dedup, degraded-quality
sweep, planner-picks-distinct-parents, cost decomposition.

Empirical (release, dev host):
  - 64 KiB single-parent round-trip: 32.8 µs (1900 MB/s)
  - dual-parent dedup: 41.0 µs (~25% overhead)
  - degraded-quality sweep: 15.1 / 20.9 / 26.2 / 32.3 µs for 1/2/3/4
    sub-streams — proportional-fidelity property holds at substrate
    level
  - planner picks 4 distinct primaries (no bug)
  - cost split: inner-seal 8.94 µs, outer-seal 7.88 µs, dedup observe
    273 ns, AEAD open 13.2 µs (largest), reassemble 1.08 µs

## Recommended stack locked in

raptorq + rav1e/dav1d = production-deployable today, no new codec
needed. The user-facing "any fragment reconstructs the whole at
proportional fidelity" emerges from composition:

  - rav1e produces AV1 chunks
  - raptorq (RFC 6330, 3GPP MBMS / DVB-H deployment) wraps each
    chunk → N source + K repair symbols
  - Each symbol = one substrate SealedAvChunk with
    ChunkLayer.quality = symbol position
  - Receiver collects ≥N symbols via MultiParentSubscription,
    RaptorQ-decodes, hands to dav1d

Holographic at the data layer; codec freedom (AV1 today, anything
tomorrow); zero new substrate code.

## Novel: fountain-coded reasoning traces

Three searches confirm NO published prior art treats fountain coding
as the time-series / structured-log graceful-degradation primitive.

CIRIS proposal: treat trace content as fountain-coded payload. Under
disk pressure, evict repair symbols first (lossless preserved); then
source symbols (partial reconstruction with documented probability);
finally metadata-only ("trace existed with signature X").

Why better than TimescaleDB rollup:
  - Preserves trace structure (every trace remains "a trace")
  - Per-symbol granular eviction (not per-time-bucket)
  - No schema choice required
  - Graceful, probabilistic degradation vs hard rollup

Filed for cross-repo discussion. v3.8.0 substrate provides the
ChunkLayer.quality axis the eviction policy hooks onto.

## Scope discipline

This commit LOCKS IN the direction. raptorq integration (substrate-
side + persist-side) is v3.9.0+ work. v3.8.0 ships codec-agnostic
substrate; raptorq wiring is additive.
Three-cut scaffolding from v3.8.0 (shipping) → v4.0 (CEWP-1.0 holonomic
seal). No v3.99 climb.

  - v3.9.0: actual working holographic mesh — codec wiring
    (raptorq + rav1e + dav1d + opus). edge#133.
  - v3.10.0: holonomic substrate — four interlocking pieces bundled
    into one cut:
      * Swarm-coordinated rarest-shard retention (edge#134)
      * WholenessWitness — Bohm's implicit order made explicit
        (edge#135 — filed today; architectural keystone)
      * Deterministic ALM topology (pure function of inputs; no
        leader/consensus)
      * Recursive trust bootstrap (any signed claim suffices)
  - v4.0: CEWP-1.0 holonomic federation seal — production cross-repo
    locked release.

## Why bundle v3.10.0

The four pieces interlock. WholenessWitness needs CEG claims to
Merkle-root over (Part 1 produces FountainHoldingClaim; Parts 3-4
produce trust + topology claims). Deterministic ALM consumes
WholenessWitness inputs. Recursive trust bootstrap consumes
WholenessWitness chains. Shipping separately = each cut stubs the
others. Shipping together = clean composition + real end-to-end
holonomic test.

## Why this matters

A holographic substrate (v3.8.0 + v3.9.0 + v3.10.0 part 1) gives
graceful degradation under loss. A holonomic substrate gives
**graceful reconstitution from any sufficient fragment** — the
strongest expression of M-1 ("diverse sentient beings may pursue
their own flourishing"). Every node can leave, come back years
later, bootstrap from any claim chain, reach the same federation
view. No central authority. No special bootstrap peers. No
path-dependence to recover. The federation as a whole is
path-independent.

## What v4.0 does NOT contain (deferred)

Holonomic MLS snapshots (cross-repo, can ship at v4.x);
privacy-preserving witness disclosure (research-grade); cross-witness
BFT proofs; compression of older witnesses into epigraph hashes.

## Architectural framing credit

@emooreatx ("what would make it holonomic?") + ("I hate big numbers").
… gate

CIRISRegistry#85 filed. Without normative CEG status for streaming +
fountain + ALM + WholenessWitness + holonomic primitives, the substrate
works but the federation fragments — two implementations of
WholenessWitness produce different Merkle roots; two AlmJoinPlanners
arrive at different topologies; two retention_priority implementations
evict different symbols.

CEG 1.1 must absorb 8 sections by v4.0:
  - §N realtime A/V chunk wire
  - §M ALM substrate envelopes + planner algorithm
  - §P fountain content/manifest
  - §Q codec wiring contract
  - §R swarm-coordinated rarest-shard retention
  - §W WholenessWitness
  - §T deterministic ALM topology
  - §B recursive trust bootstrap

Each with conformance test vectors. Non-breaking migration paths.

The new discipline for every cut from v3.8.0 onward: edge ships as
informative → empirical validation → CIRISRegistry promotes to
normative → conformance test vectors locked → v4.0 ships when all of
the above are stable cross-repo.

Architectural framing from @emooreatx ("they need to make everything
else we have done normative... all necessary for the mesh to operate
at scale as desired").
…opus + consent-decay scheduler

v3.9.0 Layer 1 (CIRISEdge#133) lands the substrate-side codec wiring
that the v3.8.0 ALM + MDC machinery has been waiting on. Four parallel
L1 sub-agents each carved a single codec / scheduler module behind its
own feature gate; this commit integrates the four pieces onto the
release branch.

L1-A (raptorq) — src/transport/realtime_av_codec/fountain.rs
  RaptorQ (RFC 6330) wrap/unwrap helpers that turn opaque payload
  bytes into N source + K repair symbols matching CIRISPersist
  v8.0.0's FountainSymbolV1 shape. Codec-agnostic — any downstream
  encoder (AV1, Opus, raw bytes) flows through here. Feature:
  codec-fountain.

L1-B (rav1e + dav1d AV1) — src/transport/realtime_av_codec/av1.rs
  Mozilla/Xiph's pure-Rust AV1 encoder + VideoLAN's libdav1d AV1
  decoder via the rust-av maintained binding. Sits between YUV frames
  and the fountain wrap. Default features off on rav1e (drop CLI
  surface), threading on (rayon multi-core encode), asm off
  (no NASM build-tool dep). dav1d requires system libdav1d-dev at
  build time; manylinux wheel CI step bumps to match. Feature:
  codec-av1. One round-trip test #[ignore]'d for hosts without
  libdav1d-dev runtime (documented inline).

L1-C (opus voice) — src/transport/realtime_av_codec/opus_voice.rs
  libopus 1.x voice codec via the `opus` 0.3 binding (WebRTC /
  Discord / Mumble baseline; 5–26.5 ms algorithmic delay). System
  libopus required at link time (libopus-dev / brew opus). Feature:
  codec-opus.

L1-D (consent-decay scheduler) — src/holonomic/consent_decay.rs
  Edge-side per-content_id Consensual Evolution Protocol decay walker
  (TEMPORARY 14-day, STANDARD 90-day, PERSISTENT longer). Orthogonal
  trigger to persist's DiskPressure — both feed the same eviction
  surface. PersistHandle FFI trait is stubbed at L1 pending the
  persist v8.x surface; the scheduler logic is fully wired and tested
  (9/9 unit tests pass). Feature: holonomic-consent-decay.

Cargo feature umbrella:
  codec-default = [codec-fountain, codec-av1, codec-opus]
  — flipped on by the Python wheel build for the realtime A/V profile.

Cross-repo alignment:
  Pins persist v7.2.0 (unchanged); CIRISPersist v8.0.0 tag exists and
  is the natural pair for FountainSymbolV1 storage but the FFI bump
  is its own cut (v3.x.x persist-bump). v3.9.0 ships the codec
  primitives so that bump is purely a dep version change when it lands.

Integration verification:
  cargo fmt --all -- --check: clean
  cargo check --features "transport-http transport-reticulum pyo3 codec-default" (RUSTFLAGS=-D warnings): clean
  cargo clippy --all-targets --features "transport-http transport-reticulum pyo3 codec-default holonomic-consent-decay" -- -D warnings: clean
  cargo test --lib codec module: 23 pass, 1 ignored (AV1 round-trip — libdav1d runtime requirement, documented)
  cargo test --lib holonomic::consent_decay: 9 pass
  Substrate-only build (no codec features): clean (substrate consumers pay zero binary-size cost when the codec features are off).

Closes CIRISEdge#133.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant