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
## 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)
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`.
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-interop2026-07-06 12:06:53 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
rnodeconfutility (frequency/bandwidth/spreading-factor/coding-rate read+write, firmware signature verification, board bootstrap) as a second PyInstaller single-file binary (archy-rnodeconf) alongside the existingarchy-reticulum-daemon, built by the samereticulum-daemon/build.sh.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 meantarchy-reticulum-daemononly 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).python3.<minor>-venvapt package when needed (the "ensurepip not available" gap hit manually twice while debugging this).archy-rnodeconf's bundledgraceful_exit()calls the bareexit()builtin, which only exists in interactive Python (injected bysite.py) — a frozen PyInstaller app skips that init, so the tool printed correct output and then crashed withNameErrorand exit code 1 on every invocation. Fixed with a small PyInstaller runtime hook pre-definingexit/quitassys.exit.Why this matters
rnodeconfis 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 -non both modified shell scriptsdeploy-to-target.sh --liveto archy-x250-exp; confirmed botharchy-reticulum-daemonandarchy-rnodeconfbuild and land at/usr/local/bin/, executablearchy-rnodeconf <port> --infoexits 0 with no traceback (previously exited 1)🤖 Generated with Claude Code
Review (pre-merge): LGTM.
exit()/quit()is the right fix and well documented — rnodeconf'sgraceful_exit()calling site.py-injected builtins is exactly the kind of frozen-app foot-gun that would have burned exit-code-scripting later.build.shdegrades gracefully whenrnodeconf.pymoves 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.python3.X-venvensurepip bootstrap check indeploy-to-target.shfixes a real Debian gotcha.Merging into
worktree-reticulum-tcp-interopso #70 carries both tomain.Reviewed and verified: ships
archy-rnodeconfas 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 intoworktree-reticulum-tcp-interopvia 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 reachmainthrough #70.