
+ (one segment) and # (rest) wildcards,
retained messages, and a choice of reliable or lossy delivery — all over the
same QUIC connection and ClutchCall relay mesh you already use for voice,
streams, games, and robotics. There is no separate broker to deploy,
secure, or scale: the relay mesh is the fan-out.
Use it for device telemetry, fleet state, config distribution, application
event buses, presence, and any “many publishers, many subscribers, routed by
topic” shape where you want typed pub/sub without operating a message broker.
At a glance
No broker
The relay mesh fans messages out. One operational story shared with every
other modality — nothing extra to run.
MQTT-style topics
Hierarchical
a/b/c topics with + (single segment) and # (multi-segment,
trailing) filters — the semantics you already know from MQTT 3.1.1.Two lanes
A lossy QUIC datagram lane for high-rate telemetry, a reliable ordered lane
for events that must arrive. Pick per message.
Retained messages
Publish current state once; every late-joining subscriber gets the last
retained value on attach.
Wire model
AData client binds to a stable clientId and speaks hierarchical topics.
Each published message carries a small header — the publisher’s clientId
and the full topic — in front of your opaque payload, so subscribers can
both filter and attribute messages without standing up a track per publisher.
Uint8Array / bytes). On the substrate, the SDK maps one MoQT track
per top-level topic segment under the data/<segment> namespace:
sensors/* traffic shares one track; all home/* traffic shares
another. The publisher sends the full topic in the frame header, and the SDK
filters the rest of the path MQTT-style on the subscriber side. The MoQT
capability tag for the track is data.pubsub.
The top-level segment of a filter must be concrete —
sensors/+/temp is
fine, +/room1/temp and # are not. The top-level segment selects the track;
a wildcard there would mean subscribing to every namespace at once. Pick a
concrete first segment and wildcard below it.Topic filters
Same semantics as MQTT 3.1.1:| Filter | Matches |
|---|---|
sensors/room1/temp | exactly that topic |
sensors/+/temp | sensors/<any one segment>/temp |
sensors/# | sensors and everything below it, any depth |
sensors/room1/# | sensors/room1 and everything under it |
events/+/+ | any two segments after events |
+matches exactly one path segment.#matches the rest of the path and must be the final segment of the filter.- An exact topic string matches itself.
Lanes and QoS
Every publish picks a lane:- Datagram lane (default)
- Reliable lane
reliable: false (the default) rides the QUIC datagram lane: lossy,
unordered, lowest latency, no head-of-line blocking. This is the right
choice for high-rate sensor readings and any signal where the freshest
value matters more than every value arriving.retained flag on each DataMessage.
Retained messages
A message published withretained: true is cached by the relay, keyed on
(namespace, topic). Any subscriber whose filter matches that topic receives
the last retained value immediately on attach, before any live messages.
This is the canonical “current state” pattern:
devices/device-7/state (or devices/device-7/#) gets
that payload on attach. Publish a zero-length payload with retained: true
to clear a retained topic — typically on clean shutdown, so a dashboard reads
“offline” without waiting for a heartbeat timeout.
Per-client demux
Because every message carries the publisher’sfromClientId, a single
subscriber can fan in from a whole fleet and still know who sent what — no track
per device. This is the “thousands of devices report to one dashboard” shape,
handled by one subscription and one filter.
When to use it
Reach for data when…
You want topic-routed pub/sub, MQTT wildcards, retained state, or a typed
event bus — and you do not want to run a broker.
Reach elsewhere when…
You need media tracks (use voice / streams), tick-rate game channels
(use games), or ROS 2 / DDS QoS semantics (use robotics).
Why MoQT instead of a broker
- No broker. The relay mesh is the fan-out. One thing to operate, shared with every other modality.
- One auth path. The same tenant token that authorizes voice, streams, and
games authorizes the data plane — the token’s claims carry the tenant and the
clientId. - Native browser client. No MQTT-over-WebSocket bridge. The browser SDK speaks the same wire as your devices over WebTransport.
- Mix with other modalities. A game client publishing match telemetry, or a voice app publishing call events, reuses its existing QUIC connection and token — no second stack.
Architecture
Underneath, data is just frames over MoQT multiplexed on one QUIC connection and fanned out by the relay mesh. The relay data plane runs an AF_XDP kernel-bypass fast path — packets move via eBPF/XSK programs over shared UMEM, zero-copy, with lock-free mcache / dcache rings on a shard-per-core (thread-per-core) reactor driven by io_uring. That is why adding the data modality to an existing app costs one more topic namespace, not one more server to run.Related
- Realtime Tracks — the MoQT primitive underneath.
- Modalities overview — the shared substrate and the full set.
- SDK Methods — the typed
Datasurface.

