
(tenant, robot) pair. Two namespaces are baked in — telemetry
from the robot and commands to the robot — so the two directions never
collide and a controller can never accidentally subscribe to the robot’s own
announce.
When to use it
Fleet telemetry
Stream odometry, LIDAR, battery, and pose from hundreds of robots to a cloud
dashboard or a recorder, each on its own
robot/<id> namespace.Teleoperation
Drive a robot from the cloud —
cmd_vel and goal commands flow down
robot/<id>/ctl on a low-latency lane, telemetry flows back up the same
session.Cross-language demux
A TypeScript dashboard and a Python analytics worker subscribe to the same
CDR track; the type-name prefix lets each pick its own decoder.
Drop-in ROS 2 transport
Swap the local middleware for a ClutchCall-backed transport so a ROS 2
node reaches the fleet over WAN-grade QUIC with zero code change.
Three transports onto the mesh
A robot can reach the relay mesh three different ways. All three land on the same namespaces and the same type-name-prefixed wire frame, so producers and consumers interoperate regardless of how each one connected.- ROS 2 (CDR)
- Zenoh-over-QUIC
- MQTT 3.1.1
The native path. A ROS 2 node serializes its messages to CDR (the DDS
Common Data Representation) and the raw CDR bytes ride the track unchanged.
The type name (
nav_msgs/msg/Odometry) is what the SDK prefixes, so a
subscriber anywhere can hand the payload straight to a CDR decoder.However a device connects, the on-the-wire frame is identical. The transport is
a property of how a peer reached the mesh, not of the data — so a CDR
publisher, a Zenoh node, and an MQTT sensor can all feed one subscriber.
Wire model & track conventions
Each robot maps to a pair of MoQT namespaces. A topic becomes a track name under the relevant namespace; one MoQT track carries one topic.| Direction | Namespace | Example track |
|---|---|---|
| Telemetry (up) | robot/<id> | robot/spot-12 · odom |
| Commands (down) | robot/<id>/ctl | robot/spot-12/ctl · cmd_vel |
Type-name-prefixed CDR frame
Every object on the wire is a self-describing envelope:- The 2-byte big-endian length bounds type names at 65 535 bytes; real ROS 2 names fit in ~64.
- The CDR payload is opaque to the SDK — you hand it the bytes your DDS,
Zenoh, or
serialize_messagealready produced. - The prefix is what makes the stream cross-language: a subscriber gets
(cdr, typeName)and routes to the matching decoder with no schema registry.
QoS → lanes
ROS 2 QoS hints map onto MoQT lanes at the relay. You pass aqos profile when
you create a publication; the relay routes and admits the track by the resulting
capability.
| QoS hint | Lane | Capability |
|---|---|---|
reliability: "reliable" | subgroup stream (ordered) | ros.reliable |
reliability: "best_effort" | QUIC datagram (lossy) | ros.best_effort |
durability: "transient_local" | adds late-join retain | ros.tl_* |
depth: N | per-track group window | — |
- Reliable rides subgroup streams — guaranteed, in order per-publisher. Use it for commands and for telemetry you can’t drop (a map update, a goal).
- Best-effort rides QUIC datagrams — lowest latency, lossy. Use it for high-rate sensor streams (pose, LIDAR) where the freshest sample wins.
- transient_local makes the relay retain the last group(s) so a subscriber that joins late immediately receives the most recent value — the analog of a latched MQTT retained message.
- depth caps the per-track group window the relay holds for late subscribers, mirroring ROS 2 keep-last history.
write.
Drop-in ROS 2 transport
A robot doesn’t have to know the relay exists. A small native bridge runs beside the ROS 2 graph and presents itself as an ordinary middleware transport — the same role a vendor RMW (ROS Middleware) plugin fills. Local nodes publish and subscribe their topics exactly as before; the bridge mirrors those topics into and out of MoQT:Local nodes stay local
Nodes talk to the bridge over the standard middleware interface — DDS or
Zenoh on the robot’s loopback. No application code changes.
Bridge mirrors topics
For each configured topic, the bridge subscribes locally and republishes the
raw CDR onto a
robot/<id> telemetry track, and subscribes a robot/<id>/ctl
command track and republishes onto the local graph.The bridge is described here by role, not by file. From the robot’s point of
view it is “the transport that carries my topics off-box”; from the cloud’s it is
“the fleet ingress”. The SDK in SDK Methods
gives you the same pub/sub surface programmatically when you want to bridge
selected topics yourself.
Architecture
The data plane is one QUIC connection per robot multiplexing every track via MoQT; the relay mesh fans tracks out to subscribers across hosts. Underneath, the engine is shard-per-core with an AF_XDP kernel-bypass NIC fast path, lock-free mcache / dcache rings between shards, and io_uring for async I/O — so a single host fans a high-rate fleet out at line rate without copying through the kernel network stack.- One connection, many tracks. Telemetry and commands for a robot share one QUIC session; MoQT multiplexes the tracks.
- One token. The tenant token that authorizes voice / streams / games authorizes the fleet — no separate broker credentials.
- No broker to run. The relay mesh is the fan-out. There’s no MQTT broker or DDS discovery domain to operate in the cloud.
Related
- SDK Methods — the typed
Roboticssurface. - Cookbook — short task-oriented snippets.
- Recipes — end-to-end worked examples.
- Realtime Tracks — the MoQT primitive underneath.

