KubeMQ
ConnectorsMQTTReference

Capabilities

What the KubeMQ MQTT connector advertises to clients — the three forced capabilities, configurable limits, protocol-version support, and session semantics.

This reference lists every capability the KubeMQ MQTT connector advertises to clients — including the values that are forced regardless of configuration — plus its configurable limits, protocol-version support, session semantics, and the limitations you must design around. Use it to decide which MQTT client features are safe to rely on and which ones are refused or silently dropped.

Forced (non-configurable) capabilities

These flags are hardcoded in the connector's CONNACK and cannot be changed at runtime. They are the three load-bearing capability bits an MQTT 5.0 client reads at connect time, plus the PUBACK-property rule:

CapabilityWire valueEffect
RetainAvailable0Retain is disabled. A retained PUBLISH at runtime is silently dropped (success PUBACK 0x00); a Will-retain=true CONNECT is rejected with CONNACK 0x9A. See the retain gotcha.
SharedSubAvailable1Required for $share/<group>/queues/<ch> queue consumption.
WildcardSubAvailable1Required for Events wildcard subscriptions (+, #).
NoInheritedPropertiesOnAcktruePUBACK never echoes the PUBLISH user properties back to the sender. Every successful QoS 1 PUBACK is property-free.

Retain is silently dropped (RetainAvailable=0). The connector advertises that retain is unavailable, but if a client sends a retained PUBLISH anyway, the retain flag is stripped, the message is dropped, and the PUBACK still succeeds (0x00) — the sender gets no wire-level indication. The only retain-related error is a Will-retain=true CONNECT, which is rejected with CONNACK 0x9A. There is no durable subscription support.

Configurable limits and defaults

All values below are tunable via CONNECTORSMQTT_CAPABILITIES_* environment variables (see Configuration).

CapabilityDefaultMinMaxNotes
MaximumPacketSize4194304 (4 MB)1Maximum PUBLISH payload + header size in bytes. Clients sending larger packets are disconnected.
ReceiveMaximum1024165535Maximum concurrent in-flight QoS 1/2 publishes per client (flow control).
MaximumInflight81921Broker-wide in-flight QoS message cap.
MaximumSessionExpiryInterval3600 s0Maximum value clients may request for SessionExpiryInterval.
MaximumMessageExpiryInterval86400 s0Maximum allowed message expiry. RPC publishes only: if MessageExpiryInterval is set on a commands/ or queries/ PUBLISH and is shorter than RpcTimeoutSeconds, the effective RPC timeout becomes min(RpcTimeoutSeconds, MessageExpiryInterval). Does not apply to queue or events publishes.
MaximumQos202Highest QoS the broker grants.
MinimumProtocolVersion4 (MQTT 3.1.1)45MQTT 3.1 (level 3) is rejected at CONNECT.
MaximumClients0 (unlimited)00 = unlimited.

Protocol version support

ProtocolLevel byteAcceptedNotes
MQTT 5.00x05YesFull feature set: user properties, reason codes, shared subscriptions, RPC flow.
MQTT 3.1.10x04YesNo user properties, no RPC, no $share queue consume.
MQTT 3.10x03RejectedCONNECT is refused for an unsupported protocol level. Because a level-3 CONNECT uses the MQTT 3.x CONNACK format, the byte returned is the v3 return code 0x01 ("unacceptable protocol version") — not one of the MQTT 5.0 reason codes.

MinimumProtocolVersion=4 is the hardcoded floor. Set CONNECTORSMQTT_CAPABILITIES_MIN_PROTOCOL_VERSION=5 to allow MQTT 5.0 clients only.

Session semantics

Sessions are in-memory and node-local:

PropertyBehavior
clean_start=false (v5) / clean_session=0 (v3.1.1)Session is preserved in the node's memory. A reconnect to the same node within SessionExpiryInterval restores subscriptions and queued QoS 1/2 messages without re-subscribing.
Reconnect to a different nodeSession is lost (not replicated across nodes).
clean_start=trueFresh session; no state carried over.

Restored sessions do not replay historical Events-Store messages — the bridge re-subscribes at start-new-only after reconnect. See the no-replay gotcha.

Transport listeners

ListenerDefault portURL schemeNotes
TCP (plain)1883tcp://host:1883Always active when Port is set (default 1883).
TLS8883tls://host:8883Active only when Security config is present. TLS min 1.2; mTLS supported. If no TLS material is configured, the listener is silently skipped with a warning.
WebSocket8083ws://host:8083/Active when WsPort is set. TLS WebSocket (wss://) when Security config present.

User properties (MQTT 5.0 only) and KubeMQ tags

MQTT 5.0 user properties map bidirectionally to KubeMQ message tags for Events, Events-Store, and Queues patterns. MQTT 3.1.1 has no user properties.

DirectionMapping
PUBLISH → KubeMQProperties.User entries copied 1:1 into KubeMQ Tags (duplicate keys: last-wins).
KubeMQ delivery → MQTT subscriberKubeMQ Tags copied into Properties.User on the injected PUBLISH.

Hardcoded caps:

CapValueViolation result
Maximum user properties per message32MQTT 5.0: PUBACK 0x97; MQTT 3.1.1: silent drop + publish.error audit.
Maximum total bytes (sum of all key + value lengths)4096 bytesSame as above.

Limitations

These are the design constraints to plan around — each is a hard property of the connector, not a configuration choice:

LimitationDetail
No retainRetainAvailable=0 — retained PUBLISH is dropped; Will-retain CONNECT rejected with 0x9A.
No durable subscriptionsSessions are node-local and in-memory; reconnecting to a different node loses the session.
Queues are publish-only over MQTTqueues/<ch> accepts produce; consuming requires $share/<group>/queues/<ch>, which is MQTT 5.0 only (3.1.1 has no shared subscriptions).
Commands / Queries are MQTT 5.0 onlyRPC uses ResponseTopic + CorrelationData; a 3.1.1 publish to commands//queries/ is silently dropped. MQTT clients can only be RPC requesters — responders run on the gRPC side.
Events-Store has no historical replayMQTT subscriptions always start new-only; messages stored before subscribe are never delivered.
Ruby client is the v3.1.1 subsetThe mqtt gem speaks MQTT 3.1.1 only — no RPC, no $share queue consume, no user properties.

Gotcha reference

Gotcha 1: retain is silently dropped at runtime

RetainAvailable=0 is advertised at CONNECT. If a client sends a retained PUBLISH anyway:

  • The retain flag is silently stripped.
  • The connector then drops the message and audits publish.error with text "retain not supported".
  • The PUBACK succeeds (0x00). The sender has no indication the message was dropped.
  • No retained copy is stored; late subscribers receive nothing.

The only case that yields a CONNACK error is Will-retain=true at CONNECT, which returns CONNACK 0x9A (retain-not-supported), enforced before the session is established.

Gotcha 2: Events-Store has no historical replay

Events-Store subscriptions over MQTT always start new-only. Unlike the REST/gRPC API (which offers six replay positions), MQTT subscribers receive only messages published after the subscription is established. Messages stored before a client subscribes are never delivered.

Gotcha 3 — $share group name is audit-only; all groups compete in one pool

Standard MQTT brokers deliver a separate copy of each message to each $share group. The KubeMQ connector does not. All $share subscribers on all groups compete in a single KubeMQ queue pool: each message is consumed exactly once, regardless of group name. The group name is recorded in audit and metrics only.

Gotcha 4 — RPC requires MQTT 5.0 and PUBACK is immediate

Publishing to commands/ or queries/ from an MQTT 3.1.1 client is silently dropped (PUBACK 0x00, no RPC issued). The PUBACK for a valid MQTT 5.0 RPC publish is also sent immediately, before the response arrives — clients must implement their own response-wait timeout.

Gotcha 5 — literal . in a topic segment conflates with /

events/a.b/c and events/a/b/c both produce KubeMQ channel a.b.c. Avoid dots inside path segments. See Topic Grammar.

Gotcha 6 — overlapping wildcard filters deliver multiple copies

Each unique subscribe filter has its own independent bridge entry. A publish matching N active wildcard filters results in N deliveries to the client. There is no cross-entry deduplication.

Was this page helpful?

On this page