Conversation
Unpack companion flood pathLen (multibyte hash modes), correlate attributed TXT_MSG rows, and enrich chat hops from late event 136 with a brief UI cue when RF corrects a stored count.
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughMeshCore path decoding and packet correlation now support late RF hop enrichment. Matching chat records are updated in memory and SQLite, corrected values receive temporary UI markers, and ChangesMeshCore late RF hop enrichment
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant MeshCore
participant meshcoreRfRxRuntime
participant applyMeshcoreLateRfHopEnrichment
participant ChatStore
participant ChatPanel
MeshCore->>meshcoreRfRxRuntime: deliver parsed text packet
meshcoreRfRxRuntime->>applyMeshcoreLateRfHopEnrichment: provide hop and message identity
applyMeshcoreLateRfHopEnrichment->>ChatStore: update matching message and persist record
ChatStore-->>ChatPanel: expose correction state
ChatPanel->>ChatPanel: render ChatRfHopLabel
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/renderer/lib/meshcoreRawPacketCorrelate.ts (1)
62-72: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winAdd sender-aware correlation for DM hop counts.
resolveMeshcoreIngestRxHopsdoes not receive the event sender. A newerTXT_MSGfrom another peer can therefore provide the hop count for the current message within the 3-second window. Matchraw fromNodeIdtoevent.payload.fromor use the message fingerprint, and add an interleaved-sender regression test.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/renderer/lib/meshcoreRawPacketCorrelate.ts` around lines 62 - 72, Update resolveMeshcoreIngestRxHops and meshcoreFindRecentTxtMsgRawPacket so TXT_MSG correlation only uses a raw packet from the current event sender, matching raw fromNodeId to event.payload.from or the message fingerprint. Preserve the existing time-window behavior, and add a regression test covering interleaved senders within the 3-second window.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/renderer/components/ChatRfHopLabel.test.tsx`:
- Around line 1-52: Add vitest-axe accessibility coverage to the ChatRfHopLabel
tests, using hydrateAxeThemeColors() so contrast uses real theme values rather
than mocked themeColors. Run axe against the rendered default and corrected
ChatRfHopLabel subtrees and assert toHaveNoViolations() for both, while
preserving the existing title and accent assertions.
In `@src/renderer/lib/meshcoreLateRfHopEnrichment.ts`:
- Around line 137-139: The GRP_TXT fallback in isBroadcastChannelCandidate must
not treat the payload hash as a channel index. Resolve innerPayload[0] against
configured channels and apply the fallback only when exactly one channel
matches; skip it when there is no match or multiple matches. Preserve existing
fingerprint-match behavior and avoid relying on meshcoreRfRxRuntime.ts to
provide the hash.
---
Outside diff comments:
In `@src/renderer/lib/meshcoreRawPacketCorrelate.ts`:
- Around line 62-72: Update resolveMeshcoreIngestRxHops and
meshcoreFindRecentTxtMsgRawPacket so TXT_MSG correlation only uses a raw packet
from the current event sender, matching raw fromNodeId to event.payload.from or
the message fingerprint. Preserve the existing time-window behavior, and add a
regression test covering interleaved senders within the 3-second window.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: QUIET
Plan: Pro Plus
Run ID: b8abfe07-21e7-422a-b2ff-34c8a40e3461
⛔ Files ignored due to path filters (16)
src/renderer/locales/cs/translation.jsonis excluded by!src/renderer/locales/**src/renderer/locales/de/translation.jsonis excluded by!src/renderer/locales/**src/renderer/locales/en/translation.jsonis excluded by!src/renderer/locales/**src/renderer/locales/es/translation.jsonis excluded by!src/renderer/locales/**src/renderer/locales/fr/translation.jsonis excluded by!src/renderer/locales/**src/renderer/locales/id/translation.jsonis excluded by!src/renderer/locales/**src/renderer/locales/it/translation.jsonis excluded by!src/renderer/locales/**src/renderer/locales/ja/translation.jsonis excluded by!src/renderer/locales/**src/renderer/locales/ko/translation.jsonis excluded by!src/renderer/locales/**src/renderer/locales/nl/translation.jsonis excluded by!src/renderer/locales/**src/renderer/locales/pl/translation.jsonis excluded by!src/renderer/locales/**src/renderer/locales/pt-BR/translation.jsonis excluded by!src/renderer/locales/**src/renderer/locales/ru/translation.jsonis excluded by!src/renderer/locales/**src/renderer/locales/tr/translation.jsonis excluded by!src/renderer/locales/**src/renderer/locales/uk/translation.jsonis excluded by!src/renderer/locales/**src/renderer/locales/zh/translation.jsonis excluded by!src/renderer/locales/**
📒 Files selected for processing (12)
src/renderer/components/ChatPanel.tsxsrc/renderer/components/ChatRfHopLabel.test.tsxsrc/renderer/components/ChatRfHopLabel.tsxsrc/renderer/lib/meshcore/meshcoreRfRxRuntime.tssrc/renderer/lib/meshcoreLateRfHopEnrichment.test.tssrc/renderer/lib/meshcoreLateRfHopEnrichment.tssrc/renderer/lib/meshcoreRawPacketCorrelate.test.tssrc/renderer/lib/meshcoreRawPacketCorrelate.tssrc/renderer/lib/meshcoreUtils.test.tssrc/renderer/lib/meshcoreUtils.tssrc/renderer/lib/protocols/MeshCoreProtocol.test.tsvitest.config.mts
| function isBroadcastChannelCandidate(c: MeshcoreLateHopCandidate): boolean { | ||
| return c.channel >= 0 && c.roomServerId == null && c.channel !== MESHCORE_ROOM_MESSAGE_CHANNEL; | ||
| } |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check whether channel index is available anywhere in RF parse context for GRP_TXT packets.
rg -n "channelIdx|channel_idx|channelIndex" src/renderer/lib/meshcore/meshcoreRfRxRuntime.ts
rg -n "buildMeshcoreRfParseContext" -A 60 src/renderer/lib/meshcore/meshcoreRfRxRuntime.ts | head -100Repository: Colorado-Mesh/mesh-client
Length of output: 4026
🏁 Script executed:
#!/bin/bash
set -eu
echo '--- enrichment implementation ---'
sed -n '1,380p' src/renderer/lib/meshcoreLateRfHopEnrichment.ts
echo '--- message types and adapter ---'
rg -n "interface ChatMessage|type ChatMessage|messageRecordToChatMessage|storeId" src/renderer/lib -g '*.ts' -g '*.tsx' | head -160
echo '--- relevant adapter/type definitions ---'
ast-grep outline src/renderer/lib/types.ts --match 'ChatMessage' --view expanded 2>/dev/null || true
ast-grep outline src/renderer/lib/storeRecordAdapters.ts --match 'messageRecordToChatMessage' --view expanded 2>/dev/null || true
echo '--- parser payload definitions and GRP_TXT handling ---'
rg -n "GRP_TXT|channel|channelIdx|channelIndex|roomServerId|messageFingerprintHex" src/renderer/lib -g '*.ts' -g '*.tsx' | head -260Repository: Colorado-Mesh/mesh-client
Length of output: 47980
🏁 Script executed:
#!/bin/bash
set -eu
echo '--- MeshCore parser files ---'
fd -i 'meshcore' src/renderer/lib | sort | head -120
rg -n "parseMeshCoreRf|parseMeshCoreRfPacket|payloadTypeNibble|MESHCORE_PAYLOAD_TYPE_GRP_TXT|channel_hash|channelHash|GRP_TXT" src/renderer/lib -g '*.ts' -g '*.tsx' | head -300
echo '--- MessageRecord and adapter details ---'
sed -n '80,145p' src/renderer/lib/storeRecordAdapters.ts
sed -n '350,435p' src/renderer/lib/types.ts
rg -n "interface MessageRecord|type MessageRecord" src/renderer/lib -g '*.ts' -g '*.tsx'
echo '--- UI key call sites ---'
rg -n "meshcoreChatHopUiKey|ChatRfHopLabel|isMeshcoreHopCorrected|viewMessages" src/renderer -g '*.ts' -g '*.tsx' | head -180Repository: Colorado-Mesh/mesh-client
Length of output: 23596
🏁 Script executed:
#!/bin/bash
set -eu
echo '--- shared parser ---'
wc -l shared/meshcoreRfPacketParse.ts
ast-grep outline shared/meshcoreRfPacketParse.ts --match 'parseMeshCoreRfPacket' --view expanded 2>/dev/null || true
rg -n "GRP_TXT|channel|hash|fingerprint|payload" shared/meshcoreRfPacketParse.ts | head -180
sed -n '1,280p' shared/meshcoreRfPacketParse.ts
echo '--- MessageRecord definition ---'
rg -n "MessageRecord" src/renderer/stores/messageStore.ts src/renderer/lib/types.ts src -g '*.ts' -g '*.tsx' | head -100
sed -n '1,130p' src/renderer/stores/messageStore.ts
echo '--- ChatPanel and label implementation ---'
sed -n '900,945p' src/renderer/components/ChatPanel.tsx
sed -n '1,90p' src/renderer/components/ChatRfHopLabel.tsxRepository: Colorado-Mesh/mesh-client
Length of output: 248
🏁 Script executed:
#!/bin/bash
set -eu
echo '--- locate parser ---'
fd -t f -i 'meshcoreRfPacketParse.ts' . || true
fd -t d 'shared' . | head -40
rg -l "function parseMeshCoreRfPacket|parseMeshCoreRfPacket" . -g '*.ts' -g '*.tsx' | head -40Repository: Colorado-Mesh/mesh-client
Length of output: 713
🏁 Script executed:
#!/bin/bash
set -eu
echo '--- parser ---'
wc -l src/shared/meshcoreRfPacketParse.ts
rg -n "GRP_TXT|channel|hash|fingerprint|payload" src/shared/meshcoreRfPacketParse.ts | head -220
sed -n '1,320p' src/shared/meshcoreRfPacketParse.ts
echo '--- MessageRecord definition ---'
rg -n "MessageRecord" src/renderer/stores/messageStore.ts src/renderer/lib/types.ts src -g '*.ts' -g '*.tsx' | head -100
sed -n '1,150p' src/renderer/stores/messageStore.ts
echo '--- ChatPanel and label implementation ---'
sed -n '920,945p' src/renderer/components/ChatPanel.tsx
sed -n '1,90p' src/renderer/components/ChatRfHopLabel.tsxRepository: Colorado-Mesh/mesh-client
Length of output: 29216
🏁 Script executed:
#!/bin/bash
set -eu
echo '--- channel-hash decoding and configured-channel mapping ---'
rg -n "channel.?hash|hash.*channel|channel.*hash|derive.*channel|channelSecret|meshcoreConfiguredChannel|channelConfigs|secret" src shared -g '*.ts' -g '*.tsx' | head -320
echo '--- RF runtime dependencies and parsed context ---'
sed -n '1,115p' src/renderer/lib/meshcore/meshcoreRfRxRuntime.ts
sed -n '225,265p' src/renderer/lib/meshcore/meshcoreRfRxRuntime.ts
sed -n '630,665p' src/renderer/lib/meshcore/meshcoreRfRxRuntime.ts
echo '--- hop enrichment tests for intended fallback behavior ---'
sed -n '1,280p' src/renderer/lib/meshcoreLateRfHopEnrichment.test.tsRepository: Colorado-Mesh/mesh-client
Length of output: 48482
🏁 Script executed:
#!/bin/bash
set -eu
echo '--- GRP_TXT raw payload handling ---'
sed -n '130,245p' src/renderer/lib/meshcoreRawPacketSender.ts
rg -n "GRP_TXT|channel hash|channel_hash|innerPayload|ciphertext|MAC" src/renderer/components/RawPacketLogPanel.tsx src/renderer/components/RawPacketLogPanel.test.tsx src/renderer/lib -g '*.ts' -g '*.tsx' | head -220
echo '--- MeshCore channel source/runtime wiring ---'
sed -n '1200,1270p' src/renderer/App.tsx
rg -n "meshcoreRfRx|attachMeshcoreConnSideEffects|MeshcoreRfRxDeps|channelsRef|channel.*Ref" src/renderer/runtime/useMeshcoreRuntime.ts src/renderer/lib/meshcore -g '*.ts' -g '*.tsx' | head -180Repository: Colorado-Mesh/mesh-client
Length of output: 19830
🏁 Script executed:
#!/bin/bash
set -eu
sed -n '240,350p' src/renderer/components/RawPacketLogPanel.tsx
rg -n "channelHash|channel_hash|hashHex|bytesToHex|hash.*secret|secret.*hash|sha|HMAC|SHA" src/renderer/components/RawPacketLogPanel.tsx src/renderer/lib src/shared -g '*.ts' -g '*.tsx' | head -240Repository: Colorado-Mesh/mesh-client
Length of output: 33436
🏁 Script executed:
#!/bin/bash
set -eu
python3 - <<'PY'
from dataclasses import dataclass
from math import inf
ROOM = -2
`@dataclass`
class Candidate:
store_id: str
channel: int
room_server_id: int | None
timestamp: int
rx_hops: int | None = None
sender_id: int = 7
status: str | None = None
received_via: str | None = 'rf'
def is_broadcast(c):
return c.channel >= 0 and c.room_server_id is None and c.channel != ROOM
def choose(candidates, now, window_ms, hop_count):
best = None
best_ts = -inf
for c in reversed(candidates):
if not is_broadcast(c): continue
if c.status == 'sending': continue
if c.received_via not in (None, 'rf', 'both', 'mqtt'): continue
if abs(now - c.timestamp) > window_ms: continue
if c.rx_hops is not None and c.rx_hops == hop_count: continue
if best is None or c.timestamp > best_ts:
best, best_ts = c, c.timestamp
return best
now = 1_700_000_000_000
candidates = [
Candidate('channel-0', 0, None, now - 200),
Candidate('channel-1', 1, None, now - 100),
]
selected = choose(candidates, now, 1000, 4)
assert selected is not None and selected.store_id == 'channel-1'
print('fallback selected:', selected.store_id, 'channel=', selected.channel)
print('incoming channel identity is not an input to the selection function')
PYRepository: Colorado-Mesh/mesh-client
Length of output: 344
🌐 Web query:
MeshCore protocol GRP_TXT channel_hash channel index mapping
💡 Result:
In the MeshCore protocol, the GRP_TXT (Group Text) payload uses a channel-specific hash to allow nodes to efficiently filter and identify relevant traffic [1][2]. The relationship between the channel, its secret, and this hash is defined as follows: Channel Secret and Hash Derivation 1. Channel Secret: Each group channel uses a 32-byte secret [3]. For private channels, this is a user-provided 16-byte pre-shared key (PSK) that is padded to 32 bytes with zeros [3][4]. For public hashtag channels (e.g., #mesh), the secret is derived deterministically from the channel name: SHA-256(name)[:16] (padded to 32 bytes) [5][6]. 2. Channel Hash: The channel_hash is a 1-byte identifier [5][2]. It is calculated as the first byte of the SHA-256 hash of the 16-byte channel secret (specifically, the unpadded PSK) [3][7][6]: channel_hash = SHA-256(secret_16_bytes) [5][7][6] Channel Index Mapping The channel index (0-7) is used primarily for Companion Protocol communication between a MeshCore device and a connected companion (e.g., a phone via BLE) [8][9]. - Index 0: Reserved for public channels (no secret) [8][9]. - Indices 1-7: Available for private channels [8][9]. The channel index is not transmitted within the over-the-air GRP_TXT packet header itself; the over-the-air packet only contains the 1-byte channel_hash [7][2]. Devices map incoming GRP_TXT packets to local channels by comparing the packet's channel_hash against the pre-calculated channel_hash of all known local channels [10][4]. If a collision occurs (multiple channels sharing the same 1-byte hash), the device must attempt to verify the MAC or decrypt the payload using each potential candidate secret to identify the correct channel [10][4]. Packet Structure (GRP_TXT) The GRP_TXT payload (Payload Type 0x05) is structured as follows [7][2]: - channel_hash: 1 byte [2] - cipher_mac: 2 bytes (truncated HMAC-SHA256 of the ciphertext) [7][2] - ciphertext: Variable length (AES-128-ECB encrypted) [7][2]
Citations:
- 1: https://docs.meshcore.io/faq/
- 2: https://wiki.eastmesh.au/meshcore/packet-structure/
- 3: https://gist.github.com/wirehack7/1c2b3fa04886705aee0b6e3d42570e6f
- 4: fix(meshcore): exact decrypt-and-match for channel 'repeaters relayed' echo (#3979 pt1) Yeraze/meshmonitor#3987
- 5: https://github.com/eddieoz/MeshCore-BC/blob/feature/bitchat-1.13.0/docs/bitchat/protocol_specification.md
- 6: Kpa-clawbot/CoreScope@c233c14
- 7: https://coreprotocol.org/core-protocol/
- 8: https://github.com/meshcore-dev/MeshCore/blob/07a3ca9e/docs/companion_protocol.md
- 9: https://docs.meshcore.io/companion_protocol/
- 10: https://github.com/chrisdavis2110/meshcore-decoder-py/blob/main/README.md
🏁 Script executed:
#!/bin/bash
set -eu
python3 - <<'PY'
from dataclasses import dataclass
from math import inf
from typing import Optional
ROOM = -2
`@dataclass`
class Candidate:
store_id: str
channel: int
room_server_id: Optional[int]
timestamp: int
rx_hops: Optional[int] = None
sender_id: int = 7
status: Optional[str] = None
received_via: Optional[str] = 'rf'
def is_broadcast(c):
return c.channel >= 0 and c.room_server_id is None and c.channel != ROOM
def choose(candidates, now, window_ms, hop_count):
best = None
best_ts = -inf
for c in reversed(candidates):
if not is_broadcast(c):
continue
if c.status == 'sending':
continue
if c.received_via not in (None, 'rf', 'both', 'mqtt'):
continue
if abs(now - c.timestamp) > window_ms:
continue
if c.rx_hops is not None and c.rx_hops == hop_count:
continue
if best is None or c.timestamp > best_ts:
best, best_ts = c, c.timestamp
return best
now = 1700000000000
candidates = [
Candidate('channel-0', 0, None, now - 200),
Candidate('channel-1', 1, None, now - 100),
]
selected = choose(candidates, now, 1000, 4)
assert selected is not None and selected.store_id == 'channel-1'
print('fallback selected:', selected.store_id, 'channel=', selected.channel)
print('incoming channel identity is not an input to the selection function')
PYRepository: Colorado-Mesh/mesh-client
Length of output: 271
Avoid ambiguous GRP_TXT hop corrections.
GRP_TXT carries a one-byte channel hash, not a channel index. The parser exposes it only through innerPayload[0], and meshcoreRfRxRuntime.ts does not pass it to enrichment. When fingerprint matching fails, the fallback selects the newest eligible broadcast message across all channels and can persist the wrong hop count.
Resolve the hash only when it maps to one unambiguous configured channel. Otherwise, skip the GRP_TXT fallback. Keep fingerprint matches unchanged.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/renderer/lib/meshcoreLateRfHopEnrichment.ts` around lines 137 - 139, The
GRP_TXT fallback in isBroadcastChannelCandidate must not treat the payload hash
as a channel index. Resolve innerPayload[0] against configured channels and
apply the fallback only when exactly one channel matches; skip it when there is
no match or multiple matches. Preserve existing fingerprint-match behavior and
avoid relying on meshcoreRfRxRuntime.ts to provide the hash.
Match TXT_MSG raw-log hops by event sender or fingerprint so interleaved DMs cannot steal counts, and add vitest-axe coverage for ChatRfHopLabel.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/renderer/components/ChatRfHopLabel.test.tsx`:
- Around line 46-47: Update the production color used by ChatRfHopLabel or its
theme token so the resolved default color contrasts sufficiently with the
`#1e293b` background for small text, then keep the test fixture aligned with that
production value and verify axe reports no violations.
In `@src/renderer/lib/meshcoreRawPacketCorrelate.ts`:
- Around line 27-50: Update meshcoreFindRecentTxtMsgRawPacket and its session
state to track selected rawPacketsRef rows with a bounded, session-scoped
consumed-row marker, and skip marked rows during correlation so one RF row
cannot be reused by multiple DMs. Ensure fromNodeId 0 does not fall back to rows
from another sender by requiring a valid sender or fingerprint match before
selecting a row. Add tests covering duplicate same-sender DMs and the fromNodeId
0 cross-sender case.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: QUIET
Plan: Pro Plus
Run ID: c9c89029-ec62-48ba-906d-1faf5aea6894
📒 Files selected for processing (5)
src/renderer/components/ChatRfHopLabel.test.tsxsrc/renderer/lib/ingest/meshcoreIngest.test.tssrc/renderer/lib/ingest/meshcoreIngest.tssrc/renderer/lib/meshcoreRawPacketCorrelate.test.tssrc/renderer/lib/meshcoreRawPacketCorrelate.ts
Use gray-400 for small hop text on slate chat chrome, and consume matched TXT_MSG raw rows so one RF packet cannot enrich multiple DMs (no from=0 fallback).
Summary
pathLenviameshcoreUnpackPathLenByteso multibyte path-hash modes (packed bytes ≥ 64) show hop pills again instead of only direct/0xFFtraffic.TXT_MSGraw-log rows, and enrich chat rows from late event 136 when hops are missing or RF disagrees (persist to SQLite).Updated from RF path), respecting Reduce motion.Test plan
pathLen, attributed TXT_MSG correlate, late-136 enrichment (fill/replace/no-op),ChatRfHopLabelaccent/titleSummary by CodeRabbit
New Features
Bug Fixes