When a CHANNEL_MSG (GRP_TXT) can't be decrypted, the decoder now includes:
- channelHashHex: zero-padded uppercase hex string of the channel hash byte
- decryptionStatus: 'decrypted', 'no_key', or 'decryption_failed'
Frontend changes:
- Packet list preview shows '🔒 Ch 0xXX (no key)' or '(decryption failed)'
- Detail pane hex breakdown shows channel hash with status label
- Detail pane message area shows channel hash info for undecrypted packets
6 new decoder tests (58 total): channelHashHex formatting, decryptionStatus
for no keys, empty keys, bad keys, and short encrypted data.
Fixes#123
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
decodeEncryptedPayload: dest(1)+src(1)+MAC(2) per PAYLOAD_VER_1
decodeAck: dest(1)+src(1)+ack_hash(4)
decodeAnonReq: dest(1)+pubkey(32)+MAC(2)
decodePath: dest(1)+src(1)+MAC(2)+data
Source: firmware/src/Mesh.cpp lines 129-130, MeshCore.h CIPHER_MAC_SIZE=2
Golden fixture tests need updating to match correct output.
ADV_TYPE_ROOM=3 (0b0011) was misread as chat+repeater because decoder
treated lower nibble as individual bits. Now correctly: type & 0x0F as
enum (0=none, 1=chat, 2=repeater, 3=room, 4=sensor).
Includes startup backfill: scans all adverts and fixes any node roles
in the DB that were incorrectly set to 'repeater' when they should be
'room'. Logs count of fixed nodes on startup.
Timestamp is decoded from the ADVERT but never persisted to the nodes
table. The validation was rejecting valid nodes with slightly-off clocks
(28h future) and nodes broadcasting timestamp=4. No reason to gate on it.