|
| 1 | +--- |
| 2 | +layout: "@/layouts/global.astro" |
| 3 | +title: Update 00 |
| 4 | +author: kixelated |
| 5 | +description: Update 00 notes |
| 6 | +cover: "/blog/update-00/bbb.png" |
| 7 | +date: 2026-06-25 |
| 8 | +--- |
| 9 | + |
| 10 | +# Update 00 |
| 11 | + |
| 12 | +sup nerds. |
| 13 | +We now doing low-energy progress reports. |
| 14 | + |
| 15 | +Here's a summary of stuff that changed in the last 4? months. |
| 16 | +IDK my brain is so fried from talking to Claude all day. |
| 17 | + |
| 18 | + |
| 19 | +## Hop-based Routing |
| 20 | +Now you can [connect relays to each other](https://doc.moq.dev/bin/relay/) however you desire. |
| 21 | +They'll gossip available broadcasts to each other and proxy any subscriptions automatically via the shortest path. |
| 22 | + |
| 23 | +For example, I can host a relay in Denver and configure it with: |
| 24 | +```bash |
| 25 | +moq-relay --cluster-connect chicago.moq.dev --cluster-connect seattle.moq.dev ... |
| 26 | +``` |
| 27 | + |
| 28 | +Imagine relays are also configured to connect to their nearest neighbors. |
| 29 | +A subscription from a user in Europe, subscribing to a broadcast in Denver, might travel:\ |
| 30 | +`Frankfurt -> London -> New York -> Chicago -> Denver` |
| 31 | + |
| 32 | +At each hop, we deduplicate subscriptions. |
| 33 | +Which means at most only one copy of a track will ever be transmitted between two relays. |
| 34 | + |
| 35 | +<figure> |
| 36 | +  |
| 37 | + <figcaption>**SPOILER**: an upcoming marketing image</figcaption> |
| 38 | +</figure> |
| 39 | + |
| 40 | +For context, the previous architecture was a mesh. |
| 41 | +Every relay would connect to every other relay, which would have resulted in:\ |
| 42 | +`Frankfurt -> Denver` (oof) |
| 43 | + |
| 44 | +Oh yeah and |
| 45 | +Your local relay (snug behind a firewall) can proxy all broadcasts to/from a public CDN: |
| 46 | +```bash |
| 47 | +moq-relay --cluster-connect https://cdn.moq.dev ... |
| 48 | +``` |
| 49 | + |
| 50 | +It'll work with any MoQ CDN that implements enough of the [`moq-transport`](https://datatracker.ietf.org/doc/draft-ietf-moq-transport/) spec (*cough* not Cloudflare yet *cough*). |
| 51 | +But you'll need `moq-lite-03+` to establish multiple connections, otherwise a cycle will ruin your day. |
| 52 | + |
| 53 | +## Standards |
| 54 | +Speaking of standards, the IETF keeps marching forward, so I have to "support" every new draft version lul. |
| 55 | + |
| 56 | +**Supported:** |
| 57 | +- [moq-transport](https://datatracker.ietf.org/doc/draft-ietf-moq-transport/) drafts 14-18. |
| 58 | +- [qmux](https://datatracker.ietf.org/doc/draft-nandakumar-moq-qmux-moqt/) drafts 00-01 |
| 59 | +- [msf](https://datatracker.ietf.org/doc/draft-ietf-moq-msf/) drafts 00-01 |
| 60 | +- [loc](https://datatracker.ietf.org/doc/draft-ietf-moq-loc/) draft 01 |
| 61 | + |
| 62 | +It takes forever to litigate every single addition/change in the IETF. |
| 63 | +I'm trying my best [to publish extensions](https://www.ietf.org/archive/id/draft-lcurley-moq-probe-00.html), but it's such a drain on my time. |
| 64 | + |
| 65 | +So instead the moq.dev libraries **prefer to negotiate:** |
| 66 | +- moq-lite drafts 01-04 |
| 67 | +- hang drafts 00-01 |
| 68 | + |
| 69 | +moq-lite-05 is coming SOON with more stuff. |
| 70 | + |
| 71 | + |
| 72 | +## Muxing |
| 73 | +My philosophy is that we should **not** transmit arbitrary media formats over MoQ. |
| 74 | +It's possible to [transmit TS over MoQ](https://www.ietf.org/archive/id/draft-gregoire-moq-msfts-00.html) verbatim, but then every player/subscriber needs to decode Transport Streams (gross). |
| 75 | + |
| 76 | +Instead, [`moq-mux`](https://doc.moq.dev/lib/rs/crate/moq-mux.html) is our transmuxing layer. |
| 77 | +We can import/export other formats without re-encoding, preserving the intent but not the original structure. |
| 78 | + |
| 79 | +[Here's the codecs we support](https://github.com/moq-dev/moq/tree/main/rs/moq-mux/src/codec): |
| 80 | +- H.264/H.265 |
| 81 | +- AV1 |
| 82 | +- VP8/VP9 |
| 83 | +- Opus |
| 84 | +- AAC |
| 85 | + |
| 86 | +[And the containers we support](https://github.com/moq-dev/moq/tree/main/rs/moq-mux/src/container): |
| 87 | +- fMP4/CMAF |
| 88 | +- FLV/RTMP |
| 89 | +- Matroska/WebM |
| 90 | +- HLS |
| 91 | +- LOC |
| 92 | +- TS |
| 93 | + |
| 94 | +Going back to Transport Streams (gross) as an example. |
| 95 | +When you import a TS stream with `moq-mux`, we'll split A/V into MoQ tracks, but we'll also preserve "unknown" tracks with a special "ts" section. |
| 96 | +If you export the same stream to TS with `moq-mux`, we'll re-assemble the TS stream and reinsert the unknown tracks. |
| 97 | + |
| 98 | +This is a **REALLY BIG DEAL** for broadcast TV. |
| 99 | +Your precious SCTE35 markers are preserved. |
| 100 | +ur welcome |
| 101 | + |
| 102 | +## CLI |
| 103 | +Note that `moq-mux` is available as both a library and a CLI via [`moq-cli`](https://doc.moq.dev/bin/cli.html). |
| 104 | + |
| 105 | +```bash |
| 106 | +# Publish: pipe in a MPEG-TS file |
| 107 | +ffmpeg -re -i input.ts -f mpegts - | \ |
| 108 | + moq-cli publish --url https://cdn.moq.dev/anon --broadcast my-stream ts |
| 109 | + |
| 110 | +# Subscribe: pull MPEG-TS back out and play it |
| 111 | +moq-cli subscribe --url https://cdn.moq.dev/anon --broadcast my-stream --format ts | \ |
| 112 | + ffplay - |
| 113 | +``` |
| 114 | + |
| 115 | +You can download all of the moq binaries (`moq-relay`, `moq-token`, etc) from your favorite package manager: |
| 116 | +- Homebrew |
| 117 | +- .deb/.rpm |
| 118 | +- Docker |
| 119 | +- Nix |
| 120 | +- ??? |
| 121 | + |
| 122 | +Cool I guess? |
| 123 | + |
| 124 | + |
| 125 | +## JSON |
| 126 | +JSON isn't known for its efficiency. |
| 127 | + |
| 128 | +[moq-json](https://github.com/moq-dev/moq/tree/main/rs/moq-json) (Rust) and [`@moq/json`](https://github.com/moq-dev/moq/tree/main/js/json) (Typescript) publish arbitrary JSON over MoQ tracks. |
| 129 | +The first frame in each group is a full snapshot, then subsequent frames are [JSON Merge Patch](https://datatracker.ietf.org/doc/html/rfc7396) deltas. |
| 130 | +We periodically make new groups so late joiners can catch up without having to download the entire stream. |
| 131 | + |
| 132 | +We also stack [moq-flate](https://github.com/moq-dev/moq/tree/main/rs/moq-flate) (Rust) and [`@moq/flate`](https://github.com/moq-dev/moq/tree/main/js/flate) (Typescript) to add DEFLATE compression on top. |
| 133 | +Normally you would compress each frame individually, like a gzip file, but it turns out we can do significantly better by using `Z_SYNC_FLUSH`. |
| 134 | +Each subsequent frame *within the same group* can reuse the previous frames as a compression window, instead of starting from scratch. |
| 135 | + |
| 136 | +Claude ran a benchmark over a synthetic stream of JSON sensor snapshots. |
| 137 | +The type of output you'd expect from a drone. |
| 138 | + |
| 139 | +| Configuration | Bitrate saved | |
| 140 | +| ---------------------------------------- | ------------- | |
| 141 | +| DEFLATE per frame | 39% | |
| 142 | +| JSON Merge Patch | 58% | |
| 143 | +| JSON Merge Patch + DEFLATE per frame | 72% | |
| 144 | +| DEFLATE per group | 89% | |
| 145 | +| **JSON Merge Patch + DEFLATE per group** | **91%** | |
| 146 | + |
| 147 | +I was surprised to see how important `Z_SYNC_FLUSH` is given it's not supported by the [browser implementation](https://developer.mozilla.org/en-US/docs/Web/API/Compression_Streams_API) and [is bugged](https://github.com/101arrowz/fflate/pull/286) in the most popular Javascript implementation. |
| 148 | +Really the main benefit of JSON Merge Patch is to save some CPU cycles. |
| 149 | + |
| 150 | +## Backends |
| 151 | +We've used Quinn for the longest time, but now you can use other QUIC backends! |
| 152 | +Why? IDK different features and performance. |
| 153 | + |
| 154 | +QUIC Backends (WebTransport + raw QUIC): |
| 155 | +- quinn |
| 156 | +- noq |
| 157 | +- quiche (Cloudflare) |
| 158 | +- iroh (p2p) |
| 159 | + |
| 160 | +You can also use non-QUIC backends with Qmux! |
| 161 | +It emulates a QUIC connection, but over a reliable transport. |
| 162 | +We do suffer more from head-of-line blocking but sometimes it be like that. |
| 163 | + |
| 164 | +Non-QUIC Backends (Qmux): |
| 165 | +- WebSocket |
| 166 | +- TLS |
| 167 | +- TCP |
| 168 | +- Unix domain sockets |
| 169 | + |
| 170 | +For example, I'm using unix domain sockets (or TCP) to run localhost workers without paying the TLS/UDP overhead tax. |
| 171 | +It's still Media over QUIC even if QUIC is not used... |
| 172 | + |
| 173 | + |
| 174 | +## UI Polish |
| 175 | +Claude made the web UI look better. |
| 176 | +tfw an AI has better taste than me. |
| 177 | + |
| 178 | +[Clone the repo](https://doc.moq.dev/setup/) and run `just dev` to see: |
| 179 | + |
| 180 | +<figure> |
| 181 | +  |
| 182 | + <figcaption>he a sniffer</figcaption> |
| 183 | +</figure> |
| 184 | + |
| 185 | +You can use the UI on your own site via the [web components](https://doc.moq.dev/bin/web.html): |
| 186 | +```html |
| 187 | +<script type="module"> |
| 188 | + import "https://esm.sh/@moq/watch/element"; |
| 189 | + import "https://esm.sh/@moq/publish/element"; |
| 190 | +</script> |
| 191 | + |
| 192 | +<moq-watch-ui> |
| 193 | + <moq-watch url="https://cdn.moq.dev/demo" name="bbb.hang"> |
| 194 | + <canvas style="max-width: 100%; height: auto; margin: 0 auto; border-radius: 1rem;"></canvas> |
| 195 | + </moq-watch> |
| 196 | +</moq-watch-ui> |
| 197 | +``` |
| 198 | + |
| 199 | +There's also one for `<moq-publish>` which is equally slick. |
| 200 | + |
| 201 | +## Polyglot (Alpha) |
| 202 | +We full [monorepo](https://github.com/moq-dev/moq) now. |
| 203 | + |
| 204 | +`moq-ffi` uses Uniffi to generate async bindings for various languages. |
| 205 | +It's all the same Rust code under the hood, but IDK now you can use it on iOS/Android/whatever. |
| 206 | + |
| 207 | +[MoQKit](https://fishjam.swmansion.com/blog/moq-boy-escaped-the-browser) uses it. |
| 208 | +neat |
| 209 | + |
| 210 | +Supported: |
| 211 | +- [Swift](https://doc.moq.dev/lib/swift/) |
| 212 | +- [Kotlin](https://doc.moq.dev/lib/kt/) |
| 213 | +- [Python](https://doc.moq.dev/lib/py/) |
| 214 | +- [Go](https://doc.moq.dev/lib/go/) (wip) |
| 215 | + |
| 216 | +I'm still working on more ergonomic bindings for each language, hence the (Alpha) tag. |
| 217 | + |
| 218 | +Oh and don't worry, we still have [C support](https://doc.moq.dev/lib/c/) via [`libmoq`](https://doc.moq.dev/lib/rs/crate/libmoq.html). |
| 219 | +`moq-ffi` is for the async languages who don't want to deal with callbacks and APIs designed for dinosaurs. |
| 220 | + |
| 221 | + |
| 222 | +## Production Stuff |
| 223 | +I'm almost done spinning up a PAID MoQ CDN. |
| 224 | +It's like a week or two away, **GET HYPED**. |
| 225 | + |
| 226 | +As part of that, I actually have to make sure everything is production-ready, which means focusing on the boring stuff like: |
| 227 | +- `mTLS`: relays can authorize via client certificates |
| 228 | +- `TLS`: cert reloading and custom root CAs |
| 229 | + |
| 230 | +And more of the static configuration is now possible via a REST API. |
| 231 | +- `--auth-api`: authorize a user |
| 232 | +- `--cluster-connect-api`: discover neighboring relay nodes |
| 233 | + |
| 234 | +And finally my favorite one, but it deserves its own blog post later: |
| 235 | +- `.stats`: deliver MoQ metrics over MoQ |
| 236 | + |
| 237 | +## Conclusion |
| 238 | +I hope you enjoyed this low-energy update. |
| 239 | +Super low. |
| 240 | + |
| 241 | +Written by [@kixelated](https://github.com/kixelated). |
| 242 | + |
0 commit comments