Language: Python (asyncio) Maintainer: Giovanni Marzot GitHub: gmarzot/aiomoqt Transport: gmarzot/aiopquic (Python/Cython bindings to picoquic)

Overview

Python async implementation of MOQ Transport, using aiopquic for the QUIC transport layer.

Draft Support

  • Draft 14 / 16 / 18 general-purpose MOQ client + server, with ALPN-based negotiation (moq-00 for draft-14, moqt-16 for draft-16, moqt-18 for draft-18)
  • Latest release: v0.11.1 (2026-09-22 00:50 UTC) — tag 0.11.1, from PR #38 (+2,351/−333 across 45 files). The headline is relay FETCH plus namespace discovery: the gap Aman Sharma hit on Sep-17 (“your relay doesn’t handle FETCH”) and Will Law answered on Sep-18 with the design rule that a cacheless relay should issue its own upstream FETCH against the original publisher. Also in the release: PUBLISH_DONE ends a subscription, not the session (with register_publish_done_handler() / SubscribedTrack(on_done=…)); the default FETCH handler rejects with NOT_SUPPORTED instead of sending a FETCH_OK it never follows with a data stream (§10.13); object timestamps move to the loc-04 TIMESTAMP (0x10, wall-clock µs) from the private 0x20, with receivers accepting both; and --draft accepts draft-18 / d18 / moqt-18 spellings. Sharma’s Sep-21 verification: “The FETCH fixes all check out, and SUBSCRIBE_NAMESPACE now delivers NAMESPACE” — with one open gap, observers never receive NAMESPACE_DONE, on either namespace withdrawal or publisher session close. It published after the Sep-22 interop nightly started, so the matrix cannot reflect it before the Sep-23 cut.
  • Latest release: v0.11.0 (final, 2026-09-17) — tag 0.11.0 (published Sep-17 14:13 UTC via PR #36), shipping alongside aiopquic v0.4.0 (PyPI). This is the first final 0.11.0 after a long rc chain (rc3 Sep-3 → rc6 Sep-13). Announced on #moq (Marzot, Sep-17) as “Drafts 14, 16, 18 general purpose MOQ client/server package … numerous performance improvements, conformance fixes and added media pipeline support w/ MSF/CMSF LOC/CMAF/TS.” It finally ships the long-in-flight SUBSCRIBE_OK vi64 decode fix (see below) in a tagged release, plus a new media pipeline carrying LOC/CMAF/TS payloads via MSF/CMSF. Prior stable: v0.10.6 (~July 8, 2026; PyPI aiomoqt)
  • v0.11.0 interop, Sep-17: Steven Riedl (Pluto TV) ran the ghcr latest (14:13Z) build through the interop-runner harness against the stitcher-moq / moq-relay endpoints and got 6/6 on raw QUIC and 6/6 on WebTransport (probe pins draft-16; DRAFT=18 also 6/6) — tests 4 and 6 pass via aiomoqt’s COMPAT path around moq-relay’s non-spec 404 (a moq-dev-side quirk, not aiomoqt). Since the nightly pulls latest, the first runner matrix with 0.11 across the board is the Sep-18 cut. Riedl also flagged an entrypoint draft-pin bug: DRAFT=draft-18 (the runner-registry spelling) makes moq_interop_client exit 2 (invalid parse_draft_spec value: 'draft-18', no TAP) and MOQT_DRAFT=draft-18 is silently ignored — only DRAFT=18 / --draft 18 work; accepting draft-NN would make the runner’s injected DRAFT safe. Separately Aman Sharma noted aiomoqt’s ersatz-relay doesn’t handle FETCH and SUBSCRIBE_NAMESPACE may be incomplete — Marzot’s reply reiterated it is “not a real relay” (no cache; a test vehicle for exercising more APIs/scenarios), extensible on request.
  • draft-18 conformance now passing 12/12 (Sep-3): the aiomoqt CI conformance job (moq-test from a pinned moxygen build) reached its first green run — d16 + d18 12/12 on Sep-3, the payoff of the Sep-2/3 hackathon relay hardening.
  • Relay role added (Sep-2), hardened into “ersatz-relay” (Sep-3): aiomoqt grew a relay — self-described “pseudo-relay … a vehicle for testing pub/sub flows as both client and server,” registered in the interop-runner as aiomoqt-relay / aiomoqt-relay-quic. Its Sep-2 conformance run drove the first relay fixes (“relay dials upstream origins,” “forward the publisher’s priority through the relay,” “relay serves the bare-PUBLISH flow,” “relay forwards objects upstream to downstream,” “relay matches announced namespaces by prefix,” “never send a control message the draft doesn’t define,” “d18 LARGEST_OBJECT carries its Length field”). On Sep-3 Aman Sharma surfaced a further batch — the relay was sending the removed PUBLISH_NAMESPACE_DONE, putting REQUEST_OK/REQUEST_ERROR on the control stream instead of the request bidi stream, doing a namespace equality check instead of prefix matching, a delivery-semantics bug (a second pub/sub run against a fresh relay delivered nothing), and not handling TRACK_STATUS — and Marzot fixed them plus added real SUBSCRIBE_TRACKS → PUBLISH fan-out (§9.5), GOAWAY send/receive, and SETUP validation. Marzot promoted the name from “next stop ersatz-relay” to “ersatz-relay now copyrighted” while keeping the “not a real relay” disclaimer.
  • Interop tested against 6 relay implementations across both drafts
  • draft-18 SUBSCRIBE_OK parse bug (found 2026-08-13; fixed in v0.11.0): v0.10.6 decodes the SUBSCRIBE_OK Track-Properties block with RFC 9000 varints instead of the LOC-style vi64, so it runs off the end of the message whenever a property value is ≥ 64 (e.g. TIMESCALE=1000, encoded 83 e8) — surfacing as “truncated trailing extensions block.” Root-caused by Giovanni Marzot after Steven Riedl (Pluto TV) hit it subscribing to a moq-dev relay (whose SUBSCRIBE_OK is spec-correct); the encode/decode fix shipped in aiomoqt v0.11.0 (Sep-17) alongside aiopquic v0.4.0. Workaround on v0.10.6: MOQTMessage._tolerate_trailing_extensions = True completes the subscribe but skips the property.
  • v0.10.6 is session-fatal against moq-relay ≥ 0.14.8 (Aug-15): moq-dev’s relay got stricter at 0.14.8 (PR #2667: the session is closed on a malformed NAMESPACE), so aiomoqt v0.10.6’s draft-18 subscribe flow now fails the whole session (err=unexpected message) where older relays warned and continued. Reported by Steven Riedl (Pluto TV), who is upgrading Pluto’s public relay to 0.14.8 — another reason to validate v0.11.0 against a current relay before release.
  • relay_probe tooling (python -m aiomoqt.tools.relay_probe --url …): reports a relay’s transport (QUIC vs H3/WT), its accepted draft set, and RTT — used Aug-14 to resolve WebTransport-path discovery for fb.mvfst.net:9448 and moqx-main.ci.openmoq.org:4433 (both want /moq-relay, negotiate draft-14/16/18). Ships in the examples with v0.10.6.

Vienna Hackathon (IETF 126)

At the July-19 IETF-126 Vienna Hackathon, Giovanni Marzot ran a control-plane interop regression with aiomoqt v0.10.6 (CI run), reporting “good results” and logging the output to the ad-hoc interop wiki. Marzot also maintains the openmoq/moqx moq_decode.py decoder — used by other Hackathon participants (afrind) to debug wire-format errors — and landed a July-19 request_id cleanup (moqx PR #496) aligning it with draft-18.

Related Projects

  • aiopquic - Python/Cython bindings to picoquic (shared 2026-03-23)
  • Giovanni also created content for the moq-transport wiki (2026-03-31)

Related