Upgrade
This page walks from the 2026-09-17 releases (moq-relay 0.14.18, moq-cli 0.11.2, moq-net 0.2.22, @moq/net 0.3.5, moq-ffi 0.3.19) to the 2026-09-23 release train (moq-relay 0.15.1, moq-cli 0.12.1, moq-net 0.3.0, @moq/net 0.4.0, moq-ffi 0.4.1). Each crate's CHANGELOG.md has the full list; this page is the subset that breaks a working setup.
A released flag, environment variable, or config key that was renamed is refused at startup with its replacement named, rather than ignored. Fix what the error lists and rerun.
Wire
Older protocol versions still negotiate, so relays and clients can be upgraded in any order, apart from pattern-only token grants and two wire changes:
- The lite 06 ALPN is
moq-lite-06, notmoq-lite-06-wip. An explicitmoq-lite-06-wipin a version list is refused (#3941). - The hang catalog's root
timelineentry isarchive, and wall time moved to a rootclock: { wall, timescale }(#3612, #3675). A newmoq export hlsfinds no timeline in an old publisher's catalog, so upgrade publishers before the HLS gateway.
Relay and CLI
The moq-relay and moq flags split into --listen-* (accepting), --connect-* (dialing), and a shared --quic-* section. Environment variables follow the flag (MOQ_SERVER_BIND is MOQ_LISTEN).
| Before | After |
|---|---|
--server-bind | --listen |
--server-*, --tls-cert, --tls-key, --tls-generate | --listen-*, --listen-tls-cert, --listen-tls-key, --listen-tls-generate |
--client-connect | --connect |
--client-* | --connect-* |
--client-failover-delay | --connect-race |
--client-reconnect=false | --connect-once (inverted) |
--tls-disable-verify, --client-tls-disable-verify | --connect-tls-insecure |
--server-quic-*, --client-quic-* | --quic-*, applied to both directions |
TOML [server], [client] | [listen], [connect] |
TOML [server.quic], [client.quic] | [quic] |
TOML listen, connect, failover_delay, reconnect, disable_verify | bind, url, race, once (inverted), insecure |
--cluster-linger | removed; a broadcast closes when its last publisher is lost |
--cluster-connect host:port | a full URL, https://host/?jwt=TOKEN |
--cluster-mesh, TOML mesh | removed; list every peer with --cluster-connect or --cluster-connect-api |
moq --origin, --name, --latency-max | --hop, --broadcast, --max-age |
moq publish, moq subscribe | moq import, moq export |
moq token, the moq-token binary | moq auth |
Other changes to a deployment:
- Auth is one contract (#3688). The relay asks an auth server per session (
--auth-url) or applies a static anonymous grant (--auth-public); exactly one is required.--auth-key,--auth-key-dir,--auth-api,--auth-api-mode,--auth-public-api,--auth-domain,--auth-mtls-tier, and--auth-tls-*are gone: runmoq auth serve --key-dir ...next to the relay and point--auth-urlat it. The flag-by-flag mapping is in Migrating from the relay flags. - Grants are patterns, not prefixes.
anonis now exactly the broadcastanon; writeanon/**for the subtree. This applies to--auth-public, TOMLpublic, and the[auth.public]table, which is nowpublic_subscribe/public_publish. A public or mTLS pattern with no wildcard refuses to start, naming the subtree to write, rather than pick one reading silently. The patterns are rooted at/, as in 0.14, soanon/**admits a client dialed at/anonand refuses one dialed outsideanon/. Earlier 0.15 releases rooted them at the dialed path instead. - Token grants are patterns. JWT
publishandsubscribeclaims are patterns, so a token grantingalicecovers onlyalice; signalice/**instead. Existingput/gettokens and key scopes keep working as subtrees, and subtree-only grants are still signed in that form, so amoq-tokendeployment can upgrade issuers and verifiers in either order. Verifiers on the pattern-onlymoq-auth0.1.0/0.1.1 or@moq/auth0.1.x/0.2.0 refuse that form, so upgrade them before their issuers. Grants only a pattern can express need an upgraded verifier. - Removed auth settings refuse to start. 0.14's
[auth]key,key_dir,auth_api,domains,mtls_tier, and[auth.tls], and their flags andMOQ_AUTH_*variables, stop the relay with the replacement named rather than being ignored. - Token claims.
iss,sub, andjtiare ignored andnbfis enforced. Any other claim refuses the token with its name,audandclusterincluded, where 0.14 ignored all butaud: an issuer adding app claims such asuser_idmust drop them. - Every credential is evaluated or refused. A relay on
--auth-publicrefuses a session presenting a token, as 0.14 did, including peers sendingcluster.token, and refuses to start with a client CA.moq auth serverefuses a session presenting both a JWT and a certificate, so a peer presents one or the other. moq auth servenever re-checks or expires by default, as 0.14 never did.--revalidateneeds--expires, and--limit-*needs--revalidate.- mTLS admits nothing on its own. A verified client certificate is reported to the auth server, which grants it.
moq auth serve --mtls-publish '**' --mtls-subscribe '**'restores the old full access for every certificate the relay's client CA verifies, so keep that CA to cluster peers. moq --listenneeds auth. A CLI listener refuses to start without--auth-urlor--auth-publicinstead of accepting everyone.- Gossip discovery is removed. A relay dials only the peers it lists or finds on the LAN, never a URL learned from an announcement, and no longer announces
.internal/origins. The/nodesendpoint lists only peers this relay dialed. Until every relay that ran--cluster-meshis upgraded, keep client grants off.internal/: an older relay still dials any URL announced there withcluster.token, and an upgraded peer still forwards it. - Other 0.14 auth differences kept. Peers identify by certificate or LAN path. An auth server's
rootalias may have any depth. A path in--cluster-connectis not refused, although it shifts the mesh frame.moq auth serve --keytakes a file, not an https or JWKS URL.moq auth sign --rootis the token root and JSverify --rootthe dialed path./.cluster*roots are reserved.--auth-public a,bsplits on commas. - noq is the only QUIC stack (#3811). The
quinnandquichecargo features and the backend setting are gone. - Stats counters are
*_started/*_ended(sessions_started,announces_ended, ...). This release still writes the oldannounced/*_closednames beside them, so move consumers now.
GStreamer
moqsinkpropertiesestimated-send-bitrate/estimated-recv-bitrateareestimated-send-rate/estimated-recv-rate, with no alias; agst-launchline naming the old ones fails at runtime.
Rust
- moq-native is moq-tokio (#2896). moq-native 0.20.0 is a stub that fails to compile with the rename.
ClientConfig::default().init()?ismoq_tokio::connect::Config::default().init(quic)?, andwith_publisher(&origin).with_subscriber(origin)iswith_origin(origin). Names sit under their modules (connection::Goaway,connection::Monitor,transport::Session; #3745). - moq-token is moq-auth (#3684).
ClaimsholdsPatterns, andClaims::authorizereturns pattern residuals. - Origins (#3400, #3804).
Origin::random().produce()ismoq_tokio::origin::spawn().create_broadcast(path, route)isorigin.publish(path, route), orcreate_broadcast(path)thenbroadcast.announce(route).with_rootplusscope(prefixes)is onescope(root, &patterns)returningResult.origin::Infoisorigin::Config. - Announcements are prefix routes (#3225, #3770).
announce::Updateis{ prefix, route, kind, captures }: skip!update.kind.is_active()and resolve the broadcast withconsumer.request_broadcast(&update.prefix). Serve a subtree on demand withorigin.dynamic(prefix, route). - Tracks.
with_latency_max/latency_maxiswith_max_age/max_age.write_datagram(Datagram)isinsert_datagram(sequence, timestamp, payload)(#3666);append_datagramis unchanged.track::SubscriberControlistrack::Control,track::GroupRequestisgroup::Request, andConnectionStatsissession::Statswithestimated_send_rate/estimated_recv_rate. - Oversized groups abort with
GroupTooLargeinstead of shedding their head (#3585). - Catalog edits are fallible (#3644, #3813). moq-mux and moq-json
lock()ismodify()?, and a guard that fails to publish on drop aborts the track.timeline::Config::wallis the broadcastClock. - moq-json config (#3718).
compression: boolis aCompressionenum; track-owning options areproducer::Config/consumer::Config. - moq-mux imports are typed.
import::Initsplits intoAudioInit,VideoInit, andContainerInit, with typed formats instead of strings, andTrack::newisTrack::audio/Track::video. - moq-relay embedding (#3638).
Relayfields are private: clone the handles you need, mount routes, then callRelay::run.Cluster::with_cachemoved tocluster::Options.
JavaScript
The JavaScript packages have no changelog; this list follows the breaking PRs, so a minor rename may be missing.
- @moq/token is @moq/auth.
sign/verifyareKey.sign/Key.verify, and claims are pattern unions (see Token grants are patterns). - One
Connection(#3614, #3636).Connection.Reloadisnew Moq.Connection({ url }), which pools one connection per relay.closedsettles only onclose(); the error that stopped retrying iserror. - Origins hold broadcasts (#2705, #3225).
connection.publish(path, broadcast)isorigin.createBroadcast(path)thenbroadcast.announce(), andconnection.announced(prefix)isorigin.announced(scope)yielding{ prefix, kind, route }.ignoreSelfis gone: reflected announces are always dropped. - Names mirror Rust (#3710).
latencyMaxismaxAge(aTime.Milli),writeDatagramisinsertDatagram, andRemoteErrorisError.Stream/Error.Session. - @moq/watch (#3396, #3817).
latency,latency-min,latency-max, andjitteraredelayandbuffer, which need a unit (delay="100ms",buffer="30s").reloadisannounced.Watch.Broadcast({ connection })isWatch.Player({ origin: connection.origin, ... }). - @moq/publish takes
origin: connection.origininstead of aconnectionsignal. - @moq/json and @moq/binary take one options object (#3640):
new Json.Snapshot.Consumer({ track })instead of(track, config). - @moq/hang reads the catalog
archiveentry instead oftimeline.
Bindings
Python (moq-rs 0.5.0), Swift (0.5.0), Kotlin (dev.moq:moq 0.5.0), and Go wrap moq-ffi 0.4. These are the moq-ffi names; each wrapper follows them in its own casing:
- Go module path is
moq.dev/moq(wasgithub.com/moq-dev/moq-go/moq). - Publish split by kind.
publish_media*ispublish_audio,publish_video, orpublish_container, each taking its own init. The raw encoder paths that used to bepublish_audio/publish_videoareencode_audio/encode_video. - Durations are microseconds (
max_age_us,MoqBackoff.initial_us), and rate estimates areestimated_send_rate/estimated_recv_rate. - Setters are fallible (#3642). Client and server configuration setters return an error (
Busyduring connect or listen) instead of dropping the value.set_tls_disable_verify(bool)isset_tls_verify(bool). - Announcements.
MoqAnnouncedisMoqAnnounceConsumerandMoqAnnouncementisMoqAnnounceUpdate;MoqBroadcastRequest::abortisreject, andMoqOriginOptionsisMoqOriginConfig. MoqAudioCodecis anopus()object (#3671).- Errors.
MoqError::Protocolcarries aMoqProtocolError(scope, wire code, kind) instead of a flattened message. - Track and group
finish()keeps the handle open so a laterabort()can still run.
C (libmoq 0.6):
- The 41
moq_client_*setters are one zero-initializablemoq_client_config, whose durations are_us. moq_publish_mediaismoq_publish_audio,moq_publish_video, ormoq_publish_container; the raw encoders aremoq_encode_audio*/moq_encode_video*. Formats are enums instead of strings.moq_announcedismoq_announce_update,moq_origin_consume_announcedismoq_origin_announced_broadcast, andmoq_broadcast_request_abortismoq_broadcast_request_reject.