Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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