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
Conversation
- 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.
Contributor
Code Coverage OverviewLanguages: JavaScript JavaScript / code-coverage/vitestThe overall coverage in the Show a code coverage summary of the most impacted files.
Updated |
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
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
2 tasks
- 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.
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
marked this pull request as ready for review
July 2, 2026 21:15
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds Reticulum as mesh-client’s third protocol tab alongside Meshtastic and MeshCore. The work completes #593: a headless Rust
mesh-client-reticulumsidecar (AGPL, axum HTTP + WebSocket), Electronreticulum:*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:
ble:///tcp://Wi-Fi), RNode Multi, BLE Peer mesh; offline/stale-port alerts; config audit/repairCMD_WIFI_*→ addtcp://host:7633on Connection)check:environment, Reticulum sidecar CI matrix (stub +rns-stack), vendored rsReticulum packet-tap patchFixes #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):
ratspeak-tauricommands — seedocs/reticulum-sidecar-ipc.md../../rsReticulum,../../rsLXMF(same as Ratspeak); CI clones outside workspace whenactions/checkoutpath limits applyratspeak.identity.v2backups; local passcode identity vault (scrypt, encrypt export JSON at rest)ratspeak.chat.v2message hashes;message_hashSQLite column; delivery status eventsble:///tcp://Wi-Fi), RNode Multi, BLE Peer meshbtleplug) on different peripherals; MAC registry + scan mutextcp://host[:7633]viarns-rnode-tcpfeatureNot yet at Ratspeak parity (tracked / out of scope):
GET /api/v1/voice/statusstub; voice clips in chat work, but no rsLXST call UIGET /api/v1/games/statusstub; nolrgp-rssiblingrns-stack— CRUD writes config; stack restart required for live RNSRust sidecar (
reticulum-sidecar/)mesh-client-reticulum— axum HTTP + WebSocket event bridgeGET/DELETE /api/v1/packets,wire_packetWS events), voice/games status stubs, BLE scan/availability, serial port enumerationrns-stack;rns-ble(BLE Peer +ble://RNode);rns-rnode-tcp(Wi-Fitcp://RNode); dev auto-build enablesrns-stack,rns-blewhen Ratspeak siblings presentblocking_readpanics); timed transport control queries (8s cap) with cache fallback; LinkDeliveryManager for async LXMF (Direct vs Propagated); Nomad page/file viaLinkClient::querywith identity-hash resolution; LXMF attachment send/receive; propagation bridge for live syncregister_packet_tapfrom rsReticulum →wire_packetWS + sniffer buffer; vendored patchreticulum-sidecar/patches/rsReticulum-packet-tap.patch(upstream ratspeak/rsReticulum#10) applied in CI beforerns-stackbuildsextra_sectionsround-trip, 4 MiB body limit, mnemonic stripped on disk, localhost CORS, loopback bind guardGetInterfaceStatsso failed RNode/KISS interfaces remain visible; preset expansion/repair on bootstrap; coordinated RNode RF profiles;enabledpreserved when live stats omit offline BLE RNodesrns-stackon linux x64, macOS arm64, Windows x64 + arm64;libdbus-1-devfor Ubunturns-ble;cargo testin workflow;electron-builderextraResourcespnpm run reticulum:sidecar:build; rebuild when Rust sources newer than binary; sidecar survives dev HMR remountsElectron main process + preload
ReticulumSidecarManager— spawn, health poll, serialized start (coalesce concurrentreticulum:start), HTTP proxy GET/POST/PUT/DELETE, WS event bridge to rendererreticulum:*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 dialogdb:clearReticulumMessages, chat outboxattempt_countpersistenceble-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 conflictsmqtt-client-teardown.ts— absorbs late mqtt.js connack/keepalive errors on force-disconnect (fixes uncaught main-process exceptions)validateIpcSenderon reticulum/vault/DB/BLE handlers; attachment path jail; proxy body/config read caps; vault 8-char passcode + unlock rate limiting; boundedreadUtf8FileBounded(TOCTOU fix)Renderer — Reticulum protocol integration
meshProtocol,ProtocolCapabilities, theme (amber),useReticulumRuntime,useReticulumSession, SQLite migrations (v39+),hydrateIdentityStoresFromDb,useReticulumPanelActionshasReticulum*,hasLxmfDeliveryStatus, etc.;check-protocol-string-gatesextended; no rawprotocol === 'reticulum'checks in new codereticulumIngest→messageStore;nodeStorecanonical for Reticulum UI;reticulumPeerStorefor favorites/names; deduplicated sidecar reads (reticulumSidecarReads), peer display names, chat last-read migration, unread countingwire_packet, LXMF delivery, peers_updated, propagation sync_progress, interface.state; autostart/reconnect on unexpected stop; debounced event refreshes (2s) during Nomad browsingTab layout & panel architecture
ReticulumStackPanelReticulumNetworkPanelReticulumAdminPanelWifiConfig.tsx), factory reset danger zoneReticulumPeerListPanelChatPanelReticulumTopologyPanelNomadNetworkPanelReticulumDiagnosticEngineRawPacketLogPanelwire_packetvariants; Graph tab hidden for ReticulumMeshtastic/MeshCore Radio, Map, Modules, and Graph tabs restored after Reticulum capability gating initially hid them (
appTabMappingsOR rules fixed).Connection tab — interface management
ReticulumInterfaceDevicePickerModalfor serial, BLE Peer, BLE RNode; Electron serial picker with known/last-used port labelsble://RNode,tcp://host[:7633]Wi-Fi RNode (not TCP Client 4242), BLE Peer mesh, Auto, TCP/UDP, KISS, Pipe, I2P, RNode MultireticulumLocalInterfaceHealthclassifier (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 changesGET /config/audit,POST /config/repair; diagnostics merge; Connection hints with repair/disable/deep-link edit viareticulumUiStoreLXMF chat (DM-only)
received_via/sent_viacolumns, sidecar egress classification)lxmf_outbound_status/LinkDeliveryManagerevents; propagation-node badge and toast when offline without PNratspeak.chat.v2reply hashes; dismiss closed DM tabs across restarts; clear DM history via App danger zone (db:clearReticulumMessages)[file:…]rendering; save/show-in-folder IPC; chunked uploadChatComposer, playback/cacherns-stackpathchatOutboxDrain)SearchModalProtocolCapabilities.lxmfPayloadLimitPeers, contacts & identity
NodeListPanelfork): path, probe, favorite, open DM, copy hash (Electron clipboard IPC)reticulumPeerStore+ SQLite destination persistence;peers_updatedWS after path/probe; footer peer count synced from path tableratspeak.identity.v2), export (passphrase required), multi-identity switcher, announce interval, clear announces, profile icons from LXMFFIELD_ICON_APPEARANCE, blocked contacts, identity activity storeNomad Network browser
nomadnetwork.nodeannounces; persists nodes withidentity_hashforLinkClient::querymicron-parser+NomadMicronPageView(DOMPurify); source toggle; in-page and cross-node navigation/file/fetches with base64 IPC round-trip; transport-aware timeouts (TCP 45s, RF hop-scaled up to 180s)networkegress when interfaces list empty; proxy timeout uses max(egress, RF) so RF page fetches are not aborted at 47sNetwork topology graph (Reticulum)
buildReticulumViaHashEdgesfor hub-to-leaf edges when API returns nodes but empty edges arrayWire packet sniffer & Stats (all protocols)
wire_packetWS →rawPacketsstore;clearRawPacketsIPCwire_packetappends batched per animation frameanchorTo: 'end',followOnAppend); unpin on scroll-up/wheel/expand; preserve anchor when reading historyrawPacketContentKey/getItemKey— no duplicate React keys for identical captureshandleSidecarEventsubscription inReticulumStackPanel)RawPacketLogReticulum variants; Graph tab removed for ReticulumRNode firmware flasher & Wi-Fi provisioning (Admin tab)
catalogKeydedup for colliding EEPROM products (LilyGO T3S3 vs RNode)WifiConfig.tsx— station/AP/off via KISSCMD_WIFI_*; then addtcp://host:7633interface on Connection tabesptool-js,@zip.js/zip.js,js-md5; firmware catalog generator scriptDiagnostics, config audit & tooltips
ReticulumDiagnosticEngine— Reticulum-native rows (local stale-port, local-offline, config audit findings); no LoRa hop-goblin semanticsDiagnosticsPingPanelreplaces Reticulum-specific ping panel; duplicate packet browser removed from Diagnostics (Sniffer covers packets)GlobalInstantTooltip— app-wide instant tooltips for nativetitleattributes;HelpTooltipshares positioningMeshtastic / MeshCore peer communication graph
buildMeshPeerTopologyGraph— self center → direct relay hubs → distant peers; 90-node budget (later 48), hop filters, relay resolution from path/neighbor infouseSvgPanZoom), hover/focus tooltips, density-aware sizing, edge dimming on inspect; hide permanent labels on dense graphsBLE — triple-protocol coexistence & dual-radio Noble
Triple-protocol (Meshtastic + MeshCore + Reticulum):
BleCoexistenceCoordinator: per-MAC ownership registry, scan-only mutex, Noble pause/resume for Reticulum scans without dropping GATTrns-blesidecar feature;BlePeerInterfacewith seed addresses;GET /api/v1/ble/scan; pause enabled BLE interfaces during picker scanDual-radio Noble (Meshtastic + MeshCore on different BLE devices):
meshcoreDualNobleBleInitcoordinator: primary/secondary startup order frommesh-client:protocollocalStorage;withNobleBleConnectMutexnarrowed to GATT + handshake onlyApp.tsxuseLayoutEffect(before ConnectionPanel auto-connect); secondary waits onawaitNobleBlePrimaryAutoConnectSettled()meshcoreEverConfiguredRefhasMqttConnectionPanel, header MQTT status while RF connecting, deferred MQTT auto-launch until JWT/password readyInternationalization (repo-wide audit)
Phase 1 — Reticulum i18n + quality (early commits):
check-i18n-quality.mjsReticulum false-friend rules (stack/stop, enable/edit, probe, sidecar-stroller, propagation, DM copy)Phase 2 — prune unused keys:
scripts/i18n-unused-keys.mjs,scripts/prune-i18n-unused.mjs, testscheck:i18nnow fails on unused English keys (skipped incheck:i18n:branch)Phase 3 — hardcoded English sweep:
meshcoreMessageI18nhelper,translateReticulumDiagnosticCause,diagnosticsLabelsfor routing portsPROTECTED_BRANDS, protocol-token checks, dynamic-key registration, locale brand repair scriptaria-labels,InactiveProtocolNotifierBLE owner protocol namesCI, build & developer experience
rns-stackmatrix; Ratspeak sibling clone outside workspace; rsReticulum packet-tap patch;libdbus-1-devon Ubuntu; Windows serial port vec type annotationpnpm run check:environment—scripts/check-environment.mjsprobes Git, Node, pnpm,node_modules, OS-native build deps; bootstrap beforepnpm install; documented in CONTRIBUTING + AGENTSpnpm installon@jsrtemp-dir races (scripts/flatpak-pnpm-install.mjs)mkdtemp), MQTT schedule import orderingchore: bump depscommits; Flatpak Electron archive syncreticulum-sidecar/ignored (no TOML parser); Rusttarget/gitignoredDocumentation
docs/reticulum.md— Connection/Network/Admin split, interface CRUD, RNode Wi-Fi workflow, Ratspeak interopdocs/reticulum-sidecar-ipc.md— full REST/WS contractdocs/troubleshooting.md— sidecar rebuild, BLE coexistence, Wi-Fi offline, Nomad timeouts, proxyGet stormsdocs/credits.md— Ratspeak attributioncheck:environmentfresh-clone guidanceCross-protocol fixes & polish (non-Reticulum)
sendToRadioFrameresolvesmqtt-client-teardownabsorbs late connack errors; MeshCore header MQTT status parity[ReticulumIPC]prefixanimate-pulse(WCAG contrast); pulse on dots/icons onlyTest plan
Reticulum core
pnpm run reticulum:sidecar:build(Rust + Ratspeak siblings) — stub andrns-stack,rns-ble,rns-rnode-tcpbuildsble://RNode,tcp://Wi-Fi, BLE Peer, TCP)tcp://interfaceratspeak.chat.v2reply threading; importratspeak.identity.v2backupReticulum features
wire_packetcapture, pause-to-inspect, scroll pinning, no duplicate rowsBLE coexistence
Meshtastic / MeshCore regression
i18n & CI
pnpm run check:i18n— no unused English keys; locale quality rules passpnpm run check:environmenton fresh clonepnpm run lint,pnpm run typecheck,pnpm run test:runPlatforms
Commits audited
87 commits on PR #596 (including 1 merge-from-main), audited via
gh pr view 596commit messages and per-commitgit show --statreview.Trivial one-line Reticulum compile/typo fixes are folded into the theme rows above rather than listed individually.