Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ Sanitize user-controlled strings before logs and IPC per [AGENTS.md](AGENTS.md).

- **UI:** `RoomsPanel.tsx` + shared `ChatComposer.tsx`; unread `meshcoreRoomsUnread.ts`.
- **Runtime:** `useMeshcoreRuntime.ts` coordinates login queue, auto-sync (`meshcoreRoomSyncScheduler.ts`), and ingest dedup (`meshcoreStoreDedup.ts`).
- **RPC/helpers:** `meshcoreRoomLoginRpc.ts`, `meshcoreRoomPostRpc.ts`, `meshcoreRoomSession.ts`, `meshcoreChannelText.ts` (SignedPlain / tapbacks / Open wire via optional App toggle), `meshcoreGifWire.ts`, `meshcoreOpenReaction.ts`. RF-only (not MQTT). User guide: [docs/meshcore-meshtastic-parity.md](docs/meshcore-meshtastic-parity.md#meshcore-room-servers).
- **RPC/helpers:** `meshcoreRoomLoginRpc.ts`, `meshcoreRoomPostRpc.ts`, `meshcoreRoomSession.ts`, `meshcoreChannelText.ts` (SignedPlain / tapbacks / Open wire via optional Radio toggle), `meshcoreGifWire.ts`, `meshcoreOpenReaction.ts`. RF-only (not MQTT). User guide: [docs/meshcore-meshtastic-parity.md](docs/meshcore-meshtastic-parity.md#meshcore-room-servers).

### UI

Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ MeshCore runs simultaneously alongside Meshtastic and Reticulum. Use the protoco
- Channel messaging and **direct messages (DMs)** with delivery ACK tracking (`expectedAckCrc`) and failure timeout; **DM threads can be closed** from the chat UI
- **Transport badges** on received messages; **RF**, **MQTT**, or **both** (persisted as `received_via` in `meshcore_messages`); MQTT JSON chat can be used when RF is down
- **Inbound dedup** (`meshcoreStoreDedup.ts`): merges duplicate RF/MQTT echoes, companion TX echoes, and tapback self-echoes so chat and Rooms stay readable
- **MeshCore Open GIFs**: inbound `g:GIFID` (and Giphy URLs) render inline in chat; outbound send via App **MeshCore Open compatibility** toggle (paste URL/ID or **GIF** composer button) — see [parity doc](docs/meshcore-meshtastic-parity.md#meshcore-open-gif-wire-ggifid)
- **MeshCore Open GIFs**: inbound `g:GIFID` (and Giphy URLs) render inline in chat; outbound send via Radio **MeshCore Open compatibility** toggle (paste URL/ID or **GIF** composer button) — see [parity doc](docs/meshcore-meshtastic-parity.md#meshcore-open-gif-wire-ggifid)
- Incoming push events: periodic advert (0x80), path update (0x81), send confirmed (0x82), message waiting (0x83), new contact (0x8A), incoming DM (7), incoming channel message (8)
- All messages and contacts persisted to SQLite (`meshcore_messages`, `meshcore_contacts` tables)

Expand All @@ -261,7 +261,7 @@ MeshCore runs simultaneously alongside Meshtastic and Reticulum. Use the protoco
- Login to room-server contacts (guest read-only or admin post); **Continue read-only** when the server guest password is empty (zero-byte password, matching the official Android client)
- Post plain UTF-8 after login; inbound **SignedPlain** pushes show author prefix stripped in the UI
- **Remember password**, **Auto-sync** (periodic re-login while connected, minimum 60 minutes per room), per-room unread badges (sidebar **Rooms** tab; separate from **Chat** badges)
- Room admin CLI in the Rooms panel; session/login queue and path sync in `meshcoreRoom*.ts` — see [docs/meshcore-meshtastic-parity.md](docs/meshcore-meshtastic-parity.md#meshcore-room-servers) and [Troubleshooting](docs/troubleshooting.md#meshcore-room-server-login-posts-and-windows-10)
- Room admin CLI / ACL setperm on the **Repeaters** tab (room rows); Rooms Members still call `get acl` via the same CLI path. Session/login queue and path sync in `meshcoreRoom*.ts` — see [docs/meshcore-meshtastic-parity.md](docs/meshcore-meshtastic-parity.md#meshcore-room-servers) and [Troubleshooting](docs/troubleshooting.md#meshcore-room-server-login-posts-and-windows-10)

**Diagnostics & Remote Queries**

Expand All @@ -272,8 +272,8 @@ MeshCore runs simultaneously alongside Meshtastic and Reticulum. Use the protoco

**Repeaters**

- **Repeaters panel** (MeshCore-only tab): list repeaters with on-demand status (noise floor, RSSI/SNR, packet counts, air time, uptime, TX queue); **Path** column shows a per-hop SNR sparkline from the last trace (last trace/path hop data is also stored in local SQLite so sparklines can survive app restarts); per-row **Neighbors** expands an inline neighbor list (same query as node detail, including **Load more**)
- **Per-repeater admin passwords**: optional **Remember** saves credentials per repeater in SQLite `app_settings` (`meshcoreRepeaterCredential:<nodeId>`); collapsible **Saved repeater passwords** sidebar section with per-repeater Forget
- **Repeaters panel** (MeshCore-only tab): list **repeaters and room servers** (All / Repeaters / Rooms filter) with on-demand status (noise floor, RSSI/SNR, packet counts, air time, uptime, TX queue); **Path** column shows a per-hop SNR sparkline from the last trace (last trace/path hop data is also stored in local SQLite so sparklines can survive app restarts); per-row **Neighbors** expands an inline neighbor list (same query as node detail, including **Load more**); room rows add **Open room** (jump to Rooms) plus room CLI pills (`get acl`, `allow.read.only`, ACL setperm)
- **Per-node admin passwords**: optional **Remember** saves credentials per repeater/room in SQLite `app_settings` (`meshcoreRepeaterCredential:<nodeId>` / room admin password); collapsible **Saved passwords** sidebar section with per-node Forget
- **Waiting-message drain**: header status indicator (queued backlog and active sync on any protocol tab; **paused/deferred** state only on the MeshCore tab) during serial companion backlog drain; **Sync now** for manual catch-up
- **Repeater CLI**: per-repeater expandable **CLI** interface; command input with Enter to send, scrollable command/response history, Up/Down arrow history navigation, quick-command bar (get name, get radio, neighbors, version, clock, clock sync, clear stats, advert, board, …), flood vs. auto (saved path) routing toggle; responses are correlated to commands via 2-character hex prefix tokens; configurable retries with dynamic timeout; **auto Ping** before the first multi-hop CLI command when no trace exists this session (info toast while establishing route); **destructive-command confirm** modal for reboot/erase/factory-reset patterns
- **Remote session authentication (optional)**: Password may be required for **CLI** and some **telemetry** paths when firmware ACL demands it. **Status** and **Neighbors** use pubkey-framed companion commands and typically work without login on direct (0-hop) repeaters; the auth modal offers “Continue without password.” Saved passwords persist when **Remember** is checked. Admin RPCs share a serialized companion queue — expect up to ~2 minutes blocked while a ping or multi-hop request runs. Status/Telemetry/Neighbors toast when the radio is disconnected.
Expand Down
12 changes: 8 additions & 4 deletions docs/agents/meshcore-repeaters.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,25 @@

Deep subsystem reference for AI assistants. Open this when a task touches MeshCore repeater admin RPCs, trace/ping, neighbors paging, CLI, or waiting-message drain. Hard rules live in [`AGENTS.md`](../../AGENTS.md).

**Repeaters & Rooms ops tab:** [`RepeatersPanel.tsx`](../../src/renderer/components/RepeatersPanel.tsx) lists **both** `hw_model === 'Repeater'` and `hw_model === 'Room'` (All / Repeaters / Rooms filter). Sidebar label is `tabs.repeaters` (**Repeaters**); in-panel title is `repeatersPanel.title` (**Repeaters & Rooms**). Rooms BBS stays on the Rooms tab; Manage there jumps here via `pendingFocusNodeId`. **Open room** on a room row jumps back to Rooms. Admin passwords use [`meshcoreInfraAdminSecrets.ts`](../../src/renderer/lib/meshcoreInfraAdminSecrets.ts) (two `app_settings` prefixes; room forget clears **admin only**). Room CLI goes through `sendRepeaterCliCommand` / thin `sendRoomAdminCliCommand` alias (forwards `confirmedDanger`); room remote CLI requires ACL admin password (not guest BBS). Room-only CLI pills: `get acl`, `allow.read.only on|off` + ACL `setperm` form.

MeshCore firmware **serializes traceroutes** — one active trace cycle per RF link. mesh-client enforces:

- **Trace queue** (`meshcoreRepeaterRpcInFlight.ts`): global ping queue; duplicate clicks coalesce per node.
- **Companion queue** (`repeaterRemoteRpcQueue.ts`): serializes RPC _sends_ (Status, Telemetry, Neighbors binary req, trace SendTracePath, CLI login).
- **Queued send** (`meshcoreRepeaterRpcQueuedSend.ts`): queue slot ends at `RESP_SENT`; response listeners run outside the slot.
- **Admin idle** (`meshcoreTraceRadioIdle.ts`): `beforeSend` waits for TraceData in flight only (not pending route registration). Same-node admin awaits ping wrapper settle (`MESHCORE_REPEATER_PING_SETTLE_MAX_MS` = 2× ping cap).
- **0-hop contract** (`meshcoreRepeaterTracePath.ts`, `meshcoreZeroHopRepeaterWorkingState.test.ts`): Status/Telemetry/Neighbors use pubkey-framed frames (no contact-list gate). Ping seeds 1-byte prefix; direct retry escalates to full pubkey only when `hopsAway === 0`. Multi-hop ping requires hash-segment path (≥2 bytes), never full destination pubkey. Status/Telemetry/Neighbors **throw** on disconnect (`MESHCORE_ERR_NOT_CONNECTED`) so RepeatersPanel / node-detail toasts fire — do not bare-`return`.
- **0-hop contract** (`meshcoreRepeaterTracePath.ts`, `meshcoreZeroHopRepeaterWorkingState.test.ts`): Status/Telemetry/Neighbors use pubkey-framed frames (no contact-list gate). Ping seeds a per-contact pubkey prefix sized from that contact’s packed `outPathLen` (1–3 bytes; plain lengths still 1-byte); direct retry escalates to full pubkey only when `hopsAway === 0`. Multi-hop ping requires hash-segment path (≥2 bytes), never full destination pubkey. Status/Telemetry/Neighbors **throw** on disconnect (`MESHCORE_ERR_NOT_CONNECTED`) so RepeatersPanel / node-detail toasts fire — do not bare-`return`.
- **One active traceroute**: firmware allows one TraceData cycle; `meshcoreTracePathMultiplex` waits for prior TraceData before the next `SendTracePath`. Room-login active traces use the same global ping queue (`runMeshcoreRepeaterRpcOnce('trace')`) and cancel the multiplex handle on route-resolve timeout.
- **Neighbors paging** (`MESHCORE_NEIGHBORS_PAGE_SIZE` = 50 request cap, `MeshcoreRequestNeighborsOpts.offset`, `mergeMeshcoreNeighborPage`, `meshcoreGetNeighboursBinary.ts`): first fetch replaces the cache; `offset > 0` appends when `offset === cache.length` (dedupe by `prefixHex`). In-flight coalesce keys by offset so refresh and Load more do not share one closed-over fetch. Firmware reply buffers often return fewer rows than requested (~11 at 6-byte prefixes); UI **Load more** on RepeatersPanel and NodeDetailModal continues from `neighbours.length`.
- **Trace route priming** (`meshcoreTraceRoutePrime.ts`, `meshcoreRepeaterTracePath.ts`, constants/wait helpers in `meshcoreHookPreamble.ts`): when multi-hop but outPath bytes are missing, **passive** PathUpdated (129) wait + contact refresh first (**15s + 5s × hops**, cap **45s**/round). For **2+ hops**, if passive fails, up to **two** **flood-advert** rounds as fallback (listener registered **before** each advert). **1-hop** targets may synthesize `[relayPrefix, destPrefix]` from a known 0-hop repeater; **2-hop** may prepend a relay byte to a stored 2-byte path. Skip priming when synthesis or a usable stored path exists. Ping/trace may fast-fail with `meshcore.errors.pingNoRoute` when priming and synthesis cannot produce a hash-segment path (≥2 bytes for multi-hop).
- **Prefix-matched push RPCs** (`meshcoreRepeaterPrefixPushRpc.ts`): Status, Telemetry, and repeater admin login share pubkey-prefix listeners; login registers LoginFail as an auxiliary event while waiting for LoginSuccess.
- **Timeouts**: Status/Telemetry/Neighbors = 120s flat; ping end-to-end = 180s; SENT wait = 45s.
- **Login**: Optional for CLI/telemetry when password saved; Status/Neighbors do not require login RPC. **Room login** rejects immediately on prefix-matched LoginFail. **Repeater admin login** matches meshcore.js — LoginFail alone does not reject (congested links may emit LoginFail before LoginSuccess); timeout after LoginFail is reported as timeout, not wrong password.
- **Repeater CLI danger**: destructive commands (`meshcoreRepeaterCliDanger.ts`) require confirm modal in Repeaters panel; runtime rejects unconfirmed sends (`meshcore.errors.cliDangerNotConfirmed`). Commands longer than **512** characters (`REPEATER_CLI_MAX_COMMAND_LENGTH`) are rejected before send. Multi-hop CLI auto-pings once per session when no trace exists (`RepeatersPanel` → `onPing`); CLI aborts when ping does not produce a trace result. Safe quick pills include `clock`, `clock sync`, `clear stats`, `advert`, `board` (firmware CLI tokens as labels).
- **Per-repeater passwords:** shared factory `meshcorePerNodeCredentialStorage.ts` with `meshcoreRepeaterCredentialStorage.ts` / `meshcoreRoomCredentialStorage.ts` (`meshcoreRepeaterCredential:<nodeId>` and room keys in `app_settings` via IPC), `useMeshcoreRepeaterRemoteAuth.tsx`, `MeshcoreRepeaterPasswordControls.tsx`; Repeaters sidebar **Saved repeater passwords** + Forget (parallel to Rooms).
- **Waiting-message drain:** event 131 → `meshcoreWaitingMessagesDrain.ts` / `meshcoreProcessWaitingMessageItem.ts`; silent auto-drain vs manual **Sync now** (`MeshcoreWaitingMessagesHeaderIndicator.tsx` in the App header via `meshcoreWaitingMessagesStatusText.ts`; **queued backlog visible on any protocol tab**; **active sync spinner and paused/deferred** state only on the MeshCore tab); defers during TraceData/admin RPC. Silent bulk `getWaitingMessages` circuit-opens after **2** consecutive timeouts (`MESHCORE_WAITING_MESSAGES_SILENT_BULK_TIMEOUT_TRIP`) and skips bulk until reconnect/success; disconnect bumps the attempt id so a late timeout cannot trip the next connection.
- **Repeater CLI danger**: destructive commands (`meshcoreRepeaterCliDanger.ts`: `reboot|erase|clkreboot|factory|shutdown|poweroff`) require confirm modal in Repeaters panel; runtime rejects unconfirmed sends (`meshcore.errors.cliDangerNotConfirmed`). Commands longer than **512** characters (`REPEATER_CLI_MAX_COMMAND_LENGTH`) are rejected before send. Multi-hop CLI auto-pings once per session when no trace exists (`RepeatersPanel` → `onPing`); CLI aborts when ping does not produce a trace result. Safe quick pills include `clock`, `clock sync`, `clear stats`, `advert`, `advert.zerohop`, `board`, `stats-*`, `get role` (firmware CLI tokens as labels).
- **Per-node admin passwords:** `meshcoreInfraAdminSecrets.ts` facade over `meshcoreRepeaterCredentialStorage.ts` / `meshcoreRoomCredentialStorage.ts` (separate prefixes); ops modal via `useMeshcoreRepeaterRemoteAuth.tsx`; room admin-only persist allowed; ops Forget on a room clears **admin only** (guest/auto-login unchanged). Accordion lists both kinds.
- **Waiting-message drain:** event 131 → `meshcoreWaitingMessagesDrain.ts` / `meshcoreProcessWaitingMessageItem.ts`; silent auto-drain vs manual **Sync now** (`MeshcoreWaitingMessagesHeaderIndicator.tsx` in the App header via `meshcoreWaitingMessagesStatusText.ts`; **queued backlog visible on any protocol tab**; **active sync spinner and paused/deferred** state only on the MeshCore tab); defers during TraceData/admin RPC. Silent bulk `getWaitingMessages` circuit-opens after **2** consecutive timeouts (`MESHCORE_WAITING_MESSAGES_SILENT_BULK_TIMEOUT_TRIP`) and skips bulk until reconnect/success; disconnect bumps the attempt id so a late timeout cannot trip the next connection. Repeater CLI waits for drain idle **before** `runMeshcoreRepeaterRpcOnce` (waiting inside the once slot deadlocks because in-flight defers drain). CLI_DATA waiting messages dispatch as CLI responses, not chat. If drain is busy at start or SENT, CLI timeout is padded by `MESHCORE_WAITING_MESSAGES_SILENT_TIMEOUT_MS` (45s), capped at `REPEATER_CLI_MAX_TIMEOUT_MS`. While CLI awaits a reply, `beginMeshcoreCliReplyHold` blocks new traces and keeps silent drain from deferring on TraceData; 0-hop CLI also preempts pending traces and force-kicks `processWaitingMessages({ force: true })` on a short interval.
- **Repeater CLI clock:** `clock` / `clock sync` are firmware tokens. `clock sync` uses the companion/computer UTC unix timestamp; MeshCore firmware **refuses to set time backwards** (`ERR: clock cannot go backwards`). Do not auto-`clkreboot`; toast `repeatersPanel.cliClockCannotGoBackwards` and leave `clkreboot` behind the danger confirm. Optional companion `syncDeviceTime()` runs before repeater `clock sync`.
- **Cross-traffic**: Room sync/auto-login defer while `meshcoreCompanionRepeaterRfBusy()`; waiting-messages drain defers during TraceData.

Do not change behavior guarded by `meshcoreZeroHopRepeaterWorkingState.test.ts` without explicit user request. See [../meshcore-meshtastic-parity.md](../meshcore-meshtastic-parity.md#serialized-traceroutes-protocol-requirement).
Loading