fix(seedbox): give the watchdog a bus to systemd, and stop passing off a daemon we watched die - #149
Merged
Merged
Conversation
…f a daemon we watched die Two defects kept leaving the box answering ECONNREFUSED despite #139–#141. 1. The watchdog could never reach systemd. `.torlnk-watchdog.sh` runs from cron, which has no login session, so `systemctl --user` failed on every invocation with "Failed to connect to user scope bus" — silently, since the call was `>/dev/null 2>&1`. The watchdog therefore skipped the restart it exists to perform and fell through to launching a bare unsupervised `--daemon`, which then fought the still-enabled `Restart=always` unit for the serve port; the loser crash-looped forever against a port it could never bind. Export XDG_RUNTIME_DIR/DBUS_SESSION_BUS_ADDRESS (resolved at run time via `id -u`, not baked in when the heredoc is written) so it talks to the lingering user manager. Also stop treating `systemctl restart` exiting 0 as proof the port is bound — restarts are async. Re-probe /health for ~12s and only fall back to an unsupervised daemon once systemd has demonstrably not taken over. 2. Provisioning reported success for a daemon it had already seen die. The script emits `stable fail` when torlink is gone within ~15s, but provisionTorlink derived `ok` from the `serve` step alone, so install-torlink returned 200, wired the fresh token into the account config, and showed a green "installed" — and the next status poll said ECONNREFUSED. Let the `stable` step veto success. Absent (older script output) still passes, and the probe now retries so one dropped packet cannot fail a healthy box. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
vu1nz Security Review0 finding(s) in PR #? No security issues found. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #139–#141, which each tried to keep torlink alive and each left the box still answering
ECONNREFUSED. Two concrete defects, both verified.1. The watchdog could never reach systemd
.torlnk-watchdog.shruns from cron. cron has no login session, sosystemctl --userfails with "Failed to connect to user scope bus" — and the call is>/dev/null 2>&1, so it failed silently on every run. Reproduced locally:So every 5 minutes the watchdog skipped the systemd restart it exists to perform and fell through to launching a bare, unsupervised
--daemon— which then fought the still-enabledRestart=alwaysunit for the serve port. Whichever lost crash-looped forever against a port it could never bind. The supervision added in #139 was being bypassed by the watchdog meant to back it up.Fix: export
XDG_RUNTIME_DIR/DBUS_SESSION_BUS_ADDRESSpointing at the lingering user manager.$(id -u)is escaped so it resolves when the watchdog runs, not when the heredoc is written (a bare$(id -u)in the unquoted heredoc would bake in the provisioning shell's uid).Also:
systemctl restartexiting 0 does not mean the port is bound — restarts are async. The oldrestart … && exit 0short-circuit treated a queued restart as success. Now it re-probes/healthfor ~12s and only falls back to an unsupervised daemon once systemd has demonstrably not taken over, so the two can't race.2. Provisioning reported success for a daemon it watched die
#141 added the
stablestep — re-probe after ~15s and report if torlink is already gone. ButprovisionTorlinkderivedokfrom theservestep alone, andRESULT|okis echoed unconditionally. Soinstall-torlinkreturned 200, wired the fresh token into the account config, and rendered a green "installed" even when the script had just reported the daemon was dead — and the next status poll saidECONNREFUSED. That's the reinstall-says-OK-then-unreachable loop.Fix: let
stable failveto success, so the route 502s with the steps (including the journal tail explaining what killed it) instead of saving a dead box. A missingstablestep — older script output — still passes. The probe now retries 3× as well, since it gates the whole install and one dropped packet must not fail a healthy box.Testing
systemctlcall (comment-stripped, so it asserts on commands not prose), uid resolved at run time, health re-probe between restart and fallback, retry loop, andprovisionTorlinkreturningok:false/token:nullonstable fail(+ back-compat when the step is absent).Note
This makes recovery work going forward; it does not revive a box that is down right now. That still needs
systemctl --user restart torlink-serve torlink-fileson the seedbox, or a re-run of the installer.