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-js created 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-js archived (“Moved to kixelated/moq. It’s much better now.“)
  • Later: Renamed/transferred to moq-dev/moq as 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-wip PRs 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 folded moq-archive in 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-21 Sep-10, moqt-22 Sep-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 (unsolicited PUBLISH_NAMESPACE holdover, SUBSCRIBE-against-foreign-publisher gaps surfaced at the Hackathon).

Rust Packages

  • moq-lite — core transport library
  • moq-relay — server/relay; exposes a Prometheus /metrics endpoint for node traffic (PR #2172, July 12)
  • moq-token — authentication; the connection transport is now forwarded to the --auth-api hook (PR #2132, July 12), private key files are written owner-only (0600) (PR #2596, Aug 2), and moq-cli gained a token subcommand for generating/managing tokens (PR #2593, Aug 2)
  • moq-tokio — QUIC helpers (renamed from moq-native in the breaking PR #2896, Aug 17, +1722/−1662); the default QUIC backend flipped to quinn (PR #2285, July 15; previously moq-dev’s own noq with quinn opt-in), with quic::Client / quic::Server transport 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); a moq transcode CLI 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” framing
  • moq-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 Kotlin just kt check (PR #2227), and a Python moq-rs 0.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 transport
  • hang — 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-only
  • publish — publisher
  • signals, 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

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 (a SOLICIT setup option so an endpoint can refuse unsolicited PUBLISH_NAMESPACE), draft-lcurley-moq-flate-00 (DEFLATE per subgroup, sync-flushed at each object boundary), and draft-lcurley-moq-mpegts-00 (an mpegts catalog 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 with moqt-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 TOKEN reaches 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, enforces nbf, 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-mesh is now refused (#4601), and quinn was hard-forked in-tree as moq-quic (#4617). The release also bundles the conformance work below — the per-request NOT_SUPPORTED decode (#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_OK accepting LARGEST_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 as moq-quic, #4913 imports quinn-udp as moq_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 | sh now installs moq (#4839, Oct-5) — a scripted install path to the moq.sh deploy.
    • 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_SUPPORTED instead 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).
  • First implementation of the new interop target: moqt-22 in 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 onto moq-noq, moq.dev’s own fork of noq (#3811, #3866), and moq-e2ee-00 was 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 / ALPN moqt-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/net port, the C bindings, and the draft had silently diverged (issues #2309–#2325) and then converged them: the Rust/JS token contract aligned, SUBSCRIBE_END corrected to the draft’s exclusive semantics, and — the durable outcome — Python, Swift, Kotlin and Go converged onto a single wrapper contract (#2345) with unified moq-ffi shapes. A moq-stats crate 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-mux pipeline plus RTMP/SRT/WebRTC/HLS gateway crates reached main, then moq-hls was 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 expanded moq-ffi / libmoq C 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 older Latency type was replaced by a plain max_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-archive chunked-archival format was drafted here and then folded into Hang as a “Recording” section rather than submitted as draft-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/flate crate) — 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 the moq-archive and moq-cluster drafts took.

  • Tracks draft-20 on the IETF adapter path (Sep 2026): within a day of moq-transport draft-20 publishing, the js/net stack 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-01 profile 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 -01 core was closed unmerged (#3663) and a moq-e2ee-00 redesign with a path epoch merged (#3690, Sep-15); the Rust -01 core stays in main but the profile is being reworked, not linearly extended. (2) Session auth via a moq-auth / Common Access Token reference server: the moq-auth + @moq/auth contract/token crates and a moq auth serve reference server landed on main (#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