@@ -232,13 +232,19 @@ media** at the FP-controlled threshold (real content / screen-record / CMAF leng
232232single-leaker tracing or c=2 only — and the FP-controlled threshold pushes these minimums
233233** up** , not down.
234234
235- ### Phase 5 — landed so far (chunks 1, 2, 6)
235+ ### Phase 5 — landed so far (chunks 1, 2, 6 + the 3/4/5 * core * )
236236
237237> Canonical chunk numbering follows [ ` PHASE5_BUILD_SPEC ` ] ( AV_WATERMARKING.md ) (1 schema · 2 codeword
238238> · 3 mint · 4 AAD-weld · 5 serve · 6 extractor · 7 overlay). The numbered list below predates that
239- > spec; the ✅ markers map the two. ** Landed = chunks 1, 2, 6** (the tractable, pipeline-free pieces
240- > built on the proven research). ** Mine, deferred to the live CENC/DASH/quorum pipeline = chunks 3
241- > (mint transcode DSP), 4 (full-variant-set AAD weld), 5 (serve-time selector).**
239+ > spec; the ✅ markers map the two. ** Landed = chunks 1, 2, 6** , plus the ** pure, pipeline-free CORE
240+ > of 3/4/5** (` ddrm-envelope::av ` ): the serve-time ** selector** (` select_symbols ` ), the
241+ > full-variant-set ** AAD weld** encoder (` variant_set_commitment ` +
242+ > ` DecryptTranscriptV1::to_aad_with_all_bindings ` ), and the mint-side ** manifest builder + per-asset
243+ > secret KDF** (` build_manifest ` , ` asset_secret_from_master ` ) — all fail-closed and unit-tested.
244+ > ** Still mine / needs the live pipeline = the WIRING** (mint emit + serve selection + decrypt-boundary
245+ > rebuild), the ** real perceptual DSP embed** (the bounded placeholder seam is specified but the
246+ > certified embed is deferred until media-survival cert), and ** media-survival certification on real
247+ > content** . See "remaining wiring" below.
242248
243249- ** Chunk 1 — variant manifest schema.** ` elastos.ddrm.av-variants/v1 ` lives in
244250 ` capsules/ddrm-envelope/src/av.rs ` (` VariantManifestV1 ` , behind the ` av-variants ` feature):
@@ -272,6 +278,27 @@ single-leaker tracing or c=2 only — and the FP-controlled threshold pushes the
272278 ` bitERR 0 ` , true leaker ranked top with registration; all-erasures / no attribution without). The
273279 Rust ` --extract-av-fingerprint ` CLI is ** deferred until the scheme is frozen/certified** (avoid a
274280 blind FFT port of an uncertified scheme — see the chunk-6 decision in ` tools/av-forensics/README.md ` ).
281+ - ** Chunks 3/4/5 — pure core (landed in ` ddrm-envelope::av ` + ` lib.rs ` ).** The cryptography and
282+ selection logic the pipeline will call, built and tested ahead of the wiring so the boundary stays
283+ byte-aligned:
284+ - ** Serve selector (chunk 5 core):** ` select_symbols(manifest, bias_q, grant_digest) ` returns the
285+ per-marked-segment symbol, fail-closed — wrong per-asset secret (bias-commitment mismatch),
286+ unsupported arity, or malformed manifest all ` Err ` ; a single-encode manifest selects nothing.
287+ Arity-2 (A/B) direct mapping (codeword bit ↔ segment), matching the proven extractor.
288+ - ** AAD weld (chunk 4 core):** ` variant_set_commitment(manifest) ` (domain-separated, length-prefixed
289+ hash of the full published set) bound via ` DecryptTranscriptV1::to_aad_with_all_bindings ` —
290+ ** strictly additive** : a non-fingerprinted open is byte-identical to the prior encoder (all goldens
291+ replay), a fingerprinted open binds the exact set (manifest swap / out-of-set variant fails the CEK
292+ unwrap closed at the decrypt boundary).
293+ - ** Mint core (chunk 3 core):** ` build_manifest(...) ` assembles + validates a fingerprinted manifest
294+ from produced variants; ` asset_secret_from_master(master, content_hash) ` derives the per-asset
295+ watermark secret so mint and serve agree on the bias/codebook ** without publishing or per-asset
296+ storing it** (manifest carries only the commitment; rotating ` master ` re-keys every asset). A
297+ round-trip test closes the mint→serve loop.
298+ - ** Known follow-up (welded honesty):** the manifest's ` interleave ` is carried + committed but
299+ ** not yet applied** across embed/select/recover (the proven extractor maps bit ` i ` →segment ` i `
300+ directly); applying it is a single change that must land on the Rust selector AND the Python
301+ extractor together, then re-run the extractor. Tracked, not silently dropped.
275302
2763031 . ✅ ** LANDED (chunk 1).** ** Spec + manifest schema.** Define the variant manifest
277304 (` elastos.ddrm.av-variants/v1 ` : which segments carry ` {A,B} ` , the per-variant bit, the codeword
@@ -288,9 +315,12 @@ single-leaker tracing or c=2 only — and the FP-controlled threshold pushes the
288315 strategies** (random / majority / minority / all-ones / all-zeros / interleave): FP ≤ ε,
289316 100% detection. The published per-asset duration bound at this FP-controlled threshold
290317 still needs ** real-media** re-validation (not code-level).
291- 3 . ** Serve-time selector (behind a flag, single-encode fallback).** ` ddrm-media-authority `
292- picks ` variant[i] ` from the codeword when a manifest exists; else serves the single
293- encode and sets ` fingerprinted:false ` .
318+ 3 . ** Serve-time selector (behind a flag, single-encode fallback).** ☑️ ** Core landed**
319+ (` av::select_symbols ` , fail-closed, unit-tested). ** Remaining = wiring:** ` ddrm-media-authority `
320+ (` compute_open_payload ` ) loads the manifest from the DASH dir, derives the per-asset bias from the
321+ node master, calls ` select_symbols ` , and reads the selected variant ciphertext per marked segment;
322+ else serves the single encode and sets ` fingerprinted:false ` . Needs the ` av-variants ` feature on
323+ ` ddrm-media-authority ` + a live serve path to validate.
294324 * Check:* with a two-variant fixture dir, two different grants produce two different
295325 served segment-byte sequences; with no manifest, both get the identical single encode.
2963264 . ✅ ** LANDED as Python (chunk 6).** ** Offline extractor.** Recovers the codeword → buyer,
@@ -299,9 +329,14 @@ single-leaker tracing or c=2 only — and the FP-controlled threshold pushes the
299329 * Check:* a file assembled from a known A/B sequence extracts back to that codeword
300330 (clean), and still extracts after a lossy re-encode + crop pass (validated: ` bitERR 0 ` , leaker
301331 ranked top with FM registration).
302- 5 . ** Mint transcode pipeline (the heavy lift).** Produce ` {A,B} ` per segment with the
303- per-variant DSP (video: spatial/temporal mark; audio: spread-spectrum/echo-hiding),
304- CENC-encrypt, publish manifest.
332+ 5 . ** Mint transcode pipeline (the heavy lift).** ☑️ ** Core landed** (` av::build_manifest ` ,
333+ ` av::asset_secret_from_master ` ). ** Remaining = wiring + the real DSP:** produce ` {A,B} ` per marked
334+ segment, CENC-encrypt each, publish the manifest beside ` stream.mpd ` . The ** embed has a bounded
335+ placeholder seam** (a valid, ignorable, byte-distinct marker — exercises mint→serve→select→weld
336+ end-to-end ** without** a perceptual claim); the ** real per-variant DSP** (video: spatial/temporal
337+ mark; audio: spread-spectrum/echo-hiding) ** swaps in behind the same interface only after
338+ media-survival certification** (avoid shipping an uncertified perceptual embed in the trusted mint
339+ path).
305340 ** Gating DSP research sub-item (the one genuine unknown):** blind ** geometric
306341 registration** of the recovered mark — Phase 0 proved a search loop is insufficient (a
307342 fine scale grid made detection * worse* ); it needs a ** deterministic estimator** (an
@@ -328,6 +363,33 @@ single-leaker tracing or c=2 only — and the FP-controlled threshold pushes the
328363Phases 1–4 are tractable and self-contained. ** Phase 5 is the real cost** (a transcode
329364pipeline) and gates the rest going live.
330365
366+ ### Remaining wiring (precise — picks up where the landed core stops)
367+
368+ The pure core (selector, AAD weld, manifest builder, secret KDF) is in ` ddrm-envelope::av ` +
369+ ` lib.rs ` , fully unit-tested. What is ** not** yet wired (each touches the trusted serve/decrypt path, so
370+ it needs a live serve path — synthetic media is enough; ** real** content is only for the perceptual
371+ cert):
372+
373+ 1 . ** ` ddrm-media-authority ` (serve, chunk 5):** add the ` av-variants ` feature; in ` compute_open_payload `
374+ load ` av-variants.json ` from the DASH dir, derive `bias = asset_bias_vector(asset_secret_from_master(
375+ node_master, content_hash), m)` , call ` select_symbols`, and read the ** selected** variant ciphertext
376+ per marked segment (fall back to the flat ` segment_paths ` when no/invalid manifest). Set
377+ ` fingerprinted ` on the serve descriptor (` serve_media ` ).
378+ 2 . ** AAD weld (chunk 4):** compute ` variant_set_commitment(manifest) ` on the serve side and pass it into
379+ ` recover_quorum ` 's transcript via ` to_aad_with_all_bindings ` ; mirror it in
380+ ` decrypt-provider::prepare_bound_open ` so the seal and rebuild stay byte-identical (extend the
381+ ` stream_segment ` unit test).
382+ 3 . ** Mint (chunk 3/5):** in ` run_prepare_mint_media ` → ` media-provider ` produce the per-marked-segment
383+ variants (placeholder embed seam), ` encrypt-provider ` CENC each, write the manifest via
384+ ` build_manifest ` , and persist the bias ` master ` →asset mapping for the serving node.
385+ 4 . ** Smoke:** extend ` quorum-helper-verify.sh ` for the two-grant divergence check (different served
386+ bytes, byte-identical with no manifest).
387+
388+ ** To validate this end-to-end I need you to:** bring up the gateway with a ** synthetic** test asset
389+ (no real content required) so I can iterate the serve+mint wiring against a live path — same bring-up
390+ you ran before (` ELASTOS_DDRM_RIGHTS=chain ` , ` ELASTOS_DKMS_CARRIER=1 ` ). ** Real media + a screen-record
391+ are only needed later** , for media-survival certification + swapping the placeholder for the real DSP.
392+
331393---
332394
333395## 6. Principles conformance (pre-commit self-review hooks)
0 commit comments