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
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ Deep, file-level subsystem detail now lives in [`docs/agents/`](docs/agents/READ
- Prefer `useProtocolFacade` and identity-scoped stores (`identityStore` / `nodeStore` / `messageStore` / `connectionStore`, keyed by `identityId`); SQLite→UI via `hydrateIdentityStoresFromDb`.
- MeshCore zero-hop Status/Telemetry/Neighbors are pubkey-framed (no contact-list gate); multi-hop ping needs a hash-segment path (≥2 bytes), never the full destination pubkey. Do not change behavior guarded by `meshcoreZeroHopRepeaterWorkingState.test.ts` without explicit user request — see [`docs/agents/meshcore-repeaters.md`](docs/agents/meshcore-repeaters.md).
- Reticulum connect = sidecar start (not `ConnectionDriver` RF); no Noble/MQTT for Reticulum's own stack (sidecar owns BLE RNode via `btleplug`); a Reticulum BLE RNode may yield Noble on macOS/Windows — see [`docs/agents/ble-serial.md`](docs/agents/ble-serial.md) and [`docs/agents/reticulum.md`](docs/agents/reticulum.md).
- Reticulum automation ownership: do **not** reimplement RNS pathfinding/announce flood in UI or sidecar; **do** own lxmd-parity LXMF client/PN loops in the sidecar; treat Auto demotion / multi-PN cascade / path-medium / DM probe as **product policy** — see [`docs/reticulum.md#ownership-rns-vs-lxmf-client-vs-mesh-client-policy`](docs/reticulum.md#ownership-rns-vs-lxmf-client-vs-mesh-client-policy).
- LoRa BLE reconnect is single-owner via `rfReconnectController`; manual disconnect must not auto-reconnect. Dual-radio Noble startup is serialized from `App.tsx` `useLayoutEffect` — see [`docs/agents/ble-serial.md`](docs/agents/ble-serial.md).

## 9. Cursor / Claude indexing
Expand Down
5 changes: 5 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Thank you for your interest in contributing.
| ------------------------------------------------------------- | ------------------------------------------------------------------ |
| Clone, prerequisites, commands, test harness, git hooks, i18n | [docs/development-environment.md](docs/development-environment.md) |
| Code style, testing, architecture, security (AI assistants) | [AGENTS.md](AGENTS.md) |
| Protocol additions (RF mesh only) | [Protocol scope](#protocol-scope) |
| PR flow and contribution expectations | **This file** |

## Code style & standards
Expand All @@ -27,6 +28,10 @@ See [Test harness setup and local quality checks](docs/development-environment.m
- Update `src/main/index.contract.test.ts` when CSP, build config, IPC limits, or log filters change.
- Accessibility: vitest-axe in component tests; see **Accessibility / axe** in [AGENTS.md](AGENTS.md#5-testing).

## Protocol scope

Mesh-Client focuses on **RF mesh** networking (LoRa and related radio meshes). Additional protocols are in scope when they support that kind of RF mesh path. Internet-only messaging is out of scope. Ham protocols are welcome under the same RF-mesh criteria; Mesh-Client is for everyone, everywhere, and is not gated or targeted specifically at people with a ham radio license. Protocols that already ship may still use internet transports _alongside_ RF. Product framing: [README — Why](README.md#why).

## PR process

1. Describe your changes and what you tested
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ With a dedicated local SQLite database, Mesh-Client keeps message history and me

From real-time diagnostics to permanent message archives, Mesh-Client delivers the desktop visibility serious mesh users require.

**Protocol scope:** Mesh-Client focuses on **RF mesh** networking—LoRa and related radio meshes. Additional protocols are in scope when they support that kind of RF mesh path. Internet-only messaging stacks are out of scope. Amateur-radio (ham) protocols are welcome when they meet the same RF-mesh bar; Mesh-Client is for everyone, everywhere, and is not gated or targeted specifically at people with a ham radio license. Protocols that already ship may still use internet transports _alongside_ RF.

**Known Bugs:**

- **Linux BLE**: uses Web Bluetooth (Chromium's built-in BLE API), with a user-visible picker and user gesture requirement to select a device. **MeshCore** may prompt for the radio's PIN and run OS-level pairing (`bluetoothctl`) before the connection completes when BlueZ reports the device as not paired (see [docs/development-environment.md](docs/development-environment.md#linux-bluetooth-ble)).
Expand Down
1 change: 1 addition & 0 deletions docs/agents/reticulum.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

Deep subsystem reference for AI assistants. Open this when a task touches the Reticulum sidecar, LXMF, propagation, Remote (rnsh/rncp), Nomad, RRC, voice, or games. Hard rules live in [`AGENTS.md`](../../AGENTS.md); this file holds the file-level detail.

- **Ownership (do not reimplement transport):** Reticulum automation has three layers — (1) **RNS transport** (path table, announce flood, Auto beacons, Links/proofs) stays in rsReticulum (+ overlays under `reticulum-sidecar/patches/`); (2) **LXMF client/PN** loops (delivery announces, Direct→Propagated driver, identity-from-announce, PN `/offer`/`/get`) are intentional **lxmd/Ratspeak parity** owned by the sidecar because rsLXMF is not a daemon; (3) **mesh-client policy** (Auto→private demotion, multi-PN cascade, path-medium preference, DM auto-probe, Nomad `force_path_refresh`) is product behavior above RNS — document it, do not mistake it for transport. Renderer mirrors WS/HTTP and configures policy; it must not invent pathfinding or announce flooding. Full write-up: [../reticulum.md#ownership-rns-vs-lxmf-client-vs-mesh-client-policy](../reticulum.md#ownership-rns-vs-lxmf-client-vs-mesh-client-policy).
- **Sidecar:** `reticulum-sidecar/` (AGPL Rust binary `mesh-client-reticulum`; path deps under repo-local `.rsstack/` via `scripts/clone-ratspeak-stack.sh` — `rsReticulum`/`rsLXMF`/`rsNomad`/`rsLXST`/`lrgp-rs`); dev: `pnpm run reticulum:sidecar:dev`. **Listen-first:** HTTP binds before `attach_live`; `/api/v1/status` `status: ok` = listening; `rns_ready`/`lxmf_ready` false until live. PN messagestore load deferred; local-prop serve waits for load. LXMF send/reaction fail closed with live-required errors until live.
- **IPC:** `reticulum:*` main handlers — `start` / `stop` / `getStatus` / `syncInterfaceIssueScope`, `proxyGet` / `proxyPost` / `proxyPut` / `proxyDelete`, **`factoryReset`** (blocked on generic proxy), config file read/import dialog, `showNomadContentSourceDialog`, `setNomadContentSource`, Remote `rncpSend` / `rncpFetch` / `setRncpListener` / `showRncpOpenFileDialog` / `showRncpSaveDirectoryDialog` / `revealInFolder`. Also `media:ensureCameraAccess`, `gps:exportGpx`, `db:setReticulumDestinationVerified`, Remote DB `db:listReticulumRemoteAddresses` / upsert / delete and `db:listReticulumInboundPolicy` / upsert / delete (`src/main/ipc/reticulum-db-handlers.ts`), `mesh-client:openUrl` / `electronAPI.deepLink.onOpenUrl`. Renderer uses `electronAPI.reticulum` proxy (no direct localhost). Proxy rate-limit soft-envelopes live inside the handler `try` (`settleReticulumProxyFailure`). `ReticulumStackPanel` + `useReticulumInterfaceSnapshot` sync enabled interface names after hydrate so TCP/TX issue banners clear when hubs are disabled — **do not** refresh interfaces on `announce.received` / `stats_update` (that flooded `GET /interfaces` after wake); poll + shared proxy backoff only. `reticulumSidecarIssueTracker` keeps that enabled set sticky while reading sidecar logs.
- **Panels:** `ReticulumStackPanel` (Connection — stack lifecycle, interfaces, issue banner), `ReticulumNetworkPanel` (Network — identity **slots** + QR share/ingest, stack/announce settings, Propagation mode Off/Auto/Manual + rename/delete, config import), `ChatDmPaperControls` (Chat DM **Share as paper** + **Scan paper**), `ReticulumMapPanel` (Map — RMAP v4 discovery), `ReticulumRmapDiscoveryControls` / `ReticulumRmapConnectionStatus` (RMAP publish: Network enable-all eligible interfaces; Connection **X of Y** status), `ReticulumAdminPanel` (Admin — RNode flasher, factory reset), `ReticulumPeerListPanel` (Peers — **Peers / History / Contacts / Favorites** sub-tabs; path request + probe + verified badge; LXMFace avatars; History = messaged `last_heard`, Contacts = explicit `is_contact` / Save as contact only), `NomadNetworkPanel` (Nomad — browse + **My Pages** watched-folder static host via `NomadPageServerPanel`/rsNomad; `nomad_serving_enabled` + `nomad_serving_content_source` restore hosting after live stack start; lazy-mount keep-alive, dual-axis page scroll; fit-width default and open-width toggle), `ReticulumRemotePanel` (Remote — rnsh multi-session shell + rncp send/receive/fetch; Saved addresses + inbound policy; Chat DM send-file via `ChatDmRncpControl`), `RrcPanel` (RRC — multi-hub relay chat)
Expand Down
2 changes: 2 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ Key outcomes:
- Multi-language support (16 languages) with offline static bundles.
- Cross-platform desktop support for macOS, Linux, and Windows.

**Protocol scope:** Mesh-Client focuses on RF mesh (LoRa and related). Additional protocols are in scope when they support that RF mesh path. Internet-only stacks are out of scope; ham protocols are fine when they meet the RF-mesh bar. Mesh-Client is for everyone, everywhere—not gated or targeted specifically at licensed amateurs. Protocols that already ship may still use internet transports _alongside_ RF. See [README — Why](https://github.com/Colorado-Mesh/mesh-client/blob/main/README.md#why).

---

## Visuals
Expand Down
54 changes: 53 additions & 1 deletion docs/reticulum.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,58 @@ The renderer **must not** call the sidecar URL directly (sandbox). All HTTP/WS g

**Listen-first connect:** The sidecar binds HTTP first, then `attach_live` brings up RNS/LXMF (path table, BLE Peer, deferred PN messagestore). Electron health is `GET /api/v1/status` with `status: "ok"` — not `rns_ready` / `lxmf_ready`. `useReticulumRuntime` marks connection **configured** once start succeeds and identity is known, then hydrates peers/DB in the background and dispatches `RETICULUM_CONFIGURED_EVENT`. TCP hubs and RRC can proceed after live attach; Chat LXMF send/reaction fail closed with `requires live rns-stack sidecar` until the bridge is up. **Cancel** / stop does not wait on cargo or BLE; Noble yield for an enabled BLE RNode starts only after health (fire-and-forget). Renderer LXMF/RRC proxy sends use a **15 s** IPC deadline (`RETICULUM_IPC_SEND_TIMEOUT_MS`).

### Ownership: RNS vs LXMF client vs mesh-client policy

Reticulum is not one blob that “does everything automatically.” Before adding sidecar or UI automation, classify the work:

```mermaid
flowchart TB
subgraph rns [RNS transport - library]
PathTable[Path table / RequestPath]
AnnounceFlood[Announce flood / Auto beacons]
Links[Links / proofs / Resources]
end
subgraph lxmf [LXMF app layer - client or lxmd must own]
DeliveryAnnounce[lxmf.delivery announces]
OutboundDriver[Direct then Propagated delivery]
PnHost[PN serve /get /offer]
IdentityLearn[Pubkey from announces / path responses]
end
subgraph mesh [mesh-client product policy]
AutoDemote[Auto vs private LAN demotion]
PnCascade[Multi-PN deposit and sync cascade]
PathMedium[Prefer RF vs network slots]
UiProbe[DM probe / Nomad force-path]
end
rns --> lxmf
lxmf --> mesh
```

| Layer | Owns | mesh-client role |
| ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **RNS transport** (rsReticulum) | Path table, announce flooding, AutoInterface beacons, RMAP discovery announce signing, Links / proofs / Resources | Call transport APIs (`RequestPath`, path table reads, destination register). Do **not** reimplement pathfinding or beacon loops in the sidecar or UI. Library gaps belong in overlays under [`reticulum-sidecar/patches/`](../reticulum-sidecar/patches/README.md), not a second routing plane. |
| **LXMF client / PN** (rsLXMF + sidecar orchestration) | `lxmf.delivery` announce schedules, Direct→Propagated outbound driver, identity learning for LRPROOF, local PN serve (`/offer`/`/get`), client inbox retrieve | Intentional **lxmd / Ratspeak parity**. rsLXMF is a library, not a full daemon — the sidecar owns these loops (`lxmf_delivery.rs`, `lxmf_outbound.rs`, `propagation_*`, `pn_inbound.rs`). Without them, Chat and offline delivery do not work. Documented elsewhere as “Host PN fabric → Chat (lxmd-style glue)”: mesh-client is PN + end-client on rsLXMF, **not** a second `lxmd` binary. |
| **mesh-client product policy** | AutoInterface demotion toward private TCP/UDP, multi-slot path failover before PN fallback, prefer path medium, multi-PN deposit/sync cascade (Off/Auto/Manual), Chat DM auto-probe, Nomad `force_path_refresh` | **Not** required by bare RNS. Exists for multi-hub / Auto+LAN / UX. Treat as intentional product behavior; do not mistake it for transport. |

**Renderer rule:** UI mirrors sidecar events and configures policy (announce interval, Propagation mode, Path/Probe buttons, RMAP publish toggles, topology layout). It must not invent peer discovery, announce flooding, or a second pathfinder. Optimistic `announce.received` peer rows and Topology graphs are **views** over the RNS path table, not routing.

**What looks “automatic” but is correct to own in the sidecar**

- Periodic / startup **LXMF delivery** announces and Network **Announce now** (Ratspeak/lxmd parity; see Network tab).
- `LxmfOutboundDriver` Direct planning, path-request gating, retries, then Propagated cascade.
- PN hosting admission, peer `/offer` bookkeeping, silent `/get` catch-up, and renderer Auto/Manual **sync cascades** that call into those APIs.
- Registering announce/path-response handlers so Direct LRPROOF has peer public keys.

**What is product policy above RNS** (keep intentional; cite when changing)

- [`auto_path_policy.rs`](../reticulum-sidecar/src/stack/auto_path_policy.rs) — RNS correctly prefers 0-hop Auto; sidecar demotes unhealthy Auto toward a live **private** path for LXMF Direct (see [Path routing](#path-routing)).
- [`path_failover.rs`](../reticulum-sidecar/src/stack/path_failover.rs) + path-medium overlays — ranked slots / medium preference before giving up Direct.
- [`pn_cascade.rs`](../reticulum-sidecar/src/stack/pn_cascade.rs) + [`reticulumPropagationAutoApply.ts`](../src/renderer/lib/reticulum/reticulumPropagationAutoApply.ts) — multi-PN deposit and sync order.
- Chat DM auto-probe (`useReticulumDmPathProbe`) — reachability UX; RNS would still path on send.
- Nomad `force_path_refresh` — DropPath→RequestPath recovery for stale TCP hub paths.

**Gate for new automation:** Is this RNS transport, LXMF client/PN (lxmd parity), or mesh-client policy? Prefer library/overlay for transport; prefer sidecar lxmd-shaped loops for LXMF; prefer explicit, documented policy modules for product overrides — never a parallel path table or announce flood in the renderer.

---

## Interface management (Connection tab)
Expand Down Expand Up @@ -324,7 +376,7 @@ IRC-style multi-pane client (`RrcPanel` + `rrcHubStore` / `rrcSessionStore`):

## Path routing

When a destination is reachable over more than one next hop, the sidecar keeps up to **three ranked path slots** (one active + backups). Failover promotes a backup (or rediscovers via another live interface) before giving up — Nomad page loads exhaust alternate paths inside one request; LXMF Direct does the same before the **multi-PN cascade**. See [troubleshooting](troubleshooting.md#nomad-network-pages-hang-or-almost-never-load) for triage.
When a destination is reachable over more than one next hop, the sidecar keeps up to **three ranked path slots** (one active + backups). Failover promotes a backup (or rediscovers via another live interface) before giving up — Nomad page loads exhaust alternate paths inside one request; LXMF Direct does the same before the **multi-PN cascade**. These failover / medium / Auto-demotion behaviors are **mesh-client product policy** on top of the RNS path table — see [Ownership](#ownership-rns-vs-lxmf-client-vs-mesh-client-policy). Triage: [troubleshooting](troubleshooting.md#nomad-network-pages-hang-or-almost-never-load).

**AutoInterface vs private TCP/UDP:** Peers learned on Auto are normal 0-hop neighbors; RNS may keep Auto active even when a private LAN hub path exists (including equal-hop ties). For LXMF Direct, the sidecar **automatically** demotes Auto toward a live **private** path when Auto is unhealthy for delivery or Direct fails on Auto — then fails over private → public → multi-PN cascade (preferred remote → other enabled remotes hop-sorted → in Auto, up to 3 Discovered PNs → local-prop last). It does **not** rewrite healthy Auto Direct, and does **not** preempt Auto to public internet hubs. See [troubleshooting — local DMs hang with AutoInterface + private TCP hub](troubleshooting.md#reticulum-local-dms-hang-with-autointerface--private-tcp-hub).

Expand Down