72 lines
2.3 KiB
Markdown
72 lines
2.3 KiB
Markdown
# ECP-0036: Multi-Variant Manifests (One Epoch, Many Variants)
|
|
|
|
Status: Draft
|
|
Author: Codex
|
|
Reviewers: founder@every.channel
|
|
Created: 2026-02-07
|
|
|
|
## Decision
|
|
|
|
Extend `ec-core` manifests to optionally carry *multiple* variant hash-lists in a single signed manifest object.
|
|
|
|
One manifest epoch can describe:
|
|
|
|
- the "master" logical stream id (base)
|
|
- N variants, each with:
|
|
- variant id
|
|
- variant stream id
|
|
- chunk index range
|
|
- per-chunk hashes
|
|
- per-variant merkle root
|
|
|
|
Subscribers verify chunks by selecting the variant entry matching the chunk's `stream_id` (derived from encryption `key_id`).
|
|
|
|
## Motivation
|
|
|
|
- Quality pools (ABR ladders) need coordination: all variants should share epoch boundaries and be verifiable under one signed statement.
|
|
- One manifest object per epoch reduces gossip/chattiness vs per-variant manifests.
|
|
- Makes it easy to proxy to playlist-based views (HLS) without coupling the transport to playlists.
|
|
|
|
## Data Model
|
|
|
|
Add to `ec-core`:
|
|
|
|
- `ManifestVariant` (new)
|
|
- `variant_id: String`
|
|
- `stream_id: StreamId`
|
|
- `chunk_start_index: u64`
|
|
- `total_chunks: u64`
|
|
- `merkle_root: String`
|
|
- `chunk_hashes: Vec<String>`
|
|
- `metadata: Vec<StreamMetadata>` (variant-specific; optional but useful)
|
|
|
|
Extend `ManifestBody`:
|
|
|
|
- `variants: Option<Vec<ManifestVariant>>`
|
|
|
|
Rules:
|
|
|
|
- Legacy manifests keep using `chunk_hashes` (single-variant).
|
|
- Multi-variant manifests set `chunk_hashes` empty and populate `variants`.
|
|
- `ManifestBody.merkle_root` becomes:
|
|
- legacy: merkle root of `chunk_hashes`
|
|
- multi-variant: merkle root of ordered `variants[].merkle_root` (sorted by `variant_id`)
|
|
|
|
## Publishing
|
|
|
|
- `ec-node moq-publish` can publish a CMAF ladder: one track per variant, one init track per variant.
|
|
- After each epoch, publish a single manifest frame containing all variants' hashes for that epoch.
|
|
|
|
## Subscribing
|
|
|
|
- Subscriber requires manifests as today.
|
|
- To validate a chunk:
|
|
- identify `stream_id` from `object.meta.encryption.key_id` (strip `/init` suffix if present)
|
|
- find a manifest in store that covers `(stream_id, chunk_index)`
|
|
- compare expected hash from the matching `ManifestVariant`.
|
|
|
|
## Reversibility
|
|
|
|
- Backwards compatible: old readers ignore `variants`, new readers accept both formats.
|
|
- If later we move to per-variant manifests, we can keep multi-variant as an optimization.
|
|
|