Mercurio
An MQTT broker, client library and command-line client written in Rust on top of Tokio. It speaks MQTT 3.1, 3.1.1 and 5.0, and reads mosquitto's password and ACL files, so existing setups move over with little change.
What's in the box
| Component | What it is |
|---|---|
mercuriod | The broker daemon. Configured with a TOML file, runs as a systemd service. |
recado | Command-line client: recado pub and recado sub. |
mercurioctl | Administers a running broker: clients, bans, retained messages, subscriptions, the log level, reloads and dynamic security. |
mercurio-client | Async Rust client library. |
mercurio-server | The broker as a library, to embed in your own program or extend with custom authentication. |
mercurio-storage | Storage for sessions, retained messages, wills and queued messages, in memory, SQLite or PostgreSQL. |
mercurio-core, mercurio-packets | Shared types and the encoding of every MQTT packet, for all three protocol versions. |
mercurio-scram | SCRAM-SHA-256 messages and key derivation, shared by the client and the broker. |
Quick start
Build everything, start the broker with its defaults (plain MQTT on 127.0.0.1:1883, state in memory), and exchange a message:
cargo build --release ./target/release/mercuriod # In a second terminal ./target/release/recado sub -t 'sensors/#' -T # In a third ./target/release/recado pub -t sensors/kitchen -m 21.5 -q 1
The subscriber prints sensors/kitchen 21.5. Any MQTT client works with the broker, mosquitto's included:
mosquitto_sub -V mqttv5 -t 'sensors/#' -v
Features
Protocol
- MQTT 3.1, 3.1.1 and 5.0, QoS 0, 1 and 2 in both directions, with retransmission on reconnect.
- Persistent sessions with session expiry, retained messages, wills and will delay.
- Shared subscriptions (
$share/group/filter), subscription identifiers, No Local, Retain As Published, Retain Handling. - Message expiry, content type, response topic, correlation data and user properties.
- Flow control: Receive Maximum, Maximum Packet Size, topic aliases in both directions.
- MQTT 5 enhanced authentication and re-authentication, server redirection, Server Keep Alive, Response Information.
Broker
- Any number of listeners: TCP, TLS, WebSocket, Unix sockets, with the PROXY protocol, mount points and their own authentication.
- Authentication with password files (Argon2 or mosquitto's hashes), client certificates, an HTTP service, or dynamic security managed at runtime.
- Topic access control in mosquitto's
acl_fileformat. - Bridges to other brokers, MQTT 5 or 3.1.1, with failover and mutual TLS.
- State in memory, SQLite or PostgreSQL, so persistent sessions survive restarts.
$SYSstatistics with mosquitto's names, logging to file or syslog.- Configuration reloads on
SIGHUP, graceful shutdown onSIGTERM. - An administration API on
$CONTROL/broker/v1and mercurioctl: list, disconnect and ban clients, delete sessions and retained messages, change the log level and reload files while the broker runs.
Where to go next
Installing & runningBuild, run as a service, signals.
Configuration referenceEvery option of the TOML file.
Authentication & accessPasswords, ACLs, TLS, dynamic security.
ListenersTLS, WebSocket, Unix sockets, mount points.
BridgesForward topics to and from other brokers.
OperationsPersistence, limits, statistics, logs.
AdministrationClients, bans and reloads at runtime, with mercurioctl.
Rust client libraryConnect, publish and subscribe from Rust.
WebAssembly pluginsExtend the broker with sandboxed components.
Migrating from mosquittoOption by option.
Performance
Measured on one 16-core machine with the load generator in mercurio-client/examples/bench.rs, against mosquitto 2.0.18 under the same load:
| Scenario | Mercurio | mosquitto |
|---|---|---|
| QoS 0, 10 publishers to 1 subscriber | 271k msg/s | 108k msg/s |
| QoS 1, 10 publishers to 1 subscriber | 94k msg/s | 28k msg/s |
| QoS 0, 1 publisher to 50 subscribers | 1.1M deliveries/s | 110k deliveries/s |
| QoS 1 round trip at 1,000 msg/s, p50 / p99 | 139 / 245 µs | 249 / 573 µs |
mosquitto runs on one thread, while Mercurio uses every core, which accounts for much of the difference. Run the scenarios on your own hardware with:
cargo run --release --example bench -- <port> [fan-in-qos0|fan-in-qos1|fan-out|latency]