code wiki / _hdl_build / nx_daemon_supervisor.nx

nx_daemon_supervisor.nx

buildroot/runtime/_hdl_build/nx_daemon_supervisor.nx

48630 B828 linesdepth 6pulls 8 transitivereach 0 importersview sourcekind tooltopic daemon
docsdependenciesstructsconstsfunctions

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

nx_syscalls.nx nx_health_probe.nx nx_metrics_ring.nx nx_dsup_lib.nx nx_daemon_supervisor.nx

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

main ds_log sys_openat_append sys_write ds_slen sys_close sys_mmap nxa_die sys_write ↻ sys_exit nxa_lock_take nxa_lock_addr sys_write ↻ nxa_lock_give nxa_lock_addr ↻ nxa_report_overrun sys_write ↻ nxa_dump_printable sys_write ↻ nxa_dump_sizes sys_write ↻ sys_mmap_shared sys_fork ds_status_server sys_socket sys_exit ↻ sys_mmap ↻ sys_setsockopt hp_sockaddr sys_bind ds_log ↻ sys_listen sys_accept sys_set_socket_timeout sys_mmap ↻ sys_setsockopt ↻ sys_munmap sys_read ds_parent_line ds_proc_read

structs

none

consts

26const DS_MAGIC_1024: i64 = 1024
27const DS_MAGIC_65536: i64 = 65536
28const DS_MAGIC_65535: i64 = 65535
29const DS_MAGIC_8000: i64 = 8000
30const DS_MAGIC_2048: i64 = 2048
31const DS_MAGIC_2047: i64 = 2047
32const DS_MAGIC_10000: i64 = 10000
34const DS_REG: *u8 = "/volume1/homes/elderwesto/nishihost/daemons.reg" as *u8
35const DS_LOG: *u8 = "/volume1/homes/elderwesto/nishihost/daemon_supervisor.log" as *u8
36const DS_CYCLE_SEC: i64 = 15
37const DS_MAX: i64 = 64
38const DS_PROBE_TMO: i64 = 3
39const DS_REGCAP: i64 = 65536
40const DS_STATUS_PORT: i64 = 18095
41const DS_SHARED_SZ: i64 = 8192
42const DS_ROW_MAX: i64 = 256 // one /status row's bound (name<=64 + verdict + counters); reserved BEFORE every row append
43const DS_TRAILER_MAX: i64 = 256 // the progress trailer's bound (tick/row/last/stage ms); reserved so a tick is NEVER dropped for space
44const DS_RING: *u8 = "metrics.ring" as *u8 // sovereign bounded TSDB the supervisor writes inline
371const DS_PROC_CAP: i64 = 512
488const DS_LIFE_CONF: *u8 = "/volume1/homes/elderwesto/nishihost/knowledge/status/daemon_supervisor.conf"
489const DS_LIFE_CAP: i64 = 4096

functions

46func ds_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
47func ds_p(s: *u8) -> i64 { sys_write(1, s, ds_slen(s)); return 0 }
48func ds_log(s: *u8) -> i64
59func ds_read_reg(buf: *u8, cap: i64) -> i64
called by 1: main calls 3: sys_openat_rdsys_readsys_close
70func ds_next_line(buf: *u8, pos: i64, end: i64) -> i64
called by 1: main
77func ds_line_end(buf: *u8, pos: i64, end: i64) -> i64
called by 1: main
83func ds_field(buf: *u8, ls: i64, le: i64, idx: i64, out: *u8, cap: i64) -> i64
called by 1: main
110func ds_atoi(s: *u8) -> i64
115func ds_streq(a: *u8, b: *u8) -> i64
called by 1: main
123func ds_launch(cwd: *u8, cmd: *u8) -> i64
172func ds_kill_wedged(needle: *u8) -> i64
236func ds_resolve_bin(cwd: *u8, cmd: *u8, out: *u8) -> i64
called by 1: main
263func ds_exe_sweep(bin: *u8, mode: i64, dbuf: *u8, path: *u8, rl: *u8) -> i64
321func ds_exitcode(status: i64) -> i64
called by 1: ds_verify_reg
326func ds_verify_reg() -> i64
372func ds_proc_read(path: *u8, buf: *u8, cap: i64) -> i64
382func ds_proc_path(dst: *u8, pid: i64, leaf: *u8) -> i64
called by 1: ds_parent_line calls 2: ds_sappendds_decw
391func ds_parent_line(body: *u8, blen: i64, pst: *u8, pwc: *u8, ppath: *u8) -> i64
415func ds_status_server(shm: *u8) -> i64
490func ds_self_recycle(cyc: i64, life: i64) -> i64
called by 1: main
495func ds_life_cycles() -> i64
513func ds_publish(shm: *u8, shq: *i64, stat: *u8, sw: i64, tb: *u8, tw: i64) -> i64
called by 1: main
526func ds_slow_row(lb: *u8, nm: *u8, total: i64, a: i64, b: i64, c: i64) -> i64
537func ds_cycle_log(lb: *u8, rows: i64, ms: i64) -> i64
546func main() -> i64