fix(meshtastic): resolve position-packet channel from decryption context, not raw hash (#3682)#3694
Conversation
…ext, not raw hash (#3682) Position packets received on a private secondary channel displayed as "channel N" where N was a raw numeric hash (e.g. 39) instead of the correct channel slot/name. Text messages on the same channel displayed correctly. Root cause: channel resolution differed by packet type. The TEXT_MESSAGE_APP dispatch threads the decryption context (decryptedBy/decryptedChannelId) into processTextMessageProtobuf, which resolves the channel slot from the Channel Database entry the packet was decrypted on (CHANNEL_DB_OFFSET + dbId, or a matching device-channel slot). The POSITION_APP dispatch dropped that context, and processPositionMessageProtobuf read meshPacket.channel directly — that field is the on-wire LoRa channel hash (xorHash(name) ^ xorHash(psk)), NOT a slot index, so it surfaced as a meaningless number. Fix: extract the text path's channel-resolution logic into a shared resolveBroadcastChannelIndex() helper and thread the same decryption context into processPositionMessageProtobuf. Server-decrypted packets now resolve to the same channel slot as text on that channel; the raw meshPacket.channel fallback is preserved for unencrypted/primary/ node-decrypted packets. Per-source scoping is unchanged. Adds meshtasticManager.positionChannel.test.ts covering the helper (server-decrypted -> offset+dbId, device-channel match, unencrypted/ node-decrypted/undefined fallbacks) and the full handler (telemetry channel = resolved slot, not the raw hash). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VBLhGGNh35oMwTL53va1Y5
|
Claude finished @Yeraze's task —— View job PR Review: fix(meshtastic): resolve position-packet channel from decryption context (#3682)
SummaryThis is a well-scoped, correctness fix for a real channel-resolution inconsistency. The root cause analysis in the PR description is accurate and matches the code. Overall the change is solid; a few observations below. Code Quality
The helper is a clean extraction and mirrors the text-message path faithfully. One observation: the inline logic for the server-decryption path in Context spreading (meshtasticManager.ts:5537–5540) await this.processPositionMessageProtobuf(meshPacket, processedPayload as any, {
...context,
decryptedBy,
decryptedChannelId: decryptedChannelId ?? undefined,
});The // TEXT_MESSAGE_APP (line 5526):
{ decryptedBy, decryptedChannelId: decryptedChannelId ?? undefined }
// POSITION_APP (line 5537):
{ ...context, decryptedBy, decryptedChannelId: decryptedChannelId ?? undefined }The asymmetry is minor but worth noting.
The method returns Potential BugsNo issues found. The logic correctly handles all four cases:
The Test CoverageThe 7 tests in
One gap: there's no test asserting the Performance
SecurityNo concerns. The fix is purely a display/storage correctness issue. Channel decryption itself is unchanged; this only affects which slot index is stored after decryption succeeds. VerdictApproved with minor suggestions. The fix is correct, well-tested, and doesn't introduce regressions. The two suggestions above (refactor text path to call the shared helper; add a |
Summary
Fixes #3682. Position packets received on a private secondary channel displayed as "channel N" where N was a raw numeric hash (e.g. 39), instead of the correct channel slot/name. Text messages on the same channel displayed the correct channel.
Root cause
Channel resolution differed by packet type in
src/server/meshtasticManager.ts:decryptedBy/decryptedChannelId) intoprocessTextMessageProtobuf, which resolves the channel slot from the Channel Database entry the packet was decrypted on — either a matching device-channel slot (same PSK + name) orCHANNEL_DB_OFFSET + decryptedChannelId.processPositionMessageProtobufreadmeshPacket.channeldirectly. That field is the on-wire LoRa channel hash (xorHash(name) ^ xorHash(psk)), not a channel slot index — so for a server-decrypted secondary channel it surfaced as a meaningless number (the "39").Fix
resolveBroadcastChannelIndex(meshPacket, context)helper, mirroringprocessTextMessageProtobufexactly (device-channel PSK+name match preference, thenCHANNEL_DB_OFFSET + decryptedChannelId).decryptedBy/decryptedChannelId) intoprocessPositionMessageProtobuffrom the POSITION_APP dispatch case.positionChannelnow store the resolved channel slot, consistent with text messages on the same channel.meshPacket.channelexactly as before.channels.getAllChannels(this.sourceId)stays source-scoped).Test
New
src/server/meshtasticManager.positionChannel.test.ts(7 tests):resolveBroadcastChannelIndex: server-decrypted →CHANNEL_DB_OFFSET + dbId(not the raw hash); device-channel slot preferred on PSK+name match; raw-hash fallback for no-context / node-decrypted; default 0 when channel undefined.processPositionMessageProtobuf: telemetrychannelis the resolved slot (not the raw hash) when server-decrypted; rawmeshPacket.channelfor unencrypted/primary.Full Vitest suite: success, 7377 passed, 0 failed, 0 failed suites.
tsc --noEmit: no new errors in touched files.🤖 Generated with Claude Code