every.channel/evolution/proposals/ECP-0036-multi-variant-manifests.md
2026-02-15 16:17:27 -05:00

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.