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

Manually merged
ai 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)
ssmithx added 2 commits 2026-07-04 16:33:30 +00:00
RNS's own rnodeconf utility (frequency/bandwidth/spreading-factor/coding-rate
read+write, firmware signature verification, board bootstrap) has been the
one tool that reliably diagnoses real RNode hardware — it's what finally
proved two live nodes were silently running at different spreading factors
(SF5 vs SF10, invisible from any of our own probe/logging code, and the
actual reason two correctly-flashed radios could detect each other's RF but
never decode a packet). Every node should have it, not just whichever one an
agent happened to build a throwaway venv on to debug a specific incident.

- reticulum-daemon/build.sh: also PyInstaller-package archy-rnodeconf
  alongside the existing archy-reticulum-daemon, same --collect-submodules/
  -d noarchive flags (same RNS.Interfaces __all__-glob requirement applies).
- scripts/deploy-to-target.sh: actually wire both packaged binaries into the
  live deploy path (neither was wired in before — a pre-existing gap noted
  in docs/RETICULUM-TRANSPORT-PROGRESS.md; this is why reticulum-daemon
  previously only worked via manual dev-venv setup on rsync-deployed nodes,
  not the ISO-imaged ones). Non-fatal on build/deploy failure — archipelago
  already falls back to its dev-venv path if the packaged binary is absent.
  Also installs the missing `python3.<minor>-venv` package when needed
  (same "ensurepip not available" gap hit manually twice this session).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Confirmed on the just-packaged binary: `archy-rnodeconf --info` printed
everything correctly, then crashed with NameError: name 'exit' is not
defined and returned exit code 1. rnodeconf.py's own graceful_exit() calls
the bare exit() builtin, which is only ever defined by site.py for
interactive Python — a frozen PyInstaller app skips that init, so any
bundled script relying on it hits this the moment it tries to quit cleanly,
after the real work already succeeded. Classic, well-documented PyInstaller
gotcha; the standard fix is a runtime hook pre-defining exit/quit as
sys.exit before the bundled script's own code runs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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.
ai manually merged commit e64e5615cb into worktree-reticulum-tcp-interop 2026-07-06 12:06:53 +00:00
Sign in to join this conversation.
No Reviewers
No Label
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: lfg2025/archy#71
No description provided.