Phase 0 gates #2/#3 (two-node LXMF-over-LoRa, external Sideband interop) passed on real hardware (.116's flashed Heltec V3 RNode <-> a phone-flashed RNode running Sideband) — RNS announce, encrypted DM round-trip, and contact binding all verified live. Fixed two bugs found in the process: the Reticulum send path wasn't stamping outbound messages as E2E despite LXMF being unconditionally encrypted, and the per-message transport pill collapsed Meshcore/Meshtastic into one generic "lora" color instead of distinguishing the three radio transports. Built on top of that link: a Columba-style image/file send experience — compression-quality presets with a real transfer-time estimate (mesh.transport-advice, now device-throughput-aware), receive-side thumbnail previews + auto-render for already-local attachments, and async voice messages, all reusing the existing ContentRef/ContentInline attachment pipeline. The headline addition is genuine RNS Resource transfer support (daemon-side RNS.Link + RNS.Resource, Rust-side send_resource/resource_recv plumbing, a new "resource-mesh" transport-advice tier) so compressed photos up to 2MB now actually transfer over LoRa for Reticulum peers instead of always falling back to Tor past the small inline-chunk cap. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
67 lines
3.2 KiB
Markdown
67 lines
3.2 KiB
Markdown
# reticulum-daemon
|
|
|
|
Host-supervised **Reticulum (RNS) + LXMF** bridge for Archipelago's Mesh tab. This is
|
|
the Python side of the [Reticulum transport plan](../../.claude/plans/enchanted-strolling-rocket.md):
|
|
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.py` — **Phase-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
|
|
```sh
|
|
python3 -m venv .venv
|
|
.venv/bin/pip install -r requirements.txt
|
|
```
|
|
|
|
## Run the spike / smoke tests (no hardware)
|
|
```sh
|
|
.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)
|
|
```sh
|
|
.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.
|
|
|
|
```sh
|
|
./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.
|