Ship archy-rnodeconf as an OS-level tool on every node #71

Manually merged
chaum merged 0 commits from feat/reticulum-daemon-packaging into worktree-reticulum-tcp-interop 2026-07-06 12:06:53 +00:00
Collaborator

Summary

  • Packages RNS's own rnodeconf utility (frequency/bandwidth/spreading-factor/coding-rate read+write, firmware signature verification, board bootstrap) as a second PyInstaller single-file binary (archy-rnodeconf) alongside the existing archy-reticulum-daemon, built by the same reticulum-daemon/build.sh.
  • Wires both packaged binaries into scripts/deploy-to-target.sh's live deploy path. Neither was wired in before this — a pre-existing, documented gap (docs/RETICULUM-TRANSPORT-PROGRESS.md) that meant archy-reticulum-daemon only ever worked via a manually-built dev venv on rsync-deployed nodes, never via the packaged binary. Non-fatal on build/deploy failure (falls back to the existing dev-venv path).
  • Also installs the missing python3.<minor>-venv apt package when needed (the "ensurepip not available" gap hit manually twice while debugging this).
  • Fixes a real bug found immediately after the first successful build: archy-rnodeconf's bundled graceful_exit() calls the bare exit() builtin, which only exists in interactive Python (injected by site.py) — a frozen PyInstaller app skips that init, so the tool printed correct output and then crashed with NameError and exit code 1 on every invocation. Fixed with a small PyInstaller runtime hook pre-defining exit/quit as sys.exit.

Why this matters

rnodeconf is what actually diagnosed the real blocker in the stacked interop PR: two live nodes were silently configured at different LoRa spreading factors (SF5 vs SF10) — invisible to any of our own probe/logging code, since both radios could detect each other's RF energy but neither could decode a packet. No amount of software-level mesh debugging would have found this without a proper radio-config tool. Every node should have it built in, not just whichever one an agent happens to hand-build a venv on mid-incident.

Test plan

  • bash -n on both modified shell scripts
  • Deployed live via deploy-to-target.sh --live to archy-x250-exp; confirmed both archy-reticulum-daemon and archy-rnodeconf build and land at /usr/local/bin/, executable
  • archy-rnodeconf <port> --info exits 0 with no traceback (previously exited 1)
  • Used the packaged binary to align two real nodes' radio configs (869.525MHz/125kHz/SF8/CR5/17dBm) and confirmed bidirectional LXMF delivery afterward

🤖 Generated with Claude Code

## Summary - Packages RNS's own `rnodeconf` utility (frequency/bandwidth/spreading-factor/coding-rate read+write, firmware signature verification, board bootstrap) as a second PyInstaller single-file binary (`archy-rnodeconf`) alongside the existing `archy-reticulum-daemon`, built by the same `reticulum-daemon/build.sh`. - Wires **both** packaged binaries into `scripts/deploy-to-target.sh`'s live deploy path. Neither was wired in before this — a pre-existing, documented gap (`docs/RETICULUM-TRANSPORT-PROGRESS.md`) that meant `archy-reticulum-daemon` only ever worked via a manually-built dev venv on rsync-deployed nodes, never via the packaged binary. Non-fatal on build/deploy failure (falls back to the existing dev-venv path). - Also installs the missing `python3.<minor>-venv` apt package when needed (the "ensurepip not available" gap hit manually twice while debugging this). - Fixes a real bug found immediately after the first successful build: `archy-rnodeconf`'s bundled `graceful_exit()` calls the bare `exit()` builtin, which only exists in interactive Python (injected by `site.py`) — a frozen PyInstaller app skips that init, so the tool printed correct output and then crashed with `NameError` and exit code 1 on every invocation. Fixed with a small PyInstaller runtime hook pre-defining `exit`/`quit` as `sys.exit`. ## Why this matters `rnodeconf` is what actually diagnosed the real blocker in the stacked interop PR: two live nodes were silently configured at *different LoRa spreading factors* (SF5 vs SF10) — invisible to any of our own probe/logging code, since both radios could detect each other's RF energy but neither could decode a packet. No amount of software-level mesh debugging would have found this without a proper radio-config tool. Every node should have it built in, not just whichever one an agent happens to hand-build a venv on mid-incident. ## Test plan - [x] `bash -n` on both modified shell scripts - [x] Deployed live via `deploy-to-target.sh --live` to archy-x250-exp; confirmed both `archy-reticulum-daemon` and `archy-rnodeconf` build and land at `/usr/local/bin/`, executable - [x] `archy-rnodeconf <port> --info` exits 0 with no traceback (previously exited 1) - [x] Used the packaged binary to align two real nodes' radio configs (869.525MHz/125kHz/SF8/CR5/17dBm) and confirmed bidirectional LXMF delivery afterward 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Collaborator

Review (pre-merge): LGTM.

  • The PyInstaller runtime hook for exit()/quit() is the right fix and well documented — rnodeconf's graceful_exit() calling site.py-injected builtins is exactly the kind of frozen-app foot-gun that would have burned exit-code-scripting later.
  • build.sh degrades gracefully when rnodeconf.py moves in a future RNS release (warn + skip, not fail), and the deploy-script integration is non-fatal on both build and deploy, so it can't take down an otherwise-good deploy.
  • The python3.X-venv ensurepip bootstrap check in deploy-to-target.sh fixes a real Debian gotcha.

Merging into worktree-reticulum-tcp-interop so #70 carries both to main.

**Review (pre-merge):** LGTM. - The PyInstaller runtime hook for `exit()`/`quit()` is the right fix and well documented — rnodeconf's `graceful_exit()` calling site.py-injected builtins is exactly the kind of frozen-app foot-gun that would have burned exit-code-scripting later. - `build.sh` degrades gracefully when `rnodeconf.py` moves in a future RNS release (warn + skip, not fail), and the deploy-script integration is non-fatal on both build and deploy, so it can't take down an otherwise-good deploy. - The `python3.X-venv` ensurepip bootstrap check in `deploy-to-target.sh` fixes a real Debian gotcha. Merging into `worktree-reticulum-tcp-interop` so #70 carries both to `main`.
Collaborator

Reviewed and verified: ships archy-rnodeconf as an OS-level tool on every node and wires the daemon tools into deploy, including the frozen-exit() fix that made the tool exit 1 on success. Content was already merged into worktree-reticulum-tcp-interop via e64e5615 during the earlier pass — the Gitea queue hit a disk-full error mid-merge, so the PR status never updated. Reconciling status now; the changes reach main through #70.

Reviewed and verified: ships `archy-rnodeconf` as an OS-level tool on every node and wires the daemon tools into deploy, including the frozen-`exit()` fix that made the tool exit 1 on success. Content was already merged into `worktree-reticulum-tcp-interop` via e64e5615 during the earlier pass — the Gitea queue hit a disk-full error mid-merge, so the PR status never updated. Reconciling status now; the changes reach `main` through #70.
chaum manually merged commit e64e5615cb into worktree-reticulum-tcp-interop 2026-07-06 12:06:53 +00:00
Sign in to join this conversation.