- Fix pull consumer fetch: send original stream subject in HMSG (not inbox) so NATS client distinguishes data messages from control messages - Fix MaxAge expiry: add background timer in StreamManager for periodic pruning - Fix JetStream wire format: Go-compatible anonymous objects with string enums, proper offset-based pagination for stream/consumer list APIs - Add 42 E2E black-box tests (core messaging, auth, TLS, accounts, JetStream) - Add ~1000 parity tests across all subsystems (gaps closure) - Update gap inventory docs to reflect implementation status
36 KiB
36 KiB
MQTT — Gap Analysis
This file tracks what has and hasn't been ported from Go to .NET for the MQTT module. See stillmissing.md for the full LOC comparison across all modules.
LLM Instructions: How to Analyze This Category
Step 1: Read the Go Reference Files
Read each Go source file listed below. For every file:
- Extract all exported types (structs, interfaces, type aliases)
- Extract all exported methods on those types (receiver functions)
- Extract all exported standalone functions
- Note key constants, enums, and protocol states
- Note important unexported helpers that implement core logic (functions >20 lines)
- Pay attention to concurrency patterns (goroutines, mutexes, channels) — these map to different .NET patterns
Step 2: Read the .NET Implementation Files
Read all .cs files in the .NET directories listed below. For each Go symbol found in Step 1:
- Search for a matching type, method, or function in .NET
- If found, compare the behavior: does it handle the same edge cases? Same error paths?
- If partially implemented, note what's missing
- If not found, note it as MISSING
Step 3: Cross-Reference Tests
Compare Go test functions against .NET test methods:
- For each Go
Test*function, check if a corresponding .NET[Fact]or[Theory]exists - Note which test scenarios are covered and which are missing
- Check the parity DB (
docs/test_parity.db) for existing mappings:sqlite3 docs/test_parity.db "SELECT go_test, dotnet_test, confidence FROM test_mappings tm JOIN go_tests gt ON tm.go_test_id=gt.rowid JOIN dotnet_tests dt ON tm.dotnet_test_id=dt.rowid WHERE gt.go_file LIKE '%PATTERN%'"
Step 4: Classify Each Item
Use these status values:
| Status | Meaning |
|---|---|
| PORTED | Equivalent exists in .NET with matching behavior |
| PARTIAL | .NET implementation exists but is incomplete (missing edge cases, error handling, or features) |
| MISSING | No .NET equivalent found — needs to be ported |
| NOT_APPLICABLE | Go-specific pattern that doesn't apply to .NET (build tags, platform-specific goroutine tricks, etc.) |
| DEFERRED | Intentionally skipped for now (document why) |
Step 5: Fill In the Gap Inventory
Add rows to the Gap Inventory table below. Group by Go source file. Include the Go file and line number so a porting LLM can jump directly to the reference implementation.
Key Porting Notes for MQTT
- MQTT runs on top of NATS — MQTT topics map to NATS subjects.
- Session state persists across reconnections (stored in JetStream streams).
- QoS 0 (at most once), QoS 1 (at least once), QoS 2 (exactly once — limited support).
- Retained messages stored as JetStream messages.
- MQTT connections use
ClientKind = MQTTand have a different parser path.
Go Reference Files (Source)
golang/nats-server/server/mqtt.go— MQTT protocol support (~5,882 lines). MQTT 3.1.1 and 5.0. Session state, QoS levels, retained messages, will messages.
Go Reference Files (Tests)
golang/nats-server/server/mqtt_test.gogolang/nats-server/server/mqtt_ex_test_test.gogolang/nats-server/server/mqtt_ex_bench_test.go
.NET Implementation Files (Source)
src/NATS.Server/Mqtt/(all files)
.NET Implementation Files (Tests)
tests/NATS.Server.Tests/Mqtt/
Gap Inventory
golang/nats-server/server/mqtt.go
Constants and Protocol Definitions (lines 41-199)
| Go Symbol | Go File:Line | Status | .NET Equivalent | Notes |
|---|---|---|---|---|
| mqttPacket* constants | mqtt.go:42-57 | PORTED | src/NATS.Server/Mqtt/MqttPacketReader.cs:3-15 | MqttControlPacketType enum covers the packet types |
| mqttProtoLevel | mqtt.go:59 | PORTED | src/NATS.Server/Mqtt/MqttBinaryDecoder.cs:87-88 | Checked in ParseConnect |
| mqttConnFlag* constants | mqtt.go:62-68 | PORTED | src/NATS.Server/Mqtt/MqttBinaryDecoder.cs:96-104 | Decoded inline in ParseConnect |
| mqttPubFlag* constants | mqtt.go:71-75 | PORTED | src/NATS.Server/Mqtt/MqttBinaryDecoder.cs:161-163 | Decoded inline in ParsePublish |
| mqttSubscribeFlags | mqtt.go:78 | PORTED | src/NATS.Server/Mqtt/MqttProtocolConstants.cs:9 | Added SUBSCRIBE flags constant (0x02) and wired parser validation in MqttBinaryDecoder.ParseSubscribe(..., flags) |
| mqttConnAckRC* constants | mqtt.go:85-91 | PORTED | src/NATS.Server/Mqtt/MqttProtocolConstants.cs:12 | Added MQTT 3.1.1 CONNACK return-code constants (0x00..0x05) for parity |
| mqttMaxPayloadSize | mqtt.go:94 | PORTED | src/NATS.Server/Mqtt/MqttProtocolConstants.cs:20 | Added max payload constant and wired reader/writer remaining-length guardrails |
| mqttTopicLevelSep, mqttSingleLevelWC, mqttMultiLevelWC | mqtt.go:97-100 | PORTED | src/NATS.Server/Mqtt/MqttBinaryDecoder.cs:254-258 | Used in TranslateFilterToNatsSubject |
| mqttMultiLevelSidSuffix | mqtt.go:105 | PORTED | src/NATS.Server/Mqtt/MqttProtocolConstants.cs:29 | Added MultiLevelSidSuffix = \" fwc\" constant |
| mqttPrefix, mqttSubPrefix | mqtt.go:108-113 | PORTED | src/NATS.Server/Mqtt/MqttProtocolConstants.cs:32 | Added MQTT internal subject prefixes (Prefix, SubPrefix) |
| mqttStreamName, mqttStreamSubjectPrefix | mqtt.go:116-117 | PORTED | src/NATS.Server/Mqtt/MqttProtocolConstants.cs:36 | Added message stream name and subject-prefix constants |
| mqttRetainedMsgsStreamName | mqtt.go:120-121 | PORTED | src/NATS.Server/Mqtt/MqttProtocolConstants.cs:38 | Added retained-message stream constants |
| mqttSessStreamName | mqtt.go:124-125 | PORTED | src/NATS.Server/Mqtt/MqttProtocolConstants.cs:40 | Added session stream constants |
| mqttQoS2IncomingMsgsStreamName | mqtt.go:131-132 | PORTED | src/NATS.Server/Mqtt/MqttProtocolConstants.cs:43 | Added QoS2 incoming stream constants |
| mqttOutStreamName, mqttPubRel* | mqtt.go:135-139 | PORTED | src/NATS.Server/Mqtt/MqttProtocolConstants.cs:47 | Added outbound/PUBREL stream and subject-prefix constants |
| mqttDefaultAckWait | mqtt.go:145 | PORTED | src/NATS.Server/Mqtt/MqttProtocolConstants.cs:23 | Added Go-parity default ack wait (TimeSpan.FromSeconds(30)) |
| mqttDefaultMaxAckPending | mqtt.go:149 | PARTIAL | src/NATS.Server/Mqtt/MqttFlowController.cs:15 | Default 1024 matches Go, but not wired to JetStream |
| mqttMaxAckTotalLimit | mqtt.go:153 | PORTED | src/NATS.Server/Mqtt/MqttProtocolConstants.cs:26 | Added max ack total limit constant (0xFFFF) for flow/ack accounting parity |
| mqttJSA* token constants | mqtt.go:156-177 | PORTED | src/NATS.Server/Mqtt/MqttProtocolConstants.cs:54 | Added JSA reply prefix, token positions, and stream/consumer/message token constants |
| mqttSessFlappingJailDur | mqtt.go:182 | PARTIAL | src/NATS.Server/Mqtt/MqttSessionStore.cs:96-106 | Flap detection exists but uses different default timing |
| sparkb* constants | mqtt.go:201-211 | PORTED | src/NATS.Server/Mqtt/MqttProtocolConstants.cs:75 | Added Sparkplug birth/death constants and topic-prefix byte arrays |
| mqttNatsHeader* constants | mqtt.go:474-492 | PORTED | src/NATS.Server/Mqtt/MqttProtocolConstants.cs:83 | Added MQTT/NATS re-encoding header-name constants |
Core Types (lines 246-498)
| Go Symbol | Go File:Line | Status | .NET Equivalent | Notes |
|---|---|---|---|---|
| srvMQTT | mqtt.go:246 | PARTIAL | src/NATS.Server/Mqtt/MqttListener.cs:8-12 | Listener exists but no integration with Server struct or authOverride |
| mqttSessionManager | mqtt.go:253-256 | PARTIAL | src/NATS.Server/Mqtt/MqttSessionStore.cs:67 | Session store exists but not multi-account |
| mqttAccountSessionManager | mqtt.go:258-270 | PARTIAL | src/NATS.Server/Mqtt/MqttSessionStore.cs:67 | Single-account only, no JetStream backing, no retained msg sublist, no session hash |
| mqttJSA | mqtt.go:277-289 | PORTED | src/NATS.Server/Mqtt/MqttParityModels.cs (MqttJsa) |
Added MQTT JetStream API helper model with account/reply-prefix/domain fields |
| mqttJSPubMsg | mqtt.go:291-296 | PORTED | src/NATS.Server/Mqtt/MqttParityModels.cs (MqttJsPubMsg) |
Added JetStream publish message model (Subject, Payload, ReplyTo) |
| mqttRetMsgDel | mqtt.go:298-301 | PORTED | src/NATS.Server/Mqtt/MqttParityModels.cs (MqttRetMsgDel) |
Added retained-message delete notification model (Topic, Sequence) |
| mqttSession | mqtt.go:303-344 | PARTIAL | src/NATS.Server/Mqtt/MqttSessionStore.cs:48-60 | MqttSessionData covers basic fields but missing pendingPublish/pendingPubRel maps, cpending, last_pi, maxp, tmaxack, JetStream consumer tracking |
| mqttPersistedSession | mqtt.go:346-353 | PORTED | src/NATS.Server/Mqtt/MqttParityModels.cs (MqttPersistedSession) |
Added persisted-session metadata model (ClientId, LastPacketId, MaxAckPending) |
| mqttRetainedMsg | mqtt.go:355-364 | PARTIAL | src/NATS.Server/Mqtt/MqttRetainedStore.cs:14 | Retained message model now includes Origin, Flags, Source in addition to topic/payload. Remaining: cache TTL + JetStream retention parity |
| mqttRetainedMsgRef | mqtt.go:366-369 | PORTED | src/NATS.Server/Mqtt/MqttParityModels.cs (MqttRetainedMessageRef) |
Added retained-message reference model (StreamSequence, Subject) |
| mqttSub | mqtt.go:375-391 | PORTED | src/NATS.Server/Mqtt/MqttParityModels.cs (MqttSub) |
Added MQTT subscription metadata model (Filter, Qos, JsDur, Prm, Reserved) |
| mqtt (client struct) | mqtt.go:393-408 | PARTIAL | src/NATS.Server/Mqtt/MqttConnection.cs:6-16 | MqttConnection exists but missing reader/writer, asm, sess, cid, rejectQoS2Pub, downgradeQoS2Sub |
| mqttPending | mqtt.go:410-414 | PARTIAL | src/NATS.Server/Mqtt/MqttQoS1Tracker.cs:89-96 | QoS1PendingMessage exists but missing sseq, jsAckSubject, jsDur fields |
| mqttConnectProto | mqtt.go:416-420 | PARTIAL | src/NATS.Server/Mqtt/MqttBinaryDecoder.cs:14-25 | MqttConnectInfo covers most fields but is a record struct, not mutable; missing rd (read deadline) |
| mqttIOReader | mqtt.go:422-425 | NOT_APPLICABLE | Go-specific IO interface; .NET uses NetworkStream directly | |
| mqttReader | mqtt.go:427-433 | PARTIAL | src/NATS.Server/Mqtt/MqttPacketReader.cs:23-41 | MqttPacketReader handles fixed header; missing streaming buffer/partial packet support (pbuf, pstart) |
| mqttWriter | mqtt.go:435-437 | PORTED | src/NATS.Server/Mqtt/MqttPacketWriter.cs:3-38 | MqttPacketWriter covers write operations |
| mqttWill | mqtt.go:439-446 | PARTIAL | src/NATS.Server/Mqtt/MqttSessionStore.cs:35-42 | WillMessage exists but missing subject, mapped byte arrays; topic is string not bytes |
| mqttFilter | mqtt.go:448-453 | PORTED | src/NATS.Server/Mqtt/MqttParityModels.cs (MqttFilter) |
Added standalone MQTT filter model (Filter, Qos, TopicToken) |
| mqttPublish | mqtt.go:455-463 | PARTIAL | src/NATS.Server/Mqtt/MqttBinaryDecoder.cs:31-37 | MqttPublishInfo covers most fields but missing subject, mapped byte arrays |
| mqttParsedPublishNATSHeader | mqtt.go:495-499 | PORTED | src/NATS.Server/Mqtt/MqttParityModels.cs (MqttParsedPublishNatsHeader) |
Added parsed NATS-header model for MQTT publish flow (Subject, Mapped, publish/pubrel flags) |
Server Lifecycle Functions (lines 501-722)
| Go Symbol | Go File:Line | Status | .NET Equivalent | Notes |
|---|---|---|---|---|
| Server.startMQTT | mqtt.go:501 | PARTIAL | src/NATS.Server/Mqtt/MqttListener.cs:27-38 | MqttListener.StartAsync exists but no TLS, no integration with Server lifecycle |
| Server.createMQTTClient | mqtt.go:542 | PARTIAL | src/NATS.Server/Mqtt/MqttListener.cs:130-158 | AcceptLoopAsync creates MqttConnection but no TLS handshake, no auth timer, no read/write loops as goroutines |
| Server.mqttConfigAuth | mqtt.go:666 | MISSING | Auth configuration override not implemented | |
| validateMQTTOptions | mqtt.go:673 | MISSING | MQTT option validation not implemented | |
| client.isMqtt | mqtt.go:726 | NOT_APPLICABLE | .NET uses MqttConnection as a distinct type rather than a flag on client | |
| client.getMQTTClientID | mqtt.go:733 | PARTIAL | src/NATS.Server/Mqtt/MqttConnection.cs:13 | _clientId field exists but no public accessor |
Protocol Parsing (lines 742-959)
| Go Symbol | Go File:Line | Status | .NET Equivalent | Notes |
|---|---|---|---|---|
| client.mqttParse | mqtt.go:742 | PARTIAL | src/NATS.Server/Mqtt/MqttConnection.cs:17-66 | RunAsync handles basic text-based protocol; missing binary MQTT packet dispatch, partial packet handling, read deadline management |
| client.mqttTraceMsg | mqtt.go:961 | MISSING | MQTT message tracing not implemented |
Connection Close and Session Cleanup (lines 976-1107)
| Go Symbol | Go File:Line | Status | .NET Equivalent | Notes |
|---|---|---|---|---|
| Server.mqttHandleClosedClient | mqtt.go:976 | PARTIAL | src/NATS.Server/Mqtt/MqttConnection.cs:71-77 | DisposeAsync calls Unregister but no session cleanup, no will message handling, no clean session logic |
| Server.mqttUpdateMaxAckPending | mqtt.go:1026 | MISSING | Max ack pending update across sessions not implemented | |
| Server.mqttGetJSAForAccount | mqtt.go:1048 | MISSING | JetStream API access per account not implemented | |
| Server.mqttStoreQoSMsgForAccountOnNewSubject | mqtt.go:1065 | MISSING | QoS message re-store on subject change not implemented | |
| mqttParsePublishNATSHeader | mqtt.go:1080 | MISSING | NATS header parsing for MQTT messages not implemented | |
| mqttParsePubRelNATSHeader | mqtt.go:1096 | MISSING | PUBREL NATS header parsing not implemented |
Account Session Manager (lines 1112-2389)
| Go Symbol | Go File:Line | Status | .NET Equivalent | Notes |
|---|---|---|---|---|
| Server.getOrCreateMQTTAccountSessionManager | mqtt.go:1112 | MISSING | Per-account session manager creation not implemented | |
| Server.mqttCreateAccountSessionManager | mqtt.go:1152 | MISSING | JetStream stream/consumer creation for MQTT not implemented (350+ lines) | |
| Server.mqttDetermineReplicas | mqtt.go:1502 | MISSING | Replica count determination for cluster not implemented | |
| mqttJSA.newRequest | mqtt.go:1533 | MISSING | JetStream API request helper not implemented | |
| mqttJSA.prefixDomain | mqtt.go:1537 | MISSING | Domain prefix for JS subjects not implemented | |
| mqttJSA.newRequestEx | mqtt.go:1547 | MISSING | Extended JS API request not implemented | |
| mqttJSA.newRequestExMulti | mqtt.go:1566 | MISSING | Multi-request JS API helper not implemented | |
| mqttJSA.sendAck | mqtt.go:1649 | MISSING | JS ack sending not implemented | |
| mqttJSA.sendMsg | mqtt.go:1654 | MISSING | JS message sending not implemented | |
| mqttJSA.createEphemeralConsumer | mqtt.go:1663 | MISSING | Ephemeral consumer creation not implemented | |
| mqttJSA.createDurableConsumer | mqtt.go:1677 | MISSING | Durable consumer creation not implemented | |
| mqttJSA.deleteConsumer | mqtt.go:1692 | MISSING | Consumer deletion not implemented | |
| mqttJSA.createStream | mqtt.go:1707 | MISSING | Stream creation not implemented | |
| mqttJSA.updateStream | mqtt.go:1720 | MISSING | Stream update not implemented | |
| mqttJSA.lookupStream | mqtt.go:1733 | MISSING | Stream lookup not implemented | |
| mqttJSA.deleteStream | mqtt.go:1742 | MISSING | Stream deletion not implemented | |
| mqttJSA.loadLastMsgFor | mqtt.go:1751 | MISSING | Last message loading not implemented | |
| mqttJSA.loadLastMsgForMulti | mqtt.go:1765 | MISSING | Multi-subject last message loading not implemented | |
| mqttJSA.loadNextMsgFor | mqtt.go:1789 | MISSING | Next message loading not implemented | |
| mqttJSA.loadMsg | mqtt.go:1803 | MISSING | Message loading by seq not implemented | |
| mqttJSA.storeMsgNoWait | mqtt.go:1817 | MISSING | No-wait message store not implemented | |
| mqttJSA.storeMsg | mqtt.go:1825 | MISSING | Message store not implemented | |
| mqttJSA.storeSessionMsg | mqtt.go:1834 | MISSING | Session message store not implemented | |
| mqttJSA.loadSessionMsg | mqtt.go:1848 | MISSING | Session message load not implemented | |
| mqttJSA.deleteMsg | mqtt.go:1853 | MISSING | Message deletion not implemented | |
| isErrorOtherThan | mqtt.go:1879 | NOT_APPLICABLE | Go error-checking helper; .NET uses exception types | |
| mqttAccountSessionManager.processJSAPIReplies | mqtt.go:1886 | MISSING | JS API reply processing callback not implemented | |
| mqttAccountSessionManager.processRetainedMsg | mqtt.go:1971 | MISSING | Retained message processing callback not implemented | |
| mqttAccountSessionManager.processRetainedMsgDel | mqtt.go:2006 | MISSING | Retained message delete callback not implemented | |
| mqttAccountSessionManager.processSessionPersist | mqtt.go:2030 | MISSING | Session persist callback not implemented | |
| mqttAccountSessionManager.addSessToFlappers | mqtt.go:2092 | PARTIAL | src/NATS.Server/Mqtt/MqttSessionStore.cs:251 | TrackConnectDisconnect exists but uses different data structures |
| mqttAccountSessionManager.removeSessFromFlappers | mqtt.go:2116 | PARTIAL | src/NATS.Server/Mqtt/MqttSessionStore.cs:322 | ClearFlapperState exists |
| mqttAccountSessionManager.createSubscription | mqtt.go:2124 | MISSING | Internal subscription creation not implemented | |
| mqttAccountSessionManager.cleanupRetainedMessageCache | mqtt.go:2140 | MISSING | Retained message cache cleanup timer not implemented | |
| mqttAccountSessionManager.sendJSAPIrequests | mqtt.go:2174 | MISSING | JS API request sending goroutine not implemented | |
| mqttAccountSessionManager.addRetainedMsg | mqtt.go:2269 | PARTIAL | src/NATS.Server/Mqtt/MqttRetainedStore.cs:51-62 | SetRetained exists but no sseq tracking, no sublist insertion |
| mqttAccountSessionManager.removeRetainedMsg | mqtt.go:2304 | PARTIAL | src/NATS.Server/Mqtt/MqttRetainedStore.cs:53-55 | SetRetained with empty payload removes, but no seq/sublist management |
| mqttAccountSessionManager.lockSession | mqtt.go:2326 | MISSING | Session locking mechanism not implemented | |
| mqttAccountSessionManager.unlockSession | mqtt.go:2345 | MISSING | Session unlocking mechanism not implemented | |
| mqttAccountSessionManager.addSession | mqtt.go:2356 | PARTIAL | src/NATS.Server/Mqtt/MqttSessionStore.cs:192-196 | SaveSession exists but no hash-based lookup |
| mqttAccountSessionManager.removeSession | mqtt.go:2372 | PARTIAL | src/NATS.Server/Mqtt/MqttSessionStore.cs:209-210 | DeleteSession exists but no hash-based removal |
Subscription Processing (lines 2389-2730)
| Go Symbol | Go File:Line | Status | .NET Equivalent | Notes |
|---|---|---|---|---|
| mqttSession.processQOS12Sub | mqtt.go:2389 | MISSING | QoS 1/2 subscription processing with JS consumer not implemented | |
| mqttSession.processSub | mqtt.go:2396 | PARTIAL | src/NATS.Server/Mqtt/MqttListener.cs:40-43 | RegisterSubscription exists but extremely simplified; no QoS handling, no JS consumer, no retained message delivery |
| mqttAccountSessionManager.processSubs | mqtt.go:2463 | MISSING | Bulk subscription processing with JS consumers not implemented (180 lines) | |
| mqttAccountSessionManager.serializeRetainedMsgsForSub | mqtt.go:2642 | MISSING | Retained message serialization for new subscriptions not implemented | |
| mqttAccountSessionManager.addRetainedSubjectsForSubject | mqtt.go:2700 | MISSING | Retained subject matching via sublist not implemented | |
| mqttAccountSessionManager.loadRetainedMessages | mqtt.go:2730 | MISSING | Retained message loading from JetStream not implemented |
Retained Message Encoding/Decoding (lines 2798-2960)
| Go Symbol | Go File:Line | Status | .NET Equivalent | Notes |
|---|---|---|---|---|
| mqttEncodeRetainedMessage | mqtt.go:2798 | MISSING | Retained message NATS encoding with headers not implemented | |
| mqttSliceHeaders | mqtt.go:2855 | MISSING | NATS header slicing utility not implemented | |
| mqttDecodeRetainedMessage | mqtt.go:2908 | MISSING | Retained message NATS decoding not implemented |
Session CRUD (lines 2972-3190)
| Go Symbol | Go File:Line | Status | .NET Equivalent | Notes |
|---|---|---|---|---|
| mqttAccountSessionManager.createOrRestoreSession | mqtt.go:2972 | PARTIAL | src/NATS.Server/Mqtt/MqttSessionStore.cs:370-397 | ConnectAsync handles basic create/restore but no JetStream persistence, no hash-based lookup |
| mqttAccountSessionManager.deleteRetainedMsg | mqtt.go:3011 | MISSING | JetStream retained message deletion not implemented | |
| mqttAccountSessionManager.notifyRetainedMsgDeleted | mqtt.go:3018 | MISSING | Retained message delete notification not implemented | |
| mqttAccountSessionManager.transferUniqueSessStreamsToMuxed | mqtt.go:3031 | MISSING | Session stream migration not implemented | |
| mqttAccountSessionManager.transferRetainedToPerKeySubjectStream | mqtt.go:3089 | MISSING | Retained message stream migration not implemented | |
| mqttAccountSessionManager.getCachedRetainedMsg | mqtt.go:3146 | MISSING | Retained message cache get not implemented | |
| mqttAccountSessionManager.setCachedRetainedMsg | mqtt.go:3169 | MISSING | Retained message cache set not implemented | |
| mqttSessionCreate | mqtt.go:3193 | PARTIAL | src/NATS.Server/Mqtt/MqttSessionStore.cs:48-60 | MqttSessionData creation exists but missing maxp, pubRelSubject, idHash |
| mqttSession.save | mqtt.go:3215 | PARTIAL | src/NATS.Server/Mqtt/MqttSessionStore.cs:417-425 | SaveSessionAsync exists but no expected-last-sequence header, simplified JSON |
| mqttSession.clear | mqtt.go:3259 | PARTIAL | src/NATS.Server/Mqtt/MqttSessionStore.cs:209-210 | DeleteSession exists but no JetStream consumer cleanup |
| mqttSession.update | mqtt.go:3316 | PARTIAL | src/NATS.Server/Mqtt/MqttSessionStore.cs:402-411 | AddSubscription exists but no filter-level granularity, no auto-persist |
| mqttSession.bumpPI | mqtt.go:3345 | PARTIAL | src/NATS.Server/Mqtt/MqttQoS1Tracker.cs:75-83 | GetNextPacketId exists but simpler (no collision check with pendingPubRel) |
| mqttSession.trackPublishRetained | mqtt.go:3375 | MISSING | Retained message publish tracking not implemented | |
| mqttSession.trackPublish | mqtt.go:3400 | PARTIAL | src/NATS.Server/Mqtt/MqttQoS1Tracker.cs:27-39 | Register exists but no JS ack subject tracking, no cpending map |
| mqttSession.untrackPublish | mqtt.go:3478 | PARTIAL | src/NATS.Server/Mqtt/MqttQoS1Tracker.cs:45-48 | Acknowledge exists but does not return JS ack subject |
| mqttSession.trackAsPubRel | mqtt.go:3503 | MISSING | PUBREL tracking not implemented | |
| mqttSession.untrackPubRel | mqtt.go:3542 | MISSING | PUBREL untracking not implemented | |
| mqttSession.deleteConsumer | mqtt.go:3562 | MISSING | JS consumer deletion from session not implemented |
CONNECT Processing (lines 3576-3988)
| Go Symbol | Go File:Line | Status | .NET Equivalent | Notes |
|---|---|---|---|---|
| client.mqttParseConnect | mqtt.go:3576 | PARTIAL | src/NATS.Server/Mqtt/MqttBinaryDecoder.cs:70-140 | ParseConnect handles binary parsing well, but missing protocol level validation (rejects non-4), QoS2 will rejection, UTF-8 validation, subject mapping |
| client.mqttConnectTrace | mqtt.go:3753 | MISSING | Connect trace formatting not implemented | |
| Server.mqttProcessConnect | mqtt.go:3792 | PARTIAL | src/NATS.Server/Mqtt/MqttConnection.cs:34-48 | Connect handling exists but extremely simplified; no account session manager, no session restore, no flapper detection, no existing client eviction, no JetStream integration |
| client.mqttEnqueueConnAck | mqtt.go:3976 | PARTIAL | src/NATS.Server/Mqtt/MqttConnection.cs:45 | Writes "CONNACK" text; missing binary CONNACK with session-present flag and return code |
| Server.mqttHandleWill | mqtt.go:3990 | PARTIAL | src/NATS.Server/Mqtt/MqttSessionStore.cs:164-178 | PublishWillMessage exists but not integrated into connection close flow |
PUBLISH Processing (lines 4023-4560)
| Go Symbol | Go File:Line | Status | .NET Equivalent | Notes |
|---|---|---|---|---|
| client.mqttParsePub | mqtt.go:4023 | PARTIAL | src/NATS.Server/Mqtt/MqttBinaryDecoder.cs:155-185 | ParsePublish handles basic parsing; missing topic-to-subject conversion, subject mapping, empty topic validation |
| mqttPubTrace | mqtt.go:4096 | MISSING | Publish trace formatting not implemented | |
| mqttComputeNatsMsgSize | mqtt.go:4113 | MISSING | NATS message size computation not implemented | |
| mqttNewDeliverableMessage | mqtt.go:4139 | MISSING | NATS deliverable message composition not implemented | |
| mqttNewDeliverablePubRel | mqtt.go:4183 | MISSING | PUBREL deliverable message composition not implemented | |
| Server.mqttProcessPub | mqtt.go:4201 | PARTIAL | src/NATS.Server/Mqtt/MqttConnection.cs:53-63 | Basic QoS0/QoS1 publish exists; missing max payload check, QoS2 flow, JetStream message storage |
| Server.mqttInitiateMsgDelivery | mqtt.go:4249 | PARTIAL | src/NATS.Server/Mqtt/MqttListener.cs:46-58 | PublishAsync delivers to subscribers; missing NATS header encoding, permission check, JetStream storage for QoS1+ |
| Server.mqttStoreQoS2MsgOnce | mqtt.go:4295 | MISSING | QoS2 message deduplication and store not implemented | |
| client.mqttQoS2InternalSubject | mqtt.go:4318 | MISSING | QoS2 internal subject composition not implemented | |
| Server.mqttProcessPubRel | mqtt.go:4326 | PARTIAL | src/NATS.Server/Mqtt/MqttRetainedStore.cs:264 | ProcessPubRel exists in QoS2StateMachine but not wired to JetStream or message delivery |
| client.mqttHandlePubRetain | mqtt.go:4366 | PARTIAL | src/NATS.Server/Mqtt/MqttRetainedStore.cs:51-62 | SetRetained handles basic store/clear but no Sparkplug B, no JetStream persistence, no network notification |
| Server.mqttCheckPubRetainedPerms | mqtt.go:4471 | MISSING | Retained message permission checking not implemented | |
| generatePubPerms | mqtt.go:4564 | MISSING | Publish permission generation not implemented | |
| pubAllowed | mqtt.go:4595 | MISSING | Publish permission checking not implemented | |
| client.mqttEnqueuePubResponse | mqtt.go:4609 | PARTIAL | src/NATS.Server/Mqtt/MqttConnection.cs:58 | Writes "PUBACK" text for QoS1; missing binary PUBACK/PUBREC/PUBREL/PUBCOMP encoding |
| mqttParsePIPacket | mqtt.go:4641 | PARTIAL | src/NATS.Server/Mqtt/MqttBinaryDecoder.cs:316-323 | ReadUInt16BigEndian reads packet ID but no zero-check wrapper |
| client.mqttProcessPublishReceived | mqtt.go:4656 | MISSING | PUBACK/PUBREC processing with JS ack not implemented | |
| client.mqttProcessPubAck | mqtt.go:4691 | PARTIAL | src/NATS.Server/Mqtt/MqttConnection.cs:62-63 | AckPendingPublish exists but no JS ack, no PUBREL tracking |
| client.mqttProcessPubRec | mqtt.go:4695 | PARTIAL | src/NATS.Server/Mqtt/MqttRetainedStore.cs:248 | ProcessPubRec exists in QoS2StateMachine but not wired to session or JetStream |
| client.mqttProcessPubComp | mqtt.go:4700 | PARTIAL | src/NATS.Server/Mqtt/MqttRetainedStore.cs:281 | ProcessPubComp exists in QoS2StateMachine but not wired to session or JetStream |
| mqttGetQoS | mqtt.go:4720 | PORTED | src/NATS.Server/Mqtt/MqttBinaryDecoder.cs:162 | Inline in ParsePublish |
| mqttIsRetained | mqtt.go:4724 | PORTED | src/NATS.Server/Mqtt/MqttBinaryDecoder.cs:163 | Inline in ParsePublish |
| sparkbParseBirthDeathTopic | mqtt.go:4728 | MISSING | Sparkplug B birth/death topic parsing not implemented |
SUBSCRIBE/UNSUBSCRIBE Processing (lines 4760-5557)
| Go Symbol | Go File:Line | Status | .NET Equivalent | Notes |
|---|---|---|---|---|
| client.mqttParseSubs | mqtt.go:4760 | PARTIAL | src/NATS.Server/Mqtt/MqttBinaryDecoder.cs:197-217 | ParseSubscribe exists but no flag validation, no UTF-8 validation |
| client.mqttParseSubsOrUnsubs | mqtt.go:4764 | PARTIAL | src/NATS.Server/Mqtt/MqttBinaryDecoder.cs:197-217 | Combined parse exists only for subscribe; no unsubscribe binary parser |
| mqttSubscribeTrace | mqtt.go:4826 | MISSING | Subscribe trace formatting not implemented | |
| mqttDeliverMsgCbQoS0 | mqtt.go:4863 | MISSING | QoS0 delivery callback not implemented (71 lines; core delivery logic) | |
| mqttDeliverMsgCbQoS12 | mqtt.go:4934 | MISSING | QoS1/2 delivery callback not implemented (70+ lines; core delivery logic) | |
| mqttDeliverPubRelCb | mqtt.go:5004 | MISSING | PUBREL delivery callback not implemented | |
| mqttMustIgnoreForReservedSub | mqtt.go:5038 | MISSING | Reserved subject ignore check not implemented | |
| isMQTTReservedSubscription | mqtt.go:5047 | MISSING | Reserved subscription check not implemented | |
| sparkbReplaceDeathTimestamp | mqtt.go:5058 | MISSING | Sparkplug B death timestamp replacement not implemented | |
| client.mqttEnqueuePublishMsgTo | mqtt.go:5107 | MISSING | MQTT PUBLISH message serialization and enqueue not implemented | |
| mqttWriter.WritePublishHeader | mqtt.go:5156 | MISSING | PUBLISH header serialization not implemented (MqttPacketWriter has generic Write but not PUBLISH-specific) | |
| mqttMakePublishHeader | mqtt.go:5184 | MISSING | PUBLISH header creation not implemented | |
| client.mqttProcessSubs | mqtt.go:5204 | PARTIAL | src/NATS.Server/Mqtt/MqttListener.cs:40-43 | RegisterSubscription exists but no session locking, no JS consumer, no QoS handling |
| mqttSession.cleanupFailedSub | mqtt.go:5224 | MISSING | Failed subscription cleanup not implemented | |
| mqttSession.ensurePubRelConsumerSubscription | mqtt.go:5238 | MISSING | PUBREL consumer subscription setup not implemented (90 lines) | |
| mqttSession.processJSConsumer | mqtt.go:5327 | MISSING | JS consumer creation/restoration for subscription not implemented (100+ lines) | |
| client.mqttSendRetainedMsgsToNewSubs | mqtt.go:5435 | PARTIAL | src/NATS.Server/Mqtt/MqttRetainedStore.cs:98-104 | DeliverRetainedOnSubscribe exists but not integrated into connection flow |
| client.mqttEnqueueSubAck | mqtt.go:5449 | PARTIAL | src/NATS.Server/Mqtt/MqttConnection.cs:51 | Writes "SUBACK" text; missing binary SUBACK with per-filter QoS return codes |
| client.mqttParseUnsubs | mqtt.go:5469 | MISSING | Binary UNSUBSCRIBE packet parsing not implemented | |
| client.mqttProcessUnsubs | mqtt.go:5480 | MISSING | UNSUBSCRIBE processing with JS consumer cleanup not implemented | |
| client.mqttEnqueueUnsubAck | mqtt.go:5531 | MISSING | Binary UNSUBACK packet encoding not implemented | |
| mqttUnsubscribeTrace | mqtt.go:5541 | MISSING | Unsubscribe trace formatting not implemented |
PING/PONG (lines 5565-5576)
| Go Symbol | Go File:Line | Status | .NET Equivalent | Notes |
|---|---|---|---|---|
| client.mqttEnqueuePingResp | mqtt.go:5565 | MISSING | Binary PINGRESP encoding not implemented (text protocol only has no PING support) |
Trace and Error Helpers (lines 5577-5582)
| Go Symbol | Go File:Line | Status | .NET Equivalent | Notes |
|---|---|---|---|---|
| errOrTrace | mqtt.go:5577 | NOT_APPLICABLE | Go-specific trace helper; .NET uses ILogger |
Subject/Topic Conversion (lines 5592-5739)
| Go Symbol | Go File:Line | Status | .NET Equivalent | Notes |
|---|---|---|---|---|
| mqttTopicToNATSPubSubject | mqtt.go:5592 | PARTIAL | src/NATS.Server/Mqtt/MqttBinaryDecoder.cs:246-264 | TranslateFilterToNatsSubject exists but uses simple char-by-char replace; missing dot-to-double-slash, empty level handling, space rejection, leading/trailing slash handling |
| mqttFilterToNATSSubject | mqtt.go:5598 | PARTIAL | src/NATS.Server/Mqtt/MqttBinaryDecoder.cs:246-264 | Same as above; wildcard-ok flag not separated |
| mqttToNATSSubjectConversion | mqtt.go:5619 | PARTIAL | src/NATS.Server/Mqtt/MqttBinaryDecoder.cs:246-264 | Core conversion exists but missing edge cases: '.' to '//', leading '/' to '/.', trailing '/' to './', empty levels to './', space error |
| natsSubjectStrToMQTTTopic | mqtt.go:5694 | MISSING | NATS subject to MQTT topic (string variant) not implemented | |
| natsSubjectToMQTTTopic | mqtt.go:5698 | MISSING | NATS subject to MQTT topic (byte variant) not implemented | |
| mqttNeedSubForLevelUp | mqtt.go:5730 | MISSING | Level-up subscription check for '#' wildcard not implemented |
Reader Functions (lines 5747-5842)
| Go Symbol | Go File:Line | Status | .NET Equivalent | Notes |
|---|---|---|---|---|
| mqttReader.reset | mqtt.go:5747 | MISSING | Streaming reader reset with partial buffer not implemented | |
| mqttReader.hasMore | mqtt.go:5760 | NOT_APPLICABLE | .NET uses Span-based parsing, no position tracking needed | |
| mqttReader.readByte | mqtt.go:5764 | PORTED | src/NATS.Server/Mqtt/MqttPacketReader.cs:30-31 | Buffer indexing in Read method |
| mqttReader.readPacketLen / readPacketLenWithCheck | mqtt.go:5773-5803 | PORTED | src/NATS.Server/Mqtt/MqttPacketReader.cs:43-62 | DecodeRemainingLength implemented |
| mqttReader.readString | mqtt.go:5805 | PORTED | src/NATS.Server/Mqtt/MqttBinaryDecoder.cs:275-289 | ReadUtf8String implemented |
| mqttReader.readBytes | mqtt.go:5814 | PORTED | src/NATS.Server/Mqtt/MqttBinaryDecoder.cs:296-310 | ReadBinaryField implemented |
| mqttReader.readUint16 | mqtt.go:5835 | PORTED | src/NATS.Server/Mqtt/MqttBinaryDecoder.cs:316-323 | ReadUInt16BigEndian implemented |
Writer Functions (lines 5850-5882)
| Go Symbol | Go File:Line | Status | .NET Equivalent | Notes |
|---|---|---|---|---|
| mqttWriter.WriteUint16 | mqtt.go:5850 | PARTIAL | src/NATS.Server/Mqtt/MqttPacketWriter.cs:12-14 | Inline in Write method, not a standalone helper |
| mqttWriter.WriteString | mqtt.go:5855 | PORTED | src/NATS.Server/Mqtt/MqttPacketWriter.cs:8 | Added standalone UTF-8 length-prefixed WriteString helper |
| mqttWriter.WriteBytes | mqtt.go:5859 | PORTED | src/NATS.Server/Mqtt/MqttPacketWriter.cs:11 | Added standalone length-prefixed WriteBytes helper with uint16 size guard |
| mqttWriter.WriteVarInt | mqtt.go:5864 | PORTED | src/NATS.Server/Mqtt/MqttPacketWriter.cs:19-37 | EncodeRemainingLength implemented |
| newMQTTWriter | mqtt.go:5878 | PARTIAL | src/NATS.Server/Mqtt/MqttPacketWriter.cs:3 | Static class, no constructor needed; Write method serves the purpose |
Keeping This File Updated
After porting work is completed:
- Update status: Change
MISSING → PORTEDorPARTIAL → PORTEDfor each item completed - Add .NET path: Fill in the ".NET Equivalent" column with the actual file:line
- Re-count LOC: Update the LOC numbers in
stillmissing.md:# Re-count .NET source LOC for this module find src/NATS.Server/Mqtt/ -name '*.cs' -type f -exec cat {} + | wc -l # Re-count .NET test LOC for this module find tests/NATS.Server.Tests/Mqtt/ -name '*.cs' -type f -exec cat {} + | wc -l - Add a changelog entry below with date and summary of what was ported
- Update the parity DB if new test mappings were created:
sqlite3 docs/test_parity.db "INSERT INTO test_mappings (go_test_id, dotnet_test_id, confidence, notes) VALUES (?, ?, 'manual', 'ported in YYYY-MM-DD session')"
Summary Counts
| Status | Count |
|---|---|
| PORTED | 39 |
| PARTIAL | 57 |
| MISSING | 94 |
| NOT_APPLICABLE | 5 |
| DEFERRED | 0 |
| Total | 195 |
Change Log
| Date | Change | By |
|---|---|---|
| 2026-02-25 | Ported MQTT constants/writer parity batch: internal subject/stream constants, JSA reply token constants, Sparkplug constants, MQTT→NATS header constants, plus standalone MqttPacketWriter.WriteString/WriteBytes helpers with targeted tests (MqttProtocolConstantsParityBatch2Tests). |
codex |
| 2026-02-26 | Ported MQTT helper-model parity batch: added missing helper/data models (MqttJsa, MqttJsPubMsg, MqttRetMsgDel, MqttPersistedSession, MqttRetainedMessageRef, MqttSub, MqttFilter, MqttParsedPublishNatsHeader) and extended retained message model with Origin/Flags/Source; verified in MqttModelParityBatch3Tests. |
codex |
| 2026-02-25 | Full gap analysis completed: 195 items analyzed. 14 PORTED, 57 PARTIAL, 119 MISSING, 5 NOT_APPLICABLE. Major gaps: JetStream integration (entire mqttJSA layer ~30 functions), binary protocol encoding (CONNACK/SUBACK/UNSUBACK/PUBLISH serialization), delivery callbacks (QoS0/QoS1/QoS2), account session management, retained message encoding/decoding, Sparkplug B support, NATS subject reverse mapping. .NET has solid foundation for packet reading/writing, connect parsing, basic pub/sub flow, QoS tracking, and retained store, but all are simplified/in-memory-only without JetStream backing. | claude |
| 2026-02-25 | File created with LLM analysis instructions | auto |