nx_restart_guard.nx
buildroot/runtime/nx_restart_guard.nx
about
nx_restart_guard.nx -- SUPERVISOR CRASH-LOOP GUARD (closes hosting_research gap #4
"watchdog-restart-loop-guard", 3/0 CONFIRMED -- the root cause of the live 2026-06-16 nx_hostctl
crash-loop). A bedrock supervision primitive any supervisor (nx_hostctl / nx_keeper / nx_torrent_up)
composes so a daemon that crashes immediately on start is NOT respawned forever.
module: nishi-core.supervision.restart_guard
capability: DAEMON_ROBUSTNESS
INCUMBENT (benchmark, sourced via the Nishi researcher -> hosting_research.tsv): systemd unit restart
policy -- Restart=on-failure + RestartSec (wait before restart, default 100ms) + StartLimitBurst (N,
default 5) within StartLimitIntervalSec (T, default 10s): more than N restarts in T -> unit enters
"failed", systemd stops trying. We MATCH that burst-limit semantics, and EXCEED it on two measured axes:
(1) DETERMINISTIC / REPLAYABLE -- given the same restart timestamps, the SAME verdict every run, so a
supervision session can be replayed bit-for-bit (systemd's behavior depends on wall-clock + monotonic
boot time and cannot be replayed). The caller passes `now`; nothing reads a hidden clock here.
(2) EXPONENTIAL BACKOFF -- rg_backoff_ms doubles the wait each restart (capped), vs systemd's FIXED
RestartSec, so a flapping daemon backs off instead of hammering at a constant rate.
STATE per daemon (caller-owned, 2 words): ws[0] = window-start timestamp, cnt[0] = restarts in window.
dependencies 0 imports · 3 importers
imports: none
imported by: nx_hostctl.nxnx_restart_guard_gate.nxnx_runsv.nx
structs
| none |
consts
| 21 | const RG_DEF_INTERVAL_MS: i64 = 10000 // systemd StartLimitIntervalSec default (10s) |
| 22 | const RG_DEF_BURST: i64 = 5 // systemd StartLimitBurst default |
| 23 | const RG_DEF_BASE_MS: i64 = 100 // systemd RestartSec default (100ms) = our backoff base |
| 24 | const RG_DEF_MAX_MS: i64 = 30000 // backoff ceiling (30s) so it never waits unboundedly |
| 29 | const RG_HEALTH_RESET_MS: i64 = 60000 |
functions
| 42 | func rg_should_restart(ws: *i64, cnt: *i64, now: i64, cap_ms: i64, base_ms: i64) -> i64 called by 34: hc_guard_onehc_guard_readerhc_guard_galx_servehc_guard_galx_gwhc_guard_wiki_gwhc_guard_hub_gw+28 calls 1: rg_backoff_ms |
| 54 | func rg_backoff_ms(restart_count: i64, base_ms: i64, max_ms: i64) -> i64 |
| 62 | func rg_remaining(cnt: i64, burst: i64) -> i64 { let r: i64 = burst - cnt; if r < 0 { return 0 } return r } |