Skip to content

feat(moq-msf): draft-01 support behind a version-agnostic snapshot API#1834

Open
kixelated wants to merge 3 commits into
devfrom
claude/msf-version-support-pk6py4
Open

feat(moq-msf): draft-01 support behind a version-agnostic snapshot API#1834
kixelated wants to merge 3 commits into
devfrom
claude/msf-version-support-pk6py4

Conversation

@kixelated

@kixelated kixelated commented Jun 20, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds support for draft-ietf-moq-msf-01 while staying backwards-compatible with draft-00 catalogs. The design goal: the wire format is an internal detail. Callers work with a version-agnostic snapshot and never touch the catalog version or the init-data indirection.

What changed between draft-00 and draft-01 (the parts that matter to us)

  • version: JSON number 1 → JSON string "draft-XX".
  • Init data: moved off the track (inline initData) into a root initDataList of { id, type:"inline", data:<base64> }, referenced per-track by initRef.
  • isLive: still nominally required, but draft-00's own mediatimeline/eventtimeline examples omit it.
  • Everything else (deltaUpdate boolean→array, addTracks/removeTracks/cloneTracksdeltaUpdate ops, new optional track fields like buffers/avgBitrate/encryptionScheme/template, new packaging/role enum values) is additive and already tolerated — unknown fields are ignored and unknown enum values fall into Unknown.

Design

moq_msf::Catalog is now a snapshot: { tracks: Vec<Track> }. No public version, no Version enum, no initRef/initDataList. The wire complexity lives in custom (de)serialization:

  • Decode accepts draft-00 and draft-01. The numeric/string version is parsed permissively (number must be 1; any "draft-XX" string is accepted). initRef is resolved against initDataList so every track ends up with inline Track::init_data. Missing isLive defaults to false.
  • Encode always emits draft-01: version: "draft-01", and inline init_data is hoisted into a deduplicated initDataList with initRef pointers (identical payloads across tracks collapse to one entry).

Net effect: moq-mux's producer/consumer didn't need to learn any of this — they keep setting/reading inline init_data, and the abstraction handles the wire form. @moq/msf mirrors the same encode/decode behavior.

This also shrinks the public API versus the previous commits on this branch (the Version enum and Catalog.version field are gone).

Files

  • rs/moq-msf — snapshot Catalog, custom serde, init-data hoist/resolve + dedup, isLive default.
  • rs/moq-mux — producer emits the snapshot (drops the version field); consumer unchanged.
  • js/msfencode/decode hide version + initDataList/initRef; public Catalog/Track are version-agnostic.
  • docs + changelogs (moq-msf, moq-mux), doc/concept/standard/msf.md.

Out of scope

Full delta-update application (deltaUpdate ops / draft-00 addTracks etc.) — we don't consume deltas. The snapshot API is the right shape to add it behind later without a breaking change.

Branch targeting

dev: MSF catalog-format change + breaking public-API change to published rs/moq-msf and js/msf.

Test plan

  • cargo test -p moq-msf -p moq-mux — draft-00 numeric version, draft-01 string version, unsupported numeric version rejected, draft-00 example catalogs (AV / timeline-without-isLive / completion), initRef→inline resolution, serialize hoist+dedup round-trip
  • cargo clippy -p moq-msf -p moq-mux --all-targets + cargo fmt --check clean
  • bun test js/msf (5 tests incl. initRef resolve + hoist/dedup round-trip) + tsc --noEmit + biome check

(Written by Claude)

claude added 3 commits June 20, 2026 16:37
draft-ietf-moq-msf-01 changed the catalog `version` field from a JSON
number (`1`) to a `"draft-XX"` string. Introduce a `Version` enum that
serializes the newest draft (`"draft-01"`) by default while still
decoding draft-00's numeric `1`, so older publishers stay compatible.

`Catalog::version` changes type from `u32` to `Version`. moq-mux now
emits `"draft-01"` and its MSF consumer accepts both encodings. The JS
`@moq/msf` schema accepts the number or any `"draft-XX"` string and
exports a `VERSION` constant.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R2ruH25xAPfNhydKJ9Q67n
Verifying backwards compatibility against the draft-ietf-moq-msf-00
examples surfaced a real gap: the spec marks `isLive` required but its
own mediatimeline/eventtimeline track examples omit it. With `isLive` as
a required field, serde and zod rejected the entire catalog.

Default `is_live` to false when absent (Rust `#[serde(default)]`, JS
`z.optional`) so draft-00 catalogs decode. Add tests built from the
draft-00 example JSON (AV, timeline tracks, completion) in both
languages.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R2ruH25xAPfNhydKJ9Q67n
draft-01 changes more than the version field: init data moved out of the
track (inline `initData`) into a root `initDataList` referenced per-track
by `initRef`, and `version` became a `"draft-XX"` string. Rather than leak
any of that, make `Catalog` a version-agnostic snapshot (`{ tracks }`) and
push the wire details into (de)serialization.

- Drop the public `version` field and `Version` enum. Parsing accepts
  draft-00 (numeric version, inline initData) and draft-01 (string
  version, initDataList + initRef); serializing always emits draft-01.
- Init data is always presented inline via `Track::init_data`: initRef is
  resolved against initDataList on parse, and on serialize identical
  payloads are hoisted into a deduplicated initDataList with initRef
  pointers. Callers never see the version or the indirection.
- moq-mux producer/consumer are unchanged except dropping the version
  field; they keep using inline `init_data` and the abstraction handles
  the wire form.
- Mirror the same abstraction in `@moq/msf` (encode/decode hide version,
  initDataList, initRef).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R2ruH25xAPfNhydKJ9Q67n
@kixelated kixelated changed the title feat(moq-msf): support draft-01 string version with draft-00 fallback feat(moq-msf): draft-01 support behind a version-agnostic snapshot API Jun 20, 2026
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.

2 participants