Skip to content

[feat] Tracking issue MQTTv5 #821

Open

Description

@robertsLando

The features that doesn't require changes on persistences are:

The ones that require changes on persistences:


Development status — mqttv5 branch / PR #1088

Most of MQTT 5.0 has landed on the mqttv5 branch: #1088 (feat/mqtt5) was merged on 2026-06-30 (merge commit e7d0ce9) after eight rounds of review. MQTT 3.1/3.1.1 behaviour is unchanged. Tests live in test/mqtt5.js (40 tests, driving a real mqtt@5 client over TCP). Note: #1088 targeted mqttv5, not the default branch, so the per-feature issues it referenced are not auto-closed — the checklist above tracks their status until mqttv5 merges to release under #194.

Companion releases (resolved): the gating packages are published and package.json now points at published ranges — aedes-packet@^4.0.1 (moscajs/aedes-packet#192 — preserve properties/messageExpiry) and aedes-persistence@^10.3.1 (moscajs/aedes-persistence#108 — persist subscriptionIdentifier). The overrides shim has been removed; the scripts/check-no-git-deps.js release guard is no longer blocked (no git deps remain).

Update — PR #1100 (2026-07-03): granular ACK reason codes (#822), Reason Strings on ACKs (#823), and Request Problem Information gating (#1092) landed on mqttv5 (merge 0f59242). A denied v5 PUBLISH now returns 0x87 (Not authorized) on PUBACK/PUBREC with the handler's error.message as the Reason String — suppressed when the client connected with Request Problem Information = 0 ([MQTT-3.1.2-29]); a denied v5 SUBSCRIBE returns SUBACK 0x87; PUBREL/PUBCOMP carry a Success reason code; and a rejecting PUBREC (≥ 0x80) terminates the QoS 2 flow without a PUBREL, releasing the stored outgoing packet id ([MQTT-5.0 §4.3.3]). Reason Strings are wired on PUBACK/PUBREC only — SUBACK/UNSUBACK/PUBREL/PUBCOMP still omit them, so #823 remains partial. A pinned Eclipse Paho interoperability workflow (tools/mqtt-compat/) tracks the conformance % (MQTT 5.0 at 62%, MQTT 3.1.1 at 100%).

Update — PR #1102 (2026-07-06): Request / Response Information (#1091) landed on mqttv5 (merge a025504). When a client sets Request Response Information on CONNECT ([MQTT-3.1.2-28], §3.1.2.11.6), the broker returns Response Information in the CONNACK (§3.2.2.3.15, server MAY) — but only when the new broker option responseInformation is configured (a static string or a (client) => string | undefined function for a per-client base; null default = never returned). Returned only on a successful CONNACK; a resolver that throws degrades to omit + surfaces on the clientError event. Broker-core only — no persistence changes, cluster-safe.

Update — PR #1115 (2026-07-06): Server Reference / connect-time redirect (#838) landed on mqttv5 (merge 8fb6bdc). A preConnect/authenticate hook can redirect a v5 client by rejecting with an error carrying a serverReference (and an optional reasonCode): the rejection CONNACK then carries 0x9C (Use another server) — or 0x9D (Server moved) when the hook sets err.reasonCode — plus the serverReference property (§4.11). A serverReference paired with a non-redirect reason code is clamped to 0x9C, and the property is dropped for non-v5 clients. Mid-session redirect already worked via client.disconnect({ reasonCode, properties: { serverReference } }) — now covered by a test. Broker-core only — no persistence changes, cluster-safe.

Persistence & cluster classification

The features split three ways by what they touch beyond the broker core:

A. Need aedes-packet changes (the packet object that travels through the emitter + persistence must preserve the new fields): property pass-through — #826, #827, #830 — and Message Expiry #824 (messageExpiry must survive storage/forwarding). Covered by moscajs/aedes-packet#192.

B. Need aedes-persistence changes (and round-trip support in the mongodb / redis / level implementations):

C. No persistence change — broker core / per-connection: #822, #823, #825, #829, #831, #832, #833, #834, #836, #837, #838, #840.

Special cluster handling required:

Conformance review (vs. OASIS MQTT 5.0)

Status: ✅ implemented & conformant · 🟡 partial · ❌ missing · ⚠️ non-conformant.
Persistence: pkt = needs aedes-packet · persist = needs aedes-persistence (+ mongodb/redis/level) · — = core only.
Cluster: ✅ cluster-safe · ⚠️ per-instance only / needs care · ❌ not cluster-safe.

# Feature Status Persistence Cluster Notes
#822 Reason code on ACKs ✅ — ✅ Landed #1100. PUBACK/PUBREC 0x87 on authz failure; SUBACK failure 0x87 (v5) / 0x80 (v3/v4); PUBREL/PUBCOMP carry Success; rejecting PUBREC (≥0x80) ends QoS 2 with no PUBREL + packet-id release; CONNACK/UNSUBACK/server-DISCONNECT carry codes. Minor: PUBACK/PUBREC emit only 0x87 (not other granular failure codes).
#823 Reason String on ACKs 🟡 — ✅ Landed #1100 (partial). reasonString from the handler's error.message on PUBACK/PUBREC, gated by Request Problem Information ([MQTT-3.1.2-29]). SUBACK/UNSUBACK/PUBREL/PUBCOMP still omit it.
#824 Message Expiry Interval ✅ pkt ✅ Recomputed/dropped on queued + retained delivery; will-msg expiry ok. Minor: not decremented on the live path (wait ≈ 0).
#825 Server disconnect ✅ — ✅ 0x8E/0x8B/0x95/0x94/0x82, v5-only; inbound 0x04 publishes will, 0x00 discards.
#826 Payload format + content type ✅ pkt ✅ Forwarded transparently.
#827 Request / Response ✅ pkt ✅ Response Topic + Correlation Data forwarded; Response Topic wildcard rejected ([MQTT-3.3.2-14]).
#828 Subscription Identifier 🟡 persist ✅ Validated/persisted/echoed for single match; retained-on-subscribe now echoes the SI. Multi-match still delivers only one SI ([MQTT-3.3.4-4]) — deferred, documented limitation.
#829 Flow control (Receive Maximum) 🟡 — ✅ ⏭️ defer (invasive to delivery hot path; advisory documented). Parsed (default 65535), 0 rejected, advertised. Outbound in-flight not capped (§4.9).
#830 User Properties ✅ pkt ✅ Forwarded; preserved on the SI-merge fan-out.
#831 Maximum Packet Size ✅ — ✅ Advertised; inbound oversized rejected pre-buffer (0x95). Outbound size-split advisory.
#832 Optional feature availability ✅ — ✅ Max QoS/Retain/Wildcard/Sub-ID advertised correctly. Shared-sub flag → see #841.
#833 Enhanced authentication 🟡 — ✅ CONNECT Authentication Method now rejected with 0x8C (§4.12). ⏭️ defer full AUTH packet flow (re-auth 0x18/0x19).
#834 Subscription options ✅ — ✅ Max QoS ✅, No Local ✅, Retain As Published ✅, Retain Handling 0/1/2 ✅ ([MQTT-3.8.3-2]).
#835 Will delay 🟡 persist ⚠️ Will persisted (no schema change); reconnect cancels. Delay timer is per-instance (cross-broker will-sweep partially covers). ⚠️ #1098: the WillDelayInterval Will-property leaks into the delivered PUBLISH (rejected by strict v5 clients) — surfaced by the #1094 Paho workflow.
#836 Server Keep Alive ✅ — ✅ Imposed when unset/over-limit; echoed in CONNACK.
#837 Assigned Client Identifier ✅ — ✅ Generated + echoed only on empty client id.
#838 Server reference ✅ — ✅ Landed #1115. preConnect/authenticate redirect via an error serverReference → rejection CONNACK 0x9C/0x9D + serverReference property (§4.11); non-redirect reason code clamped to 0x9C; property dropped for non-v5. Mid-session redirect via client.disconnect().
#839 Session Expiry Interval 🟡 persist ❌ ⚠️ Correct within a running broker; inbound-DISCONNECT expiry validation now enforced ([MQTT-3.14.4-3]). ⏭️ defer cross-restart persistence (needs schema + reaper) — in-memory timers only.
#840 Topic Alias 🟡 — ✅ ⏭️ defer (outbound is optional / MAY). Inbound ✅ (resolve/register/strip/alias-0 + over-max reject, per-connection reset). Outbound ❌ (broker-assigned).
#841 Shared Subscriptions 🟡 persist ❌ $share SUBSCRIBE now refused with 0x9E ([MQTT-3.2.2-23]). ⏭️ defer cluster-aware feature; single-instance impl on the sharedsubs branch (persistence-backed, not cluster-safe). #1088 advertises sharedSubscriptionAvailable: false.
#1091 Request / Response Information ✅ — ✅ Landed #1102. CONNECT Request Response Information 0x19 ([MQTT-3.1.2-28]) → CONNACK Response Information 0x1A (§3.2.2.3.15), returned only when the client requests it and the broker sets the new responseInformation option (static string or (client) => string | undefined; null default = never). A throwing resolver degrades to omit + clientError. Distinct from #827 (per-message Response Topic) and #832 (capability flags).
#1092 Request Problem Information ✅ — ✅ Landed #1100. CONNECT 0x17 ([MQTT-3.1.2-29]) now suppresses the #823 Reason String on PUBACK/PUBREC when set to 0. Absent ⇒ enabled; wire-0 ⇒ suppressed.

Triage — complete in #1088 vs. defer

Principle: fix in #1088 anything where the PR already advertises or accepts a capability but then mishandles it (spec violation / misleading behavior). Defer net-new scope, persistence-schema, and cluster work. Legend: 🔧 #1088 · 🤔 judgment · ⏭️ defer.

✅ Completed in #1088 (landed in ceed520, verified against source):

  1. Retain Handling 1/2 (Subscription options: I think this should already be implemented, expecially the noLocal flag but we should check this #834) — [MQTT-3.8.3-2]. rh=2 no longer sends retained on subscribe; rh=1 only for a brand-new subscription (subscribe.js).
  2. $share → 0x9E rejection (Shared subscriptions #841) — [MQTT-3.2.2-23]. A v5 $share/... SUBSCRIBE is refused with 0x9E instead of becoming a dead literal subscription.
  3. Inbound DISCONNECT Session-Expiry validation (Server disconnect #825/Session expiry: we actually don't have storage support for this #839) — [MQTT-3.14.4-3]. A non-zero Session Expiry on DISCONNECT is rejected when CONNECT declared 0.
  4. Reject unsupported Authentication Method 0x8C (Enhanced authentication #833 guard) — §4.12. A CONNECT carrying authenticationMethod is rejected with 0x8C instead of a silent password downgrade.
  5. Retained-on-subscribe SI echo (Subscription id (persistence addSubscription stores the entire sub packet so we don't have to make edits on that side) #828, part) — [MQTT-3.3.4]. Retained deliveries now carry the SUBSCRIBE Subscription Identifier.
  6. Response Topic wildcard validation — [MQTT-3.3.2-14]. A PUBLISH with a wildcard Response Topic is rejected.

✅ Landed in #1100 (mqttv5, merge 0f59242):

  1. Granular ACK reason codes (Reason code on all ACKs #822) — PUBACK/PUBREC 0x87, SUBACK 0x87 (v5), PUBREL/PUBCOMP Success, and rejecting-PUBREC QoS 2 termination with packet-id release ([MQTT-5.0 §4.3.3]).
  2. Reason String on ACKs (Reason string on all ACKs #823, partial) — handler error.message surfaced on PUBACK/PUBREC.
  3. Request Problem Information gating (Request Problem Information (CONNECT 0x17) #1092) — [MQTT-3.1.2-29]. Reason String suppressed on PUBACK/PUBREC when the client set RPI = 0.

✅ Landed in #1102 (mqttv5, merge a025504):

  1. Request / Response Information (Request / Response Information (CONNECT 0x19 → CONNACK 0x1A) #1091) — [MQTT-3.1.2-28] / §3.2.2.3.15. CONNACK Response Information returned when the client requests it and the new broker responseInformation option is set (static string or per-client function; null default = never). A throwing resolver degrades to omit + clientError.

✅ Landed in #1115 (mqttv5, merge 8fb6bdc):

  1. Server Reference / connect-time redirect (Server reference #838) — §4.11. preConnect/authenticate redirect via an error carrying serverReference → rejection CONNACK 0x9C (Use another server) / 0x9D (Server moved); a non-redirect reason code paired with a reference is clamped to 0x9C; property dropped for non-v5 clients. Mid-session redirect via client.disconnect({ reasonCode, properties: { serverReference } }).

🤔 Judgment call:

⏭️ Defer to follow-ups (net-new scope / persistence / cluster / spec-optional):

(#1091 Request / Response Information landed in #1102; #1092 Request Problem Information landed in #1100, now gating #823.)

Status generated from a spec-conformance audit of mqttv5 @ PR #1088 head (276cb2e), updated for #1100 (0f59242), #1102 (a025504) and #1115 (8fb6bdc). Shared-subscription code currently lives on the separate sharedsubs branch (single-instance, pre-v5). Items marked 🟡/❌/⚠️ are the remaining work toward full v5 conformance (#194).

Activity

  1. pinned this issue on Jun 17, 2025
  2. added a commit that references this issue on Jun 30, 2026
  3. robertsLando commented on Jun 30, 2026

    @robertsLando
    MemberAuthor

    ✅ #1088 merged into mqttv5 on 2026-06-30 (merge commit e7d0ce9).

    • Companion releases are out, so package.json now uses published ranges (aedes-packet@^4.0.1, aedes-persistence@^10.3.1); the overrides shim and the git-dep release block are gone.
    • Because feat: MQTT 5.0 support #1088 targeted mqttv5 (not the default branch), the per-feature issues it referenced were not auto-closed — the checklist at the top continues to track them until mqttv5 lands for release under [feat] Mqtt v5 support  #194.

    The development-status section above has been updated to reflect the merge.

  4. BenjaminDobler commented on Jun 30, 2026

    @BenjaminDobler
    Contributor

    Proposed sequence for the remaining no-persistence items

    Per @robertsLando's suggestion (start with the ones that don't touch persistence). Grouped into reviewable PRs against mqttv5, ordered by conformance value × independence. All of these are broker-core only and cluster-safe.

    PR 1 — Reason codes & reason strings on ACKs (#822, #823, #1092)

    PR 2 — Request / Response Information (#1091)

    CONNECT 0x19 → CONNACK responseInformation 0x1A. Small and standalone; optional (server MAY, §3.2.2.3.15) but cheap. Needs a small operator hook (broker option/callback) to supply the response-topic base.

    PR 3 — Server Reference (#838)

    serverReference (0x1C) on CONNACK (0x9C use-another-server) and DISCONNECT (0x9C/0x9D server-moved). DISCONNECT already carries it via client.disconnect({ reasonCode, properties: { serverReference } }); the new part is a tiny reject-with-reference path at connect.

    Deferred (happy to propose separately when you want them)

    Does this order work? I'd start with PR 1. I'll align each PR with your mqttv5 e2e compatibility workflow so the tests cover features as they land — and shout if you'd rather I sequence around what your e2e suite checks first.

  5. robertsLando commented on Jun 30, 2026

    @robertsLando
    MemberAuthor

    Yes go

  6. added a commit that references this issue on Jul 6, 2026
  7. BenjaminDobler commented on Jul 6, 2026

    @BenjaminDobler
    Contributor

    Next sequence — remaining MQTT 5.0 work

    The no-persistence-first batch is all merged 🎉 (#1100, #1102, #1115). Proposing the next steps, grouped and ordered by value × independence × risk. Base is mqttv5; each item is its own reviewable PR.

    Phase A — close the remaining Paho v5 compat gaps (broker-core, cluster-safe)

    Each of these currently sits in EXPECTED_GAPS in tools/mqtt-compat/run_compat.py; landing it flips a ⚠️ expected gap into a pass and lifts the reported MQTT 5.0 compat %:

    1. Subscription id (persistence addSubscription stores the entire sub packet so we don't have to make edits on that side) #828 — multiple matching Subscription Identifiers [MQTT-3.3.4-4] → test_subscribe_identifiers. Today a delivery matching several of a client's overlapping subscriptions echoes only one SI (the documented limitation); this delivers all matching ones. Touches the dedupe/delivery path, no persistence. Suggest starting here — clearest conformance win, smallest blast radius.
    2. Topic alias #840 — outbound (broker-assigned) Topic Alias (server MAY) → test_server_topic_alias. Inbound aliases already work; this assigns aliases on outbound PUBLISH per connection. Per-connection state, moderate hot-path.
    3. Flow control #829 — outbound Receive Maximum enforcement (§4.9) → test_flow_control1. Cap in-flight QoS 1/2 toward each client. The most invasive of the three (shared delivery hot path) — deferred originally for exactly that reason — so I'd do it last and benchmark it.

    Phase B — Enhanced Authentication (#833), independent feature

    Full AUTH-packet flow (§4.12): CONNECT Authentication Method → AUTH (0x18 continue) exchange → CONNACK, plus re-auth (0x19). Today an auth-method CONNECT is rejected with 0x8C. Needs a broker auth-exchange hook (SASL / token style). No persistence, cluster-safe, and not tied to a Paho compat test, so I've split it out. Higher design surface — happy to post a short hook-API proposal for sign-off before writing code.

    Phase C — persistence / cluster (multi-repo, later track)

    These ripple into aedes-persistence + the mongodb/redis/level backends and/or need cluster coordination, so best treated as a separate track once A/B settle:


    Does this order work? I'd kick off with #828 unless you'd rather I clear all three Phase-A compat gaps first and leave #833 for after. For #833 I'll send a short hook-API sketch before implementing. 👍

  8. robertsLando commented on Jul 6, 2026

    @robertsLando
    MemberAuthor

    I would work on: #823 #829 #833 #840 first and then start with the once that require persistence changes

  9. robertsLando commented on Jul 6, 2026

    @robertsLando
    MemberAuthor

    let's clear all the no-persistence work before opening the persistence/cluster track. Two reasons it's the right cut: every such PR stays single-repo and cluster-safe (no aedes-persistence + mongodb/redis/level coordination), and each one flips a Paho EXPECTED_GAPS entry into a pass, so the reported MQTT 5.0 compat % keeps climbing as visible progress.

    Worth noting the two plans barely disagree — @BenjaminDobler's Phase A + Phase B are already all broker-core/cluster-safe, and only Phase C touches persistence. The one thing to correct: the batch isn't quite "all merged" yet.

    #823 is still partial. Reason String landed on PUBACK/PUBREC only (#1100); SUBACK/UNSUBACK/PUBREL/PUBCOMP still omit it. That's pure broker-core, no persistence — so it belongs in this batch

    Reconciling both lists against the table, the complete no-persistence set is:

    On ordering inside the batch I'd keep @BenjaminDobler's risk-based sequencing:

    Then #839 / #835 / #841 as one coherent multi-repo persistence/cluster epic once the above settles. 👍

  10. robertsLando commented on Jul 6, 2026

    @robertsLando
    MemberAuthor

    FYI aedes@2.0.0-beta.1 is out now 🎉

  11. added a commit that references this issue on Jul 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions