code wiki / _hdl_build / nx_daemon_supervisor.nx
nx_daemon_supervisor.nx
buildroot/runtime/_hdl_build/nx_daemon_supervisor.nx
about
nx_daemon_supervisor.nx -- SOVEREIGN data-driven daemon supervisor. THE organ that makes daemon
lifecycle the ecosystem's job, not Claude's: a daemon is a REGISTRY ROW (data), not compiled into a
guard's target list and not nursed by a bash loop. Reads daemons.reg HOT every cycle, responds-health
probes each via hp_probe (SERVING / HUNG / REFUSED -- catches the wedge that PID/port checks miss), and
REVIVES the dead by fork+execve of the declared argv. 2-consecutive-fail discipline (in-memory, keyed by
name) so a single transient probe never triggers a relaunch. On HUNG (wedged, holding the port) it kills
the matching process first (the new instance could not bind otherwise), then launches. Zombies reaped
every cycle (wait4 WNOHANG). Add a daemon = a registry line + reload -- NO recompile, NO shell.
BOUNDED-VSZ DISCIPLINE (2026-07-16): every reusable buffer is allocated ONCE before the loop and reused
each cycle. The hot loop performs ZERO sys_mmap. The prior version mmap'd per cycle -- fatally, it read
the registry with sys_read_file, whose 4 GiB default cap (used whenever lseek can't size the file) maps
4 GiB of VSZ per cycle and never frees it. That drove VSZ to 116 GB in ~29 cycles, and under heuristic
overcommit fork() then returns -ENOMEM (proven: REVIVED ... pid=-12), so revives silently stopped. The
registry is now read into a fixed buffer via sys_read; ds_decw is temp-free; VSZ stays flat forever.
daemons.reg (pipe-delimited; lines starting # or blank ignored; args are space-separated, no spaces-in-args):
<name> | <cwd> | <argv0 [arg1 arg2 ...]> | <health_port> | <arm: revive|watch>
health = hp_probe(port): ANY HTTP response on 127.0.0.1:port = healthy (401/404 count).
revive = relaunch (and kill-the-wedge) on 2 consecutive non-SERVING probes; watch = detect+log only.
license_tier: ORIGINAL module: nishi-core.ops.daemon_supervisor
dependencies 4 imports · 0 importers
imports: nx_syscalls.nxnx_health_probe.nxnx_metrics_ring.nxnx_dsup_lib.nx
imported by: nobody (leaf or entry point)
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 26 | const DS_MAGIC_1024: i64 = 1024 |
| 27 | const DS_MAGIC_65536: i64 = 65536 |
| 28 | const DS_MAGIC_65535: i64 = 65535 |
| 29 | const DS_MAGIC_8000: i64 = 8000 |
| 30 | const DS_MAGIC_2048: i64 = 2048 |
| 31 | const DS_MAGIC_2047: i64 = 2047 |
| 32 | const DS_MAGIC_10000: i64 = 10000 |
| 34 | const DS_REG: *u8 = "/volume1/homes/elderwesto/nishihost/daemons.reg" as *u8 |
| 35 | const DS_LOG: *u8 = "/volume1/homes/elderwesto/nishihost/daemon_supervisor.log" as *u8 |
| 36 | const DS_CYCLE_SEC: i64 = 15 |
| 37 | const DS_MAX: i64 = 64 |
| 38 | const DS_PROBE_TMO: i64 = 3 |
| 39 | const DS_REGCAP: i64 = 65536 |
| 40 | const DS_STATUS_PORT: i64 = 18095 |
| 41 | const DS_SHARED_SZ: i64 = 8192 |
| 42 | const DS_ROW_MAX: i64 = 256 // one /status row's bound (name<=64 + verdict + counters); reserved BEFORE every row append |
| 43 | const DS_TRAILER_MAX: i64 = 256 // the progress trailer's bound (tick/row/last/stage ms); reserved so a tick is NEVER dropped for space |
| 44 | const DS_RING: *u8 = "metrics.ring" as *u8 // sovereign bounded TSDB the supervisor writes inline |
| 371 | const DS_PROC_CAP: i64 = 512 |
| 488 | const DS_LIFE_CONF: *u8 = "/volume1/homes/elderwesto/nishihost/knowledge/status/daemon_supervisor.conf" |
| 489 | const DS_LIFE_CAP: i64 = 4096 |
functions
| 46 | func ds_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } |
| 47 | func ds_p(s: *u8) -> i64 { sys_write(1, s, ds_slen(s)); return 0 } |
| 48 | func ds_log(s: *u8) -> i64 called by 4: ds_status_serverds_slow_rowds_cycle_logmain calls 4: sys_openat_appendsys_writeds_slensys_close |
| 59 | func ds_read_reg(buf: *u8, cap: i64) -> i64 |
| 70 | func ds_next_line(buf: *u8, pos: i64, end: i64) -> i64 called by 1: main |
| 77 | func ds_line_end(buf: *u8, pos: i64, end: i64) -> i64 called by 1: main |
| 83 | func ds_field(buf: *u8, ls: i64, le: i64, idx: i64, out: *u8, cap: i64) -> i64 called by 1: main |
| 110 | func ds_atoi(s: *u8) -> i64 |
| 115 | func ds_streq(a: *u8, b: *u8) -> i64 called by 1: main |
| 123 | func ds_launch(cwd: *u8, cmd: *u8) -> i64 |
| 172 | func ds_kill_wedged(needle: *u8) -> i64 |
| 236 | func ds_resolve_bin(cwd: *u8, cmd: *u8, out: *u8) -> i64 called by 1: main |
| 263 | func ds_exe_sweep(bin: *u8, mode: i64, dbuf: *u8, path: *u8, rl: *u8) -> i64 called by 1: main calls 9: ds_slensys_openat_rdsys_getdents64dirent_reclendirent_namesys_readlinkat+3 |
| 321 | func ds_exitcode(status: i64) -> i64 called by 1: ds_verify_reg |
| 326 | func ds_verify_reg() -> i64 |
| 372 | func ds_proc_read(path: *u8, buf: *u8, cap: i64) -> i64 |
| 382 | func ds_proc_path(dst: *u8, pid: i64, leaf: *u8) -> i64 |
| 391 | func ds_parent_line(body: *u8, blen: i64, pst: *u8, pwc: *u8, ppath: *u8) -> i64 called by 1: ds_status_server calls 6: ds_proc_readds_sappendds_stat_ppidds_decwds_proc_pathds_stat_state_at |
| 415 | func ds_status_server(shm: *u8) -> i64 |
| 490 | func ds_self_recycle(cyc: i64, life: i64) -> i64 called by 1: main |
| 495 | func ds_life_cycles() -> i64 |
| 513 | func ds_publish(shm: *u8, shq: *i64, stat: *u8, sw: i64, tb: *u8, tw: i64) -> i64 called by 1: main |
| 526 | func ds_slow_row(lb: *u8, nm: *u8, total: i64, a: i64, b: i64, c: i64) -> i64 |
| 537 | func ds_cycle_log(lb: *u8, rows: i64, ms: i64) -> i64 |
| 546 | func main() -> i64 |