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); wire tracks the -05 revision (published 2026-06-30), hardened by a pre-merge moq-net/js API pass (PR #2170, July 12) with session Role now advertised in the -05 SETUP (PR #2201, merged July 14). A -06 design cycle is underway in-repo — the
moq-lite-06-wipPRs add typed announce ids (PR #2160, merged July 12) and cost-based cache-aware routing with a vendored route-cost Internet-Draft (PR #2179, July 12). Cost-based routing landed July 20 — PR #2424 route by cumulative cost on lite-06 announcements (+1174/−109), paired with PR #2419 unannounce-as-soon-as-the-last-route-detaches. IETF draft sources are now vendored into the monorepo and built with nix + just (PR #2159, July 10). On 2026-08-04 Luke Curley submitted three of these vendored drafts to the IETF Datatracker in one ~02:08 UTC batch: the new [[moq-cluster|draft-lcurley-moq-cluster-00]] (relay-mesh Hop-ID path vector + accumulated route cost — the standards form of the route-cost/gossip work above), [[moq-timestamp|draft-lcurley-moq-timestamp-01]] (age-based relay decisions, re-framed onto the LOC-registered TIMESTAMP/TIMESCALE properties), and [[moq-hang|draft-lcurley-moq-hang-02]] (the Hang conferencing profile). The cluster extension was implemented over IETF moq-transport (not just moq-lite) in PR #2629 (merged 2026-08-05, +2662/−408). - Hang: Media-specific conferencing/streaming layer on top of moq-lite (
draft-lcurley-moq-hang-02, 2026-08-04; also the intended home for MoQ recording/DVR after PR #2574 foldedmoq-archivein as a “Recording” section) - MSF: draft-01 supported behind a version-agnostic snapshot
- IETF adapter shims: interop with IETF draft implementations (draft-14 through draft-19); first open-source implementation to ship draft-18 (PR #1418, 2026-05-18), and shipped draft-19 (
moqt-19) within hours of the July-6 cut (PR #2106). The Hang CDN (cdn.moq.pro) does not implement the draft-19 filters and Luke says it likely never will — filters “complicate billing” (a relay would have to charge on the unfiltered byte count), stated at the July-19 Vienna Hackathon. The IETF path is also “nowhere near as tested” as Hang’s own clients (unsolicitedPUBLISH_NAMESPACEholdover, SUBSCRIBE-against-foreign-publisher gaps surfaced at the Hackathon).
Rust Packages
moq-lite— core transport librarymoq-relay— server/relay; exposes a Prometheus/metricsendpoint for node traffic (PR #2172, July 12)moq-token— authentication; the connection transport is now forwarded to the--auth-apihook (PR #2132, July 12), private key files are written owner-only (0600) (PR #2596, Aug 2), andmoq-cligained atokensubcommand for generating/managing tokens (PR #2593, Aug 2)moq-tokio— QUIC helpers (renamed frommoq-nativein the breaking PR #2896, Aug 17, +1722/−1662); the default QUIC backend flipped to quinn (PR #2285, July 15; previously moq-dev’s ownnoqwith quinn opt-in), withquic::Client/quic::Servertransport config (PR #2161, July 11); the alternate quiche backend was brought to parity with quinn (PR #2514, July 25, breaking)moq-mux— media pipeline (per-codec splitters, container import/export)moq-transcode— just-in-time transcoding of Hang broadcasts (NVENC-capable), so one ingested broadcast can be served in multiple codecs/renditions (PR #2140, July 10); amoq transcodeCLI verb plus decode-once-per-source + GPU resize fanout followed (PR #2158, July 12)moq-json— generic (non-media) JSON tracks, split into snapshot/stream modules and exposed through moq-ffi/libmoq (PR #2196, July 12) — reinforces the “generic for any live data” framingmoq-hls,moq-rtmp,moq-srt,moq-rtc— media gateway crates (see Media gateways below)moq-ffi/libmoq— C FFI surface for Go/Swift/Kotlin bindings; the July 11–12 expansion added a group-FETCH API (PR #2142), a reworked raw-track C ABI (PR #2171), track-info accessors (PR #2177), raw-frame timestamps + track datagrams (PR #2174 / PR #2175), JSON tracks, and a Go-wrapper catch-up (PR #2168); July 13 layered ergonomic per-language wrappers on top — Swift JSON wrappers with explicit snapshot mode (PR #2236), Go raw-frame-timestamp writes (PR #2230), a compile+test Kotlinjust kt check(PR #2227), and a Pythonmoq-rs0.3.2 release with JSON stream wrappers (PR #2214)moq-video— native capture/encode/decode; gained PipeWire screen capture on Linux (PR #2238, July 13) and macOS window/app/system-audio capture + device enumeration (PR #2293, July 16) as native capture sources alongside the browser paths, and now adapts encoder bitrate to the congestion-control estimate (PR #2303, July 16 — the first congestion-control → encoder link, implementing roadmap issue #2283)moq-audio— native audio capture/encode/decode; a July-24 audio push added a PCM codec (PR #2493), Opus pre-skip + encoder-control propagation (PR #2492), a bounded capture-buffer queue (PR #2487), and capture-source enumeration on Linux/Windows (PR #2486); a plan to upstream the iroh-live native media stack (playback engine + echo cancellation, issue #2478/#2481) is in flight
TypeScript Packages (js/)
lite— browser-compatible moq-lite transporthang— Hang media layer (total rewrite, not derived from kixelated/moq-js)watch— viewer/subscriber; the MSE backend was removed and the WebCodecs pipeline inlined (PR #2288, July 15) — the player is now WebCodecs-onlypublish— publishersignals,clock,common,token— supporting packages
(The UI migrated from SolidJS to vanilla Web Components in May 2026, removing @moq/ui-core.)
Safari support landed July 12 (fperex): net handles WebTransport datagram-API variants (PR #2198) and exposes the negotiated transport on Established (PR #2192); watch surfaces unsupported-codec errors (PR #2197) and no longer latches the connection off after a Safari pagehide (PR #2185); Safari hardware-encode + worker capture on the publish side and 48 kHz-Opus resampling remain in flight (PR #2190 / PR #2191, OPEN). This superseded the earlier single umbrella PR #2163, which was closed and split into these focused changes.
Media gateways
Bidirectional ingest and egress bridges between MoQ broadcasts and legacy media transports, built on moq-mux:
- RTMP / enhanced-RTMP, SRT, WebRTC (WHIP/WHEP), HLS / LL-HLS, MPEG-TS — WHIP ingest bridges H.264/H.265/AV1 symmetric with WHEP egress (PR #2139, July 10)
- Native hardware codecs (H.264/H.265 encode + decode via VideoToolbox, Media Foundation/DXGI, NVENC/NVDEC — NVDEC now also decodes AV1, PR #2178, July 12 — VAAPI), dropping the ffmpeg runtime dependency; a zero-copy NVDEC → NVENC GPU transcode path keeps frames in GPU memory (PR #2145, July 10)
- CMSF muxer/demuxer (first contributed by AWS)
Public Infrastructure
cdn.moq.dev/anon— browser pub/sub testing (QUIC + WebTransport)- Interop docs: doc.moq.dev/concept/standard/interop.html
Recent Highlights (as of July 2026)
Day-by-day PR/issue history lives in the wiki log; this section keeps only durable milestones.
- First open-source impl to ship IETF draft-18 (PR #1418, May 18) — 6 days after publication, the fastest draft-revision turnaround the wiki has tracked. Wire
0xff000012/ ALPNmoqt-18. Version matching switched to “newest defaults forward” so future drafts inherit unless opted out. - moq-lite-05 wire landed late June and was finalized in early July: SETUP + PATH parameter, TRACK_INFO, SUBSCRIBE_END, mandatory per-frame timestamps + per-track timescale, and QUIC datagram delivery.
- Media-gateway breadth reached
mainthrough June viadev→mainbackport sweeps — the fullmoq-muxpipeline plus the RTMP/SRT/RTC/HLS gateway crates. External users now file gateway bugs (e.g. open-GOP round-trip, catalog-track lifetime), a sign of real usage.moq-hlsis being hardened into a standalone HLS origin (July 14–15):export::Broadcasterrewritten as an owned poll-driven state machine (PR #2258) plus byte-range honoring, master-variant audio-group handling, and catalog-rendition reconciliation (PR #2271 / PR #2264 / PR #2266) — the behavior needed to serve legacy HLS players from a MoQ ingest. - GPU transcoding pipeline landed July 10: a new
moq-transcodecrate for just-in-time NVENC transcode of Hang broadcasts plus NVDEC hardware decode and a zero-copy NVDEC → NVENC path — a complete GPU decode→transcode→encode chain — and the WebRTC WHIP ingest gained H.265/AV1 bridges to match WHEP egress. Amoq transcodeCLI verb + decode-once/GPU-resize fanout and NVDEC AV1 decode followed July 12. - C-FFI / embedding surface expansion (July 11–13): the
moq-ffi/libmoqC ABI grew a group-FETCH API, raw-track ABI, track-info accessors, raw-frame timestamps, track datagrams, and generic JSON tracks, with the Go wrapper caught up; July 13 layered ergonomic per-language wrappers on top — Swift JSON wrappers with explicit snapshot mode, Go raw-frame-timestamp writes, a compile+test Kotlin build check, and a Pythonmoq-rs0.3.2 release — the plumbing that lets non-Rust (Go/Swift/Kotlin/Python) consumers drive the gateway, transcode, and generic-data features. - Platform reach broadened (July 12–14): Safari support landed in the TS
net/watch/publishstack (WebTransport datagram-API variants, negotiated-transport exposure,pagehideconnection fix, unsupported-codec errors), and the Safari hardware-encode path closed out July 14 with fperex’s PR #2211 (prefer the codecs Safari actually HW-encodes);moq-videoalso gained PipeWire screen capture on Linux (PR #2238) and macOS window/app/system-audio capture (PR #2293, July 16) — MoQ now reaches more browsers and more native capture sources (Linux + macOS desktop), not just Chromium + camera. - Cross-language contract-drift audit → binding convergence (July 16–17): after weeks of widening the multi-language/multi-gateway surface, kixelated catalogued where the Rust core, js/net port, C bindings, and draft had silently diverged (issues #2309–#2325: SUBSCRIBE_END off-by-one, a token-verify contract rejecting Rust-signed root-scoped tokens across the JS boundary, hardcoded Opus in
subscribe_audio) and then converged it: the Rust/JS token contract aligned (PR #2329),SUBSCRIBE_ENDfixed to the draft’s exclusive semantics (PR #2333), and — the durable move — py/swift/kt/go converged onto one wrapper contract (PR #2345) with moq-ffi shapes unified (PR #2373). A real stats module landed (PR #2348) and was extracted into its ownmoq-statscrate with compressed tracks (PR #2380, merged July 18). The convergence continued July 18: a largenetrefactor routing everything throughcreate_broadcastand gating announce onRoute.live(PR #2396, +2217/−2482), dropping moq-net’s direct tokio dependency (PR #2377), and moq-hls Producer/Consumer cursors for recording a broadcast (PR #2389 — first code toward the DVR/recording roadmap items 2281). Shipped as moq-ffi v0.2.32 / moq-cli v0.8.6 (July 17), then a fresh relay train moq-relay v0.13.7 (+moq-ffi v0.2.33 / moq-cli v0.8.7 / libmoq v0.3.14, July 18). The audit’s biggest fix landed July 22 — a ~2,100-LOC contract correction for the catalog, timeline, token, and teardown surfaces found in API review (PR #2439, +1487/−633) — alongside qlog trace capture across the quinn / quiche / noq backends (#2451), hvc1/HEVC import from new external contributor Kuba Migdał (#2444), carrying the TS/DVB service layer as opaque SI sections (#2440), and js/net stats refactors — cut as a minor-version release train: moq-relay v0.14.0, moq-cli v0.9.0, moq-ffi v0.3.0, moq-gst v0.3.0 (July 22–23), then a resilience-and-audio follow-up train moq-relay v0.14.1 (July 23) → v0.14.2 (July 24) → v0.14.3 (July 25): broadcast-linger across an ungraceful source loss (#2469) and a delay-based-CC default (July 23), then amoq-audioaudio push (PCM codec, Opus pre-skip/controls, bounded capture, Linux/Windows device enumeration), relay--cache-durationand idle-upstream teardown, and a first AWS code contribution — Kyle Sletmoe’s GOAWAY graceful-drain + cluster-migration PR (#2490, OPEN, July 24). A new individual draft draft-lcurley-moq-archive (chunked archival format for MoQ tracks) landed in-repo July 25 (#2504 merged), alongside a July-25 batch bringing the quiche backend to quinn parity (#2514), amoq-videosingle-raw-Frame refactor carrying timestamps through encode (#2503), releasing idle spliced tracks after a linger (#2505), avoiding duplicate subscribe streams (#2513), and hang catalog/codec robustness fixes (#2516/#2511). Then v0.14.4 (July 27) consolidated the post-Vienna work: the memory-leaking global cache pool was replaced with per-track write-time eviction (#2526, +2069/−1020, MERGED — closing the July-26/27 leak saga),moq-audiogained decoded-PCM speaker playback (#2529) and microphone echo cancellation (#2538), captions became standalone text tracks in Hang (#2533), and the AWS-seeded GOAWAY drain was reshaped into a first-class API with migration folded into Reconnect (#2542). The echo-cancellation, standalone-caption, and GOAWAY-reshape PRs (#2538/#2533/#2542) finished merging July 29–30 — closing the native-audio playback/echo epic (issue #2478) — joined by client-side GPU frame rendering with color spaces carried end to end (#2552, +2817/−107) and a timeline-as-single-track-of-complete-segments rework (#2547, +3500/−1243), cut as moq-ffi v0.3.5 / moq-cli v0.9.5 (July 29; relay v0.14.4 stands). July 30–31 then added DASH-manifest serving from the moq-hls timeline (#2566 — the origin now emits DASH alongside HLS), frame-precise subscription/fetch bounds (#2537, +3089/−243), a JS-publish overhaul (demux-based file publishing #2541 + fan-out to every consumer #2561), and route/relay-lifecycle fixes (#2565, #2563), cut as moq-relay v0.14.5 (July 31). Two corrections to note: the standalone-text-track captions (#2533) were reverted July 30 (#2571 “keep captions on dev”), so onmainHang captions are not yet standalone text tracks; and themoq-archiveformat may not ship as a standalone draft after all — OPEN #2574 proposes folding it into Hang as a “Recording” section rather than submittingdraft-lcurley-moq-archive. July 31 was then the repo’s biggest single merge day (~6,000 LOC / 11 merges), its durable thread being spec alignment: #2578 “prune dormant drafts, align relay-hops with moq-lite-06, adopt LOC-04 timestamps” (+1/−996) — the first in-repo move onto the LOC-04 registry fix (published July 20), continued in #2581 (adopt thedraft-ietf-moq-loc-04Timestamp code point, merged Aug 1) — landed alongside the two breaking (!) refactors that had been open (#2568 mux fMP4-by-segment, +983/−99; #2569 subscription-bounds-as-positions, +415/−227), the raw-QUIC-path-in-SETUP fix (#2572, closing issue #2570), a kio WaiterCell/Queue primitive (#2560), capture-recovery (#2559), and identity-less peer-origin (#2577); relay v0.14.5 stands. Aug 1 followed through on the same spec-alignment thread at smaller scale — #2581 (LOC-04 code point) merged, plus relay-lifecycle (#2582 keep parked groups across a prune, revive a buried route) and native-video (#2584 keep Media Foundation decoded frames on the GPU) hardening — and opened a new front: local-network peer discovery/mesh via mDNS (OPEN #2585). Aug 2–3 then opened two smaller threads: an auth-token-tooling push — amoq-clitokensubcommand (#2593) plus owner-only key files (#2596) — and a DNS / Happy-Eyeballs push — amoq-nativeresolver-address-selection fix (#2592, merged) with RFC-8305-style dual-stack dialing in flight (OPEN #2594 + issue #2595 parallel A/AAAA resolution via hickory-resolver) — alongside the kio primitive rename WaiterCell→Park (#2583). Aug 3 was another busy day (~12 merges, 3 releases): a new release train — moq-relay v0.14.6 / moq-ffi v0.3.6 / moq-cli v0.9.6 — plusmoq-hlsnested/encoded-route support (#2598), a Discord CI-failure alert (#2599), Windows/macOS PR-gate removal (#2600), and native-video GPU Direct3D11 texture resize (#2601, +2033/−141). Its durable thread was moq-lite-06 draft authoring: #2611 “spec broadcast epochs and ended broadcasts for moq-lite-06” landed (+249/−52), paired with new issue #2610 “Dynamic announcements, broadcast epochs, and ended (VOD) broadcasts” and OPEN #2607 “drafts: render gate, Hop ID 0, cluster rename, and a simplification pass” (+820/−766) — fleshing out the vendored moq-lite-06 draft with VOD-broadcast lifecycle semantics ahead of any datatracker submission. A fresh moq-net reconnect/caching cluster opened just after midnight Aug 4 (all OPEN): a reconnectingConnectionhandle (#2614), 15 s default group retention + relayed-cache-miss fix (#2615), namespace-announce withdrawal on end (#2616), a native-encoder raw-video publish path (#2608), and a kioFanshared-waiter primitive (#2604). Aug 4–5 then drained most of that cluster intomainacross ~26 merges (relay v0.14.6 stands): the native raw-video encoder binding landed (#2608, +3076/−248), RFC-8305 Happy-Eyeballs dialing merged (#2594, +979/−252, closing the DNS thread), solicitedPUBLISH_NAMESPACE+ rejected-PUBLISH handling (#2643),announcedBroadcast/ resolve-lite-restarts-by-publisher-identity (#2617), and the cluster/hop draft-authoring pass (#2607) that fed the Aug-4 datatracker batch. Its durable new front is the MoQ Cluster extension: #2629 “implement MoQ Cluster extension over moq-transport” (merged 2026-08-05, +2662/−408) carries the just-submitted [[moq-cluster|draft-lcurley-moq-cluster-00]] onto the IETF wire, alongside the native raw-video encoder and Happy-Eyeballs dialing above. Aug 5 then drained a ~22-merge moq-net/libmoq hardening pass on top of the merged cluster code — origin resume/route serving (#2666), stop a reflected announce from evicting the publish source (#2684), publisher group order moved out of message parameters (#2677), v14/v15 namespace lookup keyed by direction (#2664), IETF subscribe/fetch rejections sent without resetting the stream (#2673), a libmoq global-lock-across-video-encode fix (#2663), synthesized-fMP4-init playability on strict players (#2679), and an HEVC codec-string fix (#2668) — cut as moq-relay v0.14.7 / moq-cli v0.9.7 / libmoq v0.5.4 (Aug 5). OPEN capability PRs from the same day: classify/pace/publish accept failures (#2687), per-frame fMP4 fragmenting + timescale control (#2680), and honoring--latency-maxon RTMP/SRT/RTC gateway ingest (#2660). Aug 6 then added amoq playCLI playback client (OPEN #2697, +2009/−124 — a new native command-line player verb, with a Windows fix #2712 in flight), a moq-mux fMP4 refactor (split the fMP4 init segment out ofFragment#2692 + timescale control & two duration fixes #2693), and a net-layer correctness sweep — remove linger so a broadcast closes with its last source (#2704, +203/−652), stop a takeover resurrecting dead-source verdicts (#2701), name a broadcast by where its reader found it (#2694), amoq-nativeServer→builder+Listener split (#2700) with serve-over-tcp:///unix://(#2689), and configurable fallback cache retention (#2702) — cut as moq-relay v0.14.8 / libmoq v0.5.5 / obs-moq v0.5.5. A large OPEN JS-netrework routes publish + consume through Origins to share one connection per relay (#2705, +3166/−450). The mDNS peer-mesh (#2585) front remains OPEN. Aug 7 then landed themoq playplayer (#2697 MERGED, +2190/−128 —moq-clinow has a first-class playback verb) and stateful per-frame fMP4 fragmenting (#2680 MERGED, +1408/−264), exposed JavaScript subscription options (#2716) and kept UNKNOWN publishers announced across relay loops (#2718) — cut as a moq-relay v0.14.9 / libmoq v0.5.6 / moq-cli v0.9.9 release train (#2703, tags pending at check time); OPEN net fixes invert stream priority to match transport send-order semantics (#2720) and tolerate incoming streams that die before their first byte (#2721). - Compression experiment (group-scoped DEFLATE, extracted into a
moq-flate/@moq/flatecrate) is being reconsidered rather than linearly shipped — the code side of Luke’s June “MoQ + Compression” list thread. - Corporate-contributor footprint spans Cloudflare, Nokia, Eyevinn, OpenMOQ, and AWS. Most day-to-day churn is Luke Curley’s “codex” AI-assisted bugfix/backport batches.
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