Files
archy/reticulum-daemon/README.md
T
archipelagoandClaude Opus 5 cc00884b98 docs: repair links left dangling by the operations-doc removal
Untracking the ops docs broke every reference to them. Repoints or removes
those references across README, architecture, ROADMAP, the archive index,
lifecycle TESTING, the hardening plan, and the security docs — pointing at
the issue tracker where a live task list was meant, and dropping the entry
entirely where it only existed to link an internal file.

Also fixes two pre-existing broken links found by validating every relative
link in the tracked docs:
- README linked docs/OPEN_SOURCE_READINESS.md, which never existed
  (underscores vs hyphens).
- reticulum-daemon/README.md linked a local Claude session plan at
  ../../.claude/plans/enchanted-strolling-rocket.md — outside the repo, and
  a path that would have shipped publicly pointing at nothing.

All relative links in tracked markdown now resolve: 0 broken.
audit-secrets.sh still 5/5.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 10:10:25 -04:00

3.2 KiB

reticulum-daemon

Host-supervised Reticulum (RNS) + LXMF bridge for Archipelago's Mesh tab. This is the Python side of the Reticulum transport work: archipelago spawns one of these per active Reticulum (RNode) radio, it owns the serial port, and the Rust mesh subsystem drives it over a Unix-socket JSON-RPC.

Why a daemon (not the Rust reticulum-rs crate): the canonical Python rns/lxmf guarantees interop with Sideband / NomadNet / MeshChat, and lets us derive the RNS identity from the existing Archy key (proven in spike_identity.py).

Layout

  • archy_rns_identity.py — derive a deterministic RNS Identity from the 32-byte Archy Ed25519 seed (identity_dir/node_key) via domain-separated HKDF. The node's LXMF destination hash is a stable function of the Archy identity.
  • spike_identity.pyPhase-0 gate #1 (no radio): proves that determinism.
  • reticulum_daemon.py — the daemon: RNS bring-up, LXMF router, announce handler, and the Unix-socket RPC. See its module docstring for the wire protocol.
  • requirements.txt — pinned rns==1.3.5, lxmf==1.0.1 (validated on Python 3.13).

Dev setup

python3 -m venv .venv
.venv/bin/pip install -r requirements.txt

Run the spike / smoke tests (no hardware)

.venv/bin/python spike_identity.py                     # gate #1: identity determinism
.venv/bin/python reticulum_daemon.py --check \
    --identity-key /path/to/node_key                   # print this node's dest hash
.venv/bin/python reticulum_daemon.py --selftest \
    --identity-key /path/to/node_key                   # bring up RNS+LXMF, no radio

Run against a real RNode (Phase-0 hardware gate, on .116 / .228)

.venv/bin/python reticulum_daemon.py \
    --identity-key /var/lib/archipelago/identity/node_key \
    --serial-port /dev/reticulum-radio \
    --socket /run/archy/reticulum.sock \
    --display-name "archy-228"

Then verify a two-node LXMF DM over LoRa and interop with a stock Sideband/MeshChat client (Phase-0 gates #2 and #3).

Packaging (Phase 1)

Ship as a PyInstaller single binary in the OTA next to /usr/local/bin/archipelago (no provision-time pip install). archipelago supervises it: start on RNode detect, kill on unplug/disable. The RPC socket and RNS config dir are archipelago-owned, 0600.

./build.sh   # → dist/archy-reticulum-daemon (~16M, fully standalone)

-d noarchive is required, not optional — see the comment in build.sh: RNS computes RNS.Interfaces.__all__ via a glob() against its own __file__ directory at import time, which only works when PyInstaller keeps modules as loose files instead of zipping them into the binary.

Status

Phase-0 gate #1 (identity determinism) passes, verified in both the dev venv and the packaged binary (same dest hash). The signed-identity announce (ARCHY:2:{ed}:{x25519} in _announce_app_data, via --archy-ed-pubkey-hex/--archy-x25519-pubkey-hex) is wired and the Rust side (reticulum.rs) already passes the node's real keys through. Packaging is done and verified standalone. What's left is entirely hardware-dependent: the live LoRa message path (Phase-0 gates #2/#3) needs a real RNode-flashed board.