fix(mqtt): attribute same-key channels by packet hash, not sort order#3754
Conversation
Multiple channel_database rows can share a key (commonly the default "AQ==" key — e.g. an auto-seeded "LongFast" plus a user's custom channel). channelDecryptionService.tryDecrypt() walked channels in sortOrder and let the first one whose key decrypted claim the packet, so whichever same-key channel sorted first absorbed ALL default-key traffic. Packets actually sent on the custom channel were attributed to the wrong channel_database row — and therefore the wrong channel permissions — which (among other things) hid those nodes from anonymous users who only had viewOnMap on the real channel. Fix: compute the Meshtastic channel hash (xorHash(name) ^ xorHash(psk)) for every cached channel, and in tryDecrypt() try hash-matching channels first (preserving sortOrder within each group) before falling back to the rest. This is a preference, not a hard skip: if no hash-matching channel decrypts (1-byte hash collision across keys, or the real channel isn't in the DB), we still fall back, so decryption is never lost — only attribution improves. enforceNameValidation remains an independent hard skip. The fix is retroactive: existing deployments are corrected without any DB edit. Deliberately did NOT flip the MQTT seed's enforceNameValidation to true: that would hard-skip the "LongFast"-named seed for non-LongFast modem presets (whose default channel hashes as "MediumSlow"/"ShortFast"/…), silently dropping their default-channel decryption. Hash-aware attribution achieves the same disambiguation without that regression; the seed stays validation-off with a comment explaining why. Tests: new "Hash-aware attribution (same-key channels)" block covering the LongFast/Custom bug, the reverse direction, fallback-when-no-match (the non-LongFast safety case), fallback past a hash-colliding wrong-key channel, and hard-skip-still-wins. Full main suite green (7553 tests). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011JEaCGwY9Wz8jeV4e22GW4
Channel Priority now reflects that a packet's channel hash selects the correct same-PSK channel before sort order, and that sort order is only the tiebreaker (decryption is never lost). Clarify that Enforce Name Validation is no longer needed merely to attribute same-key channels — it now strictly *rejects* non-matching packets — and note leaving it off is the safer default across modem presets. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011JEaCGwY9Wz8jeV4e22GW4
|
Claude finished @Yeraze's task —— View job PR Review: fix(mqtt): attribute same-key channels by packet hash, not sort orderTasks:
SummaryThis is a well-designed fix for a real attribution bug. The core logic is sound, tests are thorough, and the fallback-not-hard-skip design avoids the regression that enforcing a hard channel hash match would cause on non-LongFast modem presets. The Meshtastic channel hash algorithm ( Issues Found🔴 Bug:
|
Summary
Multiple
channel_databaserows can share a key — most commonly the defaultAQ==key (e.g. an auto-seededLongFastplus a user's custom channel). The server-side decryption service walked channels in sort order and let the first one whose key decrypted claim the packet, so whichever same-key channel sorted first absorbed all default-key traffic. Packets actually sent on the custom channel were attributed to the wrong row — and therefore the wrong channel permissions — which (among other things) hid those nodes from anonymous users who only hadviewOnMapon the real channel. This makes attribution follow the packet's channel hash so the correct same-key channel is credited.Changes
channelDecryptionService: compute the Meshtastic channel hash (xorHash(name) ^ xorHash(psk)) for every cached channel, not only name-validated ones.tryDecrypt()now tries hash-matching channels first (preservingsortOrderwithin each group), then the rest. It's a preference, not a hard skip: if nothing matches (1-byte hash collision across keys, or the real channel isn't in the DB) it still falls back and decrypts, so decryption is never lost — only attribution improves.enforceNameValidationremains an independent hard skip, unchanged.mqttIngestionbootstrap seed left atenforceNameValidation: falseon purpose, with a comment: enforcing it would hard-skip theLongFast-named seed for non-LongFast modem presets (whose default channel hashes asMediumSlow/ShortFast/…), silently dropping their default-channel decryption. Hash-aware attribution achieves the same disambiguation without that regression.docs/features/channel-database.mdupdated to describe hash-aware priority and the narrowed role of Enforce Name Validation.Issues Resolved
None (reported directly by a deployment operator: same-key
LongFast/Customchannels caused MQTT-bridge nodes to be misattributed and hidden from anonymous users).Documentation Updates
docs/features/channel-database.md— "Channel Priority and Ordering" now explains hash-matching-first selection; "Enforce Name Validation" clarified as a strict hard-reject (no longer needed merely to attribute same-key channels), with leaving it off as the safer default.Testing
success: true, 7553 passed, 0 failed, 0 suite failures; sibling.claude/worktrees/checkout excluded)tsc -p tsconfig.server.jsononly shows pre-existingTelemetryChart.tsxfrontend noise)Hash-aware attribution (same-key channels)test block: the LongFast/Custom bug, the reverse direction, fallback-when-no-match (non-LongFast preset safety), fallback past a hash-colliding wrong-key channel, and hard-skip-still-winsAQ==)LongFast+Customsetup attributes packets to the channel matching each packet's hash, and that default-key traffic on a non-LongFast preset still decrypts🤖 Generated with Claude Code