Skip to content

MQTT protocol compatibility

Pulse MQTT supports MQTT 5.0 and MQTT 3.1.1. MQTT 5.0 is the default. MQTT 3.1.1 is supported for brokers that do not speak MQTT 5, but it has no packet properties, no AUTH packet, no response topic/correlation request-response convention, and fewer acknowledgement details.

The library owns the protocol boundary:

  • MQTT 5-only helper APIs fail fast when the client is configured for MqttProtocolVersion.V311.
  • Packet codecs reject MQTT 5-only packet properties when ProtocolVersion is V311, instead of silently dropping them.
  • Pulse.Mqtt.Analyzers reports PMQ0004 for explicit MQTT 3.1.1 packet initializers that set known MQTT 5-only properties.
  • MqttProtocolFeatures, ResilientMqttClient.CanUseProtocolFeature, and ResilientMqttClient.EnsureProtocolFeature provide explicit feature guards for application branching.
  • ResilientMqttClient.GetBrokerCapabilitiesSnapshot() exposes negotiated protocol and broker feature support after a successful connection.

Feature matrix

FeatureMQTT 5.0MQTT 3.1.1Library behavior
Basic CONNECT, PUBLISH, SUBSCRIBE, UNSUBSCRIBE, DISCONNECTSupportedSupportedSame public client APIs.
QoS 0, QoS 1, QoS 2 publish flowsSupportedSupportedSame public client APIs.
Retained messagesSupportedSupportedSame publish flag and subscribe behavior.
Username and password credentialsSupportedSupportedSame MqttConnectPacket fields.
Clean session / clean startSupportedSupportedCleanStart maps to the protocol's session-start flag.
Session expiry intervalSupportedNot supportedMqttConnectPacket.SessionExpiryInterval is rejected on V311.
Receive maximumSupportedNot supportedMqttConnectPacket.ReceiveMaximum and CONNACK receive maximum are rejected on V311.
Maximum packet size negotiationSupportedNot supportedMQTT 5 CONNACK limit is enforced when negotiated; packet-size properties are rejected on V311.
Topic aliasesSupportedNot supportedAlias properties are rejected on V311; capability snapshot reports topic aliases as not supported.
Payload format, content type, message expirySupportedNot supportedPublish/will properties are rejected on V311; encode metadata in the payload for MQTT 3.1.1.
User propertiesSupportedNot supportedPacket user properties are rejected on V311; use payload envelopes for MQTT 3.1.1 metadata.
Response topic and correlation dataSupportedNot supportedRequest/response helpers throw NotSupportedException on V311.
Enhanced authentication and re-authenticationSupportedNot supportedRawMqttClientOptions.Authenticator and ReAuthenticateAsync throw on V311.
Reason strings and server referencesSupportedNot supportedDISCONNECT, CONNACK, and ack reason metadata is rejected on V311.
Negative acknowledgement reason codesSupported for protocol paths that can carry themNot supportedInbound publish rejection exposes CanReject = false on MQTT 3.1.1.
Subscription identifiersSupportedNot supportedSUBSCRIBE subscription identifiers are rejected on V311; capability snapshot reports not supported.
NoLocal, RetainAsPublished, RetainHandling subscription optionsSupportedNot supportedMqttTopicFilter options are rejected when encoded in a V311 SUBSCRIBE packet.
Shared subscriptionsStandardizedBroker-specific extension at bestDocumented as MQTT 5 behavior; check broker support before relying on it.
Trace context in user propertiesSupportedNot supportedPropagateTraceContext uses user properties only on MQTT 5; use trace envelopes for MQTT 3.1.1.
Broker capabilities snapshotSupportedSupported with unknown/not-supported markersMQTT 5 CONNACK values are populated when negotiated; MQTT 3.1.1 reports non-negotiated optional support as Unknown and MQTT 5-only features as NotSupported.

Runtime guardrails

Packet objects keep one shape so advanced users can work close to the wire. The codec enforces the negotiated packet version when writing bytes:

csharp
var packet = new MqttPublishPacket
{
    Topic = "orders/created",
    ProtocolVersion = MqttProtocolVersion.V311,
    ContentType = "application/json", // throws during encode/send
};

Use MQTT 5.0 when metadata needs protocol-level properties:

csharp
var packet = new MqttPublishPacket
{
    Topic = "orders/created",
    ProtocolVersion = MqttProtocolVersion.V500,
    ContentType = "application/json",
};

For MQTT 3.1.1 deployments, put metadata in the payload envelope or in a documented topic convention. The observability guide shows the same pattern for trace context.

Capability checks

Use protocol feature guards when application code needs to branch before using MQTT 5-only behavior:

csharp
if (client.CanUseProtocolFeature(MqttProtocolFeature.RequestResponse))
{
    StatusReply reply = await client.RequestAsync<StatusRequest, StatusReply>(
        "devices/boiler-1/status",
        request,
        cancellationToken: token);
}

For strict paths, fail explicitly at the boundary:

csharp
client.EnsureProtocolFeature(MqttProtocolFeature.TraceContextUserProperties, "trace propagation");

MqttProtocolFeatures.IsSupported and MqttProtocolFeatures.EnsureSupported are lower-level helpers for code that has only an MqttProtocolVersion. Client-level helpers additionally return Unknown for broker-negotiated features such as topic aliases, subscription identifiers, and shared subscriptions until the client is connected.

After a successful connection, inspect broker-negotiated support before enabling optional MQTT 5 behavior:

csharp
var capabilities = client.GetBrokerCapabilitiesSnapshot();

if (capabilities?.GetFeatureSupport(MqttProtocolFeature.TopicAliases) == MqttBrokerFeatureSupport.Supported)
{
    logger.LogInformation(
        "Broker supports {AliasCount} topic aliases",
        capabilities.EffectiveTopicAliasMaximum);
}

The snapshot is null until connected and is cleared when the client leaves Connected.

Released under the MIT License.