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 README.md
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ MeshCore runs simultaneously alongside Meshtastic and Reticulum. Use the protoco

**Room servers (BBS)** — **Rooms** tab (RF only; not MQTT)

- 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)
- Login to room-server contacts; **blank** guest password for read-only when allowed; **`"hello"`** as the default read/write guest password; **Continue read-only** also sends blank
- 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 / 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)
Expand Down
6 changes: 3 additions & 3 deletions docs/agents/meshcore-rooms.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

Deep subsystem reference for AI assistants. Open this when a task touches MeshCore Rooms login/post, session RPCs, saved passwords, auto-sync scheduling, or room wire text. Hard rules live in [`AGENTS.md`](../../AGENTS.md).

- **UI:** `RoomsPanel.tsx` — login overlay, post composer (`ChatComposer`), auto-sync toggles, Members ACL refresh (`get acl`); **Manage on Repeaters** jumps to Repeaters & Rooms ops (CLI / setperm / infra). Sidebar badge via `meshcoreRoomsUnread.ts` (`mesh-client:meshcoreRoomsUnread`).
- **UI:** `RoomsPanel.tsx` — login overlay, post composer (`ChatComposer`), auto-sync toggles, Members ACL refresh (`get acl`); **Manage on Repeaters** jumps to Repeaters & Rooms ops (CLI / setperm / infra). Sidebar badge via `meshcoreRoomsUnread.ts` (`mesh-client:meshcoreRoomsUnread`). Blank guest **Login** = read-only when allowed; **`"hello"`** is the default read/write guest password; **Continue read-only** also sends blank. **Cancel login** aborts the outer login op (path resolve + SendLogin) via `meshcoreBeginRoomLoginOperation` / `meshcoreCancelRoomLogin`; queue TX-spacing waits are interruptible so Cancel does not sit out the 60s mesh spacing.
- **Ops (not on Rooms tab):** Ping / Status / Neighbors / Sensor / CLI / ACL setperm live on `RepeatersPanel` (mixed Repeater + Room table). **Open room** on a room row jumps back to Rooms. Guest passwords stay in `meshcoreRoomCredentialStorage`; ops admin uses `meshcoreInfraAdminSecrets` (admin-only records allowed).
- **Room remote CLI:** needs room **admin** password via SendLogin ACL (`meshcoreCancelRoomLogin` then admin login inside `sendRepeaterCliCommand`); guest BBS login is not enough (`repeatersPanel.roomCliNeedsAdminPassword`).
- **Post length (single-packet):** room posts use the shared `ChatComposer` (`variant="room"`) and are **single-packet** like MeshCore chat — over the room payload limit (`getMeshcoreRoomPayloadLimit` in `chatComposerLimits.ts`) the send is blocked with the same `meshcoreSingleNotice` callout rather than split into `[i/N]` parts, and the ~5s fast-send advisory applies. See [`chat.md`](chat.md) (Composer limits / send cadence).
- **Session / RPC:** `meshcoreRoomSession.ts`, `meshcoreRoomLoginRpc.ts`, `meshcoreRoomPostRpc.ts`, `meshcoreRoomLogoutRpc.ts`, `meshcoreRoomLoginQueue.ts`, `meshcoreRoomLoginPathSync.ts`, `meshcoreRoomSentWait.ts`; credentials in `meshcoreRoomCredentialStorage.ts` / `meshcoreRoomSyncStorage.ts`.
- **Session / RPC:** `meshcoreRoomSession.ts`, `meshcoreRoomLoginRpc.ts`, `meshcoreRoomPostRpc.ts`, `meshcoreRoomLogoutRpc.ts`, `meshcoreRoomLoginQueue.ts`, `meshcoreRoomLoginPathSync.ts`, `meshcoreRoomSentWait.ts`; credentials in `meshcoreRoomCredentialStorage.ts` / `meshcoreRoomSyncStorage.ts`. **LoginSuccess:** prefer v7+ `permissions` (PERM_ACL_*); `reserved` is the legacy admin/guest hint (0=RW, 1=admin, 2=guest) — do not treat it as ACL. **Hops:** `resolveMeshcoreRoomLoginHopsAway` uses route bytes when UI hops are 0, and treats sticky UI hops with an empty path as 0-hop so SendLogin is not blocked as noRoute. **History catch-up:** `resetMeshcoreRoomCompanionSyncSinceForCatchUp` remove+re-adds the companion contact (firmware zeroes `sync_since` only on new contacts) when this device has no local last-post watermark; add is retried if remove already succeeded so the contact is not left deleted.
- **Saved passwords:** `meshcoreRoomSavedSecrets.ts` — sidebar/overlay **Forget** / **Stop auto-login**; `forgetMeshcoreRoomSavedSecrets` clears credential + disables auto-login and auto-sync; `disableMeshcoreRoomLoginAfterAuthFailure` disables both without clearing password or in-memory failure UI.
- **Scheduler:** `meshcoreRoomSyncScheduler.ts` + `useMeshcoreRuntime.ts` — periodic re-login (Auto-sync, RF-only); single-flight ticks; background route resolve uses `skipTrace` / `MESHCORE_ROOM_SYNC_ROUTE_RESOLVE_FAST_MS`. Auth failure disables auto-sync and auto-login via `disableMeshcoreRoomLoginAfterAuthFailure`. Timeouts in `timeConstants.ts` (shorter for TCP / 0-hop).
- **Scheduler:** `meshcoreRoomSyncScheduler.ts` + `useMeshcoreRuntime.ts` — periodic re-login (Auto-sync, RF-only) goes through the same `loginRoom` path as the UI (path sync, catch-up, abort, waiting-message drain) with `schedulerFastPath` (skipTrace / `MESHCORE_ROOM_SYNC_ROUTE_RESOLVE_FAST_MS`). Single-flight ticks. Auth failure disables auto-sync and auto-login via `disableMeshcoreRoomLoginAfterAuthFailure`. Timeouts in `timeConstants.ts` (shorter for TCP / 0-hop).
- **Connect auto-login:** `meshcoreRoomAutoLoginOnConnect.ts` + `useMeshcoreRuntime.ts` — 500ms debounce (`MESHCORE_ROOM_AUTO_LOGIN_DEBOUNCE_MS`) when Room contacts appear (`roomAutoLoginReadyKey`, including pubkey-ready). Overlapping triggers share one pass (`runMeshcoreRoomAutoLoginSingleFlight`) and dirty-re-run so a second room hydrating mid-pathSync still logs in. Disconnect bumps a generation so a dying pass must not `SendLogin` on the new conn. Skips logged-in, queued (`meshcoreIsRoomLoginQueued`), failed (`getMeshcoreRoomAutoLoginFailure`), and not-yet-hydrated contacts.
- **Wire text:** `meshcoreChannelText.ts` — channel/DM/room payloads, SignedPlain inbound strip, tapback/reply lines; `meshcoreGifWire.ts` — Open `g:GIFID`; `meshcoreOpenReaction.ts` — Open `r:HASH:INDEX`. Default companion keyless outbound; opt-in Open wire via Radio `meshcoreOpenWireCompatEnabled` (UI on `RadioPanel`).
2 changes: 1 addition & 1 deletion docs/agents/reticulum.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Deep subsystem reference for AI assistants. Open this when a task touches the Re
- **Propagation mode / sync:** Network → Propagation nodes owns Off/Auto/Manual (default **Off**; persisted values including legacy App-panel `auto` are honored). Auto one-time syncs via `startPropagationSyncCascade` + sidecar `destination_hash` sync in order: **finite-hop discovered** (no Add/Preferred) → **configured remotes** → **unknown-hop discovered** → local-prop (skips remotes when no enabled interfaces); runtime hook `useReticulumPropagationAutoSync`. Sidecar `start_propagation_sync` is **client `/get`-primary** (inbox retrieval; UI progress from `PropagationClient`) — peer `/offer` inventory push stays on the local-host peer loop when serving (avoids AwaitingResponse hangs against non-peer remotes with a nonempty messagestore). Hard-fails with `PROPAGATION_PATH_UNKNOWN` when `ensure_path_for_direct` fails after announce settle (same path gate as offer probe). Manual uses Preferred, else picks the best configured remote **for that sync only** (no Preferred write), then the remaining remotes, then local-prop. Off = **no PN support**: `startPropagationSyncCascade` returns early (per-row Sync is disabled in UI), `hasEffectiveReticulumPropagationTarget` / `hasReticulumPnCascadeCapacity` are false, `ReticulumPropagationNotice` is hidden, and the sidecar disarms the outbound PN plus empties cascade candidates (`propagation_mode` in `mesh_client_stack.json`, `POST /api/v1/propagation/mode`, `candidates_for_propagation_mode`); renderer pushes the mode on change and on sidecar-ready. **Ignore for Auto:** `POST/DELETE /api/v1/propagation/auto-blacklist` persists `propagation_auto_blacklist` (32-hex, cap 256); filters Auto sync ranking **and** Auto deposit (`auto_discovered_candidates` + configured retain in Auto); Manual Prefer/Sync still allowed. Ownership: mode in renderer localStorage (+ sidecar mirror); blacklist + deposit candidates in sidecar; sync cascade orchestration in `reticulumPropagationAutoApply.ts`; `startSync` attempt stamps must be unique across same-ms supersession. `reticulumPropagationStore` / `reticulumPropagationSync.ts` — Establishing stall (~45s) + hard ceiling (~180s), auto-sync interval from last success with failure cooldown, error keys for identity / non-PN / path-unknown / peering stamp; stamps `lastPropagationSyncAttemptAt` / `activePropagationSyncAttemptAt` for WS correlation. Cancel mid-`/get` must call `PropagationClient::abort_transfer` (rsLXMF overlay) or the next Sync stays `PROPAGATION_RETRIEVE_BUSY`. Silent Host `/get` terminal clear must only drop the latch when `propagation_sync_target` still equals that peer. **Nothing-to-sync is not a failure:** when the cascade contacts no node it writes `syncNoTarget` / `syncLocalLoading` / `syncRetrieveBusy` (never overwriting a real error from an attempted node), the local row reports sidecar `status: "loading"` while the messagestore reads (`local_propagation_status` + `PropagationBridge::messagestore_load_pending`, per-row Sync disabled), and the 30 s tick calls `refreshFromSidecar` while `hasPropagationCascadeCandidate` is false so a fresh stack recovers on its own — `refreshFromSidecar` must **not** clear the active attempt while `sync.active`. Debug snapshot `propagationClient` exposes mode/preferred/autoTarget/resolvedSyncTargetId/autoBlacklist. **Auto also deposits on Discovered PNs:** sidecar `auto_discovered_candidates` (`pn_cascade.rs`, Auto only, cap 3, hop-sorted with `MAX_PLAUSIBLE_PROPAGATION_HOPS=32`, skips inactive / self / already-configured / Auto-blacklist / over `max_peering_cost`) appends after configured remotes and before local-prop, rebuilt from the shared `rebuild_pn_cascade_candidates` helper in `live.rs`; capacity helpers count non-blacklisted discovered rows in Auto. **Chat notice dismiss:** `chatNoticeDismissed` with **Don't show again** / Network toggle. **Named sync target:** `startSync` stamps `syncTargetId`. **Attempts settle before the cascade advances:** `startSync` returns `accepted` | `deferred` | `failed`. Soft-defer (`PROPAGATION_SYNC_OUTBOUND_BUSY` / `PROPAGATION_RETRIEVE_BUSY` / `PROPAGATION_STACK_NOT_LIVE`) advances **without** 15‑min backoff; all-remote soft-defer + local-only settle must **not** advance `lastPropagationSyncAt` as a full success. Remote budget `PROPAGATION_CASCADE_BUDGET_MS` (5 min); per-attempt ~60s; single-flight cascade. Auto `/api/v1/interfaces` probe **fails closed** (treat read/rate-limit as no interfaces → local-only settle). **Retrieval vs peer sync:** User Sync progress is **client `/get`-primary**. Peer `/offer` runs only on the **local Host peer loop**. Logs: `propagation-retrieve` (`retrieve_mode=get|get_post_peer|get_periodic|local`); peer-offer `propagation-sync … peer_outcome=*` (**not** retrieval). `local-prop` Sync uses `drain_local_inbox` and returns `PROPAGATION_STACK_NOT_LIVE` when live is absent.
- **Host PN fabric → Chat (lxmd-style glue):** When local Host is enabled, mesh-client is both PN and end-client on rsLXMF (not a second lxmd). Path: outbound deposit → host peer `/offer` push (generation-gated; lxmd terminal bookkeeping: `sync_complete` / `mark_offer_generation_processed` / `take_handled_updates` + `save_peer`); inbound peer Resource accept → `request_inbox_drain` → maintenance `drain_local_inbox` → `delivery_callback` → Chat; after host peer `/offer` Completes for peer `P`, sequenced **silent** client `/get` (`retrieve_mode=get_post_peer`); while serving and quiet, **~90s** periodic silent `/get` round-robin over peered remotes (Prefer/outbound first, `retrieve_mode=get_periodic`) for inbox catch-up — not a timed empty re-`/offer`. Re-`/offer` when `offer_generation` advances, offer policy changes, or a partial sync left work; maintenance polls every ~2s but only _starts_ work when idle. Guards: coalesce drain; one internal `/get`; skip when user Sync target / outbound deposit owns the hash or `sync_active` / `client_download_active`. Do **not** re-attach peer `/offer` to the Sync button. Dual full-index exchange in one Link stays upstream rsLXMF; remote inventory for re-propagation arrives when peers `/offer` to our serve path.
- **PN hosting:** Network **Advanced PN hosting** / `ReticulumPnHostingDangerZone`; shared `pnHostingPolicy.ts` + sidecar `pn_hosting_policy.rs` / `pn_hosting_apply.rs`; `POST /api/v1/propagation/hosting-policy`; rsLXMF policy-setters overlay ([ratspeak/rsLXMF#6](https://github.com/ratspeak/rsLXMF/pull/6)). Messagestore loads in background on live attach; enabled `local-prop` serve/announce waits until load completes.
- **Interface modes:** rnsd `mode` via `reticulumInterfaceMode.ts` + sidecar `normalize_interface_mode` (keep catalogs in sync — `pnpm run check:reticulum-interface-modes` in pre-commit/`release.sh`); add defaults TCP/UDP/I2P → `boundary`, RNode → `access_point`; UI in `ReticulumInterfacesPanel`; default hub presets add/repair missing mode to `boundary` (do not overwrite valid non-boundary). See [../reticulum.md#interface-modes](../reticulum.md#interface-modes).
- **Interface modes:** rnsd `mode` via `reticulumInterfaceMode.ts` + sidecar `normalize_interface_mode` (keep catalogs in sync — `pnpm run check:reticulum-interface-modes` in pre-commit/`release.sh`); add defaults TCP/UDP/I2P → `boundary`, RNode → `access_point`; UI in `ReticulumInterfacesPanel`; default hub presets add/repair missing mode to `boundary` (do not overwrite valid non-boundary). Discoverable + Full/Roaming/Boundary stamps `ignore_config_warnings = Yes` (`reconcile_ignore_config_warnings` / `repair_ignore_config_warnings_in_config`) so RNS does not auto-correct runtime mode to Access Point; Connection shows **Effective: Access Point** when live `runtime_mode` still diverges. See [../reticulum.md#interface-modes](../reticulum.md#interface-modes) and [../reticulum.md#rmap-publish-and-interface-mode](../reticulum.md#rmap-publish-and-interface-mode).
- **Share instance defaults:** missing keys bootstrap to `share_instance = No` / `instance_name = mesh-client` (does not overwrite explicit Yes/`default`); SharedInstanceClient banner + `disable_share_instance` repair; offline lint via `reticulum:validateConfig` / Network **Check config** / `pnpm run reticulum:config:check`
- **LXMF replies:** sidecar stamps `FIELD_REPLY_TO` / capped `FIELD_REPLY_QUOTE` before sign; renderer ingest/Chat use `reticulum_reply_to_hash` + quote preview + jump-by-hash
- **RNode flasher timeouts:** `RNODE_COMMAND_TIMEOUT_MS` (30 s serial), `RNODE_BT_PAIRING_TIMEOUT_MS` (90 s BLE pairing), `ESP32_FLASH_STALL_TIMEOUT_MS` / `NRF52_DFU_STALL_TIMEOUT_MS` (60 s no-progress → `ESP32_FLASH_STALLED` / `NRF52_DFU_STALLED`); humanized via `flasherErrorHumanize.ts`
Expand Down
4 changes: 2 additions & 2 deletions docs/meshcore-meshtastic-parity.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,11 @@ Shared UI gates use `ProtocolCapabilities` in [`src/renderer/lib/radio/BaseRadio

Room servers (`hw_model === 'Room'`, contact type 3) are BBS nodes on the mesh. The companion radio must be connected over **RF** (BLE, serial, or TCP); **MQTT does not carry room login/post**.

**Login:** Guest read-only uses **zero password bytes** when the server guest password is empty (**Continue read-only** on the login overlay). Admin login uses the configured password. Login RPC, queue, and path sync live under `src/renderer/lib/meshcoreRoom*.ts` (e.g. [`meshcoreRoomLoginRpc.ts`](../src/renderer/lib/meshcoreRoomLoginRpc.ts), [`meshcoreRoomLoginQueue.ts`](../src/renderer/lib/meshcoreRoomLoginQueue.ts)); timeouts are shorter on TCP and 0-hop paths ([`timeConstants.ts`](../src/renderer/lib/timeConstants.ts)).
**Login:** Blank guest Login sends **zero password bytes** (read-only when `allow.read.only` is on; same wire as **Continue read-only**). **`hello`** is the default read/write guest password. Admin login uses the configured password. LoginSuccess ACL is the companion v7+ `permissions` byte (PERM_ACL_*); `reserved` is only the legacy admin/guest hint (do not treat as ACL). Login RPC, queue, and path sync live under `src/renderer/lib/meshcoreRoom*.ts` (e.g. [`meshcoreRoomLoginRpc.ts`](../src/renderer/lib/meshcoreRoomLoginRpc.ts), [`meshcoreRoomLoginQueue.ts`](../src/renderer/lib/meshcoreRoomLoginQueue.ts)); timeouts are shorter on TCP and 0-hop paths ([`timeConstants.ts`](../src/renderer/lib/timeConstants.ts)).

**Posts:** Outbound room posts use plain UTF-8 (`TXT_TYPE_PLAIN`) after login and are **single-packet** — mesh-client does not emit multi-part `[i/N]` room posts; over-limit text is blocked in the composer (same rationale as chat/DM). Inbound **SignedPlain** pushes include a four-byte author prefix; the **Rooms** UI strips it, and inbound multi-part from other clients is still merged for display. Posts appear in the **Rooms** tab (channel `-2`), not Chat channel pills.

**Sync:** Firmware only **pushes new posts** after login (no history backfill). **Auto-sync** re-logs in on a timer while the radio stays connected (minimum 60 minutes per room, [`meshcoreRoomSyncScheduler.ts`](../src/renderer/lib/meshcoreRoomSyncScheduler.ts)). Saved passwords: SQLite `app_settings` (same pattern as Meshtastic remote admin keys). Session clears on disconnect.
**Sync:** After login, the room server pushes posts newer than the companion `sync_since` watermark (ring buffer, typically ~32). mesh-client resets that watermark when the device has no local last-post time yet, then drains waiting messages. **Auto-sync** re-logs in on a timer while the radio stays connected (minimum 60 minutes per room, [`meshcoreRoomSyncScheduler.ts`](../src/renderer/lib/meshcoreRoomSyncScheduler.ts)). Saved passwords: SQLite `app_settings` (same pattern as Meshtastic remote admin keys). Session clears on disconnect.

**Unread:** Room BBS traffic increments the **Rooms** sidebar badge ([`meshcoreRoomsUnread.ts`](../src/renderer/lib/meshcoreRoomsUnread.ts)) and system-tray unread when backgrounded; it does not increment the **Chat** tab badge.

Expand Down
Loading