wolfMQTT 2.1.0 Released: Broker Session Persistence, Stronger Security, and MQTT v5 Reliability

wolfMQTT is wolfSSL’s lightweight MQTT client and broker library, purpose-built for embedded and IoT devices. It is small, portable, and pairs directly with the wolfSSL embedded TLS library for secure MQTT over TLS 1.3, giving resource-constrained devices a fully authenticated, encrypted messaging stack in a tiny footprint. The wolfMQTT 2.1.0 release extends the built-in broker with durable session persistence, tightens security across the client, broker, and MQTT-SN decoders, and adds MQTT v5 reliability improvements that surface broker rejections instead of silently swallowing them. This post walks through what changed and what upgrading applications need to know.

What’s New in 2.1.0

  • Broker session persistence with pluggable hooks and a ready-to-use POSIX backend, including message ordering, an offline message queue, and AES-GCM encryption of persisted state at rest.
  • New public packet-validation helpers for topic names, topic filters, wildcards, fixed-header flags, and SUBACK return codes.
  • A WOLFMQTT_MAX_QOS build-time cap to compile out the QoS 2 state machine on devices that never need it.
  • Broad security hardening: strict UTF-8 validation per the MQTT specification, a broker authentication fix, and dozens of decoder-hardening changes.
  • MQTT v5 behavior fixes so applications reliably detect rejected publishes and missing message callbacks.

Broker Session Persistence

The headline feature of 2.1.0 is durable persistence for the built-in wolfMQTT broker. Previously, broker state – client sessions, subscriptions, retained messages, and in-flight QoS 1 and QoS 2 messages – lived only in memory and was lost on restart. The broker can now persist that state through a small, pluggable hook interface, so a device or gateway that reboots resumes exactly where it left off.

Key capabilities:

  • Pluggable hooks via MqttBroker_SetPersistHooks, so you can back persistence with a filesystem, flash, a key-value store, or your own database.
  • A built-in POSIX file backend (MqttBrokerNet_PersistPosix_Init / MqttBrokerNet_PersistPosix_Free) that stores each record under a directory, defaulting to /var/lib/wolfmqtt.
  • Message ordering preserved across restarts, plus an offline message queue so messages destined for a disconnected persistent session are held and delivered on reconnect.
  • AES-GCM encryption of persisted state at rest, so session data and payloads written to disk are confidential and integrity-protected.

Wiring up the default POSIX backend takes only a few lines:

MqttBrokerPersistHooks hooks;
MqttBroker broker;
int rc;

/* Use the built-in POSIX file-based persistence backend. */
rc = MqttBrokerNet_PersistPosix_Init(&hooks, "/var/lib/wolfmqtt");
if (rc == 0) {
    rc = MqttBroker_SetPersistHooks(&broker, &hooks);
}

/* Run the broker. Sessions, subscriptions, retained messages, and
 * queued QoS 1/2 messages now survive a restart. */

/* On shutdown - releases the directory handle; does not delete files. */
MqttBrokerNet_PersistPosix_Free(&hooks);

To implement a custom backend (for example, writing to raw flash or an embedded database), populate a MqttBrokerPersistHooks structure with your own callbacks and pass it to MqttBroker_SetPersistHooks instead of calling the POSIX initializer.

Packet Validation Helpers and the QoS Build Cap

2.1.0 promotes several MQTT conformance checks to public API, so client and broker applications can validate untrusted input the same way the library does internally: MqttPacket_TopicNameValid, MqttPacket_TopicFilterValid, MqttPacket_TopicFilterIsWildcard, MqttPacket_FixedHeaderFlagsValid, and MqttPacket_SubAckReturnCodeValid.

For deeply constrained targets, the new WOLFMQTT_MAX_QOS build option caps the maximum supported QoS at 0, 1, or 2. Setting it below 2 compiles out the QoS 2 state machine (saving code space) and makes the broker advertise the cap through the MQTT v5 MAX_QOS property in its CONNACK.

Security Hardening

Security is the other major theme of this release. Highlights:

  • Strict UTF-8 validation. Per [MQTT-1.5.3-1], MqttDecode_String now validates every decoded MQTT string against RFC 3629 and rejects ill-formed sequences and encodings of surrogate code points (U+D800 through U+DFFF) with MQTT_CODE_ERROR_MALFORMED_DATA. The check covers Client IDs, will topics, topic names, topic filters, usernames, and MQTT v5 string and string-pair properties, on both client and broker builds.
  • Broker authentication fix. The broker now requires auth_user and auth_pass to be configured as a pair. Previously, setting only one silently enabled single-factor authentication – any password would authenticate against a matching username, or any username against a matching password. MqttBroker_Start now rejects a partial credential configuration with MQTT_CODE_ERROR_BAD_ARG, and the connect-time check fails closed as defense in depth.
  • Extensive decoder hardening. Guided by wolfSSL’s internal Fenrir and Skoll analysis tools, this release adds bounds checks, credential-buffer scrubbing, WebSocket fan-out use-after-free fixes, enforced TLS peer verification in the example clients, and log-injection sanitization of broker-controlled strings across the client, broker, and MQTT-SN code paths.

API and Behavior Changes to Note When Upgrading

A few changes make previously silent failures visible. Applications upgrading from 2.0.0 should review these:

  • Rejected publishes are now reported. MqttClient_Publish and MqttClient_Publish_ex return the new MQTT_CODE_ERROR_PUBLISH_REJECTED (-21) when an MQTT v5 broker rejects a QoS greater than 0 publish through a PUBACK, PUBREC, or PUBCOMP reason code of 0x80 or higher (for example Not Authorized, Quota Exceeded, Topic Name Invalid). Previously, these were reported as success. The specific reason is available in MqttPublish.resp.reason_code. For QoS 2, a PUBREC rejection now ends the exchange without sending PUBREL, per [MQTT-4.3.3], instead of timing out. MQTT v3.1.1 publishes are unaffected.
  • A missing message callback is now an error. An incoming PUBLISH received by a client with no callback registered (msg_cb == NULL) now returns MQTT_CODE_ERROR_CALLBACK (-13) instead of MQTT_CODE_SUCCESS. Previously the payload was silently read and discarded, and for QoS 1 and 2 a PUBACK or PUBREC was still sent, falsely telling the broker the message was delivered. A NULL callback remains valid for a publish-only client that never receives messages.
  • Underlying decode errors now propagate. MqttDecode_Publish and MqttDecode_Props now return the real underlying error (such as MQTT_CODE_ERROR_MALFORMED_DATA) instead of masking it, and the CONNECT password is treated as binary data per [MQTT-3.1.3.5] rather than being run through UTF-8 validation.

Getting Started

wolfMQTT 2.1.0 is available now on GitHub at https://github.com/wolfSSL/wolfMQTT. Clone or download the release, build with autotools or CMake, and enable TLS by building against wolfSSL. The --enable-broker configure option turns on the built-in broker and its new persistence support.

wolfSSL products are dual licensed under GPLv3 and a commercial license, and are backed by the wolfSSL support team. For commercial licensing, FIPS 140-3 validated cryptography, or help integrating wolfMQTT into your embedded or IoT product, contact us at facts@wolfssl.com or licensing@wolfssl.com.

If you have questions about any of the above, please contact us at facts@wolfssl.com or call us at +1 425 245 8247.

Download wolfSSL Now