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-22;
moqt-21Sep-10,moqt-22Sep-21, and the late-September test matrix runs drafts 14/15/16/19/22, #4482); 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
Day-by-day PR/issue history lives in the wiki log; this section keeps only durable milestones.
-
Four new individual drafts published from the moq-dev design surface (2026-09-24) — Luke Curley submitted
draft-lcurley-moq-e2ee-00(end-to-end encryption profile: a shared 32-byte broadcast secret, publisher-minted epochs, HKDF-SHA-256-derived opaque track names and per-track AES-128-GCM keys, separate key domains for grouped frames and datagrams),draft-lcurley-moq-solicit-00(aSOLICITsetup option so an endpoint can refuse unsolicitedPUBLISH_NAMESPACE),draft-lcurley-moq-flate-00(DEFLATE per subgroup, sync-flushed at each object boundary), anddraft-lcurley-moq-mpegts-00(anmpegtscatalog section preserving PIDs, PMT descriptors and SI tables through demux), alongside revisions of hang-03 and lite-06. The e2ee profile had been implemented in-tree for roughly ten days before the draft appeared — the code preceded the spec, as withmoqt-22. -
Late-September release trains, then a pre-Seattle conformance push (Sep 26 → Oct 1).
- Releases: moq-relay v0.15.7 (Sep-26) and v0.15.8 (Sep-28) completed the auth plumbing, so the SETUP
AUTHORIZATION TOKENreaches the verifier (#4278). Then moq-relay 0.16.0 shipped on Sep-30 (release PR #4320, with moq-auth 0.2.0, moq-cli 0.13.0 and moq-tokio 0.19.20). It went out as a tag and on crates.io only, with no GitHub Release. 0.16.0 is breaking: #4319 “restore 0.14 auth parity” makes removed 0.14 settings refuse startup, enforcesnbf, refuses unknown claims, and makes a public-only relay refuse JWTs. - moq-relay 0.17.0 shipped Oct-3 (release PR #4596, tag 20:10 UTC; alongside moq-cli 0.14.0, libmoq 0.6.10, moq-gst 0.4.10, obs-moq 0.6.10, moq-ffi 0.4.10). It is API-breaking: cluster gossip discovery was removed, so
--cluster-meshis now refused (#4601), and quinn was hard-forked in-tree asmoq-quic(#4617). The release also bundles the conformance work below — the per-requestNOT_SUPPORTEDdecode (#4610) and the moxygen-compatibility line (#4253). - 0.17.1 then 0.17.2 patch trains followed Oct-5/Oct-6 — 0.17.1 (moq-relay v0.17.1, moq-cli v0.14.1, libmoq v0.6.11, moq-gst v0.4.11, obs-moq v0.6.11) was the non-breaking round-up after the Oct-4 “a path is one broadcast” net refactor; 0.17.2 shipped Oct-6 ~20:15–20:50 UTC (moq-relay v0.17.2, moq-cli v0.14.2, libmoq v0.6.12, moq-gst v0.4.12, obs-moq v0.6.12, moq-ffi v0.4.12). Together they fold in a long run of net/mux fixes — a relay resuming mid-group now asks upstream for the group’s head (#4829),
REQUEST_OKacceptingLARGEST_OBJECT(#4872), lingering an empty-group broadcast before unannouncing it (#4871/#4882), fMP4 export keeping every track it declared in moov (#4883), importers publishing the catalog at their first frame (#4885), and a rejoined IETF reader keeping its open group (#4895) — plus continued in-tree vendoring: after 0.17.0 hard-forked quinn asmoq-quic, #4913 imports quinn-udp asmoq_sock::udp(+4,422/−71). (The post-0.17.2 net refactor #4942 “carry publisher epochs on routes” is unreleased — see log.) - One-line installer:
curl -fsSL https://moq.sh | shnow installs moq (#4839, Oct-5) — a scripted install path to themoq.shdeploy. - Operational change: 0.15’s renamed listen settings (
--server-bind→--listen) left the interop runner’s docker relay red from Sep-24 until runner fix #134, which cleared ~8 of the cells on the Oct-3 cut. - Conformance push “ahead of the Seattle interop”: #4610 decodes every legal request for drafts 14–22 and refuses unsupported ones per request with
NOT_SUPPORTEDinstead of closing the session. It also adds the draft-20+ FETCH layout. The moxygen-compatibility line closed with #4253: OBJECT_DATAGRAM, plus single-group FETCH on drafts 14–19, with multi-group FETCH and any draft-20+ FETCH refused. The relay gained a graceful GOAWAY drain (#4132).
- Releases: moq-relay v0.15.7 (Sep-26) and v0.15.8 (Sep-28) completed the auth plumbing, so the SETUP
-
First implementation of the new interop target:
moqt-22in moq-net (#3858, 2026-09-21) — shipped the same day interim-24 named draft-22 an interop target alongside draft-18, continuing the pattern set with draft-18 in May. In the same window the QUIC layer was consolidated onto a single backend and then ontomoq-noq, moq.dev’s own fork of noq (#3811, #3866), andmoq-e2ee-00was implemented with epoch-scoped generations (#3863). -
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.
-
Cross-language contract convergence (July 2026, ongoing): after months of widening the multi-language / multi-gateway surface, kixelated audited where the Rust core, the
js/netport, the C bindings, and the draft had silently diverged (issues #2309–#2325) and then converged them: the Rust/JS token contract aligned,SUBSCRIBE_ENDcorrected to the draft’s exclusive semantics, and — the durable outcome — Python, Swift, Kotlin and Go converged onto a single wrapper contract (#2345) with unifiedmoq-ffishapes. Amoq-statscrate was extracted in the same pass. This is the repo’s answer to a structural problem: one protocol core exposed through five language surfaces drifts unless it is periodically re-pinned. -
The origin became a full media gateway (June–August 2026): the whole
moq-muxpipeline plus RTMP/SRT/WebRTC/HLS gateway crates reachedmain, thenmoq-hlswas hardened into a standalone HLS origin and then gained DASH manifest serving from the same timeline (#2566) — so a MoQ ingest can serve legacy HLS and DASH players. A GPU transcoding chain (moq-transcode: NVENC encode, NVDEC decode, zero-copy NVDEC→NVENC) and native capture on Linux (PipeWire) and macOS landed alongside, plus Safari support in the TypeScript stack and an expandedmoq-ffi/libmoqC ABI (group-FETCH, raw tracks, track datagrams) that lets Go, Swift, Kotlin and Python consumers drive the gateway and transcode features. -
MoQ Cluster extension implemented over moq-transport (#2629, merged 2026-08-05, +2662/−408; wired into the JS/net stack by #2910, Aug-18) — carrying [[moq-cluster|
draft-lcurley-moq-cluster-00]] onto the IETF wire in the same repo that authors it. Relay-mesh work continued through August with per-core QUIC workers steered by connection ID (#2921) and warm/cold route pricing so warm relays can be ranked (#2925). -
Latency became a first-class, enforced concept (August 2026): a subscriber latency budget landed in
moq-net(#2890, +3607/−453) and in the JS stack (#2919), and the olderLatencytype was replaced by a plainmax_age(#2955, breaking). Paired with partial PROBE reporting (#2945). -
Spec authoring happens in-repo. The vendored moq-lite-06 draft gained broadcast epochs and ended/VOD broadcast semantics; drafts are pruned, renamed, and simplified as code changes. The
moq-archivechunked-archival format was drafted here and then folded into Hang as a “Recording” section rather than submitted asdraft-lcurley-moq-archive. LOC-04 timestamp code points were adopted within two weeks of publication. -
Media-format breadth kept growing (August 2026): AAC-LC decode (#2968), MPEG-TS PCR as a uniform grid (#2967), TS/DVB service information carried as opaque SI sections and proxied TDT/TOT, resampler timestamp correctness (#2992), and containers mapped one-to-one onto MSF packaging (#2987, @arielmol). A GStreamer front (
moq-gst) is under active third-party development. -
Packaging (breaking, 2026-08-21): the Go wrapper is now published as
moq.dev/moq(#2957) — a durable import-path change for Go consumers. -
Compression experiment (group-scoped DEFLATE, extracted into a
moq-flate/@moq/flatecrate) — the code side of Luke’s June “MoQ + Compression” list thread. After a period of being reconsidered rather than linearly shipped, it is now being written up as an in-repo IETF draft (#3715 “draft(flate): group-scoped DEFLATE as an IETF draft”, Sep-17) — the same author-in-repo path themoq-archiveandmoq-clusterdrafts took. -
Tracks draft-20 on the IETF adapter path (Sep 2026): within a day of moq-transport draft-20 publishing, the
js/netstack began honoring draft-20 Location Filters and serving fills (#3297, +1619/−101), with draft-20 merge-regression repairs (#3301) — cut in the moq-relay 0.14.14 release train (Sep-2, #3309; libmoq 0.5.12 / moq-token-cli 0.5.46 / hang 0.20.9). moq-dev’s usual rapid draft turnaround, this time on the IETF-filter surface. -
In-band security became a sustained, two-front workstream (Sep 2026, still in early churn). (1) End-to-end object encryption: a
moq-e2ee-01profile with shared test vectors was drafted in-repo (#3629, Sep-12) and the Rust core landed (#3665, Sep-14, +3381/−23), then the profile was reset — the TypeScript-01core was closed unmerged (#3663) and amoq-e2ee-00redesign with a path epoch merged (#3690, Sep-15); the Rust-01core stays inmainbut the profile is being reworked, not linearly extended. (2) Session auth via amoq-auth/ Common Access Token reference server: themoq-auth+@moq/authcontract/token crates and amoq auth servereference server landed onmain(#3684 +3839/−3446, #3686, #3688 — all merged Sep-15), admitting every session through a moq-auth lease — the same in-band CAT/CWT theme as moq-rs’s still-OPEN CAT #235 and moq-transport #1927. A genuinely new security direction beyond transport/media; neither front is in a tagged release yet. -
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