2026-06-07: Three-day cycle (June 4→7, ~20 merges + several OPEN, all kixelated) adds a third gateway vertical (HLS), VP8/VP9 codec coverage, a maturing GStreamer plugin, and a core moq-net
TrackAPI reshape. New gateway — PR #1626 OPEN June 4 “Add moq-hls: HLS / LL-HLS gateway for MoQ” (+1695/−49) — a newmoq-hlscrate exposing MoQ broadcasts as HLS / Low-Latency HLS (multivariant + media playlists + segments), the consume-side complement to the May-29 moq-rtc WebRTC bridge and the June-2 MPEG-TS bridge: MoQ now has WebRTC, MPEG-TS, fMP4, MKV, and HLS interchange surfaces. VP8/VP9 codec coverage: PR #1625 MERGED “moq-mux: add VP8 and VP9 codec modules (import + fMP4 export)” (+1085/−12) + PR #1632 MERGED “gstreamer: add VP8 and VP9 codec support” — extends moq-mux/moq-gst beyond H.264/H.265/AAC/Opus. moq-gst (GStreamer plugin) maturation — now a first-class focus: PR #1627 MERGED “follow catalog updates dynamically in moqsrc” (+217/−36), PR #1633 MERGED “pump moqsrc pads directly instead of bridging to glib” (+180/−400), PR #1646 MERGED June 7 “stop moqsrc panicking on backwards timestamps; globally unique pad ids” — moq-gst released v0.2.4 (PR #1622). Core moq-netTrackreshape: PR #1631 MERGED June 5 “reshape Track into TrackInfo + an async TrackConsumer handle” (+1331/−1217, 02:24 UTC) — the highest-churn merge of the cycle, separating immutable track metadata (TrackInfo) from the live consumer handle; PR #1634 MERGED “serve one subscription and N fetches concurrently per track” (+960/−572); PR #1640 MERGED “RAII broadcast announcements via OriginPublish; remove broadcast abort/close” (+355/−225); PR #1635 MERGED tag broadcasts with a per-connection origin hop when the wire carries none; PR #1636 MERGED make Group/Frame producers track-only constructible. (NB: this reshape is the most likely cause of the interop −13 drop June 5 — the matrix runs against moq-dev/moqmainand theTrack/TrackConsumerAPI changed mid-day.) moq-json catalog deltas — PR #1637 MERGED June 7 “moq-json: JSON Merge Patch snapshot/delta helper; route hang catalog through it” (+1418/−83) — a newmoq-jsoncrate implementing RFC 7386 JSON Merge Patch so thehangcatalog can ship a snapshot + incremental deltas rather than a full document per update. moq-lite-05 wire: PR #1648 OPEN June 7 “moq-lite-05: add TRACK stream, drop SUBSCRIBE_OK/FETCH_OK” (+1582/−663) — the successor to PR #1609 (TRACK_INFO Track Stream), now going further to drop SUBSCRIBE_OK/FETCH_OK entirely in favor of the dedicated TRACK stream0x6; PR #1601 MERGED June 5 “implement FETCH for past groups via TrackConsumer::fetch” (+984/−82). moq-relay ops: PR #1628 OPEN “graceful shutdown via GOAWAY drain” (+473/−31) + PR #1630 OPEN “reload TLS certs on filesystem change instead of SIGUSR1”. Docs/CI: PR #1638 fill env pages + install examples MERGED, PR #1641just cleanworktree sweep MERGED, PR 1624 release-artifact CI, PR #1639 OPEN migrate published-package smoke tests in-tree (+3261/−2). External contrib: arielmol SCTE-35 PR #1617 updated June 6 (still OPEN). Carry-forward: moq-dev/moq’s gateway surface is now WebRTC + MPEG-TS + HLS/LL-HLS plus fMP4/MKV interchange and 6-codec coverage (H.264/H.265/VP8/VP9/AAC/Opus) — the broadest media-bridge footprint of any tracked MoQ impl, four days before London. London interim 4 days away.2026-06-04: Fourth consecutive high-throughput cycle (June 3 ~05:00 UTC → June 4 ~05:00 UTC, ~12 merges + 7 OPEN, all kixelated unless noted). Theme: JS watch-side polish + voice-AI/TTS latency model + CI release-pipeline shakedown + 2 new external contributors (arielmol SCTE-35 + vipyne AI-voice flush). Operational reach into AI-voice use-case — PR #1620 OPEN June 4 02:55 UTC “feat(watch): latency range with buffered playback” (+726/−75, 14f). Reframes playback latency in
@moq/watchas a range[latency-min, latency-max], unifying live (minimize-latency) and buffered playback into a single mechanism. “Latency was never really a single point — it’s a range, and ‘minimize latency’ is just the degenerate case where the range collapses (max == min).” The sync reference (wall-clock anchor) is re-anchored only when keeping it would push latency past the bounds. Unblocks voice-AI / TTS use cases (pipecat-ai/pipecat#4629) where a response is written faster than real-time with future timestamps and should be buffered + played at the encoded pace, rather than the player aggressively minimizing latency and skipping ahead. First explicit moq-dev/moq feature targeting AI agents at the watch layer (pairs with the parallel ingest-side flush/cancel PR #1615 below). moq-lite-05 wire features: PR #1601 updated (TrackConsumer::fetch single-group FETCH, OPEN since June 3); PR #1609 updated (TRACK_INFO Track Stream0x6, OPEN since June 3) — kixelated also files the corresponding moq-transport-side issue moq-wg/moq-transport#1644 raising the same “duplicate Track Properties on repeated SUBSCRIBE_OK/FETCH_OK” gap as a draft-18 design item. Watch component refactor: PR #1591 MERGED June 4 00:31 UTC “js/signals: add Computed derived signals” (+401/−29, 8f); PR #1592 MERGED 00:41 UTC “js: make @moq/watch component signals explicit inputs vs outputs” (+1211/−907, 40f) — separates inbound configuration signals from outbound state signals on<moq-watch>web component; PR #1588 MERGED June 4 02:43 UTC “watch: stop audio/video downloads when muted, paused, or off-screen” — power/bandwidth savings on background tabs. moq-relay infrastructure polish: PR #1621 MERGED June 4 03:37 UTC “moq-net: spawn a task per lite control stream” (+84/−91, 1f); PR #1611 MERGED June 3 17:06 UTC “fix(infra): serve the apt keyring dearmored, rename to moq-keyring.gpg” (+59/−32, 7f); PR #1616 MERGED June 3 23:35 UTC “fix(publish): honor requested audio channel count on macOS”. CI release pipeline: PR #1619 MERGED June 4 02:48 UTC “ci(release): publish moq-gst so release-plz tags it”; PR #1622 MERGED 03:48 UTC “ci(moq-gst): fix zigbuild glib link + release v0.2.4”; PR #1623 MERGED 05:01 UTC “ci: cross-compile all x86_64-darwin release artifacts on Apple Silicon” (+346/−179, 11f). External contributor #1 — arielmol PR #1617 OPEN June 4 01:30 UTC “Ingest SCTE-35 from MPEG-TS into a data catalog track” (+646/−27, 8f). Adds SCTE-35 ingest to the MPEG-TS importer: SCTE-35 PIDs detected,splice_info_sections reassembled, published verbatim on a newdatacatalog track. “This is the ingest half of the TS ↔ MoQ SCTE-35 bridge; export (remux back to TS) will follow in a separate PR.” New optionaldatasection inhangcatalog (DataConfig { schema, streamType, container }), omitted when empty so existing catalogs serialize unchanged. Mirrored injs/hang(zod). First broadcast-ad-marker plumbing in moq-dev/moq — Ad Insertion bridge between TS pipelines and MoQ. External contributor #2 — vipyne Issue #1614 + PR #1615 OPEN June 3 20:22-20:23 UTC “Add a flush/cancel primitive to AudioProducer (Python) for real-time interruption use cases (ai voice agents)” (+235/−5, 12f) — pairs ingest-side with PR #1620’s watch-side latency range to give Python AI-voice ingest a way to cancel any in-flight buffered audio in mid-response. Together PR #1620 + PR #1615 + PR #1617 = 3 PRs adding new use-case verticals in 24h: AI-voice playback, AI-voice ingest, broadcast-ad-marker carriage. Kotlin polish: PR #1610 MERGED June 3 15:43 UTC by Qizot “chore(kotlin): bump jna version” — Qizot’s 2nd contribution (after May 30 Android logcat PR #1541). Other: PR #1607 MERGED June 3 03:31 UTC chore moq-relay 0.12.7; PR #1608 MERGED 17:41 UTC chore release (bot); PR #1612 MERGED June 4 02:29 UTC chore moq-token-cli release v0.5.29; PR #1613 MERGED June 3 19:59 UTC “docs: move hang watch/publish guides into their own packages” (+3/−252, 4f). OPEN at cycle end: PR #1601 + #1609 (moq-lite-05 wire), PR #1605 dependabot reqwest-middleware, PR 1615 (vipyne), PR #1617 (arielmol), PR #1618 chore release, PR #1620 (latency range). Carry-forward: combined moq-dev/moq state post-June 4 = production-grade CDN substrate (PR #1604 /health + PR #1571 cluster-connect-api + PR #1574 per-auth-root billing) + first AI-voice-agent stack in MoQ ecosystem (PR #1620 latency range + PR #1615 flush/cancel) + first broadcast-ad-marker carriage (PR #1617 SCTE-35) + moq-lite-05 wire spec maturing fastest in ecosystem (5+ wire features in 8 days, all gated) + 2 new external contributors in 24h (arielmol + vipyne) on top of last week’s nuts-rice + Qizot. London hackathon 5 days away — the demo surface keeps widening even as pre-London backlog clears.2026-06-03: Third consecutive ~14-merge cycle (June 2 20:00 UTC → June 3 04:00 UTC, ~14 merges + 3 OPEN, all kixelated). Theme: moq-relay load-shedding endpoint (first explicit production-CDN feature) + moq-lite-05 wire-feature triple + MPEG-TS fMP4 export close-out + dependency wave with code fixes. Operational milestone — PR #1604 MERGED June 3 03:04 UTC “moq-relay: add /health load-shedding endpoint” (+905/−5, 11f) —
GET /healthreturns200 okwhen every configured threshold passes or503 overloadedfollowed by one plain-text line per breached threshold. With no thresholds configured it’s a pure liveness probe. Host metrics from cross-platformsysinfocrate; load-average flag Unix-only. Each threshold independent and only enforced when set. Config under[web.health]with matching--web-health-*flags +MOQ_WEB_HEALTH_*env vars:cpu(75or75%),ram(80%or32GB/32GiB),rx/tx(unit required, lowercaseb= bits / uppercaseB= bytes). Pairs with PR #1571 May 31 (externalized--cluster-connect-apipeer list) + PR #1574 May 31 (per-auth-root presence-based billing) — together: moq-dev/moq is now structurally a production CDN substrate with load-balancer integration, peer-list externalization, presence billing, and the moq-pro pattern (proprietary policy in moq-pro, agnostic substrate in OSS moq-dev/moq) consolidated. moq-lite-05 wire-feature triple — 3 PRs implementing moq-dev/drafts#24 and #25: PR #1595 MERGED June 3 00:40 UTC “moq-lite-05: add Frame Start to FETCH” (+147/−6, 3f) addsFrame Start (i)field afterGroup Sequenceto the moq-lite FETCH message in both Rust (rs/moq-net) and JS (js/net); publisher skips earlier frames, subscriber resumes partway through a partially-received group.0returns entire group. PR #1601 OPEN June 3 00:56 UTC “moq-lite-05: implement FETCH for past groups via TrackConsumer::fetch” (+1027/−63, 7f) — addsTrackConsumer::fetch(group: u64, options: impl Into<Option<Fetch>>) -> Result<GroupConsumer>withpub struct Fetch { pub priority: u8 }; first-class one-shot fetch of single past group without holding a live subscription. Cached group returned directly; otherwise bridges to wire moq-lite FETCH blocking on FETCH_OK (previously rejected by publisher withUnexpectedStream). Reuses dynamic track-request machinery for one group. PR #1609 OPEN June 3 03:50 UTC “moq-lite-05: move immutable track props to a Track Stream (TRACK_INFO)” (+435/−213, 6f) — implements moq-dev/drafts#25: moves a track’s immutable publisher properties (Priority,Ordered,Cache,Timescale,Compression) out of SUBSCRIBE_OK onto dedicated Track Stream (0x6) answered with singleTRACK_INFO. Scoped to WIPLite05version;Lite01-Lite04keep SUBSCRIBE_OK unchanged for backward compat. Rationale: properties were echoed on every SUBSCRIBE_OK despite being lifetime-fixed; fetching once over their own stream + caching removes per-response repetition and would let group-by-group FETCHes reuse one lookup. Keeping immutable avoids a relay fan-out problem (publisher-side change would otherwise propagate to every downstream subscriber). moq-lite-05 wire spec maturing at pace: counting from PR #1518 (Lite05Wip variant May 27) the wire-feature additions are now 5 in 7 days — deflate (PR #1531 May 28) + AnnounceOk (PR #1573 June 1) + Frame Start in FETCH (PR #1595 June 3) + TrackConsumer::fetch (PR #1601 OPEN) + TRACK_INFO Track Stream (PR #1609 OPEN) — all gated onLite05Wipso default ALPN/Versions don’t advertise. moq-lite-05 is structurally the most active wire-protocol evolution venue in the MoQ ecosystem, outpacing moq-transport draft-18 → draft-19 prep both in feature count and merge cadence. MPEG-TS fMP4 export close-out: PR #1590 MERGED June 2 20:10 UTC out-of-band avc1/hvc1 in MPEG-TS export (covers June 2 PR #1587’s known limitation that TS export required in-band video avc3/hev1). PR #1593 MERGED June 2 20:11 UTC “moq-mux: synthesize AAC esds in fMP4 export; guard MKV header race” (+164/−3, 4f) — fMP4 export previously failed “can’t synthesize CMAF init for audio codec AAC” for any TS-imported or raw-AAC broadcast (synthesize_audio_trakinrs/moq-mux/src/container/fmp4/mod.rsonly handled Opus); now buildsmp4_atom::Mp4asample entry with fullesdsfrom catalogAudioConfig.description(the AudioSpecificConfig the TS importer sets viaaac::Config::encode). Closes the gap so MPEG-TS-imported broadcasts can be re-exported as fMP4 — full bidirectionalts ↔ moq ↔ fmp4plumbing complete with audio. API ergonomics: PR #1606 MERGED June 3 03:33 UTC “moq-net: accept impl Into<Option> for subscribe, drop subscribe_default” (+119/−210, 23f) —TrackProducer::subscribe+TrackConsumer::subscribenow takeimpl Into<Option<Subscription>>converting once at the public boundary via.into().unwrap_or_default(). Removes redundantsubscribe_default(). Release & dependency wave: PR #1607 MERGED June 3 03:31 UTC chore release moq-relay 0.12.7; PR #1596 MERGED June 3 00:20 UTC ci dependabot 7-day cooldown for cargo/bun/uv + dropbun.lockb; PR #1598 dependabot bun group (20 updates) MERGED; PR #1599 dependabot github-actions group MERGED; PR #1602 dependabot uv group + Container re-export for pyright MERGED; PR #1603 dependabot cargo group with code fixes forrand/rubato/rcgenMERGED. Other: PR #1589 MERGED June 2 02:54 UTC moq-boy exit non-zero on reconnect give-up instead of hanging. OPEN follow-ons: PR #1601 + PR #1609 (above), PR #1588 decouple download gating from Renderer, PR #1591 js/signals Computed derived signals, PR #1592@moq/watchcomponent signals inputs vs outputs explicit, PR #1583 (external nuts-rice) js Opus dtx WIP, PR #1605 dependabot reqwest-middleware bump, PR #1608 chore release. Carry-forward: combined moq-dev/moq state post-June 3 = production-grade CDN substrate (/healthload-shedding + externalized--cluster-connect-apipeer list + per-auth-root presence billing + qmux pinning) + moq-lite-05 wire spec maturing fastest in the ecosystem (5 wire features in 7 days, all gated) + full bidirectional MPEG-TS ↔ fMP4 with audio close-out. London hackathon 6 days away — pre-London PR backlog effectively cleared, with each OPEN PR a discrete moq-lite-05 wire feature ready to land indevbranch ahead of merge tomain.2026-06-02: kixelated 14-merge + 6-OPEN burst June 1-2 — second consecutive day of high-throughput PR landing; clears long-pending backlog including 3 PRs OPEN multiple days. Theme: MPEG-TS bridge (new MSFTS/moq2ts adjacency), moq-lite-05 wire feature consolidation, qmux version pinning, CDN-dashboard auth integration, FFI shrinkage. Major adjacency milestone — PR #1587 OPEN Jun 2 02:25 UTC “moq-mux: add MPEG-TS (transport stream) import and export” (+1548/−2, 15 files) — adds
container/tsdemux/mux tomoq-mux, mirrors existing fMP4 and MKV interchange formats. Use cases unblocked:ffmpeg -i input.mp4 -c copy -f mpegts - | moq-cli publish --url ... --broadcast x tsandmoq-cli subscribe --url ... --broadcast x --format ts | ffplay -without transcode. Codecs H.264 / H.265 / AAC; singlempeg2tscrate handles both import and export (rationale documented in PR body: “MPEG-TS is a frozen format, so the crate’s lower release cadence matters little here; a second crate would only buy robustness on malformed streams”).ts::Importdrives persistentmpeg2tsreader, reassembles PES per PID, routes to existingh264/h265/aaccodec importers; handles PAT/PMT discovery + 90 kHz → microsecond PTS with 33-bit unwrap + ADTS → raw AAC.ts::Exportsubscribes viaCatalogSource+ per-trackConsumer, emits PAT/PMT at keyframes for mid-stream tune-in, packetizes each frame into 188-byte TS packets with continuity counters + PCR. Known limitation: TS export requires in-band video (avc3/hev1, which moq’s importers produce); out-of-band avc1/hvc1 rejected with clear error (cf. PR #1590 OPEN as immediate follow-up addressing this). PR body adds CLAUDE.md guidance “prefer a maintained third-party crate over hand-rolling non-core functionality” — first explicit codebase architectural note about scope discipline. Cross-impl adjacency: same June 2 02:25 UTC window, mondain/moq2ts (Paul Gregoire’s C++ MSFTS demonstrator, publicly announced June 1 19:43 UTC on Slack) is shipping MSFTS-spec-conformant MPEG-TS publish/subscribe. PR #1587 + moq2ts together = two complementary MoQ-side MPEG-2 TS paths (moq-dev/moq codec-agnostic + moq2ts MSFTS-conformant). Long-pending PRs that finally MERGE: PR #1439 MERGED Jun 1 19:13 UTC “Add per-track timescale and frame timestamps to moq-lite” (finally lands); PR #1540 MERGED 15:34 UTC “moq-net: make subscribe_track async, blocking on SUBSCRIBE_OK” (+1221/−632, 55 files, was OPEN since May 29 = 4 days) — picks up #1439 idea; reshapessubscribe_trackso subscription resolves once publisher confirms via SUBSCRIBE_OK, separatesSubscription(subscriber wire params: priority/ordered/max_latency/start_group/end_group) fromTrack(publisher immutable properties: justname), concurrent subscribers coalesce onto one request. PR #1513 MERGED 19:03 UTC “moq-net: map MoQ versions to required qmux versions” — concrete code for WG-decided qmux pinning (moq-transport-18 must ride on qmux-01, 14-17 on qmux-00, moq-lite unconstrained). moq-lite-05 wire spec maturing: PR #1573 MERGED Jun 1 16:09 UTC AnnounceOk (was OPEN in June 1 entry; merged ~13h after open intodevbranch). CDN-dashboard auth: PR #1581 MERGED Jun 2 02:25 UTC “relay: unified —auth-api (one call returns key + public + alias)” (+576/−24, 6f) — pairs with moq-pro PR #47 for MoQ CDN dashboard giving each project stable id + editable vanity path resolved via one HTTP call; continues moq-pro pattern (proprietary policy in moq-pro, agnostic substrate in OSS moq-dev/moq). JS-side polish: PR #1582 re-export Hang from@moq/publish/@moq/watch; PR #1584 prefer esm.sh over jsDelivr for no-build CDN usage; PR #1591 OPEN Computed derived signals; PR #1592 OPEN@moq/watchcomponent signals inputs vs outputs; PR #1588 OPEN decouple download gating from Renderer. External contribution: PR #1583 OPEN by nuts-rice (new external contributor) “js: add Opus dtx for voice WIP”. Other merges: PR #1576 split TrackConsumer into track handle + TrackSubscriber, PR #1577 LTO-shrink moq-ffi + libmoq staticlibs (unblocks moq-go mirror push), PR #1579 don’t advertise illegalqmux-00.moqt-18pair, PR #1580 WebSocket keep-alive on client path, PR #1585 moq-net per-track cache age in SUBSCRIBE_OK + draft field order + port timescale to js/net, PR #1586 libmoqmoq_error()exposed (stop logging FFI errors), PR #1589 moq-boy exit non-zero on reconnect give-up, PR #1575 chore release. OPEN follow-on PR #1590 moq-mux out-of-band avc1/hvc1 in MPEG-TS export (covers the limitation above). Carry-forward: combined moq-dev/moq state post-June 2 = first MoQ implementation to ship a working MPEG-TS↔MoQ codec-agnostic bridge + completed long-pending refactor backlog (subscribe_track async + per-track timescale + qmux mapping all MERGED in single 12h window) + moq-pro pattern continues to externalize policy (auth-api unified, cluster-connect-api). London hackathon 7 days away — pre-London PR backlog effectively cleared.2026-06-01: Cluster-mesh infrastructure burst — 5 merges + 1 OPEN in ~12 hours May 31 17:51 UTC → June 1 03:20 UTC, all kixelated. Theme: externalize cluster topology to an operator-owned endpoint + deterministic route tie-break + per-auth-root billing + moq-lite-05 AnnounceOk wire feature. Headlines: PR #1569 MERGED May 31 17:51 UTC “relay: dedup mesh dials with a URL-order tiebreaker” (+30/−2) — gossip-discovered peers only dialed by lexicographically-smaller node (
peer > self_url); inbound connection still arrives for skipped side; cluster session bidirectional so one connection suffices. Eliminates redundant outbound dial per pair. Scope: gossip only — explicit--cluster-connectalways dials. PR #1570 MERGED 19:26 UTC “moq-net: deterministic route tie-break for equal-length paths” (+83/−18) — equal-hop-count tie-break via FNV-1a(hop_count, hash)lex compare so every node converges on the same winner across rolling deploys. FNV-1a instead ofDefaultHasherbecause std’s output is explicitly not stable across Rust versions — during rolling deploys, mismatched binaries must still agree on a route. Mixing broadcast name in spreads equal-length routes across upstreams. PR #1571 MERGED June 1 00:22 UTC “moq-relay: add--cluster-connect-apiand split cluster identity from gossip” (+874/−150, 10f) — externalizes peer-list source tohttp(s)URL or local file returning bare JSON["a.pop.example", "b.pop.example"]. Polled withCache-Controlsemantics (max-age+stale-while-revalidate) + ETag/Last-Modified + fail-static on error. File re-read on mtime change. The relay’s--cluster-nodesent as?node=, cluster mTLS cert identifies caller → per-node peer lists possible. Relay stays topology-agnostic; the/cluster/connectendpoint itself lives in moq-pro. Re-adds--cluster-node <url>for identity;--cluster-meshbecomes boolean gossip toggle (breaking change to recent flag from PR #1504 May 24). PR #1572 MERGED 01:11 UTC “simplify cluster-connect-api polling onto the HTTP cache” (+38/−93) — rebased ondev, delegates freshness to existinghttp-cache-reqwestmiddleware instead of hand-parsingCache-Control(net −58 LOC); PR body documents crate landscape check (no crate does background SWR for reqwest; existing middleware already provides the freshness behavior wanted). PR #1574 MERGED 03:20 UTC “moq-relay: count connected sessions per auth root for billing” (+355/−43, 3f) — presence-based billing: newsessions.json(external) +internal/sessions.json(internal) stats tracks mapping auth root →{sessions, sessions_closed}. Counts presence regardless of data flow (idle authenticated session billable); RAIISessionStatsguard via newStatsHandle::session(root). Per-(broadcast, session)atomic from PR #1553 can’t derive node-level session count, hence new track. PR #1573 OPEN 02:47 UTC “moq-lite-05: add AnnounceOk message (responder origin + initial active count)” (+493/−36, 16f) targetingdev— second concrete moq-lite-05 wire feature after PR #1531 deflate compression (May 28). NewAnnounceOkmessage sent once by publisher right after readingAnnounceInterest, two purposes: (1) reports responder’s origin id once instead of per-Announce trailing hop; in Lite05 receiver stamps remote sender’s origin on receipt (stored hop chain byte-identical to Lite04 so loop detection unchanged); (2) reportsactive: Ncount followed by exactlyNinitialAnnounce::Active= discrete initial-set boundary (successor toAnnounceInit). Enablesconnect()to block until initial set landed via newSyncLatch, closes startup race where synchronousget_broadcast()post-connect could miss broadcasts live-but-not-yet-gossiped. JS mirror lands wire change; JS does NOT add connect-blocking (pull-basedConnectiondoesn’t have the race). Opt-in:Lite05Wipnot advertised over ALPN — nothing negotiates by default. Plus dependabot merges: PR 1568 docker/build-push-action 7.1→7.2 + docker/setup-buildx-action 4.0→4.1 + flake-checker-action bump. PR #1575 chore: release closes the May 31 PR #1557 release-plz cycle (9e9f324aJune 1 03:47 UTC). OPEN follow-ons: #1540 moq-net subscribe_track async (blocking on SUBSCRIBE_OK, +1221/−632, 55f, picks up #1439 idea ondev); #1573 above; #1566 dependabot nix-installer-action bump. Carry-forward: combined moq-dev/moq state post-June 1 = first production-grade externalized-cluster-topology MoQ relay (operator-owned peer-list endpoint + deterministic FNV-1a route convergence under rolling deploys + per-auth-root billing presence) + moq-lite-05 wire spec maturing (deflate + AnnounceOk + Lite05Wip gated version variant); the moq-pro pattern (proprietary routing in moq-pro, agnostic substrate in moq-dev/moq) now visible — externalize policy, leave mechanism in OSS. London hackathon 8 days away.2026-05-31: Polyglot-split day — Swift + Kotlin + Go all execute the May 30 Python-split template within hours; ~14 PRs in 24h. The previous day’s PR #1551 framed the Python split as “the repeatable pattern for Swift/Kotlin/Go to follow later” — and on May 30 19:44 UTC → May 31 03:48 UTC kixelated proceeded to do exactly that, all three language splits opened and merged within 8 hours of each other. Headlines: PR #1561 MERGED May 31 02:47 UTC “swift: split MoqFFI bindings from the Moq wrapper + Swift-native redesign” (+1810/−489, 32f) — ships as two SPM packages each mirrored to its own repo (
moq-dev/moq-swift-ffiraw +moq-dev/moq-swiftergonomic). Wrapper pinsMoqFFIat.upToNextMinor(from: <crate version>)substituted fromrs/moq-ffi/Cargo.tomlat package time (SPM analog of py’smoq-ffi ~= 0.2.x). Plus Swift-native ergonomic redesign usingAsyncSequence/async throws. PR #1562 MERGED May 31 02:49 UTC “moq-ffi (Kotlin): split bindings from an independent ergonomic wrapper” (+704/−181, 19f) — splitsdev.moq:moqMaven bundle intodev.moq:moq-ffi(raw UniFFI bindings + native libs, tracksmoq-fficrate via-Pmoqffi.version) +dev.moq:moq(pure-Kotlin ergonomic library, starts at 0.3.0 to leave room above the published 0.2.17 lockstep line). Maven dependency rangedev.moq:moq-ffi:[0.2,0.3)means consumers transitively float to newest bindings patch without library re-cut. PR #1563 MERGED May 31 03:48 UTC “go: split moq-go-ffi bindings from the ergonomic moq-go wrapper, add the wrapper” (+2333/−309, 33f) —go/ffi/→github.com/moq-dev/moq-go-ffi(raw bindings, lockstep onmoq-ffi-v*tags) +go/wrapper/→github.com/moq-dev/moq-go(new ergonomic wrapper, full re-wrap withcontext.Contextcancellation viarunCancellable, Goerrorreturns +IsShutdownhelper, Go 1.23iter.Seq2stream iterators, starts at 0.3.x). PR body notes a clever solution to Go’s minimum-version-selection problem (MVS resolves to maxmoq-go-ffiversion that any module in the build graph asks for, so even if wrapper pins0.2.16the binary uses newermoq-go-ffiif anything else asks for it). Plus: PR #1559 MERGED “moq-rs: close FFI parity gaps and smooth subscribe ergonomics” (+228/−30, 13f, Python) — adds top-levelError/log_level()/Session(withclosed()/cancel(code)/shutdown()+async withgraceful shutdown). PR #1554 MERGED “Ergonomic announced streams in Swift and Kotlin wrappers” (+35/−23, 8f) —MoqAnnouncedconforms to SwiftAsyncSequencesofor try await announcement in announced { ... }; Kotlin replaces stutteringMoqAnnounced.announcements()with idiomaticFlow. PR #1560 MERGED “moq-net: wire session stats into the IETF protocol path” (+296/−112, 7f) — fixes a long-standing IETF-vs-lite gap where any IETF MoQT session contributed zero toStats/StatsHandlecounters (broadcasts/subscriptions/bytes/frames/groups); the dashboard “viewers” metric and any billing built on these silently undercounted/excluded IETF clients. Pre-existing TODO// ietf code path does not yet record statsinserver.rsremoved. PR #1553 MERGED “stats: count viewers as distinct per-session subscriptions” (+152/−25, 4f) —broadcasts/broadcasts_closedbecome real per-(broadcast, session) atomics so summed across sessionsbroadcasts - broadcasts_closed= number of distinct peer sessions subscribed to a broadcast (egress-side viewer count). PR #1556 MERGED “test: drop the in-repo cross-language smoke test” (+12/−654, 14f) — cross-language smoke test moves to its own repo moq-dev/smoke. The in-tree version proved the source worked but couldn’t catch missing wheels / stale Homebrew formulas / broken.debs / un-buildable Go modules / exports that didn’t survive packaging. The new repo installs the published artifacts (cargo/brew/apt/nix for binaries + PyPI + npm + Go module proxy + SPM + Maven Central + libmoq release assets) and runs the same publish/subscribe matrix. Phase 4 (CI workflow) effectively moves to a sibling repo. PR #1564 MERGED May 31 03:54 UTC “nix: declare the Cachix cache in the flake and document the tagged-release caveat” (+25/−6, 2f) —nix run github:moq-dev/moq#moq-relaynow actually benefits from the Cachix cache vianixConfigblock; doc clarifies only tagged releases are pushed. PR #1558 MERGED “ci: scrub nix-store libiconv from macOS release binaries” (+127/−51, 6f). PR #1552 MERGED libmoq addsmoq_origin_consume_announced(+208/−3, 4f). PR #1555 MERGED ci pins released cachix paths. OPEN: PR #1557 release-plzchore(moq-net): release v0.1.8(CHANGELOG cites #1553 + #1560). Carry-forward: combined moq-dev/moq state post-May 31 = 4 independently-versioned FFI distributions (moq-ffiraw +moqergonomic wrapper) × 4 languages (Python/Swift/Kotlin/Go) + 6-language coverage (Rust + TypeScript + Python + Swift + Kotlin + Go) + 9-channel distribution + WebRTC↔MoQ bridge + lite-05 deflate + REANNOUNCE + full 3×3 cross-language CI harness (now externalized) + libmoq auto-reconnect. First multi-language FFI release architecture in any tracked MoQ implementation where ergonomic wrappers float to the latest bindings patch via language-native compatibility ranges (PEP 440~=, SPM.upToNextMinor, Maven[0.2,0.3), Go0.2.xMVS) and the polyglot-split-day execution (Python opened + merged + 3 follow-on language splits opened + all merged within 24h, fully driven by a single contributor) is itself the template moment that the May 22-30 multi-language-binding sprint was building toward.2026-05-30: kixelated burst continues — ~15 merges + 3 OPEN May 29 ~05:00 UTC → May 30 ~06:00 UTC. Theme: cross-language smoke phase 2 completes 3×3 rust×python×js-browser matrix + libmoq production-grade auto-reconnect + Python release-flow architecture documented as Swift/Kotlin/Go template + Qizot Android logcat completes platform-logging parity. Headlines: PR #1528 moq-rtc WebRTC↔MoQ bridge MERGED May 30 00:41 UTC (was OPEN May 28 19:58 UTC; 36-hour open-to-merge cycle for +2590/−17 across 30 files). PR #1542 MERGED “test/browser: headless-browser smoke client (phase 2)” (+228/−5, 11f) — Playwright runner driving nix-provisioned full Chromium (
channel: "chromium"becausechrome-headless-shelllacks WebTransport/WebCodecs/mediaDevices) against real<moq-publish>/<moq-watch>web components using fake-camera H.264 WebCodecs encode/decode. Fullrust × python × js-browsercross-language matrix now passes both ways (3×3 = 9 cells: rust↔rust, rust↔python, rust↔js-browser, python↔rust, python↔python, python↔js-browser, js-browser↔rust, js-browser↔python, js-browser↔js-browser). First browser-included cross-language interop harness in any tracked MoQ implementation. Phase 1 was PR #1529 rust↔python; Phase 3 will add Swift/Kotlin/Go; Phase 4 will add CI workflow. PR #1544 MERGED “libmoq: auto-reconnect sessions; conducer-based Reconnect notifications” (+160/−42, 5f) —moq_session_connectnow drives moq-native’sReconnecthelper (same one as moq-cli/moq-boy/hang examples), exponential backoff, origins outlive the connection so broadcasts re-announce + consumers re-subscribe automatically. Reconnect rebuilt onkio(channel instead oftokio::sync::watch) matching the rest of moq-net;closed()/poll_closedawait termination unchanged. PR #1551 OPEN May 30 05:12 UTC “py: split moq-ffi bindings from the ergonomic wrapper into two packages” (+2284/−1016, 133f) — architectural Python release-flow change: 2 PyPI distributions (moq-ffi= raw uniffi atpy/moq-ffitrackingrs/moq-ffivia existingmoq-ffi-v*tag;moq= pure-python wrapper atpy/moqdepending onmoq-ffi ~= 0.2.16PEP 440 compatible release so installs float to the latest moq-ffi patch automatically without wrapper re-release). Workspace root renamedmoq→moq-workspaceto free themoqname. Documents the repeatable pattern for Swift/Kotlin/Go to follow later — multi-language reference architecture for FFI-distributed protocol implementations beyond MoQ. PR #1541 MERGED by Qizot (new external contributor) “moq-ffi: route Android logs to logcat” — adds android-locat tracing subscriber so logs route to ADB / Android Studio via logcat (parity with iOS Xcode log inspection); first contribution from Qizot completes the iOS-Xcode/Android-Logcat platform-logging parity gap. Plus PR #1546 MERGED libmoq terminal-callback lifetime contract for C consumers (+330/−226, 10f); PR #1547 MERGED kio: rename conducer crate to kio (+142/−182, 32f, Rust-side naming consistency); PR #1548 MERGED stats: retain entries by liveness instead of tick-retention window (+209/−345, 13f); PR #1549 MERGED go: ship moq.h + linux staticlibs so Go module builds for consumers (+27/−14); PR #1536 MERGED moq-net auto-create Origin on connect/accept, expose via Session (+591/−410, 45f); PR #1537 MERGED stats StatsConfig value type; PR #1543 MERGED nix: format moq-relay module with flake-pinned nixfmt 1.2.0; PR #1545 MERGED claude: load direnv/nix env on session start so Bash uses pinned tools. OPEN follow-ons: #1530 REANNOUNCE still OPEN Day +2; #1527 qmux 0.1.1 negotiation OPEN; #1540 async subscribe_track OPEN; #1513 qmux version mapping OPEN Day +3; #1551 Python split OPEN; #1550 chore release OPEN. Carry-forward: combined moq-dev/moq state post-May 30 = 6-language coverage (Rust + TypeScript + Python + Swift + Kotlin + Go) + 9-channel distribution (cargo/npm/pip/PyPI/brew/apt/dnf/SPM/Maven/go-get) + WebRTC↔MoQ bridge + lite-05 deflate compression + REANNOUNCE atomic broadcast replacement + full 3×3 cross-language CI harness + libmoq production-grade auto-reconnect + Python release-flow architecture documented as Swift/Kotlin/Go template. London pitch structurally complete 10 days before hackathon.2026-05-29: kixelated ~25-PR single-day burst May 28 ~14:00 UTC → May 29 ~05:00 UTC = largest single-day push by single contributor the wiki has tracked across any MoQ implementation (exceeds afrind’s 13-event May 27 openmoq/moqx single-day burst; previously the record was kixelated’s overnight ~17-PR waves spanning 8+ hours). Theme: completes moq-dev/moq’s London pitch with first WebRTC↔MoQ bridge + first concrete moq-lite-05 wire feature + REANNOUNCE atomic broadcast replacement + cross-language interop smoke test. Headlines: PR #1528 OPEN May 28 19:58 UTC “moq-rtc: WebRTC (WHIP/WHEP) gateway, both ingest and egress” (+2590/−17, 30 files, targeting
devbranch) — newrs/moq-rtccrate with 2×2 matrix (server/client × publish/subscribe) over str0m 0.19 + axum 0.8 signaling + reqwest client-side dial, supports Opus / H.264 / VP8 / VP9 end-to-end; H.264 ingest uses moq-mux’s Avc3 importer (Annex-B input with SPS/PPS lifted into catalog), egress handles both avc3 passthrough and avc1 length-prefix → Annex-B with SPS/PPS prefixed on every keyframe. “WebRTC is the de facto contribution and last-mile distribution protocol for browsers, OBS, mobile SDKs, capture tools, and most camera vendors. Today there’s no WebRTC bridge into MoQ.” — first WebRTC↔MoQ bridge in any tracked MoQ implementation, narrows Luke Curley’s May 9 HN flame war “MoQ replaces WebRTC” framing to “MoQ ingests-from + serves-to WebRTC peers”. PR #1531 MERGED May 29 04:08 UTC “lite-05: negotiate per-frame compression via SUBSCRIBE_OK (Rust + JS)” (+790/−113) — first concrete moq-lite-05 wire feature since PR #1518 reserved theLite05Wipversion variant May 27: opt-in raw DEFLATE per-frame compression negotiated via newCompressioncodec field inSUBSCRIBE_OK, hop-by-hop not end-to-end, lite-04 always negotiatesNone(wire stays backwards compatible), pure-Rustflate2/miniz_oxide+ browserCompressionStream "deflate-raw"produce identical bytes, 16 MiB inflated-size cap rejects zip bombs. Demonstrates the PR #1518 Lite05Wip unadvertised version variant works as intended — features land gated without wire exposure (no peer negotiates Lite05Wip yet because it’s omitted fromALPNSandVersions::all()). PR #1530 OPEN May 28 23:34 UTC “add REANNOUNCE; AnnounceConsumer yields (path, Announced)” (+716/−252) — implements moq-dev/drafts#23 atomic broadcast replacement (ANNOUNCE status2);AnnounceConsumernow yields(PathOwned, Announced)whereAnnouncedisActive/Reannounce/Ended. Backup promotion + shorter-hop-path arrival surface as singleReannouncedelivery rather than Ended-then-Active pair. Wire (lite) gatesAnnounceStatus::Reannounce = 2byreannounce_supported(version)(moq-lite-05+); IETF moq-transport has no REANNOUNCE so always splits to namespace_done + namespace. PR #1529 MERGED May 29 04:26 UTC “moq-ffi: streaming media import + cross-language interop smoke test” (+535/−7) — addsBroadcastProducer.publish_media_stream(format)so publisher can pipe encoder stdin straight in without manifest/NAL-splitting, plusjust test smokeorchestrator running rust↔python H.264 2×2 + negative control (4 POSITIVE + 2 NEGATIVE cells all PASS). Phase 2 = browser JS via Playwright; Phase 3 = Swift/Kotlin/Go; Phase 4 = CI workflow. Plus 20+ additional merges/opens: #1487 moq-mux catalog filter/target + Annex-B exporters (+1766/−109, 19 files); #1514 moq-net linger upstream subscriptions across consumer churn for moq-lite (+725/−164, 5-second linger via SubscribeUpdate priority=0 + FIN); #1517 moq-net stats aggregate per-node into single gzipped broadcast (+1092/−718); #1521 cap frame size at 16 MiB on receive path; #1522 docs route breaking changes to dev branch; #1523 moq-relay stop downgrading WebSocket clients to moq-lite-02; #1524 moq-gst assert no /nix/store leaks in postFixup + load-test in CI; #1525 js/net time out SUBSCRIBE_OK to surface browser stream-limit hangs; #1526 swift+kt re-export FFI + session.shutdown() + explicit Origin wiring; #1434 split OriginConsumer into cheap read handle + announcement cursor (opened May 21, finally MERGED); #1495 moq-mux replace anyhow with thiserror; #1473 moq-net runtime Timescale/Timestamp + container::Frame keeps source scale; #1533 add libmoq catalog producer + raw moq-net track API (+721/−0); #1534 moq-native fix broadcast linger test broken by AnnounceConsumer split; #1535 moq-relay scope mTLS grants to connection URL path (mTLS publisher dialing/demonow correctly announces underdemo/instead of cluster root; cluster mesh dialing/still gets cluster-wide access); #1537 moq-net stats take StatsConfig value type; #1538 flake nixfmt; #1539 js package version bump. OPEN follow-ons: #1527 moq-net advertise both qmux drafts on WebSocket fallback; #1532 duration-based skipping in container jitter buffer; #1536 moq-ffi auto-create Origin on connect + expose via MoqClientSession; #1540 moq-net make subscribe_track async blocking on SUBSCRIBE_OK; #1513 qmux version mapping still OPEN Day +2. Carry-forward: combined with May 22-28 packaging story (Homebrew + .deb + .rpm) + cluster discovery + preferred_address + qmux version mapping + mTLS path scoping, moq-dev/moq has structurally completed the “single-tree polyglot full-stack production-grade MoQ deployment” story 11 days before London — exceeds any other tracked implementation’s deployment-readiness story by structural margin.2026-05-28: 5 merges + 3 OPEN PRs + Issue #1499 CLOSED May 27 ~06:00 UTC → May 28 ~06:00 UTC. Theme: CI lint coverage + version-mapping prep for moq-lite-05 + WG-spec qmux pinning + cluster-topology clarification. PR #1515 MERGED “moq-mux: add seek(sequence) on importers for explicit group boundaries” (+221/−2) — adds
seek(sequence: u64)to everymoq-muximporter so callers can open the next group at an explicit sequence (joining mid-stream + signalling discontinuities); auto-rotate behavior unchanged when seek not invoked. PR #1518 MERGED May 27 21:26 UTC “moq-net: add Lite05Wip version variant (unadvertised)” (+60/−5) — reserves wire code0xff0dad05+ ALPN stringmoq-lite-05-wip+ RustLite05Wip/ JSDRAFT_05_WIPenums, deliberately omitted fromALPNSandVersions::all()so future moq-lite-05 features can land as gatedmatch versionarms without changing default server/client behavior. PR #1519 MERGED 23:24 UTC “ci: lint shell, workflows, TOML, Nix, and justfiles via nix devShell” (+973/−656) — wiresshellcheck+shfmt(19 shell scripts),actionlint(workflows),taplo(every TOML in-tree),nixfmt(flake.nix),just --fmt --unstable(17 justfiles) intojust check/just ci; closes the major unlinted surfaces in the repo. PR #1520 MERGED “moq-ffi: bump to 0.2.15” (+9/−2). PR #1513 OPEN “moq-net: map MoQ versions to required qmux versions” (+430/−116) — “The MoQ WG decided qmux’s draft version is tied to the moq-transport version: moq-transport-18 must ride on qmux-01, moq-transport-14..17 on qmux-00. moq-lite is unconstrained.” AddsQmuxVersionenum +Version::qmux_versionstable +Versions::qmux_alpnsforSec-WebSocket-Protocollist construction. First explicit moq-dev/moq commitment to the moq-transport ↔ qmux version-pinning decision. PR #1517 OPEN “moq-net(stats): aggregate per-node into a single gzipped broadcast” (+653/−684) — collapses per-(level × node) stats fan-out into single.stats/node/{name}broadcast, gzipped JSON map of cumulative counter snapshots. Issue #1499 (natmurella cluster discovery) CLOSED May 27 19:42 UTC by kixelated’s explanation: “The cluster nodes now proxy, so there can be multiple hops. San Jose → Texas → Virginia → London. Before, every node would connect to every other node directly (ex. San Jose → London). Hurting the cache efficiency. But yeah the downside is you need to manually specify the hop possibilities.” natmurella requests the old gossip-style behavior preserved as opt-in (kixelated invites a review of PR #1504 if so). Carry-forward: PR #1513 + PR #1518 are paired moves staging moq-lite-05 — the unadvertised version variant lets feature work land gated without wire exposure, the qmux mapping table closes the WG-decided spec coordination gap with concrete code.2026-05-27: 5 merges + 4 OPEN PRs + 1 new external issue May 26 ~06:00 UTC → May 27 ~06:00 UTC. Theme: operator-quality features + 4 more PRs dogfooding the PR #1503 AI Attribution H2 norm. Headline: PR #1512 MERGED 17:20 UTC “moq-native: advertise QUIC preferred_address in the server config” (+81/−0, 3 files) — adds
preferred_v4/preferred_v6fields tomoq_native::ServerConfig(CLI / env / TOML), plumbs toquinn::ServerConfig::preferred_address_v{4,6}so QUIC’s RFC 9000 §9.6preferred_addresstransport parameter is announced during handshake. Unlocks a clean BGP anycast deployment shape: anycast/24handshake target + per-host unicastpreferred_address, “steady-state connections pin to the unicast IP and survive BGP reconvergence, an overloaded host can withdraw the anycast route from BGP without dropping existing connections”. Chrome M131+ (Nov 2024) on by default with ~99% migration success per Google’s measurements. PR #1513 / #1514 / #1515 OPEN all with(Written by Claude)markers: #1513 “moq-net: map MoQ versions to required qmux versions” (+171/−0) buildsQmuxVersionenum +Version::qmux_versionstable somoq-transport-18 ↔ qmux-01andmoq-transport-14..17 ↔ qmux-00mappings are spec-grounded (moq-lite keeps both qmux-00+qmux-01 for back-compat); #1514 “moq-net: linger upstream subscriptions across consumer churn (moq-lite)” (+397/−95, 5 files) keeps an upstreamTrackProduceralive for up to 5 seconds after the last consumer drops, sendsSubscribeUpdate(priority=0)+ FIN and waits for Complete / Cancelled / Reused; first moq-lite-only operator-quality feature since the moq-lite → moq-net rename May 19-20 (no IETF MOQT counterpart); #1515 “moq-mux: add seek(sequence) on importers for explicit group boundaries” (+221/−2, 12 files) addsseek(sequence: u64)to everymoq-muximporter so callers can open the next group at an explicit sequence — useful for joining mid-stream and signalling discontinuities. 4 PRs in 24h carrying the disclaimer (#1512 / #1513 / #1514 / #1515) — continues dogfooding the PR #1503 AI Attribution H2 norm. External-contributor merges: PR #1510 by diegonieto (Diego Nieto, first contribution) “docs: update GStreamer text” MERGED 22:27 UTC; PR #1511 kixelated CHANGELOG repair fixes release-plz blocker. New Issue #1516 by danrossi May 27 05:12 UTC “Cargo project audit checks — Crates is now vulnerable to supply chain attacks” referencescargo-vetMozilla tool — danrossi’s third external-user issue in 5 days (#1501 May 25 + #1516 May 27), follow-on to PR #1486 cargo-deny May 24. Carry-forward: combined with the May 22-25 packaging story (Homebrew + .deb + .rpm + Cloudflare Worker apt/rpm hosts), the May 24 cluster-discovery PR #1504 still-open in-flight, and the May 26 preferred_address landing, moq-dev/moq has moved from “kixelated’s research prototype” to “first MoQ implementation operationally on par with HTTP/3 CDN deployments” within a 5-day window.2026-05-26: Lighter ~8-PR day May 25 06:00 UTC → May 26 ~05:00 UTC breaks the 3-consecutive-overnight-wave pattern (May 22-25 ~40 merges). Theme shifts from feature-velocity to convention-tightening + first downstream-regression-feedback resolution + Swift release-pipeline shakedown. Headlines: PR #1504 OPEN “moq-relay: restore gossip-style cluster discovery via —cluster-node” (+797/−121, 7 files) opened May 25 17:18 UTC ~12 hours after Issue #1499 by natmurella “old leaf discovery strategy gone?” May 25 05:02 UTC — re-introduces
--cluster-node <self-url>publishing.internal/origins/<url>placeholder broadcasts so peers reached via--cluster-connectauto-discover; pairs with new.block(prefix)view that refuses publishes and hides announces under a prefix (applied to non-mTLS sessions so JWT and anonymous clients can never see.internal/*). First downstream-user-flagged regression in the May 22-25 refactor wave to get a same-day fix-in-flight. PR #1503 MERGED May 25 17:25 UTC “docs(claude): tighten conventions for cross-package sync, tests, comments” (+33/−3 toCLAUDE.md) — AI Attribution promoted from a buried Comment Conventions bullet into its own H2 covering “LLM-authored prose visible to humans” with an explicit no-tag list (code, doc comments,/docpages exempt to avoid disclosure noise). Third evolution of the LLM-disclaimer norm in 4 days (PR #1469 source-code rule May 23 → PR-body dogfooding via #1484 + #1494 May 24 → policy H2 promotion May 25). PR #1503 also adds Cross-Package Sync table (touchingmoq-ffirequires updatinglibmoq/py/moq-rs/Swift/Kotlin docs), Testing Approach defaults to end-to-end, Refactor As You Go rule (4+ args or repeated trio = struct in same PR), divan Benchmarks convention with before/after numbers in PR description. PR #1509 MERGED May 26 04:26 UTC “moq-native(jemalloc): drop runtime activation; fixes moq-boy startup crash” (+11/−10) — 28-minute self-issue-to-merge cycle (Issue #1507 by kixelated May 26 03:58 UTC). Root cause: dead-code branch attempting to flipprof.active=trueat runtime fails with EINVAL unlessMALLOC_CONF=prof:truewas set at process start; moq-relay’s systemd unit always sets it (tookOk(true)and never noticed); moq-boy doesn’t, took the brokenOk(false)and crashed on every startup. Swift release-pipeline shakedown: PR #1502 (decouple release manifest from devPackage.swift, gate publish on SPM resolve) + PR #1505 (manualmoq-ffi0.2.14 bump to exercise the pipeline end-to-end —release-plzdoesn’t detect binary-onlycdylib/staticlibcrate changes viacargo semver-checks) + PR #1506 (CI fix: SPM derives path-based package identity from final path componentmoq-ffi-0.2.14-swift, notname:field — symlink staged dir to fix dependency lookup). New external-user issues May 25-26: #1500 mirakae HLS fMP4 audio produces ~47 MoQ groups/s with no aggregation control (1 group per AAC frame), #1501 danrossi JSConnection.reload()silently hangs on relay unavailable with no error promise or retry, #1508 kixelated self-fileReconnectdoesn’t retry on DNS failure (still open). Pattern: 3 of 5 issues from external production-deploying users in 24h — adoption signal from operators willing to file actionable bug reports against May 22-25 refactor breakage.2026-05-25: Third consecutive overnight merge wave May 24 06:00 UTC → May 25 ~05:00 UTC (~17 PR events, all kixelated plus 2 external-contributor merges; three-day cumulative ~40 merges). Theme is audio FFI gap closure + Rust/JS namespace cleanup + first dogfooding of the PR #1469 LLM-disclaimer norm. Headline: PR #1484 MERGED May 24 22:41 UTC “feat: add moq-audio crate, raw-audio FFI, and rename moq-codec to moq-video” (+2576/−71 across 39 files) — new
rs/moq-audiocrate (Opus encode/decode of raw PCM overmoq-mux+hang, rubato resampler so callers can pass any WebCodecsAudioData.format/rate, genericEncoder/Decodertraits with Opus impl at fixed 20 ms frames);moq-ffi+libmoqgain raw-audio publish/subscribe APIs so Python/Swift/Kotlin/C callers can drive a microphone or speaker without bringing their own codec library. ~340 KB stripped size impact for bundled libopus. Renames emptymoq-codecplaceholder →moq-video(squat the name; pre-positions video-side raw-frame FFI symmetry). PR description ends with(Written by Claude)— first PR to apply the PR #1469 LLM-disclaimer norm in practice, <22h after that norm merged. PR #1492 “Remove moq-lite stub crate” MERGED 23:00 UTC (+3/−479) — completes the moq-lite → moq-net Rust rename. PR #1498 “js: re-export @moq/net as Net (deprecate Lite/Moq aliases)” MERGED May 25 01:36 UTC — JS namespace standardisation across@moq/publish+@moq/watch+@moq/hang. PR #1494 “moq-clock: convert to a moq-native example” MERGED May 25 00:02 UTC (+224/−764) — deletes standaloners/moq-clockcrate, moves source tors/moq-native/examples/clock.rs; also ends with(Written by Claude)disclaimer. External-contributor double-merge by metapox (taku): PR #1396 + PR #1397 implement SUBSCRIBE_UPDATE API for JS subscriber/publisher (track.updatePriority()) closing Issue #1363 from Apr 30 (24-day cycle); motivated by metapox’s own moq-multicam project where camera switching needs instant priority changes without re-subscribe. PR #1485 continues the pre-1.0 API freeze (#[non_exhaustive]on hang catalog config). PR #1486 addscargo-denyCI audit. Plus 8 smaller merges (#1480/#1481 dependabot, #1482 docs lib/bin, #1483 SIGNING_KEY reuse, #1488 Kotlin release fix, 1491 stats fixes, #1490 token-cli stdin/stdout, #1497 ci rpm). Open follow-ons: #1473 (moq-net runtime Timescale/Timestamp), #1487 (moq-mux catalog filter + Annex-B exporters), #1495 (moq-mux thiserror), #1371 (hang cross-broadcast track refs). New Issue #1499 by natmurella May 25 05:02 UTC “old leaf discovery strategy gone?” — first downstream notice of an unannounced relay-topology behavior change post-refactor. Three-day cumulative pattern: theme-coherent waves (May 22-23 container-format, May 23-24 binding + distribution, May 24-25 audio FFI + cleanup + LLM-disclaimer dogfooding) rather than scattershot velocity. The May 22→25 sequence positions moq-dev/moq’s London pitch as “single-tree multi-language full-stack with native codec helpers”: Python apps can drive a microphone end-to-end viamoq_publish_raw_audio_opus(...)without ever touching a codec library or WebCodecs.2026-05-24: Second consecutive ~14-PR overnight merge wave May 23 17:56 UTC → May 24 01:30 UTC (all kixelated; two-day total ~24 merges). Theme shifts from May 22-23’s container-format triple-merge to distribution + binding + API hardening. Headline merges: PR #1470 MERGED May 23 23:02 UTC “feat(go): add Go bindings for moq-ffi” (+827/−4, 13 files,
uniffi-bindgen-gov0.7.1+v0.31.0) — Go added as 4th FFI language target (after Python + Kotlin + Swift); 5-target CI build matrix (linux x86_64/arm64, darwin x86_64/arm64, windows x86_64); consumer modelgo get github.com/moq-dev/moq-go@vX.Y.Zvia mirror repo. Net language coverage: Rust + TypeScript + Python + Swift + Kotlin + Go from a single tree — first 6-language MoQ stack. PR #1417 MERGED 19:56 UTC “Add MoQ server API with session acceptance and handshake” (+1120/−5, 12 files) — newMoqServer+MoqRequesttypes in moq-ffi expose server role through all FFI bindings (previously client-only); Pythonserver_smoke.pyexample demonstrates Python-as-MoQ-server. PR #1469 MERGED 20:34 UTC “docs: require LLM disclaimer on AI-authored comments” (+2/−0 inCLAUDE.md) — first project-level mandatory-LLM-disclaimer norm in any tracked MoQ codebase, direct institutional response to May 22 moq-wg/moq-transport Issue #1636 afrind AI-hallucination incident. PR #1456 Homebrew tap + PR #1457 .deb/.rpm packaging (apt.moq.dev / rpm.moq.dev Cloudflare Workers) — first Linux/macOS binary distribution channels for moq binaries. PR #1452 moq-mux restructured into per-codec + per-container modules (extensibility for future containers like m2ts). PR #1472 Tighten public APIs ahead of release:#[non_exhaustive]+ builder pattern — pre-1.0 API freeze signal. Plus 6 CI fixes + 5 dependabot bumps. Open follow-ons: PR #1473 “moq-net: runtime Timescale/Timestamp; container::Frame keeps source scale” (follow-on to #1439). Net distribution model post-May 24: cargo / npm / pip / PyPI / brew / apt / dnf / SPM + Maven (in-flight) +go get— 9 distribution channels in production or in-flight, unique footprint for an MoQ implementation.2026-05-23: Overnight 10-PR merge wave May 22 18:24 UTC → May 23 02:26 UTC (~8h), all kixelated — first impl with all THREE major MoQ media container formats (CMAF/fMP4 + LOC + MKV/WebM) unified on
main. Headline merges: PR #1444 MERGED 21:12 UTC “feat: Unified CMSF/Hang pipeline (cleanup of #1429)” (+1278/−14, kixelated) — kixelated forks AWS’s #1429, strips out-of-scope C API + caller-driven group boundaries, merges the MSF-catalog core; third AWS-vs-kixelated design-cycle resolution in 9 days (#1413 close → #1408→#1429 50% shrink → #1429→#1444 33% shrink; total AWS net code in merged result ~25-30% of original #1408 scope). PR #1388 MERGED 22:53 UTC “Add Low Overhead Container (LOC) frame format support” (+844/−16, 30 files) — first LOC implementation in moq-dev/moq stack: newmoq-locRust crate +@moq/locJS package, integrated intomoq-mux+ hang catalog + watch player; votes-with-code for moq-transport-18 §15.8-2 property type assignments (TIMESTAMP=0x06, TIMESCALE=0x08) over the conflicting draft-ietf-moq-loc-02 values (TIMESTAMP=0x02) — resolves the cross-spec coordination gap surfaced by moq-wg/loc Issue #20 on the implementation side. PR #1438 MERGED 22:44 UTC “Add Matroska/WebM import and export support” (+3087/−66, 18 files) — bidirectional MKV/WebM viars/moq-mux/src/import/mkv.rs+rs/moq-mux/src/export/mkv.rs; supports H.264/H.265/VP8/VP9/AV1 video + AAC/Opus audio; first non-CMAF, non-LOC container packaging shipped in any tracked MoQ implementation, predating any individual draft (nodraft-*-mkv-moqexists). PR #1442 MERGED 20:56 UTC “Add stats via MoQ broadcasts” (+1492/−70) — per-connection stats dogfooded over MoQ itself; observability as a broadcast. Plus 6 smaller merges: PR #1440 (hang re-emit deprecated CMAF timescale/trackId), PR #1441 (direnv auto-GC), PR #1443 (reconnect timeout mandatory 5min default), PR #1394 (auto-detect catalog format from broadcast name extension), PR #1446 (Opus encoder kind — voice vs. music presets), PR #1447 (tighten moq-ffi release pipeline ahead of first publish). Aggregate: ~+7160/−355 across ~120 files in 8 hours, single-contributor. Plus PR #1448 “swift: wire SPM mirror publish (Phase A)” OPENED May 23 03:08 UTC. Net structural state: moq-dev/moq is now positioned to be the only implementation able to interop on all three packaging values (cmaf,loc, andmkv) by the London hackathon;hang::Catalogserves as single in-memory IR serializable to MSF or Hang catalog formats; MKV-out exporter means standard players (VLC, MPV, browsers) can consume MoQ broadcasts via the exporter without needing a MoQ-aware player — a structural new deployment shape.2026-05-22: Record-tying May 21 PR cluster — 7 merges + 4 opens + 1 self-close + 1 close in 24h. Headline: PR #1432 MERGED May 22 00:38:56 UTC by kixelated — “Add Swift and Kotlin FFI wrappers with packaging and publishing” (+1997/−569 across 48 files). Swift Package (iOS+macOS) + Kotlin libraries (Android+JVM) wrapping moq-ffi UniFFI bindings with idiomatic
AsyncSequence/Flowasync APIs. The timing is structurally significant: gazzy’s Moqintosh iOS Swift client was announced on#moqMay 20 15:22 UTC as a “Pure Swift Client Only draft-14 based” implementation; PR #1432 was opened 24h later with draft-18 (current main) and both client + relay capabilities — positions moq-dev/moq as the first-party native iOS/macOS/Android/JVM stack in a way that gazzy’s exploratory effort was not. Plus PR #1438 OPENED May 21 22:47 UTC — “Add Matroska/WebM import and export support” (+3092/−39, kixelated) — bidirectional MKV/WebM viars/moq-mux/src/import/mkv.rsandrs/moq-mux/src/export/mkv.rs, third container muxer/demuxer pipeline after CMAF and CMSF (PR #1429 still OPEN). Plus PR #1439 OPENED May 21 23:10 UTC — “Add per-track timescale and frame timestamps to moq-lite” (+936/−546) —Timescale<const SCALE: u64>type alias → concreteTimestampstruct carrying raw value + scale; per-track timescale negotiation via SUBSCRIBE_OK (a media-sync mechanism that moq-transport draft-18 does not have at the same level — moq-lite-leads-moq-transport-follows precedent). Plus PR #1437 self-closed unmerged May 21 22:20 UTC by kixelated 1h after open — “review feedback led us to the right architectural conclusion: the avc3/hev1 importers shouldn’t transcode their own output. If a downstream consumer needs avc1/hvc1, that’s the consumer’s transcode and belongs in an exporter layer.” — third “right architectural conclusion” self-redirect in 7 days (#1413 close → AVC fallback as separate PR; #1408 close → #1429 unified pipeline; #1437 close → exporter-layer transcode). Plus 5 other merges: PR #1358 (Origin rewrite, CLOSED after 23 days stale, 15:40 UTC); PR #1425 / #1431 (release-plz autos); PR #1433 “Replace mpsc with conducer for coalesced origin consumer updates” (kixelated, 18:43 UTC); PR #1435 “Claude/cargo update 3 fx or” (kixelated, 20:19 UTC); +2 external-contributor merges — PR #1410 (YogiSotho, hide buffering overlay while offline) + PR #1362 (Qizot, audio encoder reconfiguration, open since Apr 29, 23 days). Open end-of-day: PR #1389 (stats, 18-day-stale activity signal touched May 22 05:52 UTC), PR #1429 (AWS CMSF awaiting review since May 20), PR #1434 (Split OriginConsumer, follow-on to #1433), PR #1436 (next release), PR #1438 (Matroska), PR #1439 (per-track timescale). Net cadence shift frommerge-then-resttomerge-and-open-stack— kixelated is staging the next 4-5 PRs while #1429 ripens.2026-05-21: PR #1428 (moq-lite → moq-net rename) MERGED May 20 14:07:45 UTC (+1230/−939, kixelated).
moq-net v0.1.0shipped on crates.io / npm / PyPI; deprecation shims (moq-liteRust no-update,@moq/liteJS runtime warning +@deprecated,py/moq-liteDeprecationWarning) all shipped intact. 2-day open-to-merge cycle (May 18 22:31 → May 20 14:07 UTC). Plus AWS files PR #1429 May 20 08:37:20 UTC: “feat: Unified CMSF/Hang pipeline” (+1969/−12, ksletmoe-aws, OPEN, “Supersedes #1408, addressing all review feedback”). PR #1408 simultaneously CLOSED unmerged by AWS at 08:37:27 UTC. PR #1429’s framing — “the key insight: CMSF is CMAF with a different catalog format” — explicitly accepts kixelated’s “support both via intermediate representation” feedback, usinghang::Catalogas the IR and a single pipeline serializing to two catalog formats. Net delta: −1937 LOC (3906 → 1969 addition count, 50.4% size reduction). Second AWS-vs-kixelated design-cycle resolution in 7 days, both ending with AWS re-architecting toward kixelated’s preferred shape:
- #1413 (May 16 → 18 CLOSED unmerged): AWS transport-layer encoder-failover stitching → kixelated “stitch at application level via SUBSCRIBE_NAMESPACE/announced” → AWS re-files AVC fallback separately
- #1408 → #1429 (May 14 → 20): AWS parallel CMSF pipeline (+3906/−458, 2400 lines duplicated CMAF logic) → kixelated “use intermediate representation, don’t copy-paste” → AWS re-files as #1429 (+1969/−12, zero duplication)
Plus kixelated’s long-stalled PR #1358 “rewrite Origin as poll-driven, conducer-based model” touched May 20 16:40 UTC (22-day-stale architectural PR opened Apr 28, signs of life — possible next-up after #1429 lands).
2026-05-19:
moq-litelibrary renamed tomoq-net(PR #1428 OPEN, kixelated, +1230/−939, “rename moq-lite package to moq-net”). Package renames across Rust (rs/moq-lite→rs/moq-netv0.1.0), JS (js/lite/@moq/lite→js/net/@moq/netv0.1.0), and Python (py/moq-lite→py/moq-netv0.1.0); all 3 old packages kept as deprecation shims. Protocol identifiers UNCHANGED: ALPNsmoq-lite-04, the internallite/submodule, spec URLs. The rename separates library identity from wire-protocol identity — “this is the networking layer; at session setup it negotiates either the moq-lite or moq-transport wire protocol”. Structural admission that moq-dev/moq has been a dual-protocol implementation since the draft-18 PR #1418 landed. Plus 7-PR cluster May 18 06:00 UTC → 23:43 UTC (PRs #1420–#1428): cluster loop detection (#1420), web-transport-iroh 0.4 bump (#1421), 2 release-plz auto-PRs (#1422 / #1425), Ended-tolerance hardening (#1423), moq-boy CI cache (#1424), probe stream error scoping (#1426 — Generated with Claude Code, +73/−53), AnnounceInterest.exclude_hop u53 overflow fix (#1427). Plus PR #1413 CLOSED unmerged May 18 23:43 UTC after kixelated rejected transport-layer encoder-failover stitching: “Sequences numbers should be += 1 just like HLS/DASH. I strongly disagree with the IETF drafts that allow, and even encourage, sequence number gaps… My thought process here is that you explicitly make two separate broadcasts and have the player switch between them. … It’s a lot less gross to stitch at the application level than stitching at the transport level.” AWS accepted, will re-file AVC description fix separately. Plus Luke Curley deployed 14-edge-node hop-based routing oncdn.moq.dev/anon(vs 13 edges the morning of May 18) and announced cdn.moq.dev/anon now claims draft-18 wire support (untested).2026-05-18: moq-dev/moq becomes first open-source implementation to ship draft-ietf-moq-transport-18 — PR #1418 MERGED May 18 05:08 UTC by kixelated (+1431/−477,
Co-authored-by: Claude). 6 days after draft-18 publication May 12 — fastest implementation turnaround the wiki has tracked for any major draft revision (draft-17 took ~3 weeks; draft-16 took ~2 weeks). Wire version0xff000012/ ALPN"moqt-18"; spec-driven changes: varint 7-byte form (#1595), SUBGROUP_HEADER FIRST_OBJECT bit (#1618), Request ID removed from PUBLISH_NAMESPACE/SUBSCRIBE (#1615), SUBSCRIBE_TRACKS rejected (#1542), optional trailing Request ID in GOAWAY (#1559). Intentionally NOT changed: LARGEST_OBJECT (#1621) — “we lie on purpose — honoring the new MUST is strictly worse for live latency” (first explicit spec non-compliance documented in a moq-lite PR body); Track Properties on REQUEST_OK (#1576) and mandatory-to-understand track extensions (#1509) read-and-dropped. Refactor switches version matches from “explicit newest” to “newest defaults forward” — Draft19 will inherit Draft18 unless explicitly opted out. Plus 4 more merges in the 3h17m window: PR #1414 audio frame-per-group (01:51 UTC), PR #1412 SolidJS → vanilla Web Components migration (02:37 UTC, +1366/−2234), PR #1411 pixel budget ABR (03:42 UTC), PR #1419 BEM CSS cleanup (04:44 UTC). Largest single-window merge volume in 2026 by PR count and LOC. Plus 2 new PRs opened May 18 06:00 UTC: PR #1420 cluster loop detection (+67/−18) fixing a ~120 MB/hr memory leak on a live nanode relay (phantomanon/la-cbs/anon/la-nbcpublishers re-announcing through 13-edge mesh, bouncing 32× before MAX_HOPS drop) — significant signal that moq-dev/moq is running production-shaped traffic; PR #1421 web-transport-iroh 0.4 bump (+477/−227) to unbreakcargo updateafter pkcs8 0.11.0 stable changedError::KeyMalformedshape.2026-05-17: kixelated reverts audio frame batching for real-time latency — PR #1414 OPENED May 16 20:29 UTC (luke-curley, +17/−50), “audio: send each frame as its own group”. Reverses ~100ms audio group batching back to one-frame-per-group across the browser publish encoder + Rust opus/aac mux importers: “For real-time use cases, that bounds end-to-end latency at the group boundary since the relay cannot forward a group until it is closed.” Codec PLC (Opus PLC, AAC PLC) handles individual frame drops. Second moq-lite design choice this month reversed in the “go back to the more obvious primitive” direction (the other: PR #1385 May 6 reverted PR #1356
insert_trackAPI change). Pattern reads as kixelated tightening real-time semantics ahead of the London hackathon. Also: PR #1405 (Karolk99 solid-js peerDep) CLOSED unmerged May 16 11:36 UTC — superseded by the SolidJS-to-Web-Components migration in PR #1412 that eliminates the Solid dependency entirely. No new merges to main since PR #1395 May 15 16:48 UTC.2026-05-16: Second AWS contribution within 24 hours of #1408 — ksletmoe-aws opened PR #1413 at 00:50 UTC, “fix(hang/consumer, watch/decoder): handle non-sequential groups and AVC description fallback” (+68/−14). Fixes two real bugs surfaced while running CMSF/EML producers against
moq-watch: (1) consumer assumed group sequences increment by 1, but CMSF/EML uses epoch-based sequences with large gaps (e.g., 85386781784064 → 85386781832192), causing every group transition to go through the latency-skip path → ~1s audio underflows and choppy playback; (2) WebCodecs rejects AVC frames without a description, so a fallback is needed when the MSF/CMSF catalog doesn’t carry one. AWS is now the highest-touch external contributor of the May 14–16 cycle. Spec-side resolution path for the same bug class: moq-wg/msf PR #157 reached editorial agreement May 16 19:08 UTC on “each subsequent Group ID SHOULD increase by 1. Any intentional gaps signaled using the MOQT Prior Group ID Gap Extension header” (kixelated “SHOULD for both” weakening).2026-05-15: luke-curley merges 8 PRs in 24 hours (May 14 16:45 UTC → May 15 16:48 UTC) — including PR #1395 (moq-cli rename), PR #1398 (Qizot activity signals), PR #1404 (Qizot catalog fix), PR #1409 (danrossi new-contributor Vite worker plugin). Same window opens 3 new PRs: PR #1410 (YogiSotho new contributor, buffering overlay fix), PR #1411 (kixelated pixel budget for ABR), PR #1412 (kixelated SolidJS → vanilla Web Components migration, net -868 LOC, removes
@moq/ui-corepackage). Largest 24-hour merge volume of 2026.2026-05-14: First AWS contribution to moq-dev/moq — ksletmoe-aws (Kevin Sletmoe) opened PR #1408 “feat(moq-mux, libmoq): add CMSF muxer, demuxer, and C API” (+3891/−457, largest single PR to the repo in 2026), bringing CMSF packaging and a C FFI surface (
libmoq) to the moq-dev stack alongside the existing Rust + TypeScript packages. Corporate-contributor footprint now spans Cloudflare, Nokia, Eyevinn, OpenMOQ, and AWS.
Language: Rust + TypeScript (monorepo) Maintainer: luke-curley GitHub: moq-dev/moq (was kixelated/moq-rs → kixelated/moq) Website: moq.dev Documentation: doc.moq.dev Slack: moq-rs (C09CG9V7A2Y) — shared channel, covers both this and moq-rs
Overview
Luke Curley’s original MOQ implementation, now a monorepo containing both Rust and TypeScript packages. Implements moq-lite, a simplified subset of the IETF moq-transport spec that prioritizes simplicity and practical deployment. Also includes Hang, a media-specific protocol layer on top of moq-lite (analogous to HLS/DASH) handling codecs, containers, and catalog management.
The project describes itself as “generic for any live data, not just media” though video streaming is the primary use case.
History
- 2022-06-29: Created as
kixelated/moq-rs— the original Rust MOQ implementation - 2023-05-24:
kixelated/moq-jscreated as a companion TypeScript library - ~2024-10: Mike English forked the codebase to create an IETF WG-aligned version (see moq-rs and moq-js)
- 2025-06-20:
kixelated/moq-jsarchived (“Moved to kixelated/moq. It’s much better now.“) - Later: Renamed/transferred to
moq-dev/moqas a combined Rust + TypeScript monorepo
The project diverged from strict IETF WG spec compliance when Luke pursued his own moq-lite design. It now has adapter shims for IETF MoQ WG drafts, enabling interop with IETF-aligned implementations.
Protocol
- moq-lite: Simplified transport protocol (Luke’s own spec, draft-lcurley-moq-lite-04)
- Hang: Media-specific encoding/streaming layer on top of moq-lite
- IETF adapter shims: Allow interop with IETF draft implementations (draft-14 through draft-18, since PR #1418 merged May 18; first open-source implementation to ship draft-18)
Rust Packages
moq-lite— core transport librarymoq-relay— server/relaymoq-token— authenticationmoq-native— QUIC helpers
TypeScript Packages (js/)
lite— browser-compatible moq-lite transporthang— Hang media layer (total rewrite, not derived from kixelated/moq-js)watch— viewer/subscriberpublish— publisherui-core— UI componentssignals,clock,common,token— supporting packages
Public Infrastructure
cdn.moq.dev/anon— browser pub/sub testing (QUIC + WebTransport)- Interop docs: doc.moq.dev/concept/standard/interop.html
Recent Activity (April–May 2026)
May 7 → May 8 Day-3 burst: PR #1387 “Revert the revert” un-reverts PR #1356 within 24h of the revert; PR #1386 Firefox stats merged; PR #1388 (LOC frame format) and PR #1389 (stats aggregation) opened
luke-curley’s third consecutive day with material main-branch activity, all between 17:42 and 18:24 UTC May 7. Headline element: PR #1387 reverts the May 6 revert (PR #1385) of PR #1356 — the type-level cleanup is back in main with the underlying clone-counting bug fixed in-place. Plus a major new feature-PR pair: LOC frame format support and stats aggregation, both Claude-Code-generated.
- PR #1387 MERGED May 7 17:47:35 UTC by luke-curley (+167/−177) — Revert the revert. Body one-liner: “Actually fix the issue by incrementing the dynamic count when cloning.” Un-reverts PR #1385’s revert of PR #1356 (
insert_tracktakesTrackConsumer). Net effect: the May 5 type-level cleanup is back inmain, with the underlying clone-counting bug now fixed in-place rather than by reverting the API change. Cycle: PR #1356 merged May 5 22:15 UTC → reverted via #1385 May 6 22:08 UTC (−24h) → reverted-back via #1387 May 7 17:47 UTC (+19h 39m). Total cycle 43h 32m. First merge → revert → revert-of-revert cycle onmainin moq-dev/moq history. - PR #1386 MERGED May 7 18:17:23 UTC by luke-curley (+72/−177) — @moq/watch: source network stats from the connection, not navigator. Final shape +72/−177 (vs opened-shape +88/−130 on May 6 — net deletes more code than originally drafted). Resolves the Firefox-
navigator.connection-unavailable gap. Second Firefox-compat PR to land in 3 days; sibling PR #1307 (Lite03+ via legacy SETUP) still open. - PR #1388 OPENED May 7 17:42:06 UTC by luke-curley (+799/−17, OPEN) — Add Low Overhead Container (LOC) frame format support. First adoption of an IETF-spec media container format in moq-dev/moq alongside its native Hang stack. New
moq-locRust crate +@moq/locJS package implementing encode/decode for the draft-ietf-moq-loc wire format; QUIC-style varint property block (delta-encoded type IDs0x06=timestamp,0x08=timescale) followed by raw codec payload. Catalog integration: hang catalog gainsContainer::Loc { timescale }(default 1,000,000 µs); audio source selection prioritizes LOC after legacy, before CMAF. Watch player audio/video decoders + MSE backends instantiate the appropriate LOC decoder based on catalog config. Per-frame timescale (0x08property) overrides catalog default. “Even-typed properties carry varint values; odd-typed properties carry length-prefixed bytes. Unknown properties are silently skipped on decode, never emitted on encode.” Body marked ”🤖 Generated with Claude Code”. - PR #1389 OPENED May 7 18:23:35 UTC by luke-curley (+1168/−39, OPEN) — Add stats aggregation and publishing for moq-lite sessions. New
Statsmodule (rs/moq-lite/src/stats.rs); per-broadcast and per-prefix stats published as.stats/<level>/<name>JSON broadcasts (1Hz snapshot, atomic counters withRelaxedordering, RAII guards record open/close + frames + bytes + groups). Hidden-path filtering: newPath::is_hidden()(segments starting with.) so stats infrastructure doesn’t recursively generate its own stats traffic;OriginConsumer::announced()filters hidden paths, complementaryannounced_hidden()exposes them. NewStatsConfigin moq-relay (name+levelsfor per-prefix bucketing depth). API surface:Client::with_stats()/Server::with_stats()builders. Body marked ”🤖 Generated with Claude Code”. Same problem domain as the May 5-closed PR #853 (fcancela’s “Minimal observability metrics”, +1261/−38) — Luke’s reformulation lands as a 1168-line opening within 2 days, occupying adjacent design space with three novel mechanisms (in-band stats broadcasts, hidden-path filtering, per-prefix bucketing). - PR #1338 updated May 7 18:32 UTC —
chore: release(moq-bot[bot]). Auto-bumped after #1387 + #1386 merges; the day-1 revert + day-3 revert-of-revert net no-op rolls forward, plus the Firefox stats fix. - PR #1374 (Lite05 DATAGRAMS) updated May 7 19:25 UTC — still open, Day +3 since open. No movement towards merge.
- PR #853 — note: closed-not-merged on May 5; received an automated cross-reference timestamp ping May 7 17:50 UTC when PR #1389 opened (PR #1389 occupies adjacent design space). State remains CLOSED.
Net: Day-3 lands two merges that net to ~zero net code change (revert-of-revert + Firefox stats refactor) but unwind yesterday’s revert with the underlying bug fixed in-place. Plus opens two large new feature PRs (+799 LOC frame format, +1168 stats aggregation), both Claude-Code-generated. Combined moq-dev/moq diff opened in the 4-day May 4 → May 7 window: PR #1374 (Lite05) +1615/−7 + PR #1378 +295/−240 + PR #1388 +799/−17 + PR #1389 +1168/−39 = +3877/−303 added across 4 Claude-Code-generated PRs, of which only PR #1378 has merged.
May 6 → May 7 Evening burst day-2: PR #1385 REVERTS yesterday’s PR #1356 within 24h; PR 1383 polish merges; PR #1386 opened (Firefox stats source)
luke-curley returns May 6 evening (~20:00–22:30 UTC) for a second burst building on the May 5 seven-PR run. Notable element: PR #1385 reverts PR #1356 (insert_track takes TrackConsumer) ~24 hours after it merged — first merge-then-revert-within-24h on main since the Apr 30 → May 2 fetch_group cycle.
- PR #1382 MERGED May 6 20:03 UTC by luke-curley (+3/−0) — Unignore moq-mux test fixtures. Test-fixture file inclusion fix following the moq-mux backport (PR #1341). Cosmetic.
- PR #1383 MERGED May 6 21:09 UTC by luke-curley (+15/−5) — @moq/watch: don’t tear down a broadcast when an unrelated path flaps. Targeted fix in the TypeScript
watchpackage for spurious broadcast tear-downs when an unrelated subscription/announcement path changes state. - Issue #1384 OPENED May 6 20:41 UTC by luke-curley — @moq/signals improvements. Tracks reactive-signals layer cleanup in the TypeScript
signalspackage. - PR #1386 OPENED May 6 21:51 UTC by luke-curley (+88/−130, OPEN) — @moq/watch: source network stats from the connection, not navigator. Replaces use of
navigator.connection(which Firefox doesn’t expose, and which other browsers report unreliably) with stats sourced directly from the QUIC connection object. Second Firefox-compatibility-affecting PR in two days alongside the still-open PR #1307 legacy SETUP fallback. - PR #1385 MERGED May 6 22:08 UTC by luke-curley (+160/−117) — Revert “moq-lite: switch insert_track to take TrackConsumer (#1356)“. Body is the standard auto-generated revert text (“This reverts commit
b611acd1.”). Backs out PR #1356 ~24 hours after it merged on May 5 22:15 UTC — the type-level cleanup that was supposed to land theTrackConsumer::produce()removal from #1300 has been pulled back. No follow-up issue or new PR yet explaining the regression that prompted the revert; the next release-train PR (#1338) will roll without that change. - PR #1338 updated May 6 22:24 UTC —
chore: release(moq-bot[bot]). Auto-bumped after the day’s merges; will drop PR #1356 from the staging release line. - PR #1358 updated May 6 21:32 UTC — Origin poll-driven rewrite, still open.
- PR #1149 updated May 6 19:06 UTC — catalog registry, still open.
- Issue #1364 CLOSED May 6 06:00 UTC — Dan Rossi’s “Cloudflare Relay” question. No comment on close.
- PR #1374 (Lite05 DATAGRAMS) — no movement today (Day +2 since open).
- PR #1307 (Firefox legacy-SETUP fallback) — no movement today.
Net: Day-2 follow-up to the May 5 burst lands two small fixes (#1382, #1383) and a notable revert (#1385) that pulls PR #1356 back out of main within 24 hours of landing. PR #1386 (network-stats source change) is the day’s only new open PR; it continues a Firefox-compatibility theme alongside PR #1307. Lite05 DATAGRAMS PR #1374 remains untouched for a 2nd day.
May 5 → May 6 Luke’s biggest single-day merge run; 7 PRs land (incl. PR #1341 moq-mux backport, PR #1378 API tightening); PR #1307 OPENED for Firefox legacy-SETUP fallback
luke-curley’s most active day on main since the Apr 29–30 wave. Seven PRs merged in a ~9-hour window, ranging from a 2588-line moq-mux backport to a single-line social-image swap. The Lite05 PR #1374 itself remains open — today’s work prepares the surrounding API surface.
- PR #1377 MERGED May 5 17:17:23 UTC by luke-curley (+52/−1, 4 files; closes #1376) — fix(config): accept single string or array for TOML list fields. Several relay TLS/auth fields are typed as
Vec<_>but were documented indemo/relay/prod.tomlas plain strings, so loading those configs failed with “invalid type: string, expected a sequence.” Appliesserde_with::OneOrMany<_, PreferMany>(already used elsewhere) so a bare string and a TOML array both deserialize into aVec. Affected fields:server.tls.{cert,key,generate,root},tls.root(ClientTls),web.https.root,auth.tls.root,auth.domains. Production-fixing config PR. - PR #1380 MERGED May 5 18:51:38 UTC by luke-curley (+130/−127, 7 files) — moq-lite: port Origin API renames from #1358. Stacked on #1378. Ports the public-API renames from #1358 without the new state model — the existing
OriginNodetree,web_async::spawncleanup, and tokio mpsc fan-out stay untouched. Renames:OriginProducer::publish_only→scope;OriginConsumer::consume_only→scope;OriginConsumer::try_consume_broadcast→get_broadcast;OriginProducer::consume_onlyandtry_consume_broadcastdropped (callers writeproducer.consume().scope(p)/.get_broadcast(p)instead). Picks up the consumer-facing API surface from PR #1358’s massive Origin rewrite without merging the substrate change. - PR #1379 MERGED May 5 19:22:22 UTC by luke-curley (+110/−11, 3 files) — Fix DNS resolution to prefer matching address family. Cross-platform stability fix: when DNS returns multiple addresses, new
pick_addr()utility inutil.rsselects an entry whose family (IPv4/IPv6) matches the local socket. Falls back to the first entry if no family match exists. ResolvesAddrNotAvailableerrors on Windows where sockets are often bound to a single address family. - PR #1381 MERGED May 5 20:03:32 UTC — Update OG image with proper dimensions for social sharing. Cosmetic.
- PR #1378 MERGED May 5 20:08:31 UTC by luke-curley (+295/−240 across 20 files) — moq-lite: tighten public API surface and remove deprecated methods. Body: “Make
ALPN_*constants,MAX_HOPS, the coding module, and the encode_params!/decode_params! macros crate-private; re-exportDecodeError/EncodeError/BoundsExceededfrom the crate root. Drop deprecatedTrackProducer::close,TrackConsumer::poll_next_group,TrackConsumer::next_group(alias),FrameProducer::write_chunk, and theOriginProducer/OriginConsumer consume_broadcastmethods. The sync lookup is preserved astry_consume_broadcastfor callers (e.g. libmoq’s FFI) that genuinely need it. RenameTrackConsumer::next_group_ordered/poll_next_group_orderedback tonext_group/poll_next_groupnow that the deprecated aliases are gone…”cargo test -p moq-lite --lib278/278 pass. Body marked ”🤖 Generated with Claude Code”. Largest API-surface tightening of the moq-lite cycle; finalizes the deprecation queue accumulated since Lite03. - PR #1356 MERGED May 5 22:15:50 UTC by luke-curley (+117/−160, 7 files) — moq-lite: switch insert_track to take TrackConsumer. Body: “Change
BroadcastProducer::insert_trackto takeTrackConsumer(by value) instead of&TrackProducer. RemoveTrackConsumer::produce()from #1300 — it was added as a workaround that this change supersedes. AddTrackConsumer::weak()(pub(crate)) so the broadcast can derive itsTrackWeakfrom a consumer.” The&TrackProducerparameter was effectively a witness that some producer existed, but the API misleadingly suggested the broadcast was taking ownership of publishing rights. Lands the type-level cleanup that was in flight since Apr 28. - PR #1341 MERGED May 6 01:20:29 UTC by luke-curley (+2588/−3594 across 82 files) — moq-mux backport + dual-API cleanup. Largest moq-dev/moq merge of the post-NAB period. Backports the
moq-muxstructural refactor fromdevtomain, then collapses the dual API surface that grew during the merge into a single canonical one. Backport elements: module reorgmoq_mux::{import,export,container,convert}; catalog-sideContainer::Cmaf { init: Bytes }(init segment in the catalog) replacing the oldtimescale/track_idshape; all codec support always compiled (no per-codec feature flags); lazy track creation, dropped per-track stats/drift tracking, simplified producer lifecycle;Decoder→Framed,DecoderFormat→FramedFormat;convert::cmaf::Convertandconvert::hang::Convertfor in-process container rewriting;hangcatalog API:Audio::insert/Video::insert/remove,OrderedProducer,Container::Cmaf { init }schema. Cleanup commit5e6d5a3collapses parallel APIs inmoq_mux::export:OrderedConsumer<F: ContainerFormat>→Consumer<F: Container>;ContainerFormattrait →container::Containertrait;OrderedFrame(BufListpayload) →container::Frame(Bytespayload);OrderedMuxer<F>(staticVec<(name, OrderedConsumer)>) →Muxed(catalog-driven, handles track changes);export::Cmaf { timescale }→container::Cmaf { trak }. Net deletion of ~1000 lines despite being a backport — much of the dev branch’s transient API churn evaporates. ksletmoe-aws’s #1359 effectively flowed back into the codebase via this merge in a different shape. - PR #1307 updated May 5 21:45:24 UTC by luke-curley (+150/−13, 7 files; still open) — moq-lite: negotiate Lite03+ via legacy SETUP when ALPN is unavailable. Body: “Firefox’s WebTransport doesn’t expose an ALPN selection API, so clients there can never pick
moq-lite-03/moq-lite-04. Previously the fallback SETUP path (baremoqlALPN or no ALPN at all) only advertised[Lite02, Lite01, Draft14], stranding Firefox on Lite02. Extend the fallback to advertise every supported moq-lite version in the draft-14 SETUP versions list. When the peer selects Lite03+, gracefully close the bootstrap SETUP stream and run the rest of the session as if it had been ALPN-negotiated (no SessionInfo control messages).” Direct Firefox-WebTransport-compatibility fix. Mirrors change in Rust + TS client/server paths; adds unit + integration tests covering no-ALPN /moqlnegotiation of Lite03 and Lite04. - PR #1338 updated May 5 22:37 UTC —
chore: release(moq-bot[bot]). Staging PR for next moq-lite release; rolls in today’s merges. - PR #853 CLOSED unmerged May 5 21:45:47 UTC — fcancela’s “Minimal observability metrics (relay & client)” (+1261/−38, 29 files). Long-stale PR retired in housekeeping.
- PR #856 CLOSED unmerged May 5 21:45:33 UTC — ac-freeman’s “WIP: Delivery timeout” (+225/−54). Same housekeeping wave.
- PR #1374 updated May 5 16:17 UTC — Lite05 DATAGRAMS PR remains open, no merge today. Surrounding PRs (API tightening, Origin renames, config robustness) reduce the surface area Lite05 has to maintain compatibility with.
- PR #1371 updated May 5 16:07 UTC —
hang: cross-broadcast track referencesremains open.
Net: Six substantive merges (#1341, #1378, #1380, #1379, #1377, #1356) clean up moq-lite’s API surface, fix Windows DNS resolution, accept TOML config strings in list fields, and backport the entire moq-mux refactor with deletions. The two-PR housekeeping close (#853, #856) retires multi-year-old open PRs predating the moq-lite split. PR #1307 (Firefox legacy-SETUP fallback) is the day’s only new open PR. Lite05 (PR #1374) still on the open queue.
May 4 → May 5 Luke OPENS PR #1374 (DATAGRAMS control stream + QUIC datagram delivery, Lite05 wire version)
The biggest moq-lite wire-level addition since the protocol’s inception — opt-in unreliable datagram delivery as a brand-new wire version, opened in the late-night UTC hours.
- PR #1374 OPENED May 4 22:57:32 UTC by luke-curley — moq-lite: add DATAGRAMS control stream + QUIC datagram delivery (Lite05) (+1615/−7 across 21 files; both Rust and TypeScript libraries). Body: “New wire version Lite05 / DRAFT_05 (ALPN moq-lite-05, code 0xff0dad05) gating an opt-in unreliable delivery path.” Key elements:
- New
DATAGRAMSbidi control stream (0x6) parallel toSUBSCRIBE. Sharing the samesubscribe_idnamespace lets a single QUIC datagram body be routed by ID alone. - QUIC datagram body:
subscribe_id (i) | sequence (i) | payload (b), payload capped at 1200 B. Sequence number is preserved on the wire (ignored by Lite05 semantics) so a future moq-transport adapter can reuse the encoding. - 33 ms publisher-side cache; per-subscriber
max_latencyfilters stale entries on forward. “max_latency = 0 is strict: only fresh arrivals (no congestion-delayed retries).” - Public API: groups-mirroring —
TrackProducer.write_datagram/append_datagram,TrackConsumer.subscribe_datagrams→DatagramsConsumer. JS exposesTrack.writeDatagram/appendDatagram/recvDatagram/skipDatagramsToLatest. - Rust files:
lite/datagram.rs+model/datagram.rsadded;model/track.rs,lite/publisher.rs,lite/subscriber.rs, version +ControlTypeenums updated. - TS files:
lite/datagram.ts+datagram.tsadded;track.ts,lite/connection.ts,lite/publisher.ts,lite/subscriber.ts, version +StreamIdupdated. - Spec draft: section + Lite05 changelog entry live in the separate
moq-wg/moq-draftsrepo. - 17 new Rust tests, 12 new TS tests; manual relay round-trip and Lite04↔Lite05 cross-version sanity still pending. Out of scope: subscriber-configurable cache age, chunked datagrams >1200B, moq-transport adapter using sequence for ordering, migrating hang/publish/watch layers to use datagrams.
- PR body explicitly notes: ”🤖 Generated with Claude Code”.
- New
- PR #1356 updated May 4 23:10 UTC by luke-curley — moq-lite: switch insert_track to take TrackConsumer (+39/−93). Body: “The
&TrackProducerparameter was effectively a witness…TrackConsumeris the honest type for ‘I have a handle to this track.‘” RemovesTrackConsumer::produce()from #1300; addsTrackConsumer::weak()so the broadcast can derive itsTrackWeakfrom a consumer. - PR #1373 updated May 4 22:25 UTC by skirsten — playback stalls / frame-rate beating fix (still open, follow-up to PR #1367).
- PR #1341 updated May 4 22:24 UTC by luke-curley — Refactor media producers and simplify fMP4 CMAF passthrough (+3808/−2025 across 79 files). Module reorg
moq_mux::import→moq_mux::producer, removed feature gates (mp4/h264/h265/hls/av1/aac/opus), init-segments now base64-encoded ftyp+moov in catalog,Decoder→Framedrename. - PR #1359 CLOSED May 4 21:25 UTC (status: closed-not-merged). ksletmoe-aws’s unify Consumer across container formats did not land in the form opened.
- PR #1338 updated May 4 21:47 UTC —
chore: release(moq-bot) — staging release commits.
Net: PR #1374 introduces the Lite05 wire version, opening unreliable datagram delivery as a peer dimension to subgroup-stream delivery. The 33ms freshness cap is firm; per-subscriber max_latency is a novel knob. Lite05 spec text lives in moq-drafts (a separate repo not yet visible to the wiki crawler). Notable that this is a wire-version increment — earlier moq-lite changes typically remained inside Lite04.
May 3 → May 4 skirsten OPENS PR #1373 superseding own PR #1367 (pull-mode renderer); ksletmoe-aws revises PR #1359
A quieter-than-May-2 day: no new merges, no new Luke PRs, no new external-contributor issues. Two notable contributor turn-arounds.
- PR #1373 OPENED May 3 16:53:49 UTC by skirsten (+146/−144 across 6 files, @moq/watch: fix playback stalls and frame-rate beating, closes #1367). Body terse: “Detailed description of both fixes is in the commits.” Effectively supersedes skirsten’s own May 1 PR #1367 (pull-mode renderer for 144Hz+ Chrome at vsync). The closes-#1367 directive treats #1373 as the proper landing of that work, addressing two distinct symptoms (playback stalls + frame-rate beating) rather than just the original 144Hz issue. coderabbitai bot review (May 3 17:02): “No actionable comments were generated.” skirsten now has 4 PRs in the May 1–3 window (#1349 + #1365 merged; #1367 + #1373 open with #1373 superseding).
- PR #1359 revised May 3 04:30 UTC by ksletmoe-aws — now +1002/−1173 across 14 files (vs. earlier reading of +971/…). Author summary unchanged: “Replace the two separate consumer implementations (Legacy and CMAF) with a single generic
Consumerclass that accepts aContainerFormatstrategy for frame parsing. This mirrors the Rustmoq-muxConsumer<F: Container>pattern… Additionally, add asequentialdelivery mode flag to fix audio stuttering caused by inter-group serialization.” The May 3 push presumably addresses Luke’s May 2 review nits (“Just call itFrame…Legacy.LegacyFormatshould be avoided IMO… We should reuseFrameandDecodedFrame”) — first revision turn-around since Luke’s Apr 30 design suggestion to refactor asOrderedConsumer<F: Container>. - No merges in the window. Open-PR slate going into May 4: PR #1370 (metapox PriorityQueue bug-with-fix-offer), #1371 (Luke cross-broadcast track refs), #1367 (skirsten pull-mode, now superseded by #1373), #1373 (skirsten playback fix), #1359 (ksletmoe-aws Consumer unify), #1362 (Qizot audio reconfiguration), 1341 (Luke earlier work). No new Luke PRs since the Apr 29 wave’s tail (#1356).
May 2 → May 3 Luke REVERTS PR #1357 fetch_group + TrackDynamic via PR #1372; metapox opens detailed SUBSCRIBE_UPDATE PriorityQueue bug PR #1370; Luke opens cross-broadcast PR #1371; sidsethupathi PR #1369 MERGED
A surprisingly busy day on moq-dev/moq for May 2 — a notable design U-turn from luke-curley, a substantive bug report with fix offer from a new external contributor, and a new feature PR.
- PR #1372 MERGED May 2 21:18:50 UTC by luke-curley — Revert moq-lite FETCH/Subscription API changes. Reverts PR #1357 (fetch_group API + TrackDynamic) and PR #1348 (Subscription model API for FETCH readiness). Body: “FETCH isn’t hooked up yet, so the breaking API change isn’t worth it; the API also wasn’t quite right.” Hop-based clustering (PR #1322) and per-frame buffer changes (PR #1353) are preserved. Notable U-turn: PR #1357 was merged Apr 30 00:01 UTC and was described in the Apr 30 wiki entry as the “first track-level FETCH path API”. Three days later Luke pulls it back as not-ready.
- PR #1371 OPENED May 2 20:28:59 UTC by luke-curley — hang: cross-broadcast track references in renditions. Adds optional
broadcastfield on video/audio rendition configs (e.g."../source") so a downstream catalog can reference tracks published in another broadcast without republishing bytes. NewPathRelativetype +Path::resolvein moq-lite Rust with full unit coverage; mirrorresolveBroadcasthelper for@moq/hang.@moq/watch’sBroadcast.trackBroadcast(effect, configBroadcast)looks up the override broadcast on the same connection; audio/video decoder + MSE backends honor it. Body explicitly notes ”🤖 Generated with Claude Code”. Use case: worker-style flow where a sidecar catalog aggregates source tracks without re-broadcasting them. - PR #1370 OPENED May 2 15:28:56 UTC by metapox — fix(lite): PriorityQueue does not update in-flight groups on SUBSCRIBE_UPDATE. Detailed bug report citing draft-ietf-moq-transport-13 §6.1: “When subscriber priority is changed, a best effort SHOULD be made to apply the change to all objects that have not been sent.”
PriorityQueue::insert()copies thetrackvalue at insertion time; whenrun_trackreceivesSUBSCRIBE_UPDATE, thePriorityQueueis not notified — existingPriorityHandles keep their stale position. Real-world impact: “Switching camera focus via SUBSCRIBE_UPDATE takes several seconds because old groups from the previously-focused camera continue to be served at high priority, starving the newly-focused camera.” Proposed fix: addsubscription_idtoPriorityItem;PriorityQueue::update_subscription(subscription_id, new_track)re-sorts and notifies handles via watch channels; widen quinn priority spread toindex * 64; wrapwrite_allintokio::select!withpriority.next(). metapox: “We have a working implementation in our fork and can submit a PR if interested.” References Issues #699 (priority tie-breaking) and #1363 (own JS SUBSCRIBE_UPDATE issue). First substantive bug-report-with-fix-offer from metapox. - PR #1369 MERGED May 2 14:53:33 UTC by luke-curley (sidsethupathi author, +39/−2, moq-gst: fix moqsink eos). The gst-launch EOS fix opened May 2 03:27 UTC lands in ~11.5 hours. sidsethupathi’s second merged PR after #1294 (Apr 12). MLB engineering presence on
moq-gstsolidifying.
May 1 → May 2 Doc fix + new contributor PRs (skirsten pull-mode renderer, sidsethupathi moqsink EOS)
luke-curley kept main moving forward with a small Cloudflare doc fix and a flake bump; two new contributor-driven PRs opened.
- PR #1366 MERGED May 1 14:58 UTC — flake.lock dependency bump. Routine.
- PR #1368 MERGED May 1 18:08:59 UTC by luke-curley (+1/−1, Update Cloudflare limitation note for latency=real-time). Single-line doc note clarifying that Cloudflare doesn’t support both
reloadANDlatency=real-time. - PR #1367 OPENED May 1 15:17:12 UTC by skirsten (Simon Kirsten) (+46/−4, @moq/watch: add pull mode to video renderer). Body: on Chrome with 144Hz+ monitors the existing Renderer caused Chrome to render at 120fps despite the draw logic being correct. Wrapping
requestAnimationFramerecursively syncs to the monitor’s vsync. Addsmode: "push" | "pull"prop on Renderer;"pull"runs self-recursive rAF and redraws only on frame change. MultiBackend WebCodecs path now usesmode: "pull". skirsten notes “we can also drop the push mode if you want.” — fourth skirsten PR after #1349, #1355, #1365 (all now merged). - PR #1369 OPENED May 2 03:27:40 UTC by sidsethupathi (Sid Sethupathi, MLB) (+39/−2, moq-gst: fix moqsink eos). Fixes the gst-launch pipeline
videotestsrc num-buffers=120 ! ... ! moqsinkso EOS fromnum-buffersis honored — previously the pipeline ran indefinitely; with the fix it exits after 2 seconds. Second sidsethupathi PR after #1294 (Apr 12 “use generated name if no sink pad name provided”) — the moq-gst contributor base is solidifying around MLB engineering.
May 1 Audio Polish + Cloudflare Relay Bug Report (May 1 01:38 → Apr 30 21:16 UTC)
- PR #1365 MERGED May 1 01:38:38 UTC by skirsten (Simon Kirsten) (+11/0) — @moq/watch: expose AudioContext on the audio backend. Body: “The WebCodecs decoder owns its own AudioContext but doesn’t surface it past the Decoder class. Browsers create the context in
suspendedstate when there’s no user gesture, and applications need a handle on it to prompt the user (e.g. a ‘click to enable audio’ button) and callresume()from within the gesture handler.” Companion to PR #1349 (static catalog format) and PR #1355 (sampleRate override) — completes the Hang/moq-watch audio-handling polish for end users hitting browser autoplay policies. - PR #1359 STILL OPEN — ksletmoe-aws posted self-summary Apr 30 21:16:33 UTC: “This PR grew a bit from the original fix — I took the opportunity to create a unified
Consumerthat mirrors the RustConsumer<F: Container>pattern.” Apr 30 22:10:45 UTC follow-up: “Sorry for the churn on this one — the commit history is messier than it should be.” luke-curley Apr 30 22:29:47 UTC: “No worries, I’ll take a look at it soon.” PR remains open with major scope increase per Luke’s design suggestion (1083 +/1173 −). - New Issue #1364 “Cloudflare Relay” opened Apr 30 14:20:51 UTC by danrossi (David Ross). Reports moq-js can’t connect to Cloudflare’s draft-14/draft-07 relays from
moqlivemockURLs. CodeRabbit auto-flagged as possible duplicate of #586. Same class of cross-impl friction as Issue #1346 (kubo6472, Apr 24).
Apr 29–30 Continued Wave: Four MERGES (#1357 fetch_group + #1350 mTLS + #1349 static catalog + #1360 jemalloc); Qizot replaces #1354 with #1362; ksletmoe-aws pivots #1359 to generic OrderedConsumer refactor; metapox opens issue #1363
luke-curley turned all four open Apr 28 PRs into merged code in a ~16-hour window (Apr 29 16:08 UTC → Apr 30 00:01 UTC). Two external contributor PRs were redesigned in flight, and a new external bug arrived.
Four merges to main
- PR #1357 MERGED Apr 30 00:01:46 UTC by luke-curley (final +427/−133) — moq-lite: add fetch_group API + TrackDynamic. First FETCH path API at the track level lands.
- New
TrackConsumer::fetch_group(seq) -> Result<GroupConsumer>with three branches: cache hit returns the cached consumer; cache miss + no fetch handler returnsErr(NotFound); cache miss + handler queues a request and returns a consumer that fills as the publisher writes frames. Concurrent fetches for the same sequence share the in-flight group. - New
TrackConsumer::latest_group() -> Option<GroupConsumer>(replaceslatest()returningOption<u64>). - New
TrackProducer::dynamic() -> TrackDynamicmirrorsBroadcastProducer::dynamic(). Drop the last dynamic and pending requests are aborted withError::Cancel. - New
TrackDynamic::poll_requested_group/requested_groupyieldsGroupProducerfor the publisher to fill. - Caller migrations:
moq-relay/src/web.rsfetch handler drops the upfrontsubscribe_trackround-trip;lite/publisher.rsandietf/publisher.rsuselatest_group()for the LargestObject case. - 8 new unit tests (
fetch_group_cache_hit,fetch_group_no_handler_returns_not_found,fetch_group_via_dynamic_handler,fetch_group_shares_in_flight,fetch_group_aborted_by_publisher,fetch_pending_aborted_when_dynamic_dropped,latest_group_returns_max_sequence_consumer,latest_group_none_on_empty_track).cargo test --workspace= 290 moq-lite tests pass (up from 282). - Wire-side FETCH hookup intentionally still returns errors:
lite::ControlType::FetchreturnsError::UnexpectedStream;ietf::run_fetch_streamStandalone returns “not supported”. The breaking API change captures the in-process API; the wire format choice is its own conversation.
- New
- PR #1350 MERGED Apr 29 16:46:18 UTC by luke-curley — moq-relay: authenticate HTTPS callers via the cluster mTLS CA. mTLS HTTPS auth lands. The CodeRabbit-flagged 🟠 Major (CORS+browser-readable-GET) was apparently resolved offline.
- PR #1349 MERGED Apr 29 16:08:52 UTC by luke-curley (skirsten’s @moq/watch: add static catalog format). Third catalog mode lands —
<moq-watch catalog-format="static">plus writableSignal<Catalog.Root | undefined>forBroadcast.catalog. - PR #1360 MERGED Apr 29 16:29:05 UTC by luke-curley (+29/−10) — moq-native: relocate jemalloc helper; wire it into moq-boy. moq-boy now production-instrumented for jemalloc heap profiling at 6+ instances.
- PR #1361 OPENED+CLOSED Apr 29 16:17 → 16:29 UTC by luke-curley — moq-native: move jemalloc profiling helper from moq-relay. Replaced by PR #1360.
External contributor activity Apr 29–30
- PR #1354 CLOSED unmerged Apr 29 16:54:30 UTC by Qizot. Closing comment: “This was wrong approach, we should have reconfigured the encoder instead.”
- PR #1362 OPENED Apr 29 17:04:41 UTC by Qizot (+40/−17) — Add audio encoder reconfiguration. Replaces PR #1354. When iOS Safari mismatch is detected (worklet’s
channelCountresolves to 2 butonmessagereceives mono), the encoder is reconfigured rather than padding the AudioData. Cleaner solution. Open under CodeRabbit review. - PR #1359 — TITLE CHANGED + RESCOPED. Originally “fix(watch): process CMAF groups sequentially in WebCodecs decoder” (+64/−67). Now “feat(hang): unify OrderedConsumer across container formats” (+971/−…). After Luke’s Apr 28 23:00 UTC review comment: “I think we need a generic
OrderedConsumer. The problem is thatrecvGroup(and MoQ in general) returns groups out-of-order. The idea behindOrderedConsumeris that we skip groups based on the target latency, which requires timestamp information unfortunately.” and Apr 29 00:29 UTC: “On the Rust side, I made an interface to parse the timestamp out of each frame. Then OrderedConsumer can be reusable.” — ksletmoe-aws (AWS) rewrote the PR as a genericOrderedConsumer<F: Container>refactor that unifies Legacy + CMAF containers behind aContainerFormatstrategy interface. Mirrors the Rustmoq-muxConsumer<F: Container>pattern. New files:container/format.ts,container/consumer.ts,container/cmaf/format.ts,container/consumer.test.ts(25 tests). 4 watch decoders migrated. Apr 30 01:43 UTC ksletmoe-aws addressed CodeRabbit nitpicks. First instance of an external moq-dev/moq contributor’s PR being expanded in scope at the maintainer’s request to align JS-side architecture with the Rust side. - Issue #1363 OPENED Apr 30 00:43:26 UTC by metapox (taku): “feat(lite): JS Subscriber lacks SUBSCRIBE_UPDATE support for dynamic priority changes”. Concrete use case: multi-camera streaming where the viewer switches focus between cameras. Each camera has a subscription, and the focused one should get higher priority — but the close→re-subscribe path causes a 1s keyframe-wait gap on every switch, while SUBSCRIBE_UPDATE would be seamless. Rust subscriber already handles this via
TrackSubscriber::update(); JS subscriber is missing the equivalent. Issue includes a proposed implementation (track.ts adds priority Signal + updatePriority; lite/subscriber.ts watches for priority changes and sends SubscribeUpdate; lite/publisher.ts applies received priority). Tested in metapox’s moq-multicam app. Total diff: 30 inserts/4 deletes across 3 files. Second time metapox surfaces a moq-lite/JS issue (after Apr 27 #1351 false-alarm).
Net effect
moq-relay’s operational layer is substantially upgraded — mTLS HTTPS auth, jemalloc heap profiling, FETCH-readiness API, third catalog mode all merged in one ~16-hour window. The model layer is fully scaffolded for FETCH; only wire-side hookup remains. External contributors are now driving non-trivial design redesigns (ksletmoe-aws’s #1359 rescoping is unprecedented in moq-dev/moq), and metapox’s #1363 issue brings a multi-camera streaming use case to JS-side priority handling.
Apr 28–29 Post-Interim Wave: Two Merges (#1352, #1353) + Five New PRs (#1356–#1360) + ksletmoe-aws + Qizot
luke-curley turned both Apr 27’s open PRs into merged code, then opened five more substantive PRs in the same window. External contributors Qizot and ksletmoe-aws (AWS) also landed activity.
Two merges (post-interim PRs land)
- PR #1352 MERGED Apr 29 01:32:29 UTC by luke-curley (final +10/−2) — Handle relays without announcement subscription support. Lands the
mediaoverquic.com-specific announcementless-relay handling (issue #1346 fix). Final size grew by 4 lines vs the original +6/0 after the CodeRabbit suffix-match-false-positive fix. - PR #1353 MERGED Apr 29 01:49:24 UTC by luke-curley (final +347/−147) — moq-lite: per-frame buffer + BufMut producer to cut relay memory. The production-profiled memory optimization (~234 MB / ~254 MB / ~446 MB attribution) lands, replacing
Vec<Bytes>per-frame chunks with singleArc<FrameBuf>allocations andBufMut-driven direct writes from quinn streams. First memory-cost-per-connection optimization to land in moq-relay. - PR #1350 (mTLS for HTTPS callers) — still OPEN. Last activity Apr 27 23:33 UTC. The CodeRabbit-flagged 🟠 Major (CORS+browser-readable-GET issue) hasn’t been addressed in pushed code yet.
- PR #1355 MERGED Apr 28 20:04:23 UTC by luke-curley (+7/−2, author Qizot) — Add encoder’s AudioContext sampleRate override. Routine fix.
Five new PRs from Luke (Apr 28 16:11 UTC → 23:55 UTC)
- PR #1356 OPENED Apr 28 16:11 UTC (+27/−86) — moq-lite: switch insert_track to take TrackConsumer. Changes
BroadcastProducer::insert_trackto takeTrackConsumer(by value) instead of&TrackProducer. RemovesTrackConsumer::produce()(added in #1300 as a workaround). AddsTrackConsumer::weak()(pub(crate)). - PR #1357 OPENED Apr 28 16:33 UTC (+319/−24) — moq-lite: add fetch_group API + TrackDynamic. Ties together the FETCH-readiness work. New
TrackConsumer::fetch_group(seq) -> Result<GroupConsumer>— first-class FETCH path at the track level. “The breaking API change is captured here so the wire-side hookup (lite ControlType::Fetch, ietf::run_fetch_stream) can land as a clean follow-up.” Pairs with PR #1348 (Subscription model API). - PR #1358 OPENED Apr 28 19:20 UTC (+994/−1289) — moq-lite: rewrite Origin as a poll-driven, conducer-based model. Massive rewrite: replaces
OriginNode/NotifyNodetree, per-publishweb_async::spawncleanup, and per-consumermpscfan-out with a flatHashMap<PathOwned, Entry>behind aMutexplus per-consumer queues. Consumers register a singleconducer::Waiteron both the shared state and each tracked entry. Net −295 lines. - PR #1360 OPENED Apr 28 23:55 UTC (+29/−10) — moq-native: relocate jemalloc helper; wire it into moq-boy. Moves the
jemallocSIGUSR1-dump helper frommoq-relayintomoq-nativebehind ajemallocfeature, re-exportstikv_jemallocator. Wiresmoq-boyfor jemalloc heap profiling — “its 6 production instances…” suggests moq-boy is now in production at 6+ instances.
External contributor activity
- PR #1359 OPENED Apr 28 21:22 UTC by ksletmoe-aws (Karl Sletmoe, AWS) (+64/−67) — fix(watch): process CMAF groups sequentially in WebCodecs decoder. “The CMAF WebCodecs decoder path in
js/watch/src/video/decoder.tsandaudio/decoder.tsspawns a concurrent async task per MoQ group viaeffect.spawn(). When groups contain a single large frame (e.g. CMAF passthrough where each group is one moof+mdat blob),readFrame()resolves immediately…” — concrete bug exposed by CMAF passthrough where each group is one moof+mdat blob. First moq-dev/moq PR from an AWS contributor. - PR #1354 OPENED Apr 28 07:23 UTC by Qizot (+21/−11) — Fix missing channel samples for audio encoder. iOS Safari WebCodecs/getUserMedia mismatch. “On iOS safari the line
channelCount: settings.channelCount ?? root.channelCount,resolves to2, but afterwards we receive mono audio inonmessage. Since the number of channels inAudioDatamust match the number of channels the encoder has been initialized with, we are fixing theAudioDataby copying the active channel to the missing one.” - Issues #1310 (worklet plugin) + #1328 (js tooling) — beeequeue’s longstanding tooling questions saw substantive Luke replies Apr 28 → Apr 29 01:24 UTC about Vite-specific URL resolution.
Net effect
The merge wave continues the SaaS-multi-tenancy push from Apr 26. The five new PRs split into two threads: moq-lite-fetch readiness API (#1356/#1357 build the TrackConsumer/fetch_group surface PR #1348 is meant to consume) and runtime substrate (#1358 Origin rewrite, #1359 ksletmoe-aws decoder fix, #1360 jemalloc-in-moq-native for moq-boy production heap profiling). External contributors (Qizot, ksletmoe-aws, earlier skirsten on #1349, earlier kubo6472 on issue #1346) are now driving 4 of the last 12 PRs/issues — the contributor base is widening rapidly.
Apr 27–28 Post-Interim Burst: Three New PRs (#1350, #1352, #1353) + Issue #1351 Closed
luke-curley opened three substantive PRs in <2 hours after the Apr 27 interim, plus a quick issue cycle:
-
PR #1350 OPENED Apr 27 22:24 UTC (+351/−18) — moq-relay: authenticate HTTPS callers via the cluster mTLS CA. The QUIC server already short-circuits to
AuthToken::unrestricted()when a peer presents a client cert signed by--server-tls-root(connection.rs:34). The HTTPS web server (/announced,/fetch,/ws/*) didn’t — it required a JWT in the query string. PR wires the same path through the HTTPS listener: when--server-tls-rootis set, the listener installs aWebPkiClientVerifier(with.allow_unauthenticated()so JWT-only callers still work), and a verified peer cert producesAuthToken::unrestricted()via a newWebState::resolve_tokenhelper. A tinyMtlsAcceptorwrapsRustlsAcceptor;SetMtlsExtensionmiddleware injectsOption<MtlsPeer>per request. Cert hot-reload via SIGUSR1 preserved (reload_from_pem_filewould silently strip client-cert verification, so the mTLS path re-runs the full builder viareload_from_config). 4 new tests inweb::tests. CodeRabbit flagged 🟠 Major: withCorsLayer::allow_origin(Any), an arbitrary website could read/announcedand/fetchthrough a browser that auto-selects or has approved a matching client cert. -
PR #1352 OPENED Apr 27 23:59 UTC (+6/0) — Handle relays without announcement subscription support. Direct response to issue #1346 (kubo6472’s Apr 24 cross-impl Cloudflare-relay catalog-discovery bug). Changes
announcedgetter type fromSet<Path.Valid>toSet<Path.Valid> | undefined. When connecting tomediaoverquic.com, explicitly setsannouncedtoundefined; broadcast reload logic treatsundefinedasreload=false. Pragmatic move: hardcodes the Cloudflare relay URL into moq-lite source — preserves user-visible behavior of<moq-watch catalog-format=msf>against a Cloudflare endpoint at the cost of a layered hardcode. CodeRabbit flagged hostname-suffix matching false-positive risk; Luke pushed a fix Apr 28 00:07 UTC. -
PR #1353 OPENED Apr 28 00:27 UTC (+346/−146) — moq-lite: per-frame buffer + BufMut producer to cut relay memory. Production-profiled memory optimization. Luke profiled a relay with ~66 connections at 2.7 GB RSS on a 4 GB box, attributing:
- ~234 MB to
FrameProducer::create(per-chunk 32 BBytesheaders inVec<Bytes>plus growth) - ~254 MB to
GroupProducer::create_group(VecDeque<FrameProducer>+ retained frame state) - ~446 MB to
quinn::endpoint::RecvState::poll_socket— quinn’s reassembly arena being pinned by heldBytes(the returnedBytesis a refcounted slice into quinn’s arena)
Replaces
FrameState.chunks: Vec<Bytes>withFrameBuf— a single Arc-shared, fixed-capacity heap allocation per frame.FrameProducernowimpl bytes::BufMut, so the receive path writes quinn stream bytes directly into the pre-allocated buffer viaread_buf(one memcpy, no per-chunk Bytes headers, no quinn-arena pinning).FrameConsumertracks a byte cursor and materializes transientBytesviews viaBytes::from_owner(buf.clone()).slice(..). - ~234 MB to
-
Issue #1351 OPENED Apr 27 23:15 UTC by metapox (taku) → CLOSED Apr 28 00:10 UTC by reporter. “Container.Legacy.Consumer.next() returns undefined after 20-60 frames with multiple concurrent tracks”. Reported against
@moq/hang0.2.4 +@moq/lite0.2.2. Luke replied Apr 27 23:18 UTC: “recvGroup() should only return undefined when the track has finished. Yeah, I need more information, this should never happen.” metapox couldn’t reproduce in clean environment; closed as false alarm with “The original report was likely caused by an unstable publisher on my side.”
PRs #1349 (skirsten static catalog) and #1348 (moq-lite-fetch Subscription model) remained open with only CodeRabbit re-reviews. No new merges to main in the Apr 26 → Apr 28 window.
Net: moq-relay entered the post-interim period with the operational layer being attacked from three directions — HTTPS auth (mTLS), peer-impl-difference handling (Cloudflare relay specifically), and memory cost-per-connection (production-profiled).
Apr 26 Big Day: PR #1343 + #1340 MERGED, #1348 Opens for FETCH, External PR #1349 (Apr 26 15:38 → Apr 27 01:32 UTC)
A productive Apr 26 afternoon UTC, plus a new external contribution overnight:
- PR #1340 MERGED Apr 26 16:26 UTC by luke-curley (+182/−5) — moq-lite: add OriginConsumer::wait_for_broadcast; deprecate consume_broadcast. Lands the announcement-aware lookup that fixes the moq-gst footgun where a sync
consume_broadcastreturnedNonebecause announcements hadn’t arrived over the wire yet. BothOriginProducer::consume_broadcastandOriginConsumer::consume_broadcastare now deprecated in favor of the new alternative. - PR #1343 MERGED Apr 26 16:35 UTC by luke-curley (+283/−26) — relay: add subdomain-based slug routing for customer isolation. The subdomain-routing primitive lands after a week of self-review and CodeRabbit iteration. Adds
--auth-domain/MOQ_AUTH_DOMAIN/TOMLdomainsto configure suffix lists for host-based routing. When a connection URL host is<slug>.<suffix>, the slug is prepended to the path before auth runs, socustomer.cdn.moq.dev/foois equivalent tocdn.moq.dev/customer/foo. Multi-label slugs (a.b.<suffix>) are rejected as400 InvalidHost. The 🔴 Critical WS/web auth-handler bypass that CodeRabbit flagged Apr 23 was resolved before merge. First SaaS-style multi-tenancy primitive in moq-relay. - PR #1348 OPENED Apr 26 15:38 UTC by luke-curley (+1049/−471) — moq-lite: backport Subscription model API for FETCH readiness. First FETCH-readiness commit on
moq-lite-fetch. Backports theSubscription/TrackSubscribermodel-layer API fromdev’s PR #1134. Goal is “to land the API surface FETCH needs without implementing FETCH wire/stream handling — fetch can plug into TrackSubscriber::update once the wire path is added.” Surgery:Tracklosespriority; newSubscription { priority, ordered, max_latency, start, end }carries that state. NewTrackSubscriberowns group iteration (recv_group,next_group,next_group_ordered,read_frame) and per-subscriberSubscriptionstate. CodeRabbit flagged 🔴 Critical: aggregator’sstart/endreduce treatsNoneas “no preference” — but the struct doc saysstart: Nonemeans “deliver all cached history” andend: Nonemeans “no end (live)” — a semantics mismatch that needs fixing. - PR #1341 (Refactor media producers, open since Apr 23) — luke-curley posted 8 inline self-review comments Apr 26 16:08–16:16 UTC after the morning merges. Highlights: “release-plz will bump this; don’t manually do it.” / “just call it
inithonestly. Also is there some serde_as thing we could use instead of String?” / “Could we avoid making this pub? I don’t want users to accidentally call the wrong methods?” / “I don’t think we should remove these jitter calculations. Maybe make a jitter.rs helper instead of copy-pasting?jitterisn’t a great name, really it should bemin_frame_durationor something.” — same self-review pattern as PR #1343 used before merging. - PR #1349 OPENED Apr 27 01:32 UTC by skirsten (Simon Kirsten) — @moq/watch: add static catalog format (+196/−13). External contributor adds a third catalog mode beyond
hangandmsf: a"static"mode where callers pass aCatalog.Rootdirectly rather than fetching it via a catalog track. Also promotesBroadcast.catalogfrom a getter to a writableSignal<Catalog.Root | undefined>. Newdemo/web/src/static.htmlpage with a textarea + Apply button to manually drive<moq-watch catalog-format="static">. CodeRabbit flagged 🟡 Minor:finallyunconditionally clearing a potentially user-owned signal. Second contributor-driven catalog-format extension to<moq-watch>after Luke’s own MSF (PR #1330) — the catalog-format-as-attribute API is gaining contributor mindshare.
Net effect: moq-dev/moq enters the Apr 27 interim with two relay infra primitives merged (slug routing, wait_for_broadcast), a major moq-lite-fetch foundation PR open, a self-reviewed media producers refactor in flight, and the catalog-format API ecosystem extending via external contribution.
Apr 25 Merges + PR #1343 Self-Review + Issue #1346 Root-Cause (Apr 25 UTC)
- PR #1345 MERGED Apr 25 15:13 UTC by luke-curley (+108/−0) — py/moq-lite: add clock + announced examples. Adds two Python examples for the moq-lite Python bindings.
- PR #1347 MERGED Apr 25 14:47 UTC by dependabot[bot] (+2/−2) — Bump
rustls-webpki0.103.12 → 0.103.13. Routine. - PR #1343 (subdomain-based slug routing) — still OPEN. Luke posted two self-review rounds on Apr 25 (22:09 UTC, 22:40 UTC) with five inline comments addressing CodeRabbit’s earlier feedback (“IMO do one strip_suffix call.” / “Maybe add the leading . to the domain after parsing the config file?” / “We could replace . with / to support multiple paths.” / “We should also lowercase and add a . prefix here.” / “Why is this public? IDK seems like it’s too specific.”). A new CodeRabbit review (Apr 24 22:22 UTC) suggests pre-canonicalizing suffixes to lowercase in
Auth::new. The 🔴 Critical WS/web auth-handler bypass is still not addressed in pushed code — these self-review notes signal an incoming rework before the next push. - Issue #1346 (catalog-discovery / “how to build something with this”) — saw ~7 substantive exchanges between @kubo6472 and Luke on Apr 25 14:17–19:15 UTC. kubo6472 reported tearing/lagging on
moq.dev/watch(Chrome/Android 12; Firefox on Linux Mint with a GTX1080) and a black screen against the Cloudflare relay. Luke pushed back on the tearing as a browser/GPU/driver issue and pointed at the OBS plugin and the moq.dev blog. Root cause confirmed at 19:15 UTC: kubo6472 switched to Chromium on Linux Mint and both/watch/liveandmoq.dev/watchstarted working — original tearing was a Firefox/GPU/driver issue. Luke also confirmed “I’m working on DVR (rewind). It’ll be at least a few months.” The underlying cross-impl Cloudflare-relay catalog-discovery bug (SUBSCRIBE_NAMESPACEnot implemented) and the docs gap remain. Issue still OPEN.
Subdomain Routing Activity + First External MSF Bug Report (Apr 24 UTC)
- PR #1343 (open) — Subdomain-based slug routing (see Apr 22–23 burst section below). Updated Apr 24 22:22 UTC after CodeRabbit flagged a 🔴 Critical issue (the WS/web auth handlers build
AuthParamsdirectly without consultingAuth::domains, leaking the slug-based isolation in the WebSocket path). Awaiting Luke’s response. - PR #1347 (open, Apr 24 17:04 UTC, dependabot[bot]) — Bump
rustls-webpkifrom 0.103.12 to 0.103.13 in the cargo group. - Issue #1346 (open, Apr 24 08:24 UTC, @kubo6472) — “Q: how to build something with this?“. First externally-reported bug exercising the new
<moq-watch catalog-format="msf">element (PR #1330, Apr 20). User pointed<moq-watch url="https://draft-14.cloudflare.mediaoverquic.com" name="room/bbb" catalog-format="msf">at the Cloudflare draft-14 endpoint, hitsCloudflare relay does not support broadcast discovery yet; skipping subscribe_namespacewarning +subscribe error: id=0 broadcast=room/bbb track=catalog error=SUBSCRIBE error: code=0 reason=internal error: Internal error. Confirms cross-impl friction at the catalog discovery layer between moq-lite/moq-dev clients and the Cloudflare moq-rs relay (which still doesn’t implementSUBSCRIBE_NAMESPACE). No reply yet from Luke.
Hop-Based Clustering MERGED (PR #1322, Apr 23 23:26 UTC)
After four days of work on the hops-port branch (opened Apr 19), luke-curley merged PR #1322 (+961/−979 on the final diff) — the full port of the hop-based clustering design from origin/dev (#1082 + #1152) onto main. This is the first substantive protocol change merge to main in weeks and a structural rework of moq-relay’s cluster plane:
OriginId: new non-zero 62-bit varint type encoded asu64on the wire.Broadcast::hops: Vec<OriginId>: every Broadcast carries its origin chain;BroadcastProducer/BroadcastConsumer/BroadcastDynamicexpose it via apub info: Broadcastfield plusDeref<Target = Broadcast>.- Loop refusal + shortest-path:
OriginProducer::publish_broadcastrefuses broadcasts whose hop chain already contains our id; on equal hop lengths the newer broadcast wins (test change:test_duplicatenow expects this). - Cluster CLI:
--cluster-root/--cluster-node/--cluster-prefixcollapse into--cluster-connect(repeat or comma-separated for mesh peers) + optional--cluster-origin-idfor deterministic IDs in tests/logs.primary/secondary/combinedtiers are gone; theinternal/origins/*registration dance is gone. Claims::cluster: now#[deprecated]— existing signed tokens still parse, but the flag no longer affects routing. The one call site that unavoidably reads it for back-compat uses#[allow(deprecated)].Lite04Announce: changes fromVec<u64>toVec<OriginId>.Lite03still sends count-only, decoded asUNKNOWNplaceholders.MAX_HOPStightened from 256 → 32.- JS
Publisher: generates a random 53-bit non-zerooriginIdviacrypto.getRandomValuesper session and appends it tohopson every outboundAnnounce::Active. Browser clients only publish their own broadcasts (no forwarding), so a per-connection id is enough for the relay to tag/dedupe. - Demo configs:
demo/relay/{leaf0,leaf1,root}.tomlswitched to the meshconnect = [...]format with pinned per-nodeorigin_ids (1 / 10 / 11) for readable logs. cargo-semver-checkswill flag this as a breaking change onmoq-liteandmoq-relay; release-plz is expected to pick up the version bumps automatically.- Local smoke test and browser-publisher interop checks remain on the unchecked test plan (wire-compatible by design, but the JS origin-id plumbing is newly executed).
A chore: release PR (#1338, moq-bot) was refreshed at Apr 23 23:42 UTC to pick up the version bumps. PR #1322 was authored with Claude Code (see the 🤖 Generated with [Claude Code] trailer in the description) — the largest Claude Code–authored PR to land on moq-dev main to date.
Python Examples: clock + announced (PR #1345, Apr 23)
- PR #1345 (open, Apr 23 20:39 UTC, +108/0) —
py/moq-lite: add clock + announced examples. Two new CLI examples in the Python binding:examples/clock.py— Python twin ofrs/moq-clockwithpublish/subscribesubcommands; publishes UTC timestamps at one group per minute, one frame per second.examples/announced.py— lists broadcasts announced under a given prefix.
Both use argparse + async with moq.Client(...). Extends the Python surface that Lullabee’s PR #1318 started (raw track publish/consume). Fifth PR in the Apr 22–23 burst.
Catalog-Format Docs, wait_for_broadcast, Producer Refactor, Subdomain Routing (Apr 22–23)
Four-PR burst by luke-curley on main:
- PR #1339 (merged Apr 22 16:51 UTC, +5/−5) — Bump JS patch versions to publish
recvGroup.@moq/lite@0.2.1on NPM was published Apr 16 before therecvGroupAPI added in PR #1324 (Apr 17).@moq/watch@0.2.9built against the new API declared@moq/lite: ^0.2.1, resolving to the broken 0.2.1 for consumers and triggering runtime errors onrecvGroup. - PR #1340 (open, Apr 22 17:16 UTC, +182/−5) —
moq-lite: add OriginConsumer::wait_for_broadcast; deprecate consume_broadcast. Synchronousconsume_broadcastis a footgun: a freshly-connected origin has not yet received announcements over the wire, so a sync lookup returnsNoneeven when the broadcast is about to arrive. moq-gst’s source hit this directly.wait_for_broadcast(path)scopes a fresh consumer to the path and loops. - PR #1341 (open, Apr 23 00:01 UTC, +748/−1145) —
Refactor media producers and simplify fMP4 CMAF passthrough. Renamesmoq_mux::import→moq_mux::producer, removesFmp4Configpassthrough flag, makes CMAF passthrough the only fMP4 mode. - PR #1343 (open, Apr 23 00:24 UTC, +226/−37) —
relay: add subdomain-based slug routing for customer isolation. New--auth-domain/MOQ_AUTH_DOMAIN/TOMLdomainssuffix list. When a connection URL host is<slug>.<suffix>, the slug is prepended to the path before auth runs:customer.cdn.moq.dev/fooequalscdn.moq.dev/customer/foo. Hosts matching a suffix exactly or matching none fall back to plain path routing. - PR #1344 (merged Apr 23 01:12 UTC, +31/−0) — Catalog-format docs for
@moq/watch:hang(default) vsmsf, HTML example, auto-negotiation note. - Issue #1342 (open, Apr 23) — “Raw QUIC doesn’t support paths”: No PATH SETUP parameter means only WebTransport works with path-based auth today.
Hop-Based Clustering, MSF Catalog, DNS Bind (Apr 19–20)
- PR #1322 (merged Apr 23 23:26 UTC — see top of this page): Major refactor — ports the hop-based clustering design from
origin/dev(#1082 + #1152) tomain. Replaces three-tierprimary/secondary/combinedorigin model andcluster: booltoken flag with a singleOriginProducerper relay tagged with a stableOriginId. EveryBroadcastnow carrieshops: Vec<OriginId>so loops are refused and the shortest path wins.Lite04Announcechanges toVec<OriginId>;Lite03decodes asUNKNOWNplaceholders.MAX_HOPStightened 256 → 32. CLI:--cluster-root/--cluster-node/--cluster-prefixcollapse into--cluster-connectfor a full mesh, plus optional--cluster-origin-id.Claims::clusteris now#[deprecated]. Browser clients generate random 53-bit non-zerooriginIdper session. Flagged as acargo-semver-checksbreaking change onmoq-liteandmoq-relay. +857/-900 lines. - PR #1330 (open, Apr 19–20): MSF catalog format support with auto-negotiation. New
@moq/msfpackage with Zod-validated schema + encode/decode/fetch helpers.js/watch/src/msf.tsconverts MSF catalogs into the internal Hang shape.<moq-watch>gains acatalog="hang"|"msf"|"auto"attribute. Negotiation: Hang gets a 100ms head start, thenPromise.any()picks the first successful catalog; winner continues for subsequent updates. - PR #1335 (open, Apr 19): Raise WebSocket fallback head start 200ms → 500ms to give QUIC more runway; adds a synchronous check so the WebSocket connect attempt bails out when WebTransport has already won the race.
- PR #1332 (merged Apr 19):
moq-nativeresolves DNS hostnames in--server-bind— acceptshost:portinputs likefly-global-services:443on Fly.io.ServerConfig::bindchanges fromSocketAddrtoString; first resolved address is used since Quinn can’t bind to multiple addresses. - PR #1331 (merged Apr 19): Update
fly.tomlto use the hosted docker image. - PR #1333 (merged Apr 19): Update
flake.lockdependencies. - PR #1284 (merged Apr 19): Add
READMEfiles for Rust crates. - PR #1336 / #1337 (merged Apr 20): Nix: downgrade crane to avoid requiring Rust 1.95; align toolchain with devShell’s
rust-overlaystable. - Releases (Apr 19–20):
chore: releasePRs #1321 and #1334.
Broadcast Queuing, Auth Refactor, TLS Flags (Apr 16–17)
- PR #1319 (merged Apr 17): Broadcast backup queue replaces the prior replace-and-reannounce strategy. A newly published broadcast on an already-active path is held in a FIFO queue; when the active broadcast closes, the oldest backup is promoted. Avoids unnecessary reannounces on rapid republishing.
- PR #1311 (merged Apr 16): Major
moq-relayauth refactor.AuthErrornow propagates viathiserror#[from], PublicAccess.api flow fixed (setsclaims.root, only calls API with zero overlap to static prefixes, propagates HTTP errors asApiUnavailable). ~15 new tests usingwiremockcovering success/404/500/network/decode/cache paths, plus an integration test withaxum-server+ self-signed CA +WebPkiClientVerifierverifying--auth-tls-identityis presented during TLS handshake. - PR #1308 (merged Apr 16): Replace
--identity(single bundled PEM) with separate--certand--keyflags — matches curl’s behavior. - PR 1316 (merged Apr 16): moq-boy game server maintenance —
capybara→songbird,fofk→runiestory(ROMs on R2), volume slider, keyboard input fix, inline landing page HTML for Nix build. - PR #1318 (open): Python
py_libraw (non-media) track publishing/consuming —RawProducer/RawConsumerclasses wrapping FFI types;publish_raw()/subscribe_raw()methods (author: Lullabee). - Releases (Apr 17): moq-lite 0.15.14, moq-cli 0.7.18, moq-clock 0.10.16, moq-ffi 0.2.6.
Browser Compatibility Push (Apr 15–16)
luke-curley landed a burst of fixes and improvements:
- Safari fixes: avc3→avc1 codec string compatibility, CSS grid layout fix
- Firefox fixes: AudioDecoder 6-channel output for stereo Opus, WebTransport BiDi stream bug workaround (force WebSocket fallback on Firefox)
- moq-lite negotiation: Fallback SETUP negotiation for Lite03+ when ALPN unavailable (Firefox workaround)
- Token encoding: Default to base64url for JWK output
- Relay landing page: HTML page for non-MoQ browser clients directing to moq.dev
- Release: moq-cli v0.7.18, moq-relay v0.10.21
Interop
- Registered in interop-runner as moq-dev-rs (Rust) and moq-dev-js (JS/Hang)
- v17 interop achieved with lorenzo-miniero’s imquic (2026-04-01): “Rust publisher, JS subscriber, so that counts as two interops”
- moq-dev-rs ←> libquicr: 6/6 pass in interop runner
- moq-dev-rs ←> moxygen: 6/6 pass in interop runner
Relationship to Cloudflare moq-rs
Both projects started from Luke’s original codebase. moq-rs (cloudflare/moq-rs) forked when Luke was not going to support the IETF WG specs directly. They are now considered sibling implementations — neither is upstream of the other. The codebases are “not too too dissimilar” on the Rust side, and ideas and code can flow back and forth. See moq-rs for the Cloudflare/IETF-aligned version.
Related
- moq-rs - Cloudflare’s IETF-aligned Rust sibling (cloudflare/moq-rs)
- moq-js - IETF-aligned JS sibling (video-dev/moq-js)
- interop-status - Cross-implementation testing
- interop-endpoints - Full endpoint listing