code wiki / (root) / nx_restart_guard.nx

nx_restart_guard.nx

buildroot/runtime/nx_restart_guard.nx

4638 B62 linesdepth 0pulls 0 transitivereach 7 importersview sourcekind librarytopic restart
docsdependenciesstructsconstsfunctions

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

nx_restart_guard.nx nx_hostctl.nx nx_restart_guard_gate.nx nx_runsv.nx

imports: none

imported by: nx_hostctl.nxnx_restart_guard_gate.nxnx_runsv.nx

structs

none

consts

21const RG_DEF_INTERVAL_MS: i64 = 10000 // systemd StartLimitIntervalSec default (10s)
22const RG_DEF_BURST: i64 = 5 // systemd StartLimitBurst default
23const RG_DEF_BASE_MS: i64 = 100 // systemd RestartSec default (100ms) = our backoff base
24const RG_DEF_MAX_MS: i64 = 30000 // backoff ceiling (30s) so it never waits unboundedly
29const RG_HEALTH_RESET_MS: i64 = 60000

functions

42func rg_should_restart(ws: *i64, cnt: *i64, now: i64, cap_ms: i64, base_ms: i64) -> i64
54func rg_backoff_ms(restart_count: i64, base_ms: i64, max_ms: i64) -> i64
62func rg_remaining(cnt: i64, burst: i64) -> i64 { let r: i64 = burst - cnt; if r < 0 { return 0 } return r }