Skip to content

feat(moq-net,js/net): moq-lite-06-wip announce ids (typed START/END/RESTART, opt-in)#2160

Merged
kixelated merged 8 commits into
devfrom
claude/moq-lite-announcement-compression-0cc276
Jul 12, 2026
Merged

feat(moq-net,js/net): moq-lite-06-wip announce ids (typed START/END/RESTART, opt-in)#2160
kixelated merged 8 commits into
devfrom
claude/moq-lite-announcement-compression-0cc276

Conversation

@kixelated

@kixelated kixelated commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

Summary

Reworks broadcast announcements for moq-lite-06 so retracting or restarting an announcement no longer repeats the broadcast path.

The announce stream now carries three independently-typed messages, mirroring the subscribe stream's SUBSCRIBE_START/END/DROP: each is framed as an outer Type discriminator before the length prefix, so messages are length-delimited and skippable.

  • ANNOUNCE_START (0x0): a broadcast is available; carries the path suffix + hop chain, and implicitly assigns the next per-stream Announce ID (a 0-based ordinal, derived from message order, zero bytes on the wire).
  • ANNOUNCE_END (0x1): a broadcast is gone; carries only the Announce ID (~3 bytes total instead of path + hops), and retires it.
  • ANNOUNCE_RESTART (0x2): atomically replace an advertisement; carries the Announce ID + the new hop list; the id stays live. This replaces the lite-05 duplicate-active idiom. A second ANNOUNCE_START for an already-available path is now a protocol violation.

Referencing an unknown or retired id is a protocol violation; ids are never reused within a stream. Announcements filtered by loop detection or exclude_hop never get an id, so their retraction sends nothing (previously a dangling ended was sent for a path the peer never saw).

Pre-06 versions keep their frozen single-ANNOUNCE_BROADCAST framing (inner status byte); all forms are version-gated at encode/decode.

Opt-in gating (not advertised by default)

lite-06's wire format is work-in-progress, so it is never advertised or negotiated by default. The ALPN is moq-lite-06-wip (wire code 0xff0dad06), but it is deliberately absent from moq_net::ALPNS, Versions::all(), and the JS default protocols list. A peer only reaches it when both sides explicitly opt in (e.g. --version moq-lite-06-wip / client_config.version = ["moq-lite-06-wip"]), which is enough to exercise it in tests. The version stays fully defined (code / ALPN / FromStr mappings), so an opt-in set negotiates normally. Finalizing is then a rename of the internal Lite06Wip marker and dropping the -wip from the ALPN.

The moq-native raw-QUIC accept path (noq/quinn/quiche) previously recognized ALPNs against the global moq_net::ALPNS const, which would reject an opt-in ALPN that isn't in the default set. It now recognizes the server's own configured ALPNs (rustls only negotiates an ALPN the server offered).

Cross-package sync

  • rs/moq-net: wire format (AnnounceBroadcast now impls Encode/Decode directly, like SubscribeResponse), publisher/subscriber id bookkeeping, Version::Lite06Wip plumbing (client/server ALPN arms, Handshake::Lite05 generalized to LiteSetup), and the opt-in exclusion from ALPNS/all().
  • rs/moq-native: raw-QUIC accept ALPN recognition keyed off the server's configured versions (noq/quinn/quiche).
  • js/net: mirrored (Version.DRAFT_06, ALPN_06_WIP, union-shaped AnnounceBroadcast with encode/decodeAnnounceBroadcast writing the outer type + length like encodeSubscribeResponse), and excluded from the default protocols list.
  • drafts/: draft-lcurley-moq-lite.md updated in-repo — the single ## ANNOUNCE_BROADCAST section is replaced by ## ANNOUNCE_START / ## ANNOUNCE_END / ## ANNOUNCE_RESTART, plus the Announce-stream prose, cross-references, and changelog.
  • doc/concept: no change needed; it documents the announced(prefix) API surface, which is unchanged.

Public API changes

  • rs/moq-net: lite::Version gains Lite06Wip; lite::Version::has_announce_id() added; lite::AnnounceBroadcast gains EndedId { id } and Restart { id, hops } variants and switches from Message to a direct Encode/Decode impl (breaking for exhaustive matches, hence dev).
  • js/net: AnnounceBroadcast is a discriminated union (active/ended/endedId/restart) with encodeAnnounceBroadcast / decodeAnnounceBroadcast[Maybe] functions (breaking); Version.DRAFT_06, ALPN_06_WIP, hasAnnounceId added.

Testing

  • Wire round-trip + cross-version rejection unit tests in both languages (including the 3-byte ANNOUNCE_END size check).
  • End-to-end lifecycle tests (initial set, live announce, unannounce, re-announce, restart) in rs/moq-native/tests/broadcast.rs and js/net/src/integration.test.ts, both sides pinned to moq-lite-06-wip.
  • Default-negotiation regression guards (NEWEST_LITE, smoke) stay on lite-05, confirming lite-06 is not picked by default.
  • just check passes.

🤖 Generated with Claude Code

kixelated and others added 2 commits July 10, 2026 13:15
Each active ANNOUNCE_BROADCAST implicitly assigns the next per-stream
ordinal; ended and restart reference that id instead of repeating the
broadcast path. Restarts get their own explicit status instead of the
duplicate-active idiom, and an unknown or retired id is a protocol
violation. Announces that were filtered (loops, exclude_hop) never
assign an id, so their retraction sends nothing.

Negotiated via the new moq-lite-06-wip ALPN (wire code 0xff0dad06),
preferred by default like previous wip drafts on dev.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Mirror of the Rust change: AnnounceBroadcast becomes a discriminated
union (active / ended / endedId / restart), the publisher assigns and
retracts by ordinal on lite-06, and the subscriber resolves ids back to
paths (an unknown or retired id is a protocol violation).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @kixelated, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

kixelated and others added 4 commits July 10, 2026 18:52
…cement-compression-0cc276

# Conflicts:
#	js/net/src/lite/announce.ts
Mirror the spec change into the in-repo drafts/ (moved here by #2159):
each active ANNOUNCE_BROADCAST implicitly assigns the next per-stream
ordinal, and ended/restart reference that id instead of repeating the
broadcast path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The wire ALPN is now `moq-lite-06` (no `-wip` suffix); the internal
`Lite06Wip` / `DRAFT_06` markers stay as the code-level not-finalized
signal. Finalizing lite-06 becomes a pure rename (Lite06Wip -> Lite06)
with no wire or config-string change, unlike lite-05 which had to rename
its ALPN on the wire when it left WIP.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…TART/END/RESTART

On lite-06 the announce stream now carries three independently-typed
messages instead of one ANNOUNCE_BROADCAST with a status-dependent body,
matching SUBSCRIBE_START/END/DROP: each is framed as an outer Type
discriminator (START=0, END=1, RESTART=2) before the length prefix, so
messages are length-delimited and skippable. lite-05 and older keep
their frozen inner-status framing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@kixelated kixelated changed the title feat(moq-net,js/net): moq-lite-06-wip announce ids feat(moq-net,js/net): moq-lite-06 announce ids (typed START/END/RESTART messages) Jul 12, 2026
…06-wip

The lite-06 ALPN is 'moq-lite-06-wip' again, but it is no longer in the
advertised sets: removed from moq_net::ALPNS and Versions::all(), and from
the JS default protocols list. lite-06 is never negotiated by default; a
peer only reaches it when both sides explicitly opt in (e.g.
--version moq-lite-06-wip). The version stays fully defined so opt-in
negotiates normally.

The moq-native raw-QUIC accept path (noq/quinn/quiche) recognized ALPNs
via the global moq_net::ALPNS const, which would reject an opt-in ALPN
that isn't in the default set. Recognize the server's own configured
ALPNs instead (rustls only negotiates an ALPN the server offered).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@kixelated kixelated changed the title feat(moq-net,js/net): moq-lite-06 announce ids (typed START/END/RESTART messages) feat(moq-net,js/net): moq-lite-06-wip announce ids (typed START/END/RESTART, opt-in) Jul 12, 2026
@kixelated kixelated enabled auto-merge (squash) July 12, 2026 01:43
Satisfies clippy::manual_contains (rust 1.96, -D warnings in just ci).
quiche's negotiated ALPN is a &str so contains() typechecks; quinn/noq
hold it as a String and keep iter().any().

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@kixelated kixelated merged commit 7fed79e into dev Jul 12, 2026
3 checks passed
@kixelated kixelated deleted the claude/moq-lite-announcement-compression-0cc276 branch July 12, 2026 01:59
kixelated added a commit that referenced this pull request Jul 12, 2026
Reconcile the API-hardening PR with overlapping work that landed on dev.
Where dev and this branch reshaped the same surface, resolved as follows:

- read_frame / poll_read_frame (group + track): keep this branch's
  `frame::Frame` return; drop dev #2174's additive `read_frame_full` /
  `poll_read_frame_full` split. Redirected the moq-ffi / libmoq / internal
  callers to the collapsed `read_frame`; kept dev's MoqFrame + timestamp_us
  FFI plumbing (sourced from the Frame).
- Subscriber::update / SubscriberControl::update: keep this branch's
  `Result` de-panic (dev's #2177 control() still panicked on a closed track);
  ignore the Result at the FFI/libmoq call sites where a closed-track update
  is meaningless.
- moq_net::Subscription -> moq_net::track::Subscription in dev's newer libmoq.
- OriginAnnounce tuple -> announce::Update struct in dev's moq-native
  lite-06 announce test.
- js subscribe options: keep this branch's `Subscription` type name (mirrors
  Rust track::Subscription) over dev #2167's SubscribeOptions; keep the hooks
  indirection that hides the detached Request constructor.
- lite/subscriber.ts: take dev's announce-id machinery (#2160, incl.
  AnnouncedOptions/ignoreSelf) wholesale; port its priority poll onto this
  branch's subscriptionSignal.

Validated: cargo check/clippy --workspace clean; moq-net/moq-ffi/libmoq/
moq-native/moq-mux tests pass; js/net tsc+biome+236 tests; py 45 tests pass.
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.

1 participant