> For the complete documentation index, see [llms.txt](https://docs.rumi.systems/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.rumi.systems/rumi-core/concepts/messaging-model/mqtt-binding.md).

# MQTT Binding

The MQTT binding provides integration with MQTT 5 message brokers for Rumi microservices.

## Overview

The MQTT binding connects a Rumi microservice to any MQTT 5 broker (for example Mosquitto, HiveMQ, or EMQX). MQTT is a natural fit for the SMA messaging model because both are topic based publish and subscribe systems, so channel keys map directly onto MQTT topics with no delimiter rewriting.

The binding speaks MQTT 5 only. It does not fall back to MQTT 3.1.1, because it relies on an MQTT 5 feature (Correlation Data) to carry Rumi's message metadata alongside the payload.

### Two operating modes

The binding operates in one of two modes, and choosing the right one is the first decision when you configure it:

* **Default (metadata) mode** — the binding carries Rumi's [message metadata](#message-metadata) on the wire (in MQTT 5 Correlation Data) alongside the payload. Use this for **Rumi-to-Rumi** messaging over MQTT, where both ends are Rumi microservices.
* **Raw mode** (`raw_mode=true`) — the binding puts **only the payload** on the topic, with no Rumi metadata. Use this to integrate with an **external, non-Rumi source or sink** (IoT devices, telemetry feeds, an existing MQTT broker). See [Connecting to External Systems](#connecting-to-external-non-rumi-systems-raw-mode).

External integration is the most common reason to use the MQTT binding, so if you are connecting to anything that is not itself a Rumi service, you almost certainly want **raw mode**. It is not the default, and the binding does not infer it — you must set `raw_mode=true` (see the note in [Connecting to External Systems](#connecting-to-external-non-rumi-systems-raw-mode) on what happens if you forget).

For configuration details, see [MQTT Binding Configuration](/rumi-core/guides/developing-applications/configuring-messaging/configuring-bus-connections/mqtt-binding.md).

{% hint style="info" %}
This is a first release of the MQTT binding. It supports `BestEffort` delivery. `Guaranteed` delivery is not yet supported, see [Quality of Service](#quality-of-service) below.
{% endhint %}

## MQTT Topic Format

MQTT uses `/` as its topic level separator, which is the same delimiter Rumi uses for hierarchical channel keys. As a result the binding uses channel keys as MQTT topics directly, unlike the Kafka and JMS bindings which rewrite `/` to `.` for their providers.

For example, a channel with the key `orders/US/IBM` publishes to, and subscribes from, the MQTT topic `orders/US/IBM`.

## Wildcard Topics

MQTT topic filters support two wildcards, and the binding maps Rumi's channel filter wildcards onto them when a channel is joined:

| Rumi filter wildcard | MQTT wildcard | Description                                                                        |
| -------------------- | ------------- | ---------------------------------------------------------------------------------- |
| `*`                  | `+`           | Matches a single topic level.                                                      |
| `>`                  | `#`           | Matches the remainder of the topic. May only appear as the last level of a filter. |

The mapping is applied only on the subscribe path. A published topic name is always fully qualified, since MQTT does not permit wildcards in a published topic.

## Message Metadata

The binding carries Rumi's [message metadata](/rumi-core/concepts/messaging-model.md) in the MQTT 5 **Correlation Data** field, which is the only binary companion slot the protocol offers alongside a payload. The serialized message payload travels in the MQTT payload, and the metadata travels in the correlation data. This mirrors the way the Kafka binding uses the record key to carry metadata.

MQTT 5 user properties were considered for this purpose but not used, because they are UTF-8 string pairs and would require base64 encoding of the binary metadata.

This section describes the **default (metadata) mode**, which is intended for Rumi-to-Rumi messaging. A consequence of carrying metadata in correlation data is that a non-Rumi MQTT subscriber can still read the payload of a message published by Rumi, but it will not interpret the correlation data. In the other direction, a message that arrives on a metadata-mode channel **without** correlation data is treated as **corrupt** and raised as an [Unhandled Message](/rumi-core/guides/developing-applications/authoring-user-code/message-processing/unhandled-messages.md), not silently accepted — to consume metadata-less messages you must use raw mode (below).

## Connecting to External (Non-Rumi) Systems (Raw Mode)

To integrate with a non-Rumi MQTT source or sink, run the channel's bus in **raw mode** by setting `raw_mode=true` on the bus descriptor. In raw mode:

* **Outbound** — the binding publishes **only the serialized message payload** to the resolved topic, with no correlation data. An external subscriber sees a plain MQTT message. Choose a message encoding the external system understands (for example a JSON or bytes message), since there is no Rumi metadata to describe it.
* **Inbound** — a message that arrives with no metadata is accepted (rather than rejected as corrupt) and delivered to the application as a bytes message. A channel subscribes an MQTT topic filter — for example `sensors/+/temp` — and the messages it attracts are delivered to your handlers.

{% hint style="warning" %}
Raw mode is **not** the default and is **not** inferred. If you point a default (metadata-mode) channel at an external broker, every inbound message from the non-Rumi publisher arrives without correlation data and is rejected as a corrupt message (an Unhandled Message), so nothing is delivered to your handlers. If an MQTT integration "receives nothing," the first thing to check is that `raw_mode=true` is set.
{% endhint %}

Metadata mode and raw mode are mutually exclusive per bus: a raw-mode bus cannot also carry Rumi metadata, so a single MQTT bus is either a Rumi-to-Rumi bus or an external-integration bus, not both.

## Quality of Service

SMA defines two qualities of service, and MQTT defines three. The binding maps them as follows:

| SMA QoS      | MQTT QoS          | Status                         |
| ------------ | ----------------- | ------------------------------ |
| `BestEffort` | 0 (at most once)  | Supported.                     |
| `Guaranteed` | 1 (at least once) | Not supported in this release. |

In this release, a `Guaranteed` send is rejected with an exception rather than being silently downgraded to best effort. A `Guaranteed` channel may still be created and joined for inbound traffic, but any attempt to send on it fails.

{% hint style="warning" %}
As with `BestEffort` on other bindings, `BestEffort` over MQTT (QoS 0) can lose messages: an MQTT broker does not queue QoS 0 messages for a subscriber that is not currently connected, so messages published while a microservice is briefly disconnected or has not yet re-established its subscription (for example across a restart) are not delivered. Use `BestEffort` for streams where occasional loss is acceptable, and configure a persistent session (see [`clean_start`](/rumi-core/guides/developing-applications/configuring-messaging/configuring-bus-connections/mqtt-binding.md)) if you need the broker to hold messages across a reconnect.
{% endhint %}

## See Also

* [MQTT Binding Configuration](/rumi-core/guides/developing-applications/configuring-messaging/configuring-bus-connections/mqtt-binding.md) - Configuration reference
* [Messaging Model](/rumi-core/concepts/messaging-model.md) - Overview of Rumi messaging
* [Configuring Bus Connections](/rumi-core/guides/developing-applications/configuring-messaging/configuring-bus-connections.md) - General bus configuration
