The MQTTSuite is a lightweight, production-ready MQTT integration system composed of four focused applications—MQTTBroker, MQTTIntegrator, MQTTBridge, and MQTTCli—all powered by SNode.C, a single-threaded, single-tasking C++ networking framework. Thanks to its very small footprint, MQTTSuite is especially suitable for resource-constrained systems (embedded Linux, routers, SBCs).
| Component | Role | Highlights |
|---|---|---|
| MQTTBroker | Full MQTT broker (MQTT 3.1.1) | Encrypted/unencrypted TCP and WebSockets; IPv4/IPv6 & UNIX domain sockets; built-in Web UI (live clients) |
| MQTTIntegrator | Mapping-driven IoT translator | Subscribes → transforms (INJA templates) → republishes; wildcards (+, #); fine-grained QoS/retain |
| MQTTBridge | Pure client-side bridge | Multiple logical bridges; connects to multiple brokers; selectively bridges topics among them |
| MQTTCli | Command-line client | Subscribe to topics and publish messages; supports TCP, WebSockets, and UNIX sockets |
SNode.C is dual-licensed under MIT and GPL-3.0-or-later. You may choose either license when using the framework.
SPDX-License-Identifier: MIT OR GPL-3.0-or-later
Note: MQTTSuite applications are built on top of SNode.C and follow the respective licensing terms of their repositories and bundled libraries.
Volker Christian ([email protected], [email protected])
- MQTTSuite
- Overview
- Table of Content
- Installation
- MQTTBroker
- Overview & Configuration
- Quick Start (Recommended Flow)
- Disable all but the in-mqtt instance
- Start a plain MQTT listener on TCP/IPv4
- Add a persistent session store (survives restarts)
- Enable the Web Interface (HTTP)
- Persist your configuration using -w (so you don’t repeat flags)
- (Optional) Embed the integrator (after the configuration has been saved)
- (For development only) Use custom HTML templates for the Web UI (after the configuration has been saved)
- Connection Methods
- Web Interface
- Additions & Field-Tested Guidance
- Quick TLS checklist for MQTT(TCP) and MQTT over WebSockets
- Making configuration persistent (recommended)
- WebSockets specifics that often trip clients
- Quick sanity checks with common tools
- Quick sanity checks with MQTTSuites command line tool
- Notes for UNIX domain sockets
- Embedded MQTTIntegrator (when --mqtt-mapping-file is provided)
- Extending transports
- Diagnostics, hardening & tips
- Protocol version note
- Why this layout works well in production
- MQTT Mapping Description
- MQTTIntegrator
- Overview & Configuration
- Quick Start (Recommended Flow)
- Disable all but the in-mqtt instance
- Connect to a plain MQTT broker over TCP/IPv4
- Add a persistent session store (survives restarts)
- Attach your mapping file (canonical test case below)
- Persist your configuration using -w
- (Optional) Use TLS (MQTTS) instead of plain TCP
- (Optional) Use WebSockets (WSS)
- (Optional) Use Unix Domain WebSockets
- Connection Methods
- Canonical Mapping & Test Case
- Behavior aligned with the Mapping Description
- Additions & Field-Tested Guidance
- Protocol version note
- Why this layout works well in production
- MQTTBridge
- MQTTCli
Installation is straightforward:
- Install the SNode.C framework.
- Install required tools and libraries.
- Clone, build, and install MQTTSuite.
Use the detailed platform-specific instructions below.
Development is primarily on Debian Sid; since Debian Bookworm it also builds cleanly on Debian stable. With the required tools and libraries installed, it should compile on most Linux distributions.
Known good targets:
- x86-64 (PC/servers)
- ARM 32/64-bit (e.g., Raspberry Pi 3/4/5)
- OpenWrt 23.05.0 and later (all architectures)
- Android via Termux (documentation in preparation)
SNode.C leverages C++20 features; therefore recent compilers are required.
- GCC ≥ 12.2
- Clang ≥ 13.0
Either toolchain is supported.
Some tools and libraries must be present. A few libraries are bundled within MQTTSuite.
git— https://git-scm.com/cmake— https://cmake.org/make— https://www.gnu.org/software/make/ orninja— https://ninja-build.org/g++— https://gcc.gnu.org/ orclang— https://clang.llvm.org/pkg-config— https://www.freedesktop.org/wiki/Software/pkg-config/
iwyu— https://include-what-you-use.org/clang-format— https://clang.llvm.org/docs/ClangFormat.htmlcmake-format— https://cmake-format.readthedocs.io/doxygen— https://www.doxygen.nl/
- SNode.C – Simple NODE in C++: https://github.com/SNodeC/snode.c
- libfmt (≥ 11.0.0) development files — https://github.com/fmtlib/fmt
- JSON Schema Validator for Modern C++ — https://github.com/pboettch/json-schema-validator
- INJA: A Template Engine for Modern C++ — https://github.com/pantor/inja
Follow the SNode.C installation guide:
https://github.com/SNodeC/snode.c?tab=readme-ov-file#installation
sudo apt update
sudo apt install libfmt-dev
## Recommended for building:
## sudo apt install git cmake build-essential ninja-build pkg-configmkdir mqttsuite
cd mqttsuite
git clone --recurse-submodules https://github.com/SNodeC/mqttsuite.git
mkdir build
cd build
cmake ../mqttsuite ## optionally: -G Ninja -DCMAKE_BUILD_TYPE=Release
make -j"$(nproc)" ## or: ninja
sudo make install ## or: sudo ninja install
sudo ldconfigTip: Use all CPU threads (
-j$(nproc)) to speed up the build—especially useful on SBCs.
Assumptions: You have SSH and SFTP access to the router, and WAN connectivity is configured.
You do not install SNode.C manually; the OpenWrt build will pull it in automatically via the feed.
High-level steps:
- Choose and download an OpenWrt SDK
- Patch the SDK to add the SNode.C (snodec) feed (contains the MQTTSuite feed)
- Install the MQTTSuite package and dependencies
- Configure the SDK
- Cross-compile MQTTSuite
- Deploy the resulting
.ipkpackages to the router
Download an SDK (23.05.0-rc1 or later) from the OpenWrt download site and extract it to <DIR>.
Example (Netgear MR8300, IPQ4019):
cd <DIR>
wget -qO - https://downloads.openwrt.org/releases/23.05.0-rc2/targets/ipq40xx/generic/openwrt-sdk-23.05.0-rc2-ipq40xx-generic_gcc-12.3.0_musl_eabi.Linux-x86_64.tar.xz | tar JxThis creates an SDK directory like:
openwrt-sdk-<version>-<arch>-<atype>_<compiler>-<cver>_<libc>_<abi>.Linux-x86_64 → referred to as <SDK_DIR>.
For the example above:
<version>= 23.05.0-rc2<arch>= ipq40xx<atype>= generic<compiler>= gcc<cver>= 12.3.0<libc>= musl<abi>= eabi
This feed contains also the definitions for compiling MQTTSuite.
cd <SDK_DIR>
echo "src-git snodec https://github.com/SNodeC/OpenWRT" >> feeds.conf.defaultcd <SDK_DIR>
./scripts/feeds update base packages snodec ## only these feeds are needed
./scripts/feeds install mqttsuitecd <SDK_DIR>
make defconfigYou can keep defaults. To customize SNode.C/MQTTSuite options:
cd <SDK_DIR>
make menuconfig
## Navigate: Network -> SNode.C
## or: Network -> MQTTSuitecd <SDK_DIR>
make package/mqttsuite/compile -j"$(nproc)"What happens:
- Feeds and package metadata are refreshed locally.
- All direct/indirect dependencies are fetched and built as needed.
- MQTTSuite is cloned and cross-compiled.
After building, the
.ipkpackages for MQTTSuite and SNode.C are usually found under:
<SDK_DIR>/bin/packages/<architecture>/snodec/
Copy and install the generated packages onto the router:
cd <SDK_DIR>/bin/packages/<architecture>/snodec
sftp root@<router-ip>
cd /tmp
put snode.c*.ipk
put mqttsuite*.ipk
exit
ssh root@<router-ip>
cd /tmp
opkg install snode.c*.ipk mqttsuite*.ipk ## add --force-reinstall to overwrite same version
exitOn first installation you may see errors about missing dependencies from the base OpenWrt repos. Install those with
opkgand rerun theopkg installfor MQTTSuite/SNode.C.
During package installation, a new UNIX group (with member root) is created and used for managing config, log, and PID files.
Note: log out and log in again to activate the new group membership.
- Persist-once workflow: All MQTTSuite applications support
--write-config/-w. Start once with explicit flags, verify, then run with no flags. - Use UNIX domain sockets for same-host integrations; they reduce overhead and surface area.
- TLS everywhere off-host: Use TLS (and client auth where appropriate) when crossing untrusted networks.
- Parallel builds: Always use
-j$(nproc)(or Ninja) to accelerate compiles.
The MQTTBroker is the central broker of the MQTTSuite and supports multiple connection instances across several transport protocols. Additional transports (e.g., Bluetooth) can be added in the mqttbroker.cpp source code.
in-mqtt: MQTT over IPv4in-mqtts: MQTTS over IPv4in6-mqtt: MQTT over IPv6in6-mqtts: MQTTS over IPv6un-mqtt: MQTT over Unix Domain Socketsun-mqtts: MQTTS over Unix Domain Socketsin-http: Web Interface and MQTT over WS via IPv4in-https: Web Interface and MQTTS over WSS over IPv4in6-http: Web Interface and MQTT over WS via IPv6in6-https: Web Interface and MQTTS over WSS via IPv6un-http: Web Interface and MQTT over WS via Unix Domain Socketsun-https: Web Interface and MQTTS over WSS via Unix Domain Sockets
One can control which instances are built by enabling or disabling them through the available cmake options.
All aspects—such as port numbers, UNIX sun paths, and per-listener options—are configurable via the SNode.C configuration system. You can set options in code, on the command line, or by using a configuration file.
If encrypted access is required, suitable certificates need to be provided.
- Persistent sessions: Configure a session store if you want client sessions to survive broker restarts by adding the command-line option
--mqtt-session-store <path-to-session-store-file>. - Embedded integrator: If the MQTTBroker should also act as an integrated MQTTIntegrator, provide a mapping description file via
--mqtt-mapping-file <path-to-mqtt-mapping-file.json>. - Web UI templates: The path to the HTML templates for the MQTTBroker Web Interface can be set with
--html-dir <dir-of-html-templates>. The default directory/var/www/mqttsuite/mqttbrokeris already configured inmqttbroker.cpp. - Persisting options: All three options above can be made persistent by storing their values in a configuration file; append
--write-configor-wto the command line.
Per default all supported connection instances are enabled. Unused instances need to be disabled.
mqttbroker in-mqtts --disabled
in6-mqtt --disabled
in6-mqtts -disabled
un-mqtt --disabled
un-mqtts --disabled
in-http --disabled
in-https --disabled
in6-http --disabled
in6-https --disabled
un-http --disabled
un-https --disabled
-wmqttbroker in-mqtt \
local --host 0.0.0.0 \
--port 1883mqttbroker --mqtt-session-store /var/lib/mqttsuite/mqttbroker/session.store \
in-mqtt \
local --host 0.0.0.0 \
--port 1883mqttbroker --mqtt-session-store /var/lib/mqttsuite/mqttbroker/session.store \
in-mqtt \
local --host 0.0.0.0 \
--port 1883
in-http \
--disabled=false \
local --host 0.0.0.0 \
--port 8080mqttbroker --mqtt-session-store /var/lib/mqttsuite/mqttbroker/session.store \
in-mqtt \
local --host 0.0.0.0 \
--port 1883
in-http \
--disabled=false \
local --host 0.0.0.0 \
--port 8080 \
-wFrom now on, a plain mqttbroker starts with the stored configuration.
mqttbroker --mqtt-mapping-file /etc/mqttsuite/mappings/mqtt-mapping.json(For development only) Use custom HTML templates for the Web UI (after the configuration has been saved)
mqttbroker --html-dir /var/www/mqttsuite/mqttbrokerMQTT clients can connect via IPv4 and IPv6, using both encrypted and unencrypted channels.
| Instance | Protocol | Encryption | Local Port |
|---|---|---|---|
in-mqtt |
IPv4 | No | 1883 |
in-mqtts |
IPv4 | Yes | 8883 |
in6-mqtt |
IPv6 | No | 1883 |
in6-mqtts |
IPv6 | Yes | 8883 |
For local (same-host) communication, the broker also supports UNIX domain sockets.
| Instance | Encryption | Local Sun Path |
|---|---|---|
un-mqtt |
No | /tmp/mqttbroker-un-mqtt |
un-mqtts |
Yes | /tmp/mqttbroker-un-mqtts |
Clients can connect via WebSockets over both IPv4 and IPv6.
| Instance | Protocol | Encryption | Local Port | Request Target | Sec-WebSocket-Protocol |
|---|---|---|---|---|---|
in-http |
IPv4 | No | 8080 |
/ or /ws |
mqtt |
in-https |
IPv4 | Yes | 8088 |
/ or /ws |
mqtt |
in6-http |
IPv6 | No | 8080 |
/ or /ws |
mqtt |
in6-https |
IPv6 | Yes | 8088 |
/ or /ws |
mqtt |
For local (same-host) communication, the broker also supports WebSockets over UNIX domain sockets.
| Instance | Encryption | Local Sun Path | Request Target | Sec-WebSocket-Protocol |
|---|---|---|---|---|
un-http |
No | /tmp/mqttbroker-un-http |
/ or /ws |
mqtt |
un-https |
Yes | /tmp/mqttbroker-un-https |
/ or /ws |
mqtt |
The MQTTBroker Web Interface provides real-time visibility into active client connections.
| Instance | Protocol | Encryption | Local Port | Request Target |
|---|---|---|---|---|
in-http |
IPv4 | No | 8080 |
/ or /clients |
in-https |
IPv4 | Yes | 8088 |
/ or /clients |
in6-http |
IPv6 | No | 8080 |
/ or /clients |
in6-https |
IPv6 | Yes | 8088 |
/ or /clients |
For local (same-host) communication, the broker also supports WebSockets over UNIX domain sockets.
| Instance | Encryption | Local Sun Path | Request Target |
|---|---|---|---|
un-http |
No | /tmp/mqttbroker-un-http |
/ or /clients |
un-https |
Yes | /tmp/mqttbroker-un-https |
/ or /client |
- Prepare the server certificate chain and private key; include a CA file/dir if you validate client certs or upstreams.
- Apply via the instance’s
tlssection (e.g.,in-mqtts,in6-mqtts,in-https,in6-https). - Verify CN/SAN covers the hostname clients use.
- Lock down key file permissions (e.g.,
chmod 600for the key, owned by the service user). - After a successful run, append
-wto persist TLS configuration.
mqttbroker in-mqtts \
local --host 0.0.0.0 \
--port 8883 \
tls --cert /etc/ssl/mqttsuite/server.crt \
--cert-key /etc/ssl/mqttsuite/server.key \
--ca-cert /etc/ssl/mqttsuite/ca.crtmqttbroker in-https \
local --host 0.0.0.0 \
--port 8088 \
tls --cert /etc/ssl/mqttsuite/server.crt
--cert-key /etc/ssl/mqttsuite/server.key- Start the broker with your desired CLI options; verify behavior.
- Run once with
-w(or--write-config) to save the configuration. - Thereafter, start with just
mqttbroker(and override via flags only when needed).
Commonly persisted options:
--mqtt-session-store <path>— persist sessions across restarts.--mqtt-mapping-file <path.json>— enable the embedded integrator.--html-dir <path>— point the Web UI to your HTML templates.
- Subprotocol: ensure clients send
Sec-WebSocket-Protocol: mqtt. Most libraries expose this as a “subprotocol” option. - Request target: use
/or/ws(both are accepted by the WS and WSS instances). - Reverse proxies: preserve
Upgrade,Connection, andSec-WebSocket-Protocolheaders through the proxy.
## Plain MQTT over TCP
mosquitto_sub -h 127.0.0.1 -p 1883 -t '#' -v
mosquitto_pub -h 127.0.0.1 -p 1883 -t 'test/topic' -m 'hello'
## MQTT over WebSockets (HTTP)
mosquitto_sub -h 127.0.0.1 -p 8080 -t '#' -v --protocol websockets## Subscribe (plain MQTT)
mqttcli in-mqtt \
remote --host 127.0.0.1 \
--port 1883 \
sub --topic '#'
## Publish (plain MQTT)
mqttcli in-mqtt \
remote --host 127.0.0.1 \
--port 1883 \
pub --topic 'test/topic' \
--message 'hello'
## MQTT over WebSockets (HTTP)
mqttcli in-wsmqtt \
remote --host 127.0.0.1 \
--port 8080 \
pub --topic 'test/topic' \
--message 'hello'
## MQTT over Unix Domain Sockets
mqttcli un-mqtt \
remote --sun-path /tmp/mqttbroker-un-mqtt \
pub --topic 'test/topic' \
--message 'hello'
## Subscribe and publish at once
mqttcli in-mqtt \
remote --host 127.0.0.1 \
--port 1883 \
sub --topic '#' \
pub --topic 'test/topic' \
--message 'hello'- Use the UNIX sockets (
/tmp/mqttbroker-un-mqtt*) for same-host services; they offer low overhead and simple isolation. - Place sockets under
/runor/var/runfor service-managed lifecycles. - Ensure appropriate ownership/group (e.g., a dedicated
mqttsuitegroup) and permissions, so clients can connect without elevating privileges.
- The broker can embed the MQTTIntegrator to transform/normalize payloads and remap topics without touching devices.
- Typical uses:
- Convert vendor-specific JSON into a normalized schema.
- Scale/rename fields (e.g.,
temp→temperature, multiply by 0.1). - Route subsets of topics to dashboards or database consumers.
Keep your mapping JSON in version control and validate changes in staging before production.
- Thanks to SNode.C’s layered network design, adding transports (e.g., Bluetooth RFCOMM/L2CAP) follows the same instance pattern as TCP/IPv6/UNIX.
- Define endpoint parameters (address/port, channel, or path), optionally layer TLS (where applicable), and expose configuration via the broker’s CLI/config.
Diagnostics
- If a listener fails to start, check for “port in use” conflicts or permission issues on UNIX sockets.
- For WebSockets upgrade failures, verify the subprotocol and target path.
Hardening
- Bind to specific interfaces as needed (e.g.,
--listen 127.0.0.1for local-only). - Use TLS for any untrusted network segments.
- Restrict access to private keys and UNIX sockets (ownership/mode).
- Apply firewall rules to limit exposure to necessary ports only.
Common pitfalls
- TLS hostname mismatch: client connects via a hostname not covered by CN/SAN.
- Missing subprotocol on WS: negotiation fails without
mqttsubprotocol. - Permission denied on UNIX sockets: fix group membership and file mode.
- The broker implements MQTT 3.1.1. Configure client libraries accordingly unless you intentionally diverge.
- Uniform instances: Every listener (TCPv4/v6, UNIX, WS) is an instance with consistent options (listen address/port or path, TLS, limits). This keeps deployments predictable.
- Predictable defaults: Conventional ports (
1883/8883,8080/8088) and request targets (/or/ws) make client configuration straightforward. - Persist-once workflow: Use
-wonce, then keep day-to-day ops minimal and repeatable.
As optional for the MQTTBroker a MQTT mapping description is mandatory for the MQTTIntegrator.
An IoT app’s logic in MQTTSuite is expressed as a JSON mapping file.
This file tells the MQTTIntegrator or MQTTBroker how to:
- subscribe to incoming MQTT topics,
- translate (remap) the topics and payloads, and
- publish the translated messages back to the same broker connection.
A complete example lives in the Canonical Mapping & Test Case section of the MQTTIntegrator description.
The file must validate against the schema at lib/mapping-schema.json.
A mapping description has three top-level objects:
discover_prefix— prefix for discovery topics (currently not used).connection— MQTT client options used by the integrator.mapping— the actual topic tree and translation rules.
{
"discover_prefix": "snode.c",
"connection": {
"keep_alive": 60,
"client_id": "Client",
"clean_session": true,
"will_topic": "will/topic",
"will_message": "Last Will",
"will_qos": 0,
"will_retain": false,
"username": "Username",
"password": "Password"
},
"mapping": {
/* see sections below */
}
}| Field | Type | Default | Notes |
|---|---|---|---|
keep_alive |
integer | 60 |
Seconds between PINGREQs. |
client_id |
string | "" |
Logical client name. |
clean_session |
boolean | true |
Start clean or resume a retained session. |
will_topic |
string | "" |
Last Will topic. |
will_message |
string | "" |
Last Will payload. |
will_qos |
integer | 0 |
0…2. |
will_retain |
boolean | false |
Retain Last Will. |
username |
string | "" |
Username (optional). |
password |
string | "" |
Password (optional). |
The integrator uses these as client settings to connect to the broker it will subscribe to and republish on.
mapping defines a topic hierarchy and subscriptions with translation rules.
- A topic level is:
- a single object describing one topic, or
- an array of such objects describing sibling topics.
- Topic levels can be recursively nested to model trees.
topic_level.namemay be a literal (no wildcards) or a single-character MQTT wildcard:+→ single-level wildcard#→ multi-level wildcard (typically used as a leaf)
Single topic level
"mapping": {
"topic_level": {
"name": "topic_level_name"
}
}Nested topic levels
"mapping": {
"topic_level": {
"name": "parent",
"topic_level": {
"name": "child"
}
}
}Siblings with a nested child
"mapping": {
"topic_level": [
{ "name": "sibling_1" },
{
"name": "sibling_2",
"topic_level": { "name": "child_of_sibling_2" }
}
]
}Single-level wildcard (+): subscribe to home/+/temperature
"mapping": {
"topic_level": {
"name": "home",
"topic_level": [
{
"name": "+",
"topic_level": {
"name": "temperature"
}
}
]
}
}Multi-level wildcard (#): subscribe to everything below sensors/#
"mapping": {
"topic_level": {
"name": "sensors",
"topic_level": {
"name": "#"
}
}
}The integrator performs correct wildcard matching when subscribing and dispatching to the defined
subscription.
"mapping": {
"topic_level": {
"name": "Home",
"topic_level": [
{
"name": "Bedroom",
"topic_level": [
{ "name": "..." },
{ "name": "..." }
]
},
{ "name": "Garage" },
{
"name": "Kitchen",
"topic_level": [
{
"name": "Fridge",
"topic_level": [
{ "name": "Temperature" },
{ "name": "IceLevel" }
]
},
{ "name": "Coffeemaker" }
]
}
]
}
}A topic_level can contain a subscription object.
This instructs the integrator to subscribe to that concrete topic (using the topic path implied by the nested names) and map incoming messages to new topics/payloads.
"topic_level": {
"name": "device",
"topic_level": {
"name": "sensor",
"subscription": {
"type": "binary_sensor",
"qos": 2,
/* one of: static / value / json (or an array of them) */
}
}
}subscription.type— freeform label (useful for UIs or discovery).subscription.qos— QoS used when the integrator subscribes at the broker (0…2).
Publishing QoS is set per mapping via its ownqosfield (see below).- Exactly one mapping section is required (or an array of them):
static— string-match mapping (exact incoming payload → mapped payload).value— template mapping wheremessageis a scalar value.json— template mapping wheremessageis a JSON object.
Each mapping section also accepts arrays (
[ … ]) to apply multiple mappings for the same subscription.
All mapping sections share commons:
mapped_topic(string, required) — destination topic.
May be a template (supports{{ ... }},{{## ...}} … {{/ ...}}) or a plain topic string (no+/#).retain(boolean, defaultfalse) — set MQTT retain on publishes.qos(integer0…2, default0) — PUBLISH QoS for the mapped message.
(Independent ofsubscription.qos.)
Exact string-to-string payload conversion.
"static": {
"mapped_topic": "other_device/some_actuator/set",
"message_mapping": [
{ "message": "pressed", "mapped_message": "on" },
{ "message": "released", "mapped_message": "off" }
],
"retain": false,
"qos": 0
}Treat the incoming payload as a scalar bound to the template variable message.
"value": {
"mapped_topic": "other_device/some_actuator/set",
"mapping_template": "{% if message == \"pressed\" %}on{% else if message == \"released\" %}off{% endif %}",
"retain": false,
"qos": 0
}Treat the incoming payload as a JSON object, available as message inside the template.
"json": {
"mapped_topic": "other_device/some_actuator/set",
"mapping_template": "{{ message.time.start }} to {{ message.time.end + 1 }}pm",
"retain": false,
"qos": 0
}{ "time": { "start": 5, "end": 10 } }-
For template mappings (
value/json), an optional field is available:"suppressions": []Use this list for implementation-specific template controls. If unused, keep it empty (
[]).
Inside mapping, you may provide an optional plugins array:
"mapping": {
"plugins": ["pluginA", "pluginB"],
"topic_level": { /* … */ }
}This is a list of plugin identifiers that the integrator may use to extend mapping behavior.
(Behavior is implementation-specific; leave empty if not needed.)
{
"discover_prefix": "",
"connection": {
"keep_alive": 60,
"clean_session": true
},
"mapping": {
"topic_level": {
"name": "devices"
}
}
}{
"mapping": {
"topic_level": {
"name": "devices",
"topic_level": {
"name": "button",
"subscription": {
"type": "binary_sensor",
"qos": 0,
"static": {
"mapped_topic": "actuators/light/set",
"message_mapping": [
{ "message": "pressed", "mapped_message": "on" },
{ "message": "released", "mapped_message": "off" }
]
}
}
}
}
}
}"subscription": {
"type": "thermo",
"qos": 1,
"json": {
"mapped_topic": "sensors/room1/summary",
"mapping_template": "T={{ message.temp }};H={{ message.hum }}"
}
}ajv validate -s lib/mapping-schema.json -d your-mapping.jsonmqttintegrator --mqtt-mapping-file your-mapping.json- Use wildcards deliberately.
+is perfect for “same depth, many devices”;#is best at a leaf to catch everything below a prefix. - Start simple. Verify flow with a
staticmapping; then move tovalue/jsontemplates. - Arrays unlock fan-out.
static/value/jsonaccept arrays—emit multiple outputs from one input. - Decide
retainintentionally. For derived state (e.g., “current mode”),retain: trueis often useful; for events, preferfalse. - QoS roles are separate.
subscription.qos= subscribe QoS; mappingqos= publish QoS. - Validate early. Keep the schema in CI; reject invalid mappings before deployment.
- Version control. Store mapping files with your app; review changes and test on staging.
- Root:
mappingis required. mapping.topic_level: a single object or an array of them.
Eachtopic_levelrequiresnameand either a nestedtopic_levelor asubscription.topic_level.name: may be a literal or a single-char wildcard+or#(per schema pattern).
#is typically used at a leaf.- Mapping sections: require either or an array of
static,value, orjson(each may be an array). mapped_topic: non-empty; either a plain topic (no+/#) or a template expression.
Model the topic tree (literals and +/#) → choose subscription points (with subscribe QoS) → translate via static / value / json (with publish QoS) → validate with the schema → run the integrator.
The MQTTIntegrator is the lightweight transformation engine of the MQTTSuite. It subscribes to topics on an MQTT broker, translates topics/payloads using the mapping description (presented earlier), and publishes the translated messages back to the same broker connection. It is not a multi-broker bridge; there are no src-* / dst-* pairs.
Additional transports can be added in the source: mqttintegrator.cpp.
in-mqtt: MQTT over IPv4 (client to remote1883)in-mqtts: MQTTS over IPv4 (client to remote8883)in6-mqtt: MQTT over IPv6 (client to remote1883)in6-mqtts: MQTTS over IPv6 (client to remote8883)un-mqtt: MQTT over Unix Domain Sockets (client to remote sun path)un-mqtts: MQTTS over Unix Domain Sockets (client to remote sun path)in-wsmqtt: MQTT over WebSockets via IPv4 (client to remote8080, target/ws)in-wsmqtts: MQTTS over WebSockets via IPv4 (client to remote8088, target/ws)in6-wsmqtt: MQTT over WebSockets via IPv6 (client to remote8080, target/ws)in6-wsmqtts: MQTTS over WebSockets via IPv6 (client to remote8088, target/ws)un-wsmqtt: MQTT over Unix Domain WebSockets (client to remote sun path, target/ws)un-wsmqtts: MQTTS over Unix Domain WebSockets (client to remote sun path, target/ws)
One can control which instances are built by enabling or disabling them through the available cmake options.
All aspects—such as remote host/port, UNIX sun paths, WebSocket request targets, and per-connection options—are configurable via the SNode.C configuration system. You can set options in code, on the command line, or via a configuration file.
If encrypted access to the broker is required, suitable certificates need to be provided for the client connection.
- Persistent sessions: Configure a session store if you want the client session to survive restarts:
--mqtt-session-store <path-to-session-store-file>. - Mapping file (required for translations): Provide
--mqtt-mapping-file <path-to-mqtt-mapping-file.json>.
The mapping syntax, wildcard support (+,#), subscribe QoS vs publish QoS, and templating are documented in the MQTT Mapping Description section placed before this one. - Active instances by default: After installation, all connection instances are enabled. Disable unused ones explicitly with
--disabledon those instances. - Persisting options: Use
--write-configor-wonce to store current options in the configuration file.
Per default, all supported connection instances are enabled. Disable what you don’t use and keep a single, clear path to your broker.
mqttintegrator in-mqtts --disabled
in6-mqtt --disabled
in6-mqtts --disabled
un-mqtt --disabled
un-mqtts --disabled
in-wsmqtt --disabled
in-wsmqtts --disabled
in6-wsmqtt --disabled
in6-wsmqtts --disabled
un-wsmqtt --disabled
un-wsmqtts --disabledmqttintegrator in-mqtt \
remote --host 127.0.0.1 \
--port 1883mqttintegrator --mqtt-session-store /var/lib/mqttsuite/mqttintegrator/session.store \
in-mqtt \
remote --host 127.0.0.1 \
--port 1883mqttintegrator --mqtt-session-store /var/lib/mqttsuite/mqttintegrator/session.store \
--mqtt-mapping-file /etc/mqttsuite/mappings/mapping-example.json \
in-mqtt \
remote --host 127.0.0.1 \
--port 1883mqttintegrator --mqtt-session-store /var/lib/mqttsuite/mqttintegrator/session.store \
--mqtt-mapping-file /etc/mqttsuite/mappings/mapping-example.json \
in-mqtt \
remote --host 127.0.0.1 \
--port 1883 \
-wFrom now on, a plain mqttintegrator starts with the stored configuration.
mqttintegrator in-mqtts --disabled=false \
remote --host broker.example.org \
--port 8883 \
tls --cert /etc/ssl/mqttsuite/client.crt \
--cert-key /etc/ssl/mqttsuite/client.key \
--ca-cert /etc/ssl/mqttsuite/ca.crtmqttintegrator in-wsmqtts --disabled=false \
remote --host broker.example.org \
--port 8088 \
tls --ca-cert /etc/ssl/mqttsuite/ca.crtmqttintegrator un-wsmqtt --disabled=false \
remote --sun-path /tmp/mqttbroker-un-http
mqttintegrator un-wsmqtts --disabled=false \
remote --sun-path /tmp/mqttbroker-un-https3 \
tls --ca-cert /etc/ssl/mqttsuite/ca.crtThe MQTTIntegrator uses a client connection to a single broker. All tables show remote endpoints (not local listeners).
Disable unused instances with --disabled.
| Instance | Protocol | Encryption | Remote Port |
|---|---|---|---|
in-mqtt |
IPv4 | No | 1883 |
in-mqtts |
IPv4 | Yes | 8883 |
in6-mqtt |
IPv6 | No | 1883 |
in6-mqtts |
IPv6 | Yes | 8883 |
| Instance | Encryption | Remote Sun Path |
|---|---|---|
un-mqtt |
No | /tmp/mqttbroker-un-mqtt |
un-mqtts |
Yes | /tmp/mqttbroker-un-mqtts |
| Instance | Protocol | Encryption | Remote Port | Request Target | Sec-WebSocket-Protocol |
|---|---|---|---|---|---|
in-wsmqtt |
IPv4 | No | 8080 |
/ws |
mqtt |
in-wsmqtts |
IPv4 | Yes | 8088 |
/ws |
mqtt |
in6-wsmqtt |
IPv6 | No | 8080 |
/ws |
mqtt |
in6-wsmqtts |
IPv6 | Yes | 8088 |
/ws |
mqtt |
| Instance | Encryption | Remote Sun Path | Request Target | Sec-WebSocket-Protocol |
|---|---|---|---|---|
un-wsmqtt |
No | /tmp/mqttbroker-un-http |
/ws |
mqtt |
un-wsmqtts |
Yes | /tmp/mqttbroker-un-https |
/ws |
mqtt |
- For all WS/WSS/UDS-WS instances, the integrator uses
Sec-WebSocket-Protocol: mqttand the request target/ws. - All instances above are enabled by default after installation; deactivate any unneeded instance with
--disabled.
This minimal, schema-valid mapping subscribes to kitchen/thermostat/1 and publishes to kitchen/heating/1/set:
"on"iftemp < 21.0"off"iftemp > 23.0- no publish in the 21.0–23.0 deadband
{
"discover_prefix": "snode.c",
"connection": {
"keep_alive": 60,
"client_id": "Client",
"clean_session": true,
"will_topic": "will/topic",
"will_message": "Last Will",
"will_qos": 0,
"will_retain": false,
"username": "Username",
"password": "Password"
},
"mapping": {
"topic_level": {
"name": "kitchen",
"topic_level": {
"name": "thermostat",
"topic_level": {
"name": "1",
"subscription": {
"type": "thermostat",
"qos": 0,
"json": {
"mapped_topic": "kitchen/heating/1/set",
"mapping_template": "{% if message.temp < 21.0 %}on{% else if message.temp > 23.0 %}off{% endif %}",
"retain": false,
"qos": 0,
"suppressions": [""]
}
}
}
}
}
}
}mqttintegrator --mqtt-session-store ~/session.store \
--mqtt-mapping-file ~/mapping-example.json \
in-mqtt \
remote --host 127.0.0.1 \
--port 1883mqttintegrator --mqtt-session-store ~/session.store \
--mqtt-mapping-file ~/mapping-example.json \
in-mqtt
remote --host 127.0.0.1
--port 1883 \
-wmqttintegrator## Observe mapped results
mqttcli in-mqtt
remote --host 127.0.0.1 \
--port 1883 \
sub --topic 'kitchen/heating/1/set'
## 20.9 → "on"
mqttcli in-mqtt
remote --host 127.0.0.1 \
--port 1883 \
pub --topic 'kitchen/thermostat/1'
--message '{"temp":20.9}'
## 23.5 → "off"mqttcli in-mqtt
remote --host 127.0.0.1 \
--port 1883 \
pub --topic 'kitchen/thermostat/1'
--message '{"temp":23.5}'
## 22.7 → deadband → no publish
mqttcli in-mqtt
remote --host 127.0.0.1 \
--port 1883 \
pub --topic 'kitchen/thermostat/1'
--message '{"temp":22.7}'QoS roles: subscription.qos controls the subscribe QoS. The json.qos inside the mapping controls the publish QoS for the mapped message.
- The mapping file defines where the integrator places subscriptions in the topic tree (including support for
+and#intopic_level.name). The integrator performs correct wildcard matching when subscribing and dispatching. subscription.qosspecifies the QoS used to subscribe at the broker; it does not control publish QoS.- Each mapping rule (in
static,value, orjson) has its own publishqosandretainflags and amapped_topic(string or template). - Arrays are allowed (
static/value/json), enabling fan-out (multiple publishes per inbound message). - The integrator republishes transformed messages back to the same broker connection from which they were received.
- Provide client certificate & key if your broker enforces mutual TLS; otherwise a CA bundle is often sufficient.
- Ensure the broker hostname you connect to is covered by the server certificate’s CN/SAN (for TCP/WSS).
- Lock down permissions on key files (e.g.,
chmod 600, owned by the service user). - Configure TLS under the chosen instance’s
tlssubsection.
mqttintegrator in-mqtts \
remote --host broker.example.org \
--port 8883 \
tls --ca-cert /etc/ssl/mqttsuite/ca.crt \
--cert /etc/ssl/mqttsuite/cert.crt \
--cert-key /etc/ssl/mqttsuite/cert.key \
--cert-key-password 'some-password'mqttintegrator in-wsmqtts \
remote --host broker.example.org \
--port 8088 \
tls --ca-cert /etc/ssl/mqttsuite/ca.crtmqttintegrator un-wsmqtts \
remote --sun-path /tmp/mqttbroker-un-https- Start with explicit CLI flags; verify connectivity and message flow.
- Run once with
-w(or--write-config) to save the configuration. - Thereafter, use
mqttintegratorwithout flags; override only deltas when needed.
Commonly persisted options:
--mqtt-session-store <path>— keep client session state across restarts.--mqtt-mapping-file <path.json>— apply your transformation/normalization rules.
- Subprotocol: the integrator sets
Sec-WebSocket-Protocol: mqtt. Ensure the broker accepts it. - Request target: use
/ws(do not change unless your broker explicitly uses a different target). - Through proxies:
Upgrade,Connection, andSec-WebSocket-Protocolmust pass through unchanged. - UDS-WS: ensure the sun path matches the broker’s WS UDS listener (e.g.,
/tmp/mqttbroker-un-httpor/tmp/mqttbroker-un-https) and that file permissions allow access.
Diagnostics
- Cannot connect: verify
remote --host/--port(or--sun-path) values and broker reachability. - TLS failures: hostname mismatch, missing CA, or wrong client certificates.
- WS upgrade errors: wrong request target (
/ws) or missingmqttsubprotocol.
Hardening
- Prefer UNIX domain sockets on the same host for reduced attack surface.
- Use TLS (and client auth where required) across untrusted networks.
- Restrict file permissions for keys and session store.
Common pitfalls
- All instances active: disable unused instances to avoid accidental connections.
- Wrong broker path: mismatched WS target or socket path leads to silent failures.
- Mapping file path typos: the integrator starts but no transforms occur—double-check the filename.
- The integrator speaks MQTT 3.1.1 as a client. Configure your broker accordingly.
- Single-broker focus: clear operational model—subscribe, transform, and republish on the same connection.
- Uniform instance model: TCPv4/v6, UNIX sockets, Unix Domain WebSockets, and IP WebSockets share a consistent configuration surface.
- Persist-once simplicity: Use
-wonce; day-to-day runs are minimal and repeatable.
The MQTTBridge is a pure client-side bridge in the MQTTSuite. It creates one or more logical bridges, where each bridge connects to two or more MQTT brokers and relays topics among them. Forwarding is governed by a single JSON file, bridge-config.json. The bridge does not expose listeners; every connection is an outbound client connection to a remote broker.
Like the other MQTTSuite apps, the bridge supports the same transport families (TCP/IPv4, TCP/IPv6, UNIX domain sockets, and WebSockets over these), plus optional Bluetooth (RFCOMM/L2CAP) if built. The network section of the JSON selects transport and security per broker.
Key properties
- Multi-broker fan-out: Messages received from one broker are forwarded to all the other brokers in the same logical bridge, subject to topic filters, prefix rules, and loop prevention.
- Prefixes: Optional bridge-level and broker-level prefixes help avoid collisions and enable simple routing.
- Loop prevention: Optional per-broker flag to reduce message echo/loops.
- QoS per subscription: Each broker’s subscription list sets the SUBSCRIBE QoS used toward that broker.
- Session stores: Optional per-broker on-disk session stores to resume state across restarts.
- Protocol: MQTT 3.1.1 client behavior.
Two brokers, bidirectional, loop prevention on:
{
"bridges": [
{
"name": "local↔edge",
"prefix": "bridge/",
"brokers": [
{
"network": {
"instance_name": "local-ws",
"protocol": "in",
"in": { "host": "127.0.0.1", "port": 8080 },
"encryption": "legacy",
"transport": "websocket"
},
"prefix": "local/",
"mqtt": {
"client_id": "bridge-local",
"clean_session": true,
"loop_prevention": true
},
"topics": [
{ "topic": "##", "qos": 0 }
]
},
{
"network": {
"instance_name": "edge-tcp",
"protocol": "in",
"in": { "host": "edge.example.net", "port": 1883 },
"encryption": "legacy",
"transport": "stream"
},
"prefix": "edge/",
"mqtt": {
"client_id": "bridge-edge",
"clean_session": true,
"loop_prevention": true
},
"topics": [
{ "topic": "##", "qos": 0 }
]
}
]
}
]
}mqttbridge bridge --definition /etc/mqttsuite/bridge-config.json## Publish on 'local' broker → should appear on 'edge' with prefixing
mqttcli in-wsmqtt remote --host 127.0.0.1 --port 8080 \
pub --topic 'sensors/temp' --message '21.5'
## Subscribe on edge:
mosquitto_sub -h edge.example.net -p 1883 -t 'bridge/local/##' -vPersist-once workflow: Like other MQTTSuite apps, you can persist CLI runtime options with
-w. The bridge topology itself lives inbridge-config.json, so you typically runmqttbridge --bridge-config-file …thereafter.
This section shows the overall file structure first, then elaborates on each element. Unknown keys are rejected (additionalProperties: false throughout the schema).
{
"bridges": [ // required, ≥ 1 items
{
"disabled": false, // optional
"name": "bridge-name", // required (string)
"prefix": "bridge/", // optional (string, default "")
"brokers": [ // required, ≥ 1 items
{
"disabled": false, // optional
"session_store": "/var/lib/mqttsuite/mqttbridge/session-1.store", // optional
"network": { // required
"instance_name": "local-ws",
"protocol": "in", // one of: in | in6 | un | rc | l2
"encryption": "legacy", // one of: legacy | tls
"transport": "websocket",// one of: stream | websocket
// exactly one of: in | in6 | un | rc | l2, matching "protocol"
"in": { "host": "127.0.0.1", "port": 8080 }
// "in6": { "host": "::1", "port": 8080 }
// "un": { "path": "/run/mqttbroker.unix" }
// "rc": { "host":"AA:BB:CC:DD:EE:FF","channel":1 }
// "l2": { "host":"AA:BB:CC:DD:EE:FF","psm":25 }
},
"mqtt": { // optional; defaults shown
"client_id": "",
"keep_alive": 60,
"clean_session": true,
"will_topic": "",
"will_message": "",
"will_qos": 0, // 0..2
"will_retain": false,
"username": "",
"password": "",
"loop_prevention": false
},
"prefix": "local/", // optional (string, default "")
"topics": [ // optional (≥ 1), default: [{ "topic":"##", "qos":0 }]
{ "topic": "##", "qos": 0 }
]
}
// ... more brokers in this bridge
]
}
// ... more bridges
]
}Forwarding logic (mental model):
Within each configured bridge, the MQTTBridge establishes connections to all configured brokers and subscribes to their specified topics, ensuring message forwarding begins immediately after connection. Incoming messages that match a subscriptions are forwarded to all the other brokers in the same bridge. When forwarding, the bridge applies (if set):
<bridge.prefix> + <source-broker.prefix> + <original-topic>
Looping is mitigated with mqtt.loop_prevention: true (per broker) plus sensible prefixing.
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
bridges |
array | ✅ | — | Must contain ≥ 1 bridge objects. Unknown keys rejected. |
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
disabled |
boolean | ✖ | false |
If true, this whole bridge is ignored. |
name |
string | ✅ | — | Logical name for logs/metrics. |
prefix |
string | ✖ | "" |
Optional bridge-wide prefix added before forwarding. |
brokers |
array | ✅ | — | ≥ 1 broker definitions that participate in this bridge. |
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
disabled |
boolean | ✖ | false |
If true, this broker does not connect/forward. |
session_store |
string | ✖ | "" |
Optional path to persist client session (useful with clean_session: false). |
network |
object | ✅ | — | Transport selection and concrete address (see network below). |
mqtt |
object | ✖ | defaults | MQTT client opts (credentials, last will, loop prevention). |
prefix |
string | ✖ | "" |
Optional source-broker prefix added when forwarding from this broker to others. |
topics |
array | ✖ | [{ "topic":"##", "qos":0 }] |
Subscription filters towards this broker (SUBSCRIBE QoS per item). Must be ≥1 if present. |
Subscribe QoS is taken from
topics[].qostoward the source broker.
Publish QoS used when forwarding is chosen by the bridge’s policy; align deployment for consistency (QoS 0/1 are common).
| Field | Type | Required | Default | Constraints / Notes |
|---|---|---|---|---|
client_id |
string | ✖ | "" |
Let the broker assign if empty, or set explicitly. |
keep_alive |
integer | ✖ | 60 |
Seconds between pings. |
clean_session |
boolean | ✖ | true |
Set false to resume session (pair with session_store). |
will_topic |
string | ✖ | "" |
Optional Last Will topic. |
will_message |
string | ✖ | "" |
Optional Last Will payload. |
will_qos |
integer | ✖ | 0 |
0..2. |
will_retain |
boolean | ✖ | false |
— |
username |
string | ✖ | "" |
Broker auth (if required). |
password |
string | ✖ | "" |
Broker auth (if required). |
loop_prevention |
boolean | ✖ | false |
Enable to mitigate echo loops. Combine with origin prefix. |
Each item is a topic object:
| Field | Type | Required | Default | Constraints / Notes |
|---|---|---|---|---|
topic |
string | ✅ | "##" |
MQTT filter (##, + allowed). |
qos |
integer | ✖ | 0 |
One of 0, 1, 2 (SUBSCRIBE QoS). |
These control which inbound topics are accepted from this broker. Matching messages are forwarded to other brokers.
| Field | Type | Required | Values / Example | Notes |
|---|---|---|---|---|
instance_name |
string | ✅ | "local-ws" |
Logical name; shows in logs. |
protocol |
string | ✅ | in | in6 | un | rc | l2 |
Selects address object below. |
encryption |
string | ✅ | legacy | tls |
tls for TLS (MQTTS/WSS). |
transport |
string | ✅ | stream | websocket |
MQTT over TCP vs WebSockets. |
in |
object | if protocol: in |
{ "host": "127.0.0.1", "port": 1883 } |
IPv4 host/port. |
in6 |
object | if protocol: in6 |
{ "host": "::1", "port": 1883 } |
IPv6 host/port. |
un |
object | if protocol: un |
{ "path": "/run/mqtt.sock" } |
Unix domain path (max ~107 chars). |
rc |
object | if protocol: rc |
{ "host":"AA:BB:CC:DD:EE:FF", "channel": 1 } |
Bluetooth RFCOMM. |
l2 |
object | if protocol: l2 |
{ "host":"AA:BB:CC:DD:EE:FF", "psm": 25 } |
Bluetooth L2CAP. |
Exactly one address object (in/in6/un/rc/l2) must be present, consistent with protocol.
WebSockets specifics: For WebSockets, the suite uses request target /ws and subprotocol mqtt. No extra fields are needed in the JSON.
Address constraints (from schema):
in,in6:port∈[1,65535],hostis a string (IPv4/IPv6 or hostname).un: POSIX path,^/(?:[^\0])+$,maxLength: 107.rc: Bluetooth MAC (^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$),channel∈[1,30].l2: Bluetooth MAC as above,psm∈[1,65535].
- Validation: Stick to the schema—unknown keys fail validation (
additionalProperties: false). - Secrets: Keep credentials out of VCS; generate the final JSON at deploy time or mount via secrets.
- Prefixes: Use a bridge prefix (e.g.,
bridge/) and origin prefixes (e.g.,local/,ttn/) to keep topics intelligible and enable filtering. - Loop prevention: Enable
mqtt.loop_prevention: truewherever bidirectional paths overlap. Combine with origin prefixing so the bridge recognizes its own forwarded traffic. - Subscriptions & QoS: Start broad (
"##", QoS 0); once data flows, narrow filters. Keep end-to-end QoS consistent. - Sessions & LWT: For unstable links, define a
session_storeand considerclean_session: false. Configure Last Will if operators rely on presence/health topics. - UNIX domain sockets: Place sockets under
/runor/var/run; set ownership/permissions for the bridge process. - WebSockets: Target
/wsand ensure the broker accepts subprotocolmqtt. - Diagnostics:
- No forwarding: Check the source broker’s
topics[]filter and verify the other brokers subscribe to the forwarded, prefixed paths. - Echo storms: Add prefixes and enable loop prevention on both sides; avoid subscribing to your own bridged prefix everywhere.
- WS handshake failures: Wrong target or missing subprotocol.
- No forwarding: Check the source broker’s
MQTTCli is the command-line client of the MQTTSuite. It can publish messages and subscribe to topics, supports the same transport families as the other apps (TCP/IPv4, TCP/IPv6, UNIX domain sockets, WebSockets over these, with optional TLS), and integrates with the shared SNode.C configuration system. Typical use cases: quick broker smoke tests, scripted data injection, and ad-hoc topic inspection.
- Subscribe to everything on a local broker (TCP/IPv4):
mqttcli in-mqtt
remote --host 127.0.0.1
--port 1883
sub --topic '#'- Publish a message:
mqttcli in-mqtt
remote --host 127.0.0.1
--port 1883
pub --topic 'test/topic'
--message 'hello'- WebSockets (HTTP) publish:
mqttcli in-wsmqtt
remote --host 127.0.0.1
--port 8080
pub --topic 'test/topic'
--message 'hello'- UNIX domain socket publish:
mqttcli un-mqtt
remote --sun-path /tmp/mqttbroker-un-mqtt
pub --topic 'test/topic'
--message 'hello'- Subscribe and publish in one go (handy for quick checks):
mqttcli in-mqtt
remote --host 127.0.0.1
--port 1883
sub --topic '#'
pub --topic 'test/topic'
--message 'hello'Tip: When publishing JSON on a shell, wrap payloads in single quotes to avoid escaping issues:
--message '{"k":1}'.
mqttcli <instance> [tls …] remote|local <endpoint-options> (sub …)? (pub …)?-
<instance>selects the transport preset (see tables below). -
remote|localchooses endpoint style:remote --host <addr> --port <n>for TCP/WebSockets, orremote --sun-path <path>for UNIX domain sockets.
-
subandpubmay be combined in a single invocation. -
TLS is configured under a
tlsgroup when using*sinstances (e.g.,in-mqtts,in-wsmqtts):tls --cert <crt> --cert-key <key> --ca-cert <ca>
| Instance | Protocol | Encryption | Default Port |
|---|---|---|---|
in-mqtt |
IPv4 | No | 1883 |
in-mqtts |
IPv4 | Yes | 8883 |
in6-mqtt |
IPv6 | No | 1883 |
in6-mqtts |
IPv6 | Yes | 8883 |
| Instance | Encryption | Path Example |
|---|---|---|
un-mqtt |
No | /tmp/mqttbroker-un-mqtt |
un-mqtts |
Yes | /tmp/mqttbroker-un-mqtts |
| Instance | Protocol | Encryption | Port | Request Target | Sec-WebSocket-Protocol |
|---|---|---|---|---|---|
in-wsmqtt |
IPv4 | No | 8080 | / or /ws |
mqtt |
in-wsmqtts |
IPv4 | Yes | 8088 | / or /ws |
mqtt |
in6-wsmqtt |
IPv6 | No | 8080 | / or /ws |
mqtt |
in6-wsmqtts |
IPv6 | Yes | 8088 | / or /ws |
mqtt |
| Instance | Encryption | Path Example | Request Target | Sec-WebSocket-Protocol |
|---|---|---|---|---|
un-wsmqtt |
No | /tmp/mqttbroker-un-http |
/ or /ws |
mqtt |
un-wsmqtts |
Yes | /tmp/mqttbroker-un-https |
/ or /ws |
mqtt |
-
Use the
*sinstances (e.g.,in-mqtts,in-wsmqtts) when you need TLS. -
Provide CA for server verification and certificate/key when mutual auth is required:
mqttcli in-mqtts remote --host broker.example.org --port 8883 tls --ca-cert /etc/ssl/mqttsuite/ca.crt -
Ensure the server name (SNI/hostname) matches the certificate SAN/CN.
- The subprotocol
mqttmust be negotiated; ensure proxies preserveUpgrade,Connection, andSec-WebSocket-Protocol. - Default request targets
/and/wsare accepted by the MQTTSuite WS endpoints.
- Prefer
/runor/var/runfor service-managed sockets; adjust file ownership/permissions for non-root users.
-
You can persist frequently reused options via
-w:mqttcli in-mqtt remote --host mqtt.local --port 1883 sub --topic 'sensors/#' -wThereafter, a plain
mqttcli sub --topic 'sensors/#'may reuse the stored configuration.
-
Live-tail everything, then publish:
mqttcli in-mqtt remote --host 127.0.0.1 --port 1883 sub --topic '#' pub --topic 'test/topic' --message 'hello' -
Publish JSON:
mqttcli in-mqtt remote --host 127.0.0.1 --port 1883 pub --topic 'sensors/room1' --message '{"temp":22.7,"hum":48}' -
WS to local Broker UI port (8080):
mqttcli in-wsmqtt remote --host 127.0.0.1 --port 8080 sub --topic '#'
- Protocol: MQTT 3.1.1 (align your client expectations accordingly).