# Data — Details

> MQTT-style typed pub/sub over QUIC + MoQT. Hierarchical topics, + / # filters, retained messages, reliable and datagram lanes — no broker to run.

<img src="/images/diagrams/data.png" alt="data data flow" />

The **data** modality is "if you'd reach for MQTT, reach for this instead."
Hierarchical topics, MQTT-style `+` (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

A `Data` 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.

```
[u8 from_len][from_client_id][u8 topic_len][topic][payload]
```

Both header fields are bounded at 255 bytes; the payload is arbitrary opaque
bytes (`Uint8Array` / `bytes`). On the substrate, the SDK maps **one MoQT track
per top-level topic segment** under the `data/<segment>` namespace:

```
sensors/room1/temperature   →   track  data/sensors
home/lights/kitchen/level   →   track  data/home
events/orders/shipped       →   track  data/events
```

So all `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`.

> **NOTE:**
> 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`                    |

Rules, matching MQTT:

- `+` 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:

  <Tab title="Datagram lane (default)">
    `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.
  </Tab>
  <Tab title="Reliable lane">
    `reliable: true` rides an ordered, retried **MoQT subgroup stream**:
    guaranteed delivery, in order per publisher. Use it for application events,
    commands, settlements, and "the alarm latched" — anything you cannot afford
    to drop.
  </Tab>

Under the hood, reliable and retained messages are sent at an elevated priority
on the stream lane; best-effort telemetry stays on the datagram lane. A
subscriber can tell a retained bootstrap message from a live one via the
`retained` flag on each `DataMessage`.

## Retained messages

A message published with `retained: 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:

```ts
// Device announces it is online — sticks until replaced or cleared.
await data.publish({
  topic:    "devices/device-7/state",
  payload:  new TextEncoder().encode(JSON.stringify({ online: true })),
  retained: true,
});
```

A later subscriber to `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.

> **TIP:**
> Pair a retained `.../state` topic (sticky current state) with a lossy
> `.../telemetry` topic (live readings). Subscribers get the snapshot instantly,
> then track the live stream.

## Per-client demux

Because every message carries the publisher's `fromClientId`, 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](/concepts/realtime-tracks) — the MoQT primitive underneath.
- [Modalities overview](/modalities/overview) — the shared substrate and the
  full set.
- [SDK Methods](/modalities/data/sdk-methods) — the typed `Data` surface.
