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

2.3 KiB

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.