Skip to content

feat: Reticulum third protocol — Ratspeak sidecar, MeshChat UX parity, RNode flasher/BLE/Wi-Fi, triple-protocol BLE, peer graph overhaul & repo-wide i18n (Fixes #593)#596

Merged
rinchen merged 89 commits into
mainfrom
593/reticulum-sidecar-scaffold
Jul 2, 2026
Merged

feat: Reticulum third protocol — Ratspeak sidecar, MeshChat UX parity, RNode flasher/BLE/Wi-Fi, triple-protocol BLE, peer graph overhaul & repo-wide i18n (Fixes #593)#596
rinchen merged 89 commits into
mainfrom
593/reticulum-sidecar-scaffold

Conversation

@rinchen

@rinchen rinchen commented Jun 29, 2026

Copy link
Copy Markdown
Member

Summary

Adds Reticulum as mesh-client’s third protocol tab alongside Meshtastic and MeshCore. The work completes #593: a headless Rust mesh-client-reticulum sidecar (AGPL, axum HTTP + WebSocket), Electron reticulum:* IPC, and a full React UI with Ratspeak/rsReticulum/rsLXMF wire interop as the primary compatibility target.

This is a large, multi-surface branch (~87 commits on the PR). At a high level it delivers:

  • Reticulum stack lifecycle — start/stop sidecar, auto-start, identity generate/import/export (passphrase-encrypted vault), multi-identity switcher, config import/export, factory reset
  • MeshChat-style UX parity — Peers/Contacts, LXMF DM chat (outbox, search, attachments, voice clips, reactions, transport badges), propagation sync, Nomad Network browser, network topology graph, wire packet sniffer
  • Interface management on Connection tab — CRUD for Auto, TCP/UDP, KISS, Pipe, I2P, RNode (USB serial / ble:// / tcp:// Wi-Fi), RNode Multi, BLE Peer mesh; offline/stale-port alerts; config audit/repair
  • RNode firmware flasher (Admin tab) — nRF52 DFU + ESP32 esptool, provision, firmware hash, Wi-Fi provisioning (CMD_WIFI_* → add tcp://host:7633 on Connection)
  • Triple-protocol BLE coexistence — Meshtastic + MeshCore + Reticulum on different BLE devices simultaneously; scan serialization without tearing down unrelated GATT links
  • Meshtastic/MeshCore peer graph overhaul — Reticulum-style hierarchical layout, pan/zoom, clutter reduction on large MQTT maps
  • Packet Sniffer improvements (all protocols) — pause-to-inspect, batch ingestion, scroll pinning, virtualizer key fixes
  • Repo-wide i18n audit — 118 unused keys pruned, CI enforcement, ~304 new keys, hardcoded-English sweep across all three protocols
  • Dev/CI toolingcheck:environment, Reticulum sidecar CI matrix (stub + rns-stack), vendored rsReticulum packet-tap patch

Fixes #593


Changes (detailed — for release notes)

Ratspeak interop (primary compatibility target)

mesh-client targets wire-compatible operation with Ratspeak peers on rsReticulum/rsLXMF — not MeshChat’s aiohttp stack.

Implemented (Ratspeak-aligned):

Area Detail
Sidecar API HTTP + WebSocket contract aligned with Ratspeak ratspeak-tauri commands — see docs/reticulum-sidecar-ipc.md
Build layout Sibling checkout ../../rsReticulum, ../../rsLXMF (same as Ratspeak); CI clones outside workspace when actions/checkout path limits apply
Identity Import/export reads ratspeak.identity.v2 backups; local passcode identity vault (scrypt, encrypt export JSON at rest)
LXMF chat Threaded replies use ratspeak.chat.v2 message hashes; message_hash SQLite column; delivery status events
Interfaces Auto, TCP Client/Server, UDP, KISS, Pipe, I2P, RNode (USB / ble:// / tcp:// Wi-Fi), RNode Multi, BLE Peer mesh
Features LXMF send/resource/reaction, contacts, peers (path/probe/favorite), propagation sync, Nomad Network, topology, wire sniffer, config audit/repair
BLE coexistence Meshtastic + MeshCore (Noble/Web Bluetooth) + Reticulum (sidecar btleplug) on different peripherals; MAC registry + scan mutex
RNode Wi-Fi Admin provisioning → Connection tcp://host[:7633] via rns-rnode-tcp feature

Not yet at Ratspeak parity (tracked / out of scope):

  • LXST voice callsGET /api/v1/voice/status stub; voice clips in chat work, but no rsLXST call UI
  • LRGP games (chess, tic-tac-toe) — GET /api/v1/games/status stub; no lrgp-rs sibling
  • Hardware identity (YubiKey/PIV) — not wired
  • Interface hot-reload under live rns-stack — CRUD writes config; stack restart required for live RNS
  • Clear announces under live stack — stub cache clears; RNS path table repopulates on refresh
  • Mobile targets (Android/iOS) — desktop Electron only

Rust sidecar (reticulum-sidecar/)

  • Binary: mesh-client-reticulum — axum HTTP + WebSocket event bridge
  • API modules: status, app info, interfaces CRUD, contacts, LXMF send/resource/reaction/delete, config import/export/audit/repair, diagnostics, factory reset, propagation sync, nomad list/favorite/page/file, topology edges, packet tap ring buffer (GET/DELETE /api/v1/packets, wire_packet WS events), voice/games status stubs, BLE scan/availability, serial port enumeration
  • Cargo features: optional rns-stack; rns-ble (BLE Peer + ble:// RNode); rns-rnode-tcp (Wi-Fi tcp:// RNode); dev auto-build enables rns-stack,rns-ble when Ratspeak siblings present
  • Live stack bridge: async reads (no blocking_read panics); timed transport control queries (8s cap) with cache fallback; LinkDeliveryManager for async LXMF (Direct vs Propagated); Nomad page/file via LinkClient::query with identity-hash resolution; LXMF attachment send/receive; propagation bridge for live sync
  • Packet tap: register_packet_tap from rsReticulum → wire_packet WS + sniffer buffer; vendored patch reticulum-sidecar/patches/rsReticulum-packet-tap.patch (upstream ratspeak/rsReticulum#10) applied in CI before rns-stack builds
  • Config: validated atomic writes, extra_sections round-trip, 4 MiB body limit, mnemonic stripped on disk, localhost CORS, loopback bind guard
  • Interface health: merge config rows with live GetInterfaceStats so failed RNode/KISS interfaces remain visible; preset expansion/repair on bootstrap; coordinated RNode RF profiles; enabled preserved when live stats omit offline BLE RNodes
  • CI: dual matrix — stub builds (no siblings) + rns-stack on linux x64, macOS arm64, Windows x64 + arm64; libdbus-1-dev for Ubuntu rns-ble; cargo test in workflow; electron-builder extraResources
  • Dev: pnpm run reticulum:sidecar:build; rebuild when Rust sources newer than binary; sidecar survives dev HMR remounts

Electron main process + preload

  • ReticulumSidecarManager — spawn, health poll, serialized start (coalesce concurrent reticulum:start), HTTP proxy GET/POST/PUT/DELETE, WS event bridge to renderer
  • reticulum:* IPC — proxy routes with path allowlist (/api/v1/), transport-aware timeouts (30s for peers/interfaces/topology/packets; Nomad page/file uses max(egress, RF) up to 180s), config file read/import dialog
  • SQLite IPC — Reticulum message search (FTS5 schema v41), delete, attachment save/show-in-folder (path jail), peer/contact delete, db:clearReticulumMessages, chat outbox attempt_count persistence
  • ble-coexistence-coordinator.ts — peripheral MAC registry, scan-only mutex (noble / reticulum / webbt), Noble pause/resume for Reticulum picker scans without dropping GATT; rejects same-MAC conflicts
  • mqtt-client-teardown.ts — absorbs late mqtt.js connack/keepalive errors on force-disconnect (fixes uncaught main-process exceptions)
  • Security hardening (manual audit): validateIpcSender on reticulum/vault/DB/BLE handlers; attachment path jail; proxy body/config read caps; vault 8-char passcode + unlock rate limiting; bounded readUtf8FileBounded (TOCTOU fix)

Renderer — Reticulum protocol integration

  • Register Reticulum in meshProtocol, ProtocolCapabilities, theme (amber), useReticulumRuntime, useReticulumSession, SQLite migrations (v39+), hydrateIdentityStoresFromDb, useReticulumPanelActions
  • Capability-gated UIhasReticulum*, hasLxmfDeliveryStatus, etc.; check-protocol-string-gates extended; no raw protocol === 'reticulum' checks in new code
  • Canonical storesreticulumIngestmessageStore; nodeStore canonical for Reticulum UI; reticulumPeerStore for favorites/names; deduplicated sidecar reads (reticulumSidecarReads), peer display names, chat last-read migration, unread counting
  • Runtime — WS subscriptions for wire_packet, LXMF delivery, peers_updated, propagation sync_progress, interface.state; autostart/reconnect on unexpected stop; debounced event refreshes (2s) during Nomad browsing
  • Header — green “configured” status when stack running; identity display name from sidecar (not uint32 fallback); ConnectIcon during connecting

Tab layout & panel architecture

Tab Panel Purpose
Connection ReticulumStackPanel Stack start/stop, auto-start, Disconnect & quit; Interfaces section (CRUD, device pickers, offline alerts) — collapsed by default
Network (was Radio) ReticulumNetworkPanel Identity generate/import/export/switch, announce interval, propagation mode, config import/export, stack settings
Admin ReticulumAdminPanel RNode firmware flasher, Wi-Fi provisioning (WifiConfig.tsx), factory reset danger zone
Peers ReticulumPeerListPanel Peers | Contacts sub-tabs, search, sort, virtual scroll, path/probe/favorite/chat, detail modal
Chat shared ChatPanel DM-only LXMF; contact tabs; transport badges; outbox; search; attachments; voice clips; reactions
Topology ReticulumTopologyPanel MeshChat-style hierarchical graph: self → interface hubs → peers; distant-peer filters
Nomad NomadNetworkPanel Favourites/Announces, Micron HTML browser, file downloads
Diagnostics shared + ReticulumDiagnosticEngine Reticulum-native rows (no LoRa hop-goblin semantics); config audit hints; unified ping panel
Sniffer / Stats shared RawPacketLogPanel Reticulum wire_packet variants; Graph tab hidden for Reticulum

Meshtastic/MeshCore Radio, Map, Modules, and Graph tabs restored after Reticulum capability gating initially hid them (appTabMappings OR rules fixed).


Connection tab — interface management

  • CRUD: add, edit (inline form: name, TCP host/port, RNode serial/preset/callsign/RF params, BLE seed addresses, Wi-Fi host/port), enable/disable, delete (ConfirmModal)
  • Device pickers: ReticulumInterfaceDevicePickerModal for serial, BLE Peer, BLE RNode; Electron serial picker with known/last-used port labels
  • Transports: USB serial, ble:// RNode, tcp://host[:7633] Wi-Fi RNode (not TCP Client 4242), BLE Peer mesh, Auto, TCP/UDP, KISS, Pipe, I2P, RNode Multi
  • Health UI: reticulumLocalInterfaceHealth classifier (stale_port vs enabled_down); Connection alert banner with Open Network / Refresh ports; cyan “connecting” grace window (30s post-start) for BLE RNode link settle; adaptive 5s/30s polling; stack restart on serial CRUD or RF patch changes
  • Config audit/repair: sidecar GET /config/audit, POST /config/repair; diagnostics merge; Connection hints with repair/disable/deep-link edit via reticulumUiStore
  • RNode RF edit: frequency, bandwidth, spreading factor, coding rate, TX power in interface editor; coordinated preset defaults; stack restart on RF changes

LXMF chat (DM-only)

  • DM tabs from LXMF contacts even without prior message history; auto-select first contact; block send without recipient
  • Transport badges: RF / TCP / NET in and out on messages (received_via / sent_via columns, sidecar egress classification)
  • Delivery status: outbound “Sending” until lxmf_outbound_status / LinkDeliveryManager events; propagation-node badge and toast when offline without PN
  • Threading: ratspeak.chat.v2 reply hashes; dismiss closed DM tabs across restarts; clear DM history via App danger zone (db:clearReticulumMessages)
  • Attachments: live LXMF file send/receive; [file:…] rendering; save/show-in-folder IPC; chunked upload
  • Voice clips: record in ChatComposer, playback/cache
  • Reactions: live LXMF reactions on rns-stack path
  • Outbox: persist attempt_count; drain on announce, path updates, MQTT reconnect (chatOutboxDrain)
  • Search: FTS5 message search in SearchModal
  • Payload limit: 4096 bytes via ProtocolCapabilities.lxmfPayloadLimit

Peers, contacts & identity

  • Dedicated Peers tab (not NodeListPanel fork): path, probe, favorite, open DM, copy hash (Electron clipboard IPC)
  • reticulumPeerStore + SQLite destination persistence; peers_updated WS after path/probe; footer peer count synced from path table
  • Identity: generate, import (ratspeak.identity.v2), export (passphrase required), multi-identity switcher, announce interval, clear announces, profile icons from LXMF FIELD_ICON_APPEARANCE, blocked contacts, identity activity store
  • Peer detail modal: avatar colors, voice-call button removed (LXST not implemented)

Nomad Network browser

  • Sidecar ingests nomadnetwork.node announces; persists nodes with identity_hash for LinkClient::query
  • UI: split list + browser pane; Micron HTML via micron-parser + NomadMicronPageView (DOMPurify); source toggle; in-page and cross-node navigation
  • Downloads: /file/ fetches with base64 IPC round-trip; transport-aware timeouts (TCP 45s, RF hop-scaled up to 180s)
  • Resilience: 5s TTL cache for interfaces GET; 60s egress cache; skip caching network egress when interfaces list empty; proxy timeout uses max(egress, RF) so RF page fetches are not aborted at 47s

Network topology graph (Reticulum)

  • MeshChat-style hierarchical layout: local identity center → configured interface hubs (human-readable names) → peers hung off matching interface
  • Show all configured interfaces as spokes even when idle; green/red online-offline styling
  • Distant-peer filters (show distant peers, max hops); enrich labels from peers, contacts, Nomad announces
  • buildReticulumViaHashEdges for hub-to-leaf edges when API returns nodes but empty edges array
  • Force-directed fallback replaced by structured layout; full-height mount without nested scroll

Wire packet sniffer & Stats (all protocols)

  • Reticulum: sidecar packet tap → wire_packet WS → rawPackets store; clearRawPackets IPC
  • Pause-to-inspect: Wireshark-style Pause/Resume with pending count; snapshot packets while paused; jump-to-latest when unpinned
  • Batch ingestion: wire_packet appends batched per animation frame
  • Scroll pinning: align with ChatPanel contract (anchorTo: 'end', followOnAppend); unpin on scroll-up/wheel/expand; preserve anchor when reading history
  • Virtualizer keys: rawPacketContentKey / getItemKey — no duplicate React keys for identical captures
  • Duplicate sniffer rows fixed (removed dual handleSidecarEvent subscription in ReticulumStackPanel)
  • Stats/Sniffer panels enabled for Reticulum with RawPacketLog Reticulum variants; Graph tab removed for Reticulum

RNode firmware flasher & Wi-Fi provisioning (Admin tab)

  • Ported from liamcottle/rnode-flasher: nRF52 DFU (SLIP/CRC16), ESP32 via lazy esptool-js 0.4.5, EEPROM provision, firmware hash, advanced tools
  • ESP32 fixes: parse flash map addresses as hex (was decimal → boot loop); Latin-1 MD5 verify for esptool; DTR reboot, bootloader entry recovery, boot-console drain
  • UX: unified green step buttons (flash → provision → set-hash); firmware download links (GitHub latest-release); meshchat-style port picker; catalogKey dedup for colliding EEPROM products (LilyGO T3S3 vs RNode)
  • Wi-Fi provisioning: WifiConfig.tsx — station/AP/off via KISS CMD_WIFI_*; then add tcp://host:7633 interface on Connection tab
  • Gated: block flashing when stack holds enabled RNode serial interface; flasher-safe hint when stack stopped
  • Dependencies: esptool-js, @zip.js/zip.js, js-md5; firmware catalog generator script

Diagnostics, config audit & tooltips

  • ReticulumDiagnosticEngine — Reticulum-native rows (local stale-port, local-offline, config audit findings); no LoRa hop-goblin semantics
  • Config audit/repair APIs surfaced in Connection panel and diagnostics
  • DiagnosticsPingPanel replaces Reticulum-specific ping panel; duplicate packet browser removed from Diagnostics (Sniffer covers packets)
  • GlobalInstantTooltip — app-wide instant tooltips for native title attributes; HelpTooltip shares positioning

Meshtastic / MeshCore peer communication graph

  • buildMeshPeerTopologyGraph — self center → direct relay hubs → distant peers; 90-node budget (later 48), hop filters, relay resolution from path/neighbor info
  • Clutter reduction: cap relay hubs at 20; demote MQTT-only direct peers to compact leaf dots; hub promotion requires RF/neighbor evidence
  • UX: pan/zoom (useSvgPanZoom), hover/focus tooltips, density-aware sizing, edge dimming on inspect; hide permanent labels on dense graphs
  • Fixes: pointer capture deferred until drag (restores node clicks); peer clicks open detail modal; label styling aligned with Reticulum
  • Default: 2 hops, distant peers off for progressive disclosure

BLE — triple-protocol coexistence & dual-radio Noble

Triple-protocol (Meshtastic + MeshCore + Reticulum):

  • Replaced exclusive adapter lease with BleCoexistenceCoordinator: per-MAC ownership registry, scan-only mutex, Noble pause/resume for Reticulum scans without dropping GATT
  • Same MAC rejected with i18n errors; only active scans serialized — never disconnect unrelated protocols
  • Reticulum BLE interfaces: rns-ble sidecar feature; BlePeerInterface with seed addresses; GET /api/v1/ble/scan; pause enabled BLE interfaces during picker scan

Dual-radio Noble (Meshtastic + MeshCore on different BLE devices):

  • meshcoreDualNobleBleInit coordinator: primary/secondary startup order from mesh-client:protocol localStorage; withNobleBleConnectMutex narrowed to GATT + handshake only
  • Init from App.tsx useLayoutEffect (before ConnectionPanel auto-connect); secondary waits on awaitNobleBlePrimaryAutoConnectSettled()
  • ConnectionPanel: defer secondary auto-connect, device name label, auto-reconnect banner, mutex wait notices; MeshCore reconnect loop gated on meshcoreEverConfiguredRef
  • MeshCore MQTT UI parity: hasMqttConnectionPanel, header MQTT status while RF connecting, deferred MQTT auto-launch until JWT/password ready

Internationalization (repo-wide audit)

Phase 1 — Reticulum i18n + quality (early commits):

  • Reticulum strings in all 16 locales; check-i18n-quality.mjs Reticulum false-friend rules (stack/stop, enable/edit, probe, sidecar-stroller, propagation, DM copy)
  • Locale fixes for MT errors (Russian chimney “stack”, Polish road barriers on “stop”, German/Dutch physical “Stapel”, Korean DM 문의 vs 연락처, etc.)

Phase 2 — prune unused keys:

  • Removed 118 orphaned keys (2834→2716) from all locale files
  • New tooling: scripts/i18n-unused-keys.mjs, scripts/prune-i18n-unused.mjs, tests
  • check:i18n now fails on unused English keys (skipped in check:i18n:branch)

Phase 3 — hardcoded English sweep:

  • ~304 new keys across Meshtastic, MeshCore, Reticulum, and shared panels (Connection, Radio, Modules, Security, Chat, Map, Log, Diagnostics, App, Rooms, Repeaters)
  • meshcoreMessageI18n helper, translateReticulumDiagnosticCause, diagnosticsLabels for routing ports
  • Expanded PROTECTED_BRANDS, protocol-token checks, dynamic-key registration, locale brand repair script
  • Final gaps: AppPanel retention aria-labels, InactiveProtocolNotifier BLE owner protocol names

CI, build & developer experience

  • Reticulum sidecar workflow: stub + rns-stack matrix; Ratspeak sibling clone outside workspace; rsReticulum packet-tap patch; libdbus-1-dev on Ubuntu; Windows serial port vec type annotation
  • pnpm run check:environmentscripts/check-environment.mjs probes Git, Node, pnpm, node_modules, OS-native build deps; bootstrap before pnpm install; documented in CONTRIBUTING + AGENTS
  • Flatpak: retry offline pnpm install on @jsr temp-dir races (scripts/flatpak-pnpm-install.mjs)
  • CodeQL: pinned Actions refs, workflow permissions, URL hostname sanitization in i18n quality script, config-read test fixtures (no mkdtemp), MQTT schedule import ordering
  • Dependency bumps: two chore: bump deps commits; Flatpak Electron archive sync
  • Prettier: reticulum-sidecar/ ignored (no TOML parser); Rust target/ gitignored

Documentation

  • docs/reticulum.md — Connection/Network/Admin split, interface CRUD, RNode Wi-Fi workflow, Ratspeak interop
  • docs/reticulum-sidecar-ipc.md — full REST/WS contract
  • docs/troubleshooting.md — sidecar rebuild, BLE coexistence, Wi-Fi offline, Nomad timeouts, proxyGet storms
  • docs/credits.md — Ratspeak attribution
  • AGENTS.md — Reticulum quick reference, dual-radio Noble BLE rules, triple-protocol BLE, i18n prune workflow, check:environment fresh-clone guidance

Cross-protocol fixes & polish (non-Reticulum)

Area Change
MeshCore Rooms Abort zombie room logins (120s wall); cancel queued auto-login on user post; hop-scaled post timeout; SENT wait after sendToRadioFrame resolves
MQTT mqtt-client-teardown absorbs late connack errors; MeshCore header MQTT status parity
Log panel Per-protocol App/Device filters; Reticulum sidecar tags as device logs; [ReticulumIPC] prefix
Accessibility Header status text no longer animate-pulse (WCAG contrast); pulse on dots/icons only
App tab gating Restore Meshtastic Modules, MeshCore Radio/Map/Graph after Reticulum capability OR fix
InactiveProtocolNotifier i18n BLE owner protocol names in background-tab toasts

Test plan

Reticulum core

  • pnpm run reticulum:sidecar:build (Rust + Ratspeak siblings) — stub and rns-stack,rns-ble,rns-rnode-tcp builds
  • Connection: start/stop stack, auto-start, Disconnect & quit; interfaces CRUD (USB RNode, ble:// RNode, tcp:// Wi-Fi, BLE Peer, TCP)
  • Network: identity generate/import/export (passphrase), multi-identity switch, announce interval, config import/export
  • Admin: RNode flasher (nRF52 + ESP32), Wi-Fi provisioning → Connection tcp:// interface
  • Ratspeak interop: LXMF DMs with Ratspeak peer; ratspeak.chat.v2 reply threading; import ratspeak.identity.v2 backup

Reticulum features

  • Peers: search, sort, path, probe, favorite, open DM, detail modal, copy hash
  • Chat: DM tabs, transport badges, outbox retry, attachments, voice clips, reactions, FTS search, dismiss tabs
  • Topology: hierarchical layout, distant-peer filters, pan/zoom, interface hub styling
  • Sniffer: live wire_packet capture, pause-to-inspect, scroll pinning, no duplicate rows
  • Nomad: favourites, announces, Micron pages, cross-node nav, file download (RF + TCP nodes)
  • Propagation: sync UX, per-node sync, chat banner when PN missing
  • Diagnostics: config audit/repair hints, local stale-port rows
  • Config audit/repair from Connection panel

BLE coexistence

  • Meshtastic BLE + MeshCore BLE (different devices) — dual-radio Noble auto-connect order
  • Meshtastic + MeshCore + Reticulum BLE (three different devices) — no forced disconnect on scan/connect
  • Reticulum BLE picker scan with mesh radios configured — scan works, mesh links stay up
  • Same-MAC conflict shows i18n error

Meshtastic / MeshCore regression

  • All sidebar tabs visible (Radio, Map, Modules, Graph as applicable)
  • Peer graph: hierarchical layout, pan/zoom, node click → detail, MQTT map not starbursting
  • MeshCore Rooms: post after failed auto-login does not hang
  • MeshCore MQTT status in header while BLE connecting
  • Packet Sniffer pause/scroll on Meshtastic/MeshCore tabs

i18n & CI

  • pnpm run check:i18n — no unused English keys; locale quality rules pass
  • pnpm run check:environment on fresh clone
  • Switch UI locale (es, de, ru, zh, …) — no missing-key console errors
  • pnpm run lint, pnpm run typecheck, pnpm run test:run

Platforms

  • darwin, linux, win32 — Connection, flasher, BLE picker, Nomad fetch timeouts

Commits audited

87 commits on PR #596 (including 1 merge-from-main), audited via gh pr view 596 commit messages and per-commit git show --stat review.

Theme Commits (approx.)
Sidecar scaffold + integration + IPC 8
Connection panel / stack lifecycle 6
Unified parity + MeshChat phases 0–8 4
Interface CRUD + Connection tab move 6
RNode flasher + Wi-Fi 5
Peers / LXMF chat / transport 8
Nomad Network 3
Topology + multi-hop delivery 6
Wire sniffer + Stats 8
BLE transport + triple coexistence 8
Dual-radio Noble BLE 2
Meshtastic/MeshCore peer graph 4
Outbox / search / diagnostics parity 3
i18n (prune + hardcoded sweep) 5
CI / CodeQL / security / tooling 12
Cross-protocol fixes (rooms, MQTT, a11y, log) 6
Docs 2

Trivial one-line Reticulum compile/typo fixes are folded into the theme rows above rather than listed individually.

- Add headless `mesh-client-reticulum` binary (axum HTTP + WebSocket)
- Expose stub routes: status, app/info, interfaces, lxmf/send, contacts
- Document rsReticulum/rsLXMF path deps for follow-up wiring
- Ignore reticulum-sidecar/ in Prettier (no TOML parser)

Tracks mesh-client#593 Phase B; Ratspeak-aligned IPC surface.
@github-code-quality

github-code-quality Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Overview

Languages: JavaScript

JavaScript / code-coverage/vitest

The overall coverage in the 593/reticulum-sideca... branch remains at 57%, unchanged from the main branch.

Show a code coverage summary of the most impacted files.
File main 33c40f5 593/reticulum-sideca... 811b669 +/-
src/renderer/ru...culumRuntime.ts 0% 23% +23%
src/renderer/co...rfacesPanel.tsx 0% 49% +49%
src/main/reticu...ecar-manager.ts 0% 51% +51%
src/renderer/st...lumPeerStore.ts 0% 61% +61%
src/renderer/co...erListPanel.tsx 0% 61% +61%
src/renderer/co...pologyPanel.tsx 0% 84% +84%
src/main/identityVault.ts 0% 93% +93%
src/renderer/li...opologyGraph.ts 0% 94% +94%
src/renderer/li...pologyLayout.ts 0% 95% +95%
src/renderer/li...lasher/rnode.ts 0% 97% +97%

Updated July 02, 2026 21:08 UTC
Code Coverage is in Public Preview. Learn more and provide us with your feedback.

@rinchen
rinchen marked this pull request as draft June 29, 2026 17:28
- Register Reticulum in protocol registry, capabilities, theme, runtime,
  session, SQLite migrations, hydrator, and panel actions; add useReticulumRuntime
  and reticulum:* IPC with ReticulumSidecarManager and dev cargo-build fallback.
- Scope App UI per protocol: Reticulum connection stack editor, DM-only chat,
  diagnostics without LoRa rows, amber boot splash, and reticulumIdentityId in
  useActiveMeshIdentity (fixes MeshCore data leaking on Reticulum tab).
- Expand reticulum-sidecar (API modules, optional rns-stack feature), CI workflow,
  electron-builder extraResources, and troubleshooting/docs for Rust toolchain.
- Audit branch Reticulum translations in all locales: preserve Reticulum, Rust,
  cargo, mesh-client, and pnpm run reticulum:sidecar:build literals; add
  connectionPanel.disconnect; extend check-i18n-quality with Reticulum rules.

Fixes #593
@rinchen rinchen changed the title feat: Reticulum sidecar scaffold (Phase B, #593) feat: Reticulum sidecar, IPC, and renderer integration (Fixes #593) Jun 29, 2026
- Drop unused SinkExt import in reticulum-sidecar/src/api/ws.rs (clean cargo build).
- Add reticulum-sidecar/target/ to root .gitignore.
- Expand reticulum-sidecar/.gitignore with standard Cargo artifacts.
Comment thread .github/workflows/reticulum-sidecar.yaml Fixed
Comment thread .github/workflows/reticulum-sidecar.yaml Fixed
Comment thread .github/workflows/reticulum-sidecar.yaml Fixed
Comment thread .github/workflows/reticulum-sidecar.yaml Fixed
Comment thread scripts/check-i18n-quality.mjs Fixed
Comment thread scripts/check-i18n-quality.mjs Fixed
rinchen added 7 commits June 29, 2026 13:32
- Add workflow-level contents: read permissions to reticulum-sidecar.yaml.
- Pin dtolnay/rust-toolchain to commit 29eef336 instead of @stable tag.
- Replace rustup.rs .includes() substring checks in check-i18n-quality.mjs
  with parsed hostname comparison (js/incomplete-url-substring-sanitization).

CodeQL flagged unpinned Actions refs, missing GITHUB_TOKEN scopes, and
unsafe URL host substring matching introduced with the Reticulum sidecar i18n
quality rules.
- Move stack Start/Stop controls to the top of ReticulumConnectionPanel;
  sidecar no longer requires identity before start (fixes proxyGet/proxyPost
  failures during the identity wizard).
- Add auto-start checkbox (localStorage + SQLite reticulumAutostart), Stop
  stack button, and Disconnect & quit matching other protocol panels.
- Track sidecar health via reticulum:getStatus and onStatus IPC events.
- Complete sidecar stack module, LXMF/identity REST, SQLite IPC, chat v2
  reply hashes, hide Diagnostics tab, and refresh reticulum.md docs.

Fixes #593
- Move Disconnect & Quit to the panel top via shared renderExitActions,
  matching Meshtastic/MeshCore idle/connected/connecting layout.
- Show Stop stack from runtime connection state (not only getStatus) so
  the button appears reliably after Start; style it like Disconnect Radio.
- Restructure the stack card with a status header and full-width Start/Stop.
- Replace blocking_read in stack bootstrap, emit_stats, and status
  handlers with async read().await so the sidecar no longer panics
  inside the Tokio runtime on start.
- Gate ReticulumConnectionPanel proxy calls on sidecarStatus.running
  (not in-flight connecting) and add catch handlers on preset/BLE
  fetches to avoid unhandled proxyGet rejections.
- Add ConnectIcon in the header while device status is connecting;
  use static SVG path variants to prevent motion d=undefined errors.
- Promote stack card title to h2 for valid heading hierarchy with
  subsection h3s.

Fixes #593
- Clone ratspeak/rsReticulum and ratspeak/rsLXMF beside the mesh-client
  workspace in reticulum-sidecar.yaml so optional path dependencies in
  Cargo.toml resolve on Linux and Windows ARM64 builders.
- Document that stub builds still need those directories on disk locally;
  CI performs the checkouts automatically.

Cargo validates optional path deps even when rns-stack is disabled, which
caused "failed to read lxmf-core/Cargo.toml" on GitHub Actions.

Fixes #593
- Report Reticulum stack as `configured` (not `connected`) so the header
  uses green status like Meshtastic/MeshCore; align ConnectIcon and panel
  connecting badge with shared yellow warn styling.
- Require backup passphrase for identity export POST (fixes 422 on empty body).
- Add mqtt-client-teardown helper so force-disconnect absorbs late mqtt.js
  connack/keepalive `error` events instead of uncaught main-process exceptions.
  Register MQTT error handlers immediately after connect; stop using
  removeAllListeners before end() in MeshCore/Meshtastic adapters.

Fixes #593
- LogPanel: classify Reticulum sidecar/runtime/IPC tags as device logs;
  default Reticulum tab to Device source; exclude reticulum device
  entries from App view. Rename IPC logs to [ReticulumIPC] and extend
  check-log-panel-filter script + tests.
- ReticulumConnectionPanel: treat stack as API-ready when parent reports
  running; hydrate identity immediately after generate/import; export
  button gated on sidecar only (passphrase validated on click).
- Use "Nodes" instead of "Contacts" for Reticulum via
  nodeListTabUsesContactsLabel: false.

Reticulum logs were mixing into App view with no device filter, export
was blocked until passphrase was typed, and the tab reused MeshCore
"Contacts" wording despite Reticulum peers being nodes.
@rinchen rinchen changed the title feat: Reticulum sidecar, IPC, and renderer integration (Fixes #593) feat: Reticulum sidecar, IPC, renderer integration, and UX hardening (Fixes #593) Jun 29, 2026
rinchen added 4 commits June 29, 2026 18:02
- Filter App view by active protocol: exclude other protocols' device
  logs and Meshtastic/MeshCore MQTT tags via isAppLogEntry /
  isOwnedByOtherProtocol.
- Stop passing MeshCore deviceLogs to Reticulum (protocolRecord third
  arg was defaulting reticulum → meshcore); gate injected deviceLogs
  through isDeviceEntry for the active tab.

Reticulum App view was flooded with Meshtastic/MeshCore MQTT lines, and
Device view inherited MeshCore hook logs from the protocolRecord default.
- Split Connection (ReticulumStackPanel) vs Radio (ReticulumRadioPanel):
  stack lifecycle on Connection; identity, interfaces, peers, propagation,
  config import/export, stack settings, factory reset on Radio.
- Canonical messageStore path via reticulumIngest; LXMF hash/reply round-trip
  in storeRecordAdapters and SQLite (message_hash column).
- Wire sendReaction, sendAttachment (LXMF resource), contact groups (selfNodeId),
  PeerGraph/Graph tab, and Reticulum-native diagnostics engine.
- Sidecar: rnsd config parse/import/export, system APIs (diagnostics, factory
  reset, voice/games status, multi-identity stub), lxmf/resource + message delete.
- CI dual matrix: stub + rns-stack builds on linux/macOS/Windows (incl. WoA).
- IPC: proxyPut, config file read/import dialog; db search/delete Reticulum messages.
- Capability flags, tests, docs/reticulum.md, and i18n for new UI strings.

Implements the unified Reticulum parity plan for cross-platform desktop
(macOS/Windows/Linux) while keeping stub mode for dev/CI fallback.

Fixes #593
- Add reticulum:proxyDelete IPC and sidecar manager DELETE proxy so the
  renderer can call DELETE /api/v1/interfaces/{id}.
- ReticulumRadioPanel: Delete button per interface row with ConfirmModal
  before removing from stack config; refresh list on success.
- English i18n for delete action and confirmation copy; contract test
  for new IPC channel.

The sidecar API existed but the UI only exposed enable/disable; users
can now remove interfaces without hand-editing config.

Fixes #593
- Edit button per interface row opens an inline form (name, TCP host/port,
  or RNode serial/preset/callsign) and saves via proxyPut to the sidecar.
- i18n keys for edit flow; editing state clears on delete or successful save.

Users could add and delete interfaces but had no way to fix misconfigured
rows without re-importing config or deleting and re-adding.
@rinchen rinchen changed the title feat: Reticulum sidecar, IPC, renderer integration, and UX hardening (Fixes #593) feat: Reticulum unified parity — sidecar, Radio tab, interface CRUD (Fixes #593) Jun 30, 2026
rinchen added 8 commits June 29, 2026 19:27
- Document Connection/Radio split, interface CRUD, full sidecar REST/IPC
  contract, troubleshooting for interface failures, and dev workflow.
- Extend check-i18n-quality for Reticulum Radio/Connection keys: must-translate
  rules, MT false-friend patterns (host, peer, probe, disable), and tests.
- Fix untranslated or mistranslated Reticulum strings across cs, de, id, it,
  ja, nl, pl, pt-BR, ru, tr, uk, and zh locales.

Docs and i18n checks were stale after interface edit/delete and unified parity;
locale fixes address pre-commit quality failures on auto-translated copy.
- Replace actions/checkout@v6 sibling checkouts with git clone into
  ${GITHUB_WORKSPACE}/../rsReticulum and ../rsLXMF across all four
  reticulum-sidecar workflow jobs.
- checkout@v6 rejects paths outside the workspace, but Cargo path deps
  still require the Ratspeak sibling layout.

Fixes build-rns-stack failures on ubuntu-latest and macos-latest for PR #596.
- List known LXMF contacts as DM tabs even without prior message history.
- Auto-select the first contact when none is active in DM-only mode.
- Disable composer and show DM-only empty/placeholder copy instead of
  channel-style “send a public message” UX.
- Block send with a visible error when no recipient is selected.
- Prefer long_name for Reticulum node labels in chat (match MeshCore).
- Add ChatPanel tests for contact tabs, auto-select send, and no-contact state.

Reticulum LXMF is direct-message only; Enter previously called send with
undefined destination and failed silently while the UI looked like
Meshtastic/MeshCore channel chat.
- Type `ports` as `Vec<serde_json::Value>` in `enumerate_serial_ports`.
- On Windows, macOS/Linux cfg blocks are omitted and nothing is pushed,
  so rustc could not infer the element type and `sort_by` failed with E0282.

Windows reticulum-sidecar CI jobs (x64, arm64, rns-stack) were failing
while Linux/macOS builds passed because only those targets populate the vec.
- Correct auto-translate false friends across 15 locales: Russian chimney
  stack ("дымовая труба"), Polish road barriers on stop stack, German/Dutch
  physical "Stapel", Ukrainian starting-vs-running stack, peer/probe column
  garbage, propagation title mistranslations, and Russian Enable→Edit plus
  sidecar→stroller (коляска) wording.
- Fix Korean Reticulum DM copy using 문의 (inquiry) instead of 연락처
  (contact); polish CS/DE/ES/FR/ID/NL/PT-BR/TR/UK/ZH peer and stack labels.
- Add chatPanel.dms and wire ChatPanel DM section header through i18next.
- Extend check-i18n-quality.mjs with Reticulum stack running/stop, enable/edit,
  probe/actions/propagation, sidecar-stroller, and chat Reticulum contact rules;
  require translation for new LXMF/Reticulum chatPanel keys.

Quality checks missed these MT errors on the branch; new rules prevent regressions
on Reticulum sidecar UI and DM-only chat strings added with Phase B work.
- Port liamcottle/rnode-flasher into renderer Web Serial stack: nRF52 DFU
  (SLIP/CRC16), ESP32 via lazy esptool-js, EEPROM provision, firmware hash,
  and advanced tools (BT, TNC, display, wipe).
- Add collapsible RNodeFlasherSection at top of ReticulumRadioPanel, gated
  by hasRNodeFlasher; block flashing when stack holds an enabled RNode serial
  interface.
- Add esptool-js, @zip.js/zip.js, js-md5; firmware catalog generator script;
  unit/axe tests and flasher.* i18n across all locales.

Greenfield workflow is flash → provision → set hash before adding a Reticulum
RNode interface. Firmware zips are picked locally; no sidecar IPC for DFU.
- Sidecar classifies RNode/TCP/Auto paths and emits received_via/sent_via
  on lxmf_message payloads; live bridge registers LXMF delivery callback
  and echoes outbound sends with transport metadata.
- Extend MessageTransport, reticulum ingest/DB (received_via column v39),
  optimistic send rows, and ChatPanel badges (Wifi/TCP/globe in, RF/TCP/NET out).

Fixes #593
- Add reticulumPeerStore (SQLite-backed favorites/names, sidecar merge)
  and shared ReticulumPeer/Contact types; runtime refreshes peers via store
  while syncing contacts into nodeStore for Chat DM tabs.
- Replace NodeListPanel on Reticulum tab 2 with ReticulumPeerListPanel
  (Peers/Contacts sub-tabs, search, sort, virtual scroll, path/probe/chat)
  and ReticulumPeerDetailModal; gate with hasReticulumPeersList and Peers
  tab label capability.
- Simplify ReticulumRadioPanel peers section to count + link to Peers tab.
- Emit peers_updated WebSocket events from sidecar after path/probe success.

Reticulum peers use destination hashes, not MeshNode rows—new protocol-
specific panels avoid pervasive NodeListPanel forks while reusing existing
SQLite destination persistence and pendingDmTarget chat wiring.
rinchen added 10 commits July 1, 2026 18:37
- sidecarCargoBuildArgs now passes rns-stack,rns-ble when Ratspeak
  siblings exist, matching pnpm run reticulum:sidecar:build.
- ensureDevSidecarBinary detects binaries built without rns-ble and
  triggers a rebuild so Radio panel BLE interface pickers appear.
- scripts/update.sh uses the same feature set when siblings are present.

Dev auto-build previously compiled only rns-stack, so /api/v1/ble/
availability returned unavailable and the add-interface BLE options
stayed hidden despite UI and sidecar BLE support landing in #593.
- Skip mesh RF auto-connect/reconnect when Reticulum holds the Bluetooth
  adapter lease; stop Noble createBleConnection retries on that error.
- Show ReticulumStackPanel on the Reticulum connection tab instead of the
  shared mesh BLE connecting spinner; skip Reticulum mount auto-connect.
- Pause enabled Reticulum BLE interfaces during sidecar peer/RNode scans so
  btleplug can discover devices, then re-enable them afterward.
- Split adapter lease helpers into reticulumBleAdapterLease.ts so
  connection.ts does not pull store/protocol deps into BLE unit tests.

Meshtastic background auto-connect was racing Reticulum BLE picker scans and
surfacing misleading Noble errors while the picker stayed empty.
- Treat configured/reconnecting/stale BLE mesh sessions as active in
  isMeshBleConnected so Reticulum scan does not tear down Noble links.
- Refuse picker scan when mesh BLE is up; release adapter lease after each
  scan; probe availability and settle before btleplug scan.
- Add mode-specific empty states, hook tests, and locale strings.

Scan was acquiring the adapter while Meshtastic/MeshCore were configured,
disconnecting both protocols and leaving the picker empty.
- Install libdbus-1-dev and pkg-config on Ubuntu before rns-ble sidecar
  builds so libdbus-sys can compile in the Reticulum sidecar workflow.
- Simplify Reticulum DM peer toU derivation in chatUnreadCounts after the
  early effectiveTo null guard (CodeQL inconvertible-type comparison).
- Use mountNomadMicronHtml in micronParser tests instead of DOMParser on
  raw HTML (CodeQL incomplete multi-character sanitization).

Ubuntu build-rns-stack was failing with libdbus-sys build.rs panic; the
remaining open CodeQL thread on PR #596 was the effectiveTo/null check.
- Replace exclusive BleAdapterCoordinator lease with BleCoexistenceCoordinator:
  peripheral MAC registry, scan-only mutex (noble/reticulum/webbt), and Noble
  pause/resume for Reticulum picker scans without dropping GATT links.
- Wire Noble connect/disconnect, Linux Web Bluetooth, and Reticulum interface
  sync to register ownership; reject same-MAC conflicts with i18n errors.
- Remove mesh↔Reticulum blocking from ConnectionPanel, ReticulumRadioPanel,
  connection.ts, and the device picker; update IPC/preload/types and tests.

The prior adapter lease forcibly disconnected Meshtastic/MeshCore when
Reticulum used BLE. Meshtastic, MeshCore, and Reticulum (BLE Peer + ble://
RNode) should each maintain a link to a different device on macOS, Windows,
and Linux; only active scans are serialized.
- Add catch-no-log-ok comments to optional RNode presets prefetch and
  BLE availability probe handlers on mount.
- Both are best-effort sidecar probes; failure leaves safe defaults
  (empty presets, bleAvailable=false).

Aligns with AGENTS.md silent-catch convention; check:silent-catches
does not cover promise-chain .catch().
- Sidecar: expand RNode presets into frequency/bandwidth/SF/CR/txpower on
  add and update; repair incomplete preset rows on stack bootstrap so BLE
  interfaces synthesize instead of failing on missing fields.
- Sidecar: merge live interface stats without overwriting config `enabled`,
  so offline BLE RNodes stay enabled in Radio/Connection UI.
- Connection/Radio: treat `ble://` ports as Bluetooth (not stale USB serial);
  adaptive 5s/30s health polling, post-start burst refreshes, and immediate
  refresh on `interface.state` so status catches up when BLE links settle.
- Connection: cyan “connecting” banner during a 30s post-start grace window
  instead of an amber error while BLE RNode is still linking; stack restart
  on local interface enable via shared restart helper.
- i18n: cross-platform BLE pairing guidance (Heltec ~7s hold, USB Admin PIN
  path, OS Bluetooth dialogs); locale key updates for offline/connecting copy.
- Log panel: include Reticulum local-interface log paths in device filter.
- Tests for preset repair, enabled merge, grace-period alerts, connecting
  block, stack restart policy, and interface refresh scheduling.

Fixes #593
- Extract useReticulumInterfaceSnapshot hook and ReticulumInterfacesPanel
  for shared interface fetch/poll/events and CRUD on the Connection tab.
- Slim ReticulumRadioPanel into ReticulumNetworkPanel (identity, stack
  settings, propagation, config import only); remove cross-tab Open Radio
  plumbing from alerts and App wiring.
- Show tabs.network for Reticulum only; consolidate hasReticulumNetworkPanel
  capability and migrate i18n keys to networkPanel/adminPanel namespaces.
- Update tests, LogPanel filter tags, locale quality rules, and docs.

Interface management now lives where users start the stack, so stale-port
alerts resolve inline without switching tabs. Meshtastic/MeshCore Radio
tabs are unchanged.
Wrap InterfacesSection in a details/summary block (matching Network
panel sections) so the add/edit UI starts collapsed and keeps the
Connection panel less cluttered on first load.
- Enable rns-rnode-tcp in sidecar builds (CI, update.sh, reticulum-sidecar-path)
  and add tcp:// RNodeInterface config round-trip test.
- Add reticulumRnodeTransport helpers for tcp:// URI parse/build (port 7633).
- Extend Connection panel interface CRUD with Wi-Fi transport (host + port),
  setup hints, and edit flow; skip stale-port checks for tcp:// interfaces.
- Add Admin flasher Wi-Fi provisioning (KISS CMD_WIFI_* via WifiConfig) and
  offline health copy for unreachable Wi-Fi RNodes.
- Document provisioning workflow in reticulum.md and troubleshooting.md.

RNode over Wi-Fi stays RNodeInterface with serial_port tcp://host — not TCP
Client (4242). Users provision over USB in Admin, then add the interface on
Connection; release sidecars now bundle rns-rnode-tcp for runtime TCP connect.
@rinchen rinchen changed the title feat: Reticulum integration — sidecar, MeshChat parity, RNode flasher, topology, sniffer & i18n audit (Fixes #593) feat: Reticulum integration — Ratspeak-aligned sidecar, MeshChat UX parity, RNode flasher, BLE/Wi-Fi transport & i18n audit (Fixes #593) Jul 2, 2026
- Main IPC: validateIpcSender on reticulum/vault/DB/BLE handlers; jail
  attachment_path and showItemInFolder; proxy body/config read caps;
  favorited truthiness and delivery_status allowlist; vault 8-char
  passcode and unlock rate limiting; BLE pauseNobleScan uses scan mutex.
- Renderer: restart stack after interface delete; restartStack hydration
  parity; chunked sendAttachment; i18n for stats/errors/BLE owners;
  IPv6 tcp:// parsing; WifiConfig validation; Nomad XSS tests.
- Sidecar: validated atomic config writes, extra_sections round-trip,
  4 MiB body limit, localhost CORS, mnemonic stripped on disk, live
  LXMF reactions/propagation identity keys, stack_restart on identity
  change, loopback bind guard.
- Docs: attachment jail, delete-restart, IPv6, vault policy.

Addresses manual pre-PR audit across IPC security, Ratspeak parity
gaps, i18n, and missing tests from PR #596 review.
Comment thread src/main/reticulum-config-read.ts Fixed
rinchen added 2 commits July 2, 2026 08:49
- Re-register rsReticulum packet tap in live bridge so wire_packet WS
  events and GET /api/v1/packets populate the sniffer again (local
  rsReticulum exposes register_packet_tap; tap was removed in d653aa4).
- Fix TOCTOU in readUtf8FileBounded with a single bounded readSync.
- Add job-level permissions on Windows arm64 workflow jobs.
- Guard reticulumDestinationColumnText calls in RawPacketLogPanel.
- Add Nomad micron XSS regression test; tighten chatUnreadCounts peer
  checks to undefined-only.

Fixes CodeQL alert on reticulum-config-read.ts from PR #596.
- scripts/check-environment.mjs probes required tooling (Git, Node, pnpm,
  node_modules, OS-native build deps) and warns on optional deps.
- Bootstrap with node scripts/check-environment.mjs before pnpm install.
- Unit tests for version parsing and exit-code logic.
- Document in development-environment.md and CONTRIBUTING.md; fresh-clone
  guidance in AGENTS.md for AI assistants.
Comment thread src/main/reticulum-config-read.test.ts Fixed
Comment thread src/main/reticulum-config-read.test.ts Fixed
Comment thread src/main/reticulum-config-read.ts Fixed
rinchen added 6 commits July 2, 2026 09:45
- Use max(egress, RF) IPC timeout for Nomad page/file proxyGet so a stale
  `network` egress guess cannot abort RF link queries at 47s.
- Skip caching `network` egress when the interfaces list is empty so the
  next fetch retries after transport contention clears.
- Resolve CodeQL insecure-temporary-file on reticulum-config-read: mkdtemp
  in tests and suppression on dialog-selected config paths.
- Sync Flatpak vendored Electron archives to 41.9.2 (matches package.json).

Nomad sites failed to load after the security audit when interfaces queries
timed out during busy transport; cached `network` egress left proxyGet
timeouts too short for RF page fetches.
- Sidecar: config audit + repair APIs (GET /config/audit, POST
  /config/repair); coordinated RNode RF profiles; force-apply preset
  defaults when params deviate or preset changes; repair_config also
  runs apply_preset for legacy preset migration.
- Renderer: ReticulumDiagnosticsSection + engine audit merge;
  Connection panel audit hints, repair/disable, deep-link edit via
  reticulumUiStore; RNode RF fields in interface edit (frequency,
  bandwidth, SF, CR, TX power); stack restart on RF patch changes.
- GlobalInstantTooltip: app-wide instant tooltips for native title
  attributes; HelpTooltip shares bubble/positioning and opts out.
- i18n, tests, and docs for diagnostics, sidecar IPC, and reticulum.

Fixes #593
- Replace os.tmpdir/mkdtemp/writeFileSync in reticulum-config-read tests with
  committed fixtures under src/main/fixtures/reticulum-config-read/.
- Remove ineffective inline CodeQL suppression on readUtf8FileBounded; alert
  #91 was cross-file taint from test temp paths, not production dialog paths.
- Move MeshProtocol type import to the top of mqttReconnectSchedule.ts.

mkdtemp under os.tmpdir still triggered js/insecure-temporary-file on
writeFileSync; the mid-file import broke conventional module ordering.
- Reticulum: runtime diagnostic causeI18n keys, interface type/row labels,
  picker scan-error mapping, and translateReticulumDiagnosticCause helper.
- Meshtastic: Radio/Connection/Module/Security panels, NodeDetailModal,
  delete-node errors, and runtime toast strings moved to translation keys.
- MeshCore: meshcoreMessageI18n helper, lib/runtime error codes, and panel
  copy in Connection, Radio, Rooms, Repeaters, App, and MessageStatusBadge.
- Shared: App shell/footer, AppPanel danger zone and theme labels, Chat/Map/
  Log/Diagnostics panels, routing port labels via diagnosticsLabels.
- i18n tooling: expand PROTECTED_BRANDS and protocol-token checks, prefix-
  scoped Reticulum/routing-port rules, dynamic-key registration, locale brand
  repair script, and Trans/angle-bracket allowlists for legitimate markup.
- Locales: backfill ~304 new keys across all non-English locales; restore
  cs/zh from truncation during brand repair; fix nl routingPort.roomAdvert.

Hardcoded user-visible English blocked review and broke non-English locales;
centralized keys preserve brand/protocol tokens (RNS, LXMF, BLE, Giphy, etc.)
while letting translators localize surrounding copy.
- Wire remaining AppPanel retention numeric inputs to appPanel.*Aria keys
  (auto-prune nodes/position history, cap nodes/messages/contacts).
- InactiveProtocolNotifier uses connectionPanel.bleOwner.* for protocol
  names in inactive-tab toasts; register bleOwner dynamic prefix.

Closes the last non-optional gaps from the repo-wide i18n hardcoded-English plan.
- Add reticulum-sidecar/patches/rsReticulum-packet-tap.patch (base
  6d2b284) exposing PacketTapEvent and register_packet_tap for the
  wire sniffer; upstream PR ratspeak/rsReticulum#10.
- Pin RS_RETICULUM_REF and apply the patch in build-rns-stack and
  build-windows-arm64-rns-stack workflow jobs before cargo build.
- Add scripts/apply-rsReticulum-packet-tap.sh and document the overlay
  in reticulum-sidecar/README.md and patches/README.md.

CI was failing E0432/E0425/E0599 because live.rs calls packet-tap APIs
that upstream rsReticulum main does not ship yet; the vendored overlay
unblocks all four rns-stack matrix jobs without removing sniffer wiring.
@rinchen rinchen changed the title feat: Reticulum integration — Ratspeak-aligned sidecar, MeshChat UX parity, RNode flasher, BLE/Wi-Fi transport & i18n audit (Fixes #593) feat: Reticulum third protocol — Ratspeak sidecar, MeshChat UX parity, RNode flasher/BLE/Wi-Fi, triple-protocol BLE, peer graph overhaul & repo-wide i18n (Fixes #593) Jul 2, 2026
rinchen added 2 commits July 2, 2026 14:13
When config audit flagged tcp_unreachable with repair_kind disable, the
panel showed both an audit Disable action and the standard row Disable
toggle. Show the audit action only for system-managed interfaces.
- Normalize sidecar propagation sync progress to 0–100% and add a 60s
  stall watchdog so unreachable nodes fail visibly instead of hanging.
- Share ReticulumPropagationSyncProgress across controls and section;
  refresh shows spinner/dimming with a minimum visible duration.
- Add user-configurable auto-sync interval (default 1h) via sidecar API,
  store, background scheduler, and Network tab dropdown.
- Add unit tests for sync helpers, auto-sync scheduler, and store wiring.

Sync progress from the sidecar mixed fractional and percentage scales,
which made the bar appear stuck at 0.1%. Refresh completed too fast for
React to paint loading feedback.
@rinchen
rinchen marked this pull request as ready for review July 2, 2026 21:15
@rinchen
rinchen merged commit 6bb126d into main Jul 2, 2026
14 checks passed
@rinchen
rinchen deleted the 593/reticulum-sidecar-scaffold branch July 2, 2026 21:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add reticulum support

2 participants