code wiki / _hdl_build / nx_hostctl.nx

nx_hostctl.nx source

↩ module page · 3750 lines · 262144 B

1// nx_hostctl.nx -- the PURE-NISHI host control plane (operator: "no .sh or any non nishi stuff in the 2// mix... pure enterprise S-class hosting and deployment"). Retires nishi_supervisor.sh. Two subcommands, 3// argv-driven, all via sovereign syscalls -- NO shell, NO cp/mv/pkill: 4// supervise : fork/execve the sites daemon, record its PID, wait4; respawn on death (crash OR deploy-kill). 5// deploy : atomically publish the staged *.new artifacts (sys_renameat), +x the new binary (nx_chmod), 6// then nx_kill the running daemon -> supervise respawns the NEW binary. Brief restart only 7// (FD-passing/SCM_RIGHTS is the future zero-downtime rung). CONTENT hot-update needs NO deploy: 8// the router serves the doc-root file per request, so pushing index.html is live next request. 9// selftest : LOCAL Engineer gate of the deploy primitives (atomic rename + pidfile + kill) -- no NAS. 10// license_tier: ORIGINAL 11// 12// SUPERVISION UPGRADE 2026-06-16 (closes hosting_research gaps #5 health-probe + #4 restart-loop-guard): 13// cmd_supervise no longer trusts PID-liveness alone. For PLAINTEXT daemons it BLACK-BOX health-probes the 14// port (nx_health_probe) and restarts a HUNG daemon (process up + bound but not serving) -- the failure 15// systemd PID-liveness and a LISTEN check both miss. Every restart (crash OR hang) is gated by the 16// crash-loop guard (nx_restart_guard) so a daemon that keeps dying/wedging backs off instead of spinning. 17// The TLS daemon (sites.elf:8443) stays PID-only -- a plaintext GET to a TLS port would false-HUNG; a 18// TLS-aware serving probe is the next rung (noted, not faked). 19import "nx_syscalls.nx" 20import "nx_file_install.nx" 21import "nx_connect.nx" // bounded connect: a raw sys_connect hangs ~127s on a black-holed host 22import "nx_health_probe.nx" // hp_probe / HP_HUNG -- real serving health 23import "nx_restart_guard.nx" // rg_should_restart / rg_backoff_ms -- crash-loop guard 24import "nx_os_introspect.nx" // pon_port_owner_pid -- the os-axis /proc perception rung (who REALLY holds a port) 25import "nx_fd_hygiene.nx" // nx_fd_scrub_inherited -- drop inherited fds so a leaked listen socket never propagates into forked daemons 26import "nx_proc_snapshot.nx" // ps_alive_or_direct / ps_refresh -- ONE /proc walk per poll (seq1318) 27import "nx_buildrun_verdict_lib.nx" // bv_verdict -- did THIS buildrun produce the artifact, or is _build/ a previous build? (2026-09-05) 28// seq961: how long a crawl may show NO log progress before the guard treats it as stalled and reaps it. 29// 600s is far above a polite fetch cycle (a healthy crawl writes a TLS trace per page) and far below the 30// hours a wedged lock would otherwise cost. Data-driven threshold, not a magic number buried in a branch. 31const HC_CRAWL_STALE_S: i64 = 600 32const HC_MAGIC_65536: i64 = 65536 33const HC_MAGIC_8192: i64 = 8192 34const HC_MAGIC_60000: i64 = 60000 35const HC_MAGIC_300000: i64 = 300000 36const HC_MAGIC_8791: i64 = 8791 37const HC_MAGIC_5000: i64 = 5000 38const HC_MAGIC_5000000: i64 = 5000000 39const HC_MAGIC_300000000: i64 = 300000000 40const HC_MAGIC_4096: i64 = 4096 41const HC_MAGIC_8443: i64 = 8443 42const HC_MAGIC_18791: i64 = 18791 43const HC_MAGIC_18190: i64 = 18190 44const HC_MAGIC_18090: i64 = 18090 45const HC_MAGIC_18095: i64 = 18095 // nx_daemon_supervisor /status (the fleet supervisor's liveness surface) 46const HC_MAGIC_8080: i64 = 8080 47const HC_MAGIC_18793: i64 = 18793 48const HC_MAGIC_18098: i64 = 18098 49const HC_MAGIC_18456: i64 = 18456 50const HC_MAGIC_8456: i64 = 8456 51const HC_MAGIC_15274: i64 = 15274 52const HC_MAGIC_18465: i64 = 18465 53const HC_MAGIC_18466: i64 = 18466 54const HC_MAGIC_8446: i64 = 8446 55const HC_MAGIC_18096: i64 = 18096 56const HC_MAGIC_16384: i64 = 16384 57const HC_MAGIC_7443: i64 = 7443 58const HC_MAGIC_18792: i64 = 18792 59const HC_MAGIC_9091: i64 = 9091 60const HC_MAGIC_8097: i64 = 8097 61const HC_MAGIC_18795: i64 = 18795 62const HC_MAGIC_18794: i64 = 18794 63const HC_MAGIC_18796: i64 = 18796 64const HC_MAGIC_18797: i64 = 18797 65const HC_MAGIC_18099: i64 = 18099 66const HC_MAGIC_18097: i64 = 18097 67const HC_MAGIC_18120: i64 = 18120 68const HC_MAGIC_7702: i64 = 7702 69const HC_MAGIC_6881: i64 = 6881 70const HC_MAGIC_8445: i64 = 8445 71const HC_MAGIC_8027: i64 = 8027 72const HC_MAGIC_8028: i64 = 8028 73const HC_MAGIC_8102: i64 = 8102 74const HC_MAGIC_8029: i64 = 8029 75const HC_MAGIC_8055: i64 = 8055 76const HC_MAGIC_8447: i64 = 8447 77const HC_MAGIC_1195: i64 = 1195 78const HC_MAGIC_5080: i64 = 5080 79const HC_MAGIC_15000: i64 = 15000 80const HC_MAGIC_1500: i64 = 1500 81const HC_MAGIC_2500: i64 = 2500 82const HC_MAGIC_4000: i64 = 4000 83const HC_MAGIC_1200: i64 = 1200 84const HC_MAGIC_2000: i64 = 2000 85const HC_MAGIC_131072: i64 = 131072 86const HC_MAGIC_3600000: i64 = 3600000 87const HC_MAGIC_262144: i64 = 262144 88const HC_MAGIC_2048: i64 = 2048 89const HC_MAGIC_40302: i64 = 40302 90const HC_MAGIC_1048576: i64 = 1048576 91const HC_MAGIC_16000: i64 = 16000 92const HC_MAGIC_16777232: i64 = 16777232 93const HC_MAGIC_16777216: i64 = 16777216 94const HC_MAGIC_90000: i64 = 90000 95const HC_MAGIC_1024: i64 = 1024 96const HC_MAGIC_65535: i64 = 65535 97const HC_MAGIC_8191: i64 = 8191 98const HC_MAGIC_2026: i64 = 2026 99const HC_MAGIC_25000: i64 = 25000 100const HC_MAGIC_86400000: i64 = 86400000 101const HC_MAGIC_3000: i64 = 3000 102const HC_MAGIC_6000: i64 = 6000 103 104const HC_ELF: *u8 = "/volume1/homes/elderwesto/nishihost/sites.elf" as *u8 105const HC_ELF_NEW: *u8 = "/volume1/homes/elderwesto/nishihost/sites.elf.new" as *u8 106const HC_ELF_PREV: *u8 = "/volume1/homes/elderwesto/nishihost/sites.elf.prev" as *u8 // rollback copy (never-brick) 107const HC_REDIRECT: *u8 = "/volume1/homes/elderwesto/nishihost/redirect.elf" as *u8 108// time-machine archive server :18801 -- serves the reconstructed page3.com/megastar.co.uk archives and 109// their local media out of web_assets/ (path traversal rejected). PORT CHOSEN AGAINST THIS REGISTRY, not 110// against the live process list: it was first written for :8080 (held by redirect.elf, above) and then 111// :18795, which is held by hc_guard_gen_orch -- a daemon that BACKS OFF until gendeploy'd and therefore 112// does NOT appear in `nx_hostctl status`. A port reserved by a not-currently-running supervised daemon is 113// invisible in the live listener list, so that collision would have surfaced only when gen was deployed. 114const HC_ARCSRV: *u8 = "/volume1/homes/elderwesto/nishihost/nx_archive_server.elf" as *u8 115const HC_VROOM: *u8 = "/volume1/homes/elderwesto/nishihost/nx_vroom_daemon.elf" as *u8 116const HC_SNIROUTER:*u8 = "/volume1/homes/elderwesto/nishihost/sni_router.elf" as *u8 // sovereign public-edge SNI router (:7443, TLS passthrough) 117const HC_XLATE: *u8 = "/volume1/homes/elderwesto/nishihost/nx_translate_daemon.elf" as *u8 118const HC_XLATE_NEW: *u8 = "/volume1/homes/elderwesto/nishihost/nx_translate_daemon.sov.elf.new" as *u8 119const HC_XLATE_PREV: *u8 = "/volume1/homes/elderwesto/nishihost/nx_translate_daemon.elf.prev" as *u8 120const HC_SIGKILL: i64 = 9 121const HC_CLOCK: *u8 = "/volume1/homes/elderwesto/nishihost/nx_clock_tickless.elf" as *u8 // the ONE consolidated scheduler (const, NOT an inline argv literal -- see the .nx gotcha note below) 122const HC_CONF: *u8 = "/volume1/homes/elderwesto/nishihost/sites.conf" as *u8 123const HC_CONF_NEW: *u8 = "/volume1/homes/elderwesto/nishihost/sites.conf.new" as *u8 124const HC_DOCROOT: *u8 = "/volume1/homes/elderwesto/nishihost/sites/andelinwest" as *u8 125const HC_INDEX: *u8 = "/volume1/homes/elderwesto/nishihost/sites/andelinwest/index.html" as *u8 126const HC_INDEX_NEW:*u8 = "/volume1/homes/elderwesto/nishihost/andelinwest_index.html.new" as *u8 127const HC_PID: *u8 = "/volume1/homes/elderwesto/nishihost/sites.pid" as *u8 128// TLS cert paths (sites.elf loads these at startup; certswap installs a renewed cert + restarts to reload) 129const HC_CERT_LIVE: *u8 = "/volume1/homes/elderwesto/nishihost/certs/le_fullchain.der" as *u8 130const HC_CERT_NEW: *u8 = "/volume1/homes/elderwesto/nishihost/certs/le_fullchain.der.new" as *u8 131const HC_CERT_BAK: *u8 = "/volume1/homes/elderwesto/nishihost/certs/le_fullchain.der.bak" as *u8 132const HC_KEY_LIVE: *u8 = "/volume1/homes/elderwesto/nishihost/certs/le_ecdsa_key.bin" as *u8 133const HC_KEY_NEW: *u8 = "/volume1/homes/elderwesto/nishihost/certs/le_ecdsa_key.bin.new" as *u8 134const HC_KEY_BAK: *u8 = "/volume1/homes/elderwesto/nishihost/certs/le_ecdsa_key.bin.bak" as *u8 135// the auth'd library reader (S-class permanence: supervise it like every other sovereign daemon) 136const HC_READER: *u8 = "/volume1/homes/elderwesto/nishi-reader/nx_media_server_auth.elf" as *u8 137const HC_READER_DIR: *u8 = "/volume1/homes/elderwesto/nishi-reader" as *u8 138const HC_READER_KEYS: *u8 = "/volume1/homes/elderwesto/nishihost/auth/keys" as *u8 139const HC_READER_STORE:*u8 = "/volume1/homes/elderwesto/nishihost/auth/store" as *u8 140// the supervisor's OWN binary path (for sovereign self-update via `selfswap`). PID 8636 runs "./nx_hostctl 141// supervise" from cwd nishihost, so the live binary is nishihost/nx_hostctl (no .elf). 142const HC_SELF: *u8 = "/volume1/homes/elderwesto/nishihost/nx_hostctl" as *u8 143const HC_SELF_NEW: *u8 = "/volume1/homes/elderwesto/nishihost/nx_hostctl.new" as *u8 144// The name /api/build ACTUALLY stages (<target>.sov.elf.new). Without this, the natural API-first 145// sequence "build hostctl then /api/deploy target=hostctl" ran selfswap with HC_SELF_NEW absent -- 146// see the fail-safe guard in cmd_selfswap and debt seq992/seq983. 147const HC_SELF_NEW_SOV: *u8 = "/volume1/homes/elderwesto/nishihost/nx_hostctl.sov.elf.new" as *u8 148const HC_SELF_PREV: *u8 = "/volume1/homes/elderwesto/nishihost/nx_hostctl.prev" as *u8 // never-brick (#26): keep the old supervisor binary for rollback (selfswap had NONE) 149const HC_BUILD_DEBUG_FLAG: *u8 = "/volume1/homes/elderwesto/nishihost/knowledge/status/build_debug.flag" as *u8 // 1 = every buildrun passes --debug (2026-09-02): DWARF .debug_line appended past the image so a crash address resolves on the deployed binary; any other content or an absent file = no flag 150const HC_SUP_LOCK: *u8 = "/volume1/homes/elderwesto/nishihost/supervisor.heartbeat" as *u8 // single-supervisor lease: epoch heartbeat (getpid is BROKEN on this backend -> can't pid-exclude self) 151// reader argv literals MUST be consts, not inline "..." as *u8 as i64 in the argv[] array: an inline literal 152// stored into an i64 array doesn't materialize as a valid pointer (.nx gotcha) -> execve gets garbage argv -> 153// EFAULT -> child exits 127 silently (no banner). HC_READER* consts work; these must too. 154const HC_REALM: *u8 = "Nishi Wiki" as *u8 155const HC_PORT: *u8 = "8791" as *u8 156const HC_SUPERVISE: *u8 = "supervise" as *u8 // selfswap's re-exec argv[1] -- same const-not-inline-literal rule 157const HC_RUNSITE: *u8 = "runsite" as *u8 // startsite's hc_dfork_exec arg -> cmd_runsite execs sites.elf (sovereign, no sh -c) 158// SOVEREIGN daemonization -- NO OS tools (operator: "nishi ecosystem only from the hardware rung up"). The OS 159// `setsid` program survived ONLY because it DOUBLE-forks + EXECVEs a fresh image. We do the SAME in pure .nx: 160// fork + nx_setsid (the setsid SYSCALL) + fork + execve(nx_hostctl <arg>) -- a FRESH execve image, NOT a direct 161// cmd_supervise() call (the inherited image dies on the channel teardown -- PROVEN). That is hc_dfork_exec. 162const HC_SLEEPER: *u8 = "sleeper" as *u8 // daemontest payload: a sovereign sleep loop (no OS /bin/sleep) 163// NAS-RESIDENT MIRROR (operator: "i want the NAS doing these things not the laptop where we dev"). Shallow-execve 164// target + its bin, launched DETACHED via hc_dfork_exec so it survives the SSH channel teardown and streams to 165// LOCAL /volume1 disk (no 9p, no WSL). Additive; disturbs no supervised daemon. 166const HC_RUNMIRROR: *u8 = "runmirror" as *u8 167const HC_MIRROR_BIN: *u8 = "/volume1/ai/apertus/nx_apertus_mirror_nas.elf" as *u8 168// PARALLEL concurrent download ON THE NAS (the max-speed gap WSL couldn't run -- its service crashed on concurrent 169// fork/TLS; the NAS's real Linux handles it). Same shallow-execve/detached pattern as the mirror action. 170const HC_RUNMIRRORPAR: *u8 = "runmirrorpar" as *u8 171const HC_MIRRORPAR_BIN: *u8 = "/volume1/ai/apertus/nx_par_pull_nas.elf" as *u8 172// MAKE-IT-REAL: publish a real NAS file into the hub distribution index (enabler -> magnet+download+api+index). 173const HC_DISTPUB_BIN: *u8 = "/volume1/ai/dist/nx_dist_publish_run.elf" as *u8 174const HC_RUNDISTPUB: *u8 = "rundistpub" as *u8 175// /dist DOWNLOAD server (loopback :18099, content-addressed) + a loopback self-test probe. argv literals MUST be 176// consts (an inline "..." as *u8 as i64 in argv[] corrupts) -- hence the HC_A_* / HC_DIST_PROBE_PATH consts. 177const HC_DISTSERVE_BIN: *u8 = "/volume1/ai/dist/nx_dist_serve.elf" as *u8 178const HC_RUNDISTSERVE: *u8 = "rundistserve" as *u8 179const HC_RUNDISTPROBE: *u8 = "rundistprobe" as *u8 180const HC_A_SERVE: *u8 = "serve" as *u8 181const HC_A_PROBE: *u8 = "probe" as *u8 182const HC_A_PORT: *u8 = "18120" as *u8 183const HC_DIST_PROBE_PATH: *u8 = "/dist/509b6d9d22987cf7b1067a7cc6b74b88cf0c8c2739748fe5e85a4da0f69ebf1a/model.safetensors.index.json" as *u8 184// The reader spawn goes through /bin/sh -c (exactly how the system launches vroom: a `setsid ... &` line). 185// The pure fork+sys_chdir+execve path silently failed under the supervisor (no banner) despite chdir + argv 186// both proving correct in isolation -- some .nx codegen edge in the deep forked-child call path. sh does the 187// cd + arg-parse robustly; `exec` REPLACES the sh so the reader is the supervisor's DIRECT child -> the loop's 188// wait4 reaps it on death and LOGS its sig/code (instrumenting WHY the supervised reader exits, when manual 189// launch of the same line stays up). >>auth.log keeps its banner/errors. cmdline stays the reader's so 190// proc_alive finds it. (Once the exit cause is known + fixed, may revert to a detached setsid form.) 191const HC_SH: *u8 = "/bin/sh" as *u8 192const HC_DASHC: *u8 = "-c" as *u8 193const HC_READER_CMD: *u8 = "cd /volume1/homes/elderwesto/nishi-reader && exec ./nx_media_server_auth.elf /volume1/homes/elderwesto/nishihost/auth/keys /volume1/homes/elderwesto/nishihost/auth/store 'Nishi Wiki' 8791 >> auth.log 2>&1" as *u8 194// --- the NSFW gallery stack (serve :18090 + OPAQUE gateway :18190), at /volume1/ai/galx -- supervised here 195// alongside the site so the bash while-loop supervisor is retired (operator: ONE sovereign supervisor). Spawned 196// via sh -c (exec) for robust cwd+args, exactly like the reader. allow_register=0 (operator-only; the gallery is 197// private -- existing accounts still log in, only NEW signups are blocked = the intended closed state). 198const HC_GALX_GW: *u8 = "/volume1/ai/galx/nx_gallery_gateway.elf" as *u8 199const HC_GALX_GW_NEW: *u8 = "/volume1/ai/galx/nx_gallery_gateway.elf.new" as *u8 200// seq1049: rollback artifact + the build-staged twin. The gateway is the AUTH surface for /gallery -- the one 201// binary that must never be promoted without a banked restore point, and it was the one binary that had none. 202const HC_GALX_GW_PREV: *u8 = "/volume1/ai/galx/nx_gallery_gateway.elf.prev" as *u8 203const HC_GALX_GW_SOVNEW: *u8 = "/volume1/homes/elderwesto/nishihost/nx_gallery_gateway.sov.elf.new" as *u8 204const HC_GALX_SERVE: *u8 = "/volume1/ai/galx/nx_gallery_serve.elf" as *u8 205const HC_GALX_SERVE_NEW: *u8 = "/volume1/ai/galx/nx_gallery_serve.elf.new" as *u8 206const HC_GALX_SERVE_CMD: *u8 = "cd /volume1/ai/galx && exec ./nx_gallery_serve.elf 18090 >>/tmp/galx_serve.log 2>&1" as *u8 207// archive server :18801 -- MEASURED DEAD under hc_spawn_path (nx_netobs bench -> ok=0 err=1 in 203us = 208// instant connection REFUSAL, not a timeout; the elf was promoted and the guard entry was correct, so it 209// was spawning and dying). hc_spawn_path execs BARE, inheriting the supervisor's stdout -- and this daemon 210// WRITES to stdout, so its first write to a dead pipe SIGPIPEs it. That is the SAME failure already 211// recorded twice in this file, against redirect.elf and the reader. cd gives it the CWD its web_assets/ 212// and seg_store (BL_PREFIX) paths are relative to; the logfile makes the NEXT crash diagnosable instead 213// of invisible, which is how this one cost a whole cycle to find. 214const HC_ARCSRV_CMD: *u8 = "cd /volume1/homes/elderwesto/nishihost && exec ./nx_archive_server.elf >>/tmp/arcsrv.log 2>&1" as *u8 215// argon2id KSF cost = m=65536 t=3 p=4 -- the ORIGINAL gallery params, RESTORED. Do NOT lower to the login's 216// 19456/2/1: registration is CLOSED ({"error":"registration closed"}), so accounts can't be re-enrolled, and the 217// operator's EXISTING gallery account was registered under THESE params -- OPAQUE re-derives the password with the 218// exact KSF params, so changing them => "invalid credentials". The >10s login was the v2 PROXY keep-alive bug 219// (FIXED: sd2_force_close + keep=0 + write-all), NEVER the KSF cost (galxauthprobe answered in 0ms). 220const HC_GALX_GW_CMD: *u8 = "cd /volume1/ai/galx && exec ./nx_gallery_gateway.elf 18190 knowledge/status/galx_gw_keys knowledge/status/galx_gw_store 1000000000 18090 0 65536 3 4 >>/tmp/galx_gw.log 2>&1" as *u8 221// --- the Nishi Family WIKI gateway (:18791): OPAQUE login + the gated 1195-page wiki, at /volume1/ai/wiki. 222// Supervised here so it auto-restarts forever (no more manual setsid/kill -> the permanent F->S fix). 223const HC_WIKI_GW: *u8 = "/volume1/ai/wiki/nx_wiki_gw.elf" as *u8 224const HC_WIKI_GW_NEW: *u8 = "/volume1/ai/wiki/nx_wiki_gw.elf.new" as *u8 225const HC_WIKI_GW_CMD: *u8 = "cd /volume1/ai/wiki && exec ./nx_wiki_gw.elf 18791 keys store 1000000000 65536 3 4 pages >>/tmp/wiki_gw.log 2>&1" as *u8 226// hub OPAQUE gateway (:18792): leveled /hub via the maturity registry. keys/store = the :9091 /login daemon's 227// (realm nishi_site_admin) so it validates the SAME no-cookie X-Nishi-Session tokens; the gateway reads them read-only. 228const HC_HUB_GW: *u8 = "/volume1/ai/hub/nx_hub_gw.elf" as *u8 229const HC_HUB_GW_NEW: *u8 = "/volume1/ai/hub/nx_hub_gw.elf.new" as *u8 230// Relationship OS daemon (:8027 loopback -- the /relate app). Staged in nishihost cwd like every mgmt target. 231const HC_RELATE: *u8 = "/volume1/homes/elderwesto/nishihost/nx_relate_daemon.elf" as *u8 232const HC_RELATE_NEW: *u8 = "/volume1/homes/elderwesto/nishihost/nx_relate_daemon.elf.new" as *u8 233const HC_RELATE_PREV: *u8 = "/volume1/homes/elderwesto/nishihost/nx_relate_daemon.elf.prev" as *u8 234const HC_RELATE_CMD: *u8 = "cd /volume1/homes/elderwesto/nishihost && exec ./nx_relate_daemon.elf >>/tmp/relate_daemon.log 2>&1" as *u8 235// Ethical CLEAN-SERVE daemon (:8102 -- /clean, SSRF-guarded fetch proxy). Staged in nishihost cwd like every target. 236const HC_CLEANSERVE: *u8 = "/volume1/homes/elderwesto/nishihost/nx_clean_serve_daemon.elf" as *u8 237const HC_CLEANSERVE_NEW: *u8 = "/volume1/homes/elderwesto/nishihost/nx_clean_serve_daemon.elf.new" as *u8 238const HC_DOCPORTAL: *u8 = "/volume1/homes/elderwesto/nishihost/nx_docportal_admin_daemon.elf" as *u8 239const HC_DOCPORTAL_NEW: *u8 = "/volume1/homes/elderwesto/nishihost/nx_docportal_admin_daemon.sov.elf.new" as *u8 240const HC_DOCPORTAL_PREV: *u8 = "/volume1/homes/elderwesto/nishihost/nx_docportal_admin_daemon.elf.prev" as *u8 241const HC_CLEANSERVE_PREV: *u8 = "/volume1/homes/elderwesto/nishihost/nx_clean_serve_daemon.elf.prev" as *u8 242const HC_CLEANSERVE_CMD: *u8 = "cd /volume1/homes/elderwesto/nishihost && exec ./nx_clean_serve_daemon.elf 8102 >>/tmp/cleanserve.log 2>&1" as *u8 243// --- the WORKER MESH: sovereign job console. serve :8029 (backend = console + SSE gen pipeline + gated image) + 244// OPAQUE gateway :8055 (sites /mesh -> here; gated exactly like the gallery, store = a copy of galx_gw_{keys,store}, 245// allow_register=0). Supervised here so the setsid daemons survive crash + reboot (closes the F: unsupervised gap). 246const HC_MESH_SERVE_CMD: *u8 = "cd /volume1/homes/elderwesto/nishihost && exec ./nx_mesh_serve.elf 8029 >>/tmp/mesh_serve.log 2>&1" as *u8 247const HC_MESH_GW_CMD: *u8 = "cd /volume1/homes/elderwesto/nishihost && exec ./nx_mesh_gateway.elf 8055 meshgw_keys meshgw_store 100000 8029 0 65536 3 4 >>/tmp/mesh_gw.log 2>&1" as *u8 248const HC_PROJECT_CMD: *u8 = "cd /volume1/homes/elderwesto/nishihost && exec ./nx_project_serve.elf 8028 opaque_keys.bin opaque_store.log 1000000000 knowledge/project/serve.conf nishi_uid_handle.tsv 19456 2 1 >>/tmp/project_gw.log 2>&1" as *u8 249const HC_HUB_GW_CMD: *u8 = "cd /volume1/ai/hub && exec ./nx_hub_gw.elf 18792 /volume1/homes/elderwesto/nishihost/opaque_keys.bin /volume1/homes/elderwesto/nishihost/opaque_store.log 1000000000 65536 3 4 >>/tmp/hub_gw.log 2>&1" as *u8 250// Sovereign MANAGEMENT API daemon (:18098 loopback HTTP). Reached as <domain>/api via proxy_routes.conf so 251// the ecosystem is operated over the wire (WebFetch), not the shell dance. Args: port keys store realm snap budget. 252// DEDICATED vault-sealed mgmt admin (operator chose "arm a vault-sealed mgmt admin"): its OWN keys+store+realm 253// (mgmt_keys + mgmt_store + realm 'nishi-mgmt', canonical KSF 8192/1/1) -- ISOLATED from the shared auth/store so 254// the control plane NEVER touches the library/operator admin. Handle 'mgmtops', passphrase sealed in the vault 255// (mgmt-admin-pass.nv). /api/login with mgmtops -> token drives /api/{restart,reconcile,deploy,...} over TLS (no shell). 256const HC_MGMT_CMD: *u8 = "cd /volume1/homes/elderwesto/nishihost && chmod +x ./nx_mgmt_api.elf 2>/dev/null; exec ./nx_mgmt_api.elf 18098 /volume1/homes/elderwesto/nishihost/opaque_keys.bin /volume1/homes/elderwesto/nishihost/opaque_store.log nishi_site_admin mgmt_snap.json 1000000000 >>/tmp/mgmt_api.log 2>&1" as *u8 257// admin.<domain> doc-portal ADMIN daemon (:8456 loopback HTTP): OWNER self-service -- invite-gated OPAQUE 258// self-registration (/admin/register) + login + doc upload. Reverse-proxied as admin.<d>/ via proxy_routes.conf. 259// Per-domain keys+store+realm => isolated accounts (andelinwest_docportal here); invites in <store>.invites. 260// Args: port keys store realm budget. Loopback-only; the sites daemon is the only public ingress. 261// budget 20000000: the docportal is a USER-FACING search daemon -- a low budget means it recycles mid-use 262// and the ~15s supervisor respawn gap is a visible hang ("takes forever", operator 2026-07-03). Its per- 263// request mmap leak is FIXED (dsv_respond* now munmap the body), so a high budget no longer grows RSS; and 264// vm.overcommit_memory=1 (hc_assert_overcommit) prevents the fork-wedge regardless. So: recycle rarely. 265const HC_ADMIN_CMD: *u8 = "cd /volume1/homes/elderwesto/nishihost && chmod +x ./nx_docportal_admin_daemon.elf 2>/dev/null; exec ./nx_docportal_admin_daemon.elf 18456 /volume1/homes/elderwesto/nishihost/andelinwest_docportal_keys /volume1/homes/elderwesto/nishihost/andelinwest_docportal_store andelinwest_docportal 20000000 >>/tmp/docportal_admin.log 2>&1" as *u8 266const HC_MAILPORTAL_CMD: *u8 = "cd /volume1/homes/elderwesto/nishihost && chmod +x ./nx_email_portal_daemon.elf 2>/dev/null; exec ./nx_email_portal_daemon.elf 18465 /volume1/homes/elderwesto/nishihost/andelinwest_mail_keys /volume1/homes/elderwesto/nishihost/andelinwest_mail_store andelinwest_mail andelinwest.com /volume1/homes/elderwesto/nishihost/mail_aw/ 20000000 >>/tmp/mail_portal.log 2>&1" as *u8 267const HC_MAGIC_18122: i64 = 18122 268// :18122 loopback-only inventory/ownership checker. argv taken VERBATIM from the live process 269// (/proc/<pid>/cmdline) -- a supervisor that respawns a daemon with DIFFERENT arguments than the 270// one it replaced is not supervision, it is a silent reconfiguration at the worst possible moment. 271const HC_INVENTORY_CMD: *u8 = "cd /volume1/homes/elderwesto/nishihost && chmod +x ./nx_inventory_serve.elf 2>/dev/null; exec ./nx_inventory_serve.elf 18122 opaque_keys.bin opaque_store.log nishi_site_admin 1000000 >>/tmp/inventory.log 2>&1" as *u8 272const HC_SITEEDIT_CMD: *u8 = "cd /volume1/homes/elderwesto/nishihost && chmod +x ./nx_siteedit_daemon.elf 2>/dev/null; exec ./nx_siteedit_daemon.elf 18466 /volume1/homes/elderwesto/nishihost/andelinwest_docportal_keys /volume1/homes/elderwesto/nishihost/andelinwest_docportal_store andelinwest_docportal andelinwest.com /volume1/homes/elderwesto/nishihost/andelinwest.site /volume1/homes/elderwesto/nishihost/sites/andelinwest 20000000 >>/tmp/siteedit.log 2>&1" as *u8 273// CONTINUOUS M&E: nx_health_eval `loop` = the FUNCTIONAL/QUALITY evaluator (nx_https_get probes of the live 274// surface, writes sites/nishifamily/health.json + ledger trend every 300s). Supervised = it runs FOREVER 275// (respawned if it ever dies) -- this is what kills "fire and pray": the surface is re-measured every poll, 276// not when a human remembers. PID-liveness guard (no port -- it makes outbound TLS, listens on nothing). 277const HC_HEALTH_CMD: *u8 = "cd /volume1/homes/elderwesto/nishihost && chmod +x ./nx_health_eval.elf 2>/dev/null; exec ./nx_health_eval.elf loop 300 >>/tmp/health_eval.log 2>&1" as *u8 278// VIDEO SIGNALING/MEDIA RELAY (:8445 loopback, reached as wss://<d>/signal/<room> via the sites proxy). 279// Task #26 near-miss closed: it was UNSUPERVISED (kill -> dead video, no respawn). Now guarded like every 280// other service; reconcile cycles it onto a freshly deployed ELF (clients auto-reconnect lanes in ~1.2s). 281const HC_SIG2_CMD: *u8 = "cd /volume1/homes/elderwesto/nishihost && chmod +x ./nx_signaling_v2.elf 2>/dev/null; exec ./nx_signaling_v2.elf >>/tmp/signaling_v2.log 2>&1" as *u8 282// media/torrent daemon (:8097): the sovereign acquirer (torrent + direct + HLS download -> gallery), migrated 283// from the dev laptop to the NAS hub. The spawn runs the PORTABLE launcher nx_media_env with the "hub" profile, 284// which deploys the bundle's worker elfs -> /tmp then execs nx_torrent_daemon with the hub's area roots. Until 285// the bundle is deployed to /volume1/ai/torrent the exec just fails -> the guard backs off (fail-safe, no harm). 286const HC_TORRENT_CMD: *u8 = "cd /volume1/ai/torrent && exec ./nx_media_env.sov.elf hub >>/tmp/torrentd.log 2>&1" as *u8 287// torrent OPAQUE gateway (:18793): the public auth front for /torrent (sites_daemon /torrent -> here -> daemon 288// :8097). Runs from /volume1 (exec-friendly). Same keys/store/costs as the :9091 login + hub gw (admin realm). 289const HC_TORRENT_GW_CMD: *u8 = "cd /volume1/ai/torrent && exec ./nx_torrent_gw.elf 18793 /volume1/homes/elderwesto/nishihost/opaque_keys.bin /volume1/homes/elderwesto/nishihost/opaque_store.log 1000000000 65536 3 4 >>/tmp/torrentgw.log 2>&1" as *u8 290// --- Elder AI GEN: orchestrator (:18795, NAS-side gen-img: UI + batch dispatch -> laptop 5080 + gallery 291// ingest) + OPAQUE gateway (:18794, family realm -> he_has_access(/gen) -> orchestrator). sites /gen -> 292// gateway :18794, exactly like /torrent. Gateway keys/store = the :9091 login daemon's (SSO). Fail-safe: 293// backs off until the elfs are deployed to /volume1/ai/gen (gendeploy). The orchestrator dispatches to the 294// laptop engine: address resolved from knowledge/swarm_nodes.conf via nx_swarm_endpoint_lib 295// (role gpu-image), firewall-allowed NAS-only. Was hardcoded .193 here; the box is .192 and this 296// comment lied for as long as the drift lasted -- seq1485. 297const HC_GEN_GW_CMD: *u8 = "cd /volume1/ai/gen && exec ./nx_gen_gateway_daemon.elf 18794 /volume1/homes/elderwesto/nishihost/opaque_keys.bin /volume1/homes/elderwesto/nishihost/opaque_store.log 1000000000 18795 0 19456 2 1 86400 >>/tmp/gen_gw.log 2>&1" as *u8 298const HC_GEN_ORCH_CMD: *u8 = "cd /volume1/ai/gen && exec ./nx_gen_orchestrator_daemon.elf 18795 192 168 8 192 7861 /volume1/ai/gen/store- /volume1/ai/gen/blob- /volume1/ai/gen/cids.tsv laptop-5080 5000 >>/tmp/gen_orch.log 2>&1" as *u8 299// SYNTH STUDIO daemon (:18796 loopback, sites /synth/api -> here via proxy_routes.conf). Renders motion/creatures 300// on demand (nx_synth_serve). Fail-safe: backs off until nx_synth_serve_daemon.elf lands in /volume1/ai/synth. 301const HC_SYNTH_CMD: *u8 = "cd /volume1/ai/synth && exec ./nx_synth_serve_daemon.elf 18796 >>/tmp/synth.log 2>&1" as *u8 302// DEV/CI API daemon (:18797 loopback, sites /api/dev -> here via proxy_routes.conf). build/gate/run organs over 303// HTTP, cap-gated (X-Nishi-Cap dev_build). Fail-safe: backs off until nx_dev_api_serve.elf lands in /volume1/ai/devapi. 304const HC_DEVAPI_CMD: *u8 = "cd /volume1/ai/devapi && chmod +x ./nx_dev_api_serve.elf ./_offc/*.elf 2>/dev/null; exec ./nx_dev_api_serve.elf serve 18797 >>/tmp/devapi.log 2>&1" as *u8 305// login OPAQUE daemon (:9091, the token MINTER -- now mints 24h sessions). keys/store relative to cwd (nishihost). 306// logintest spawns the .new on THROWAWAY :9099 to prove it starts BEFORE logindeploy touches live :9091 (no lockout). 307const HC_LOGIN: *u8 = "/volume1/homes/elderwesto/nishihost/nx_opaque_login.elf" as *u8 308const HC_LOGIN_NEW: *u8 = "/volume1/homes/elderwesto/nishihost/nx_opaque_login.elf.new" as *u8 309const HC_LOGIN_PREV: *u8 = "/volume1/homes/elderwesto/nishihost/nx_opaque_login.elf.prev" as *u8 310// argon2id KSF cost = m=19456 KiB (19 MiB) t=2 p=1 -- the OWASP-2024 interactive-login FLOOR. ~5x faster login 311// than the old 64MiB/t3/p4 (the operator's latency complaint); still a standard memory-hard config for a 312// single-operator media realm. CHANGING these REQUIRES re-registering accounts (KSF params are baked at register). 313// argv[8]=wikidir argv[9]=HR ROSTER (2026-07-30). The roster is what olgd_signup_ok consults for the 314// 'only the family' half of the signup gate; with argv[9] absent hrstore is null and EVERY signup is denied 315// by construction, so the gate could never admit anyone. argv[8] must be passed to reach argv[9], and it is 316// passed as the EXACT compiled-in default so this change is behaviour-preserving for the wiki-serving path. 317// ⚠THAT DEFAULT IS A LAPTOP PATH (/mnt/c/Users/elder/...) baked into a production daemon and it does not 318// exist on the NAS -- preserved verbatim here deliberately rather than silently "fixed" in a supervisor edit; 319// filed separately. The roster file itself is ABSENT on purpose: hra_is_invited returns 0 for a missing store, 320// so signup stays FAIL-CLOSED until an operator provisions invites. Wiring the slot is a technical step; 321// deciding WHO is invited is not mine to make. 322const HC_LOGIN_CMD: *u8 = "cd /volume1/homes/elderwesto/nishihost && exec ./nx_opaque_login.elf 9091 opaque_keys.bin opaque_store.log 1000000000 19456 2 1 /mnt/c/Users/elder/nishi-core/nxc2/knowledge/wiki_pages /volume1/homes/elderwesto/nishihost/nishi_hr.log >>/tmp/login.log 2>&1" as *u8 323const HC_LOGIN_TEST_CMD: *u8 = "cd /volume1/homes/elderwesto/nishihost && exec ./nx_opaque_login.elf.new 9099 opaque_keys.bin opaque_store.log 50 19456 2 1 >>/tmp/logintest.out 2>&1" as *u8 324// --- SOVEREIGN mTLS reverse-proxy TEST launch (NO /bin/sh): hc_dfork_exec(HC_RUNMTLSPROXY) -> cmd_runmtlsproxy 325// shallow-execve's the proxy from the fresh main-dispatch image, exactly like cmd_runsite. :9444 -> loopback 326// wiki_gw :18791; the LE cert is read from an absolute path; the proxy is left RUNNING for a browser test. 327const HC_RUNMTLSPROXY: *u8 = "runmtlsproxy" as *u8 328const HC_MTLS_PROXY_BIN: *u8 = "/volume1/homes/elderwesto/nishihost/nx_aw_mtls_proxy.elf" as *u8 329const HC_MP_PORT: *u8 = "9444" as *u8 330const HC_MP_BACKEND: *u8 = "18791" as *u8 331// --- platformtest: sovereign-launch the carved andelinwest CLIENT daemon (:8453, LE ECDSA) + the SNI router 332// (:8444 -> backend 8443 sites.elf; routes by the property registry) on INTERNAL ports -- :443 untouched. 333const HC_RUNANDELIN: *u8 = "runandelin" as *u8 334const HC_ANDELIN_BIN: *u8 = "/volume1/homes/elderwesto/nishihost/nx_andelinwest_daemon.elf" as *u8 335const HC_RUNSNIR: *u8 = "runsnir" as *u8 336const HC_SNIR_BIN: *u8 = "/volume1/homes/elderwesto/nishihost/nx_sni_router.elf" as *u8 337const HC_SNIR_PORT: *u8 = "8444" as *u8 338const HC_SNIR_BACKEND: *u8 = "8443" as *u8 339const HC_NFTREAD_BIN: *u8 = "/volume1/homes/elderwesto/nishihost/nx_nft_read.elf" as *u8 // sovereign netfilter R0: read-only nat-table probe (grounds legacy-vs-nft) 340const HC_NETTAP_BIN: *u8 = "/volume1/homes/elderwesto/nishihost/nx_netscope_capture.elf" as *u8 // NX-NETSCOPE L-CAPTURE: the sovereign wire tap (AF_PACKET -> nx_packet_dissect) 341// sites.elf spawn via sh -c (exec) -- the SAME proven mechanism the gallery/reader use. The pure-.nx 342// fork+setsid+execve(sites.elf) path hits the deep-forked-child codegen edge (sites.elf dies); sh's exec 343// dodges it. cd sets cwd so sites.elf finds its relative certs/. (Full sovereignty here is blocked ONLY by 344// that nx_cc codegen edge -- a separate compiler fix.) 345const HC_SITES_CMD: *u8 = "cd /volume1/homes/elderwesto/nishihost && exec ./sites.elf >>/tmp/sites_run.log 2>&1" as *u8 346// --- SOVEREIGN LIBRARY backend (nishifamily.com/research -> :8095, 16K-taxon full-text catalog). 347// Runs from its own dir so it resolves knowledge/libstore-* + libindex.nxinv relatively; sites.elf's 348// /research route reverse-proxies to it. libdeploy: promote nx_lib_httpd.elf.new -> .elf, kill any old 349// (FULL cmdline -- name is 16 chars, comm-truncation would silently miss it), relaunch detached. ADDITIVE: 350// the supervise loop is untouched (a crash leaves /research empty via the proxy fail-safe; nothing else). 351const HC_LIB_ELF: *u8 = "/volume1/homes/elderwesto/nishihost/lib/nx_lib_httpd.elf" as *u8 352const HC_LIB_ELF_NEW: *u8 = "/volume1/homes/elderwesto/nishihost/lib/nx_lib_httpd.elf.new" as *u8 353const HC_LIB_CMD: *u8 = "cd /volume1/homes/elderwesto/nishihost/lib && exec ./nx_lib_httpd.elf >>/tmp/lib_run.log 2>&1" as *u8 354// genprobe: one-shot LIVE frontend proof -- runs nx_gen_live_probe.elf (drives the deployed /gen gateway end-to-end) 355// and captures its PASS/FAIL to a file the subcommand reads back (relayed to the operator via nx_aw_hostctl). 356const HC_GENPROBE_CMD: *u8 = "exec /volume1/ai/gen/nx_gen_live_probe.elf >/volume1/ai/gen/genprobe.out 2>&1" as *u8 357// Sovereign agent-facing TOOLS API (:18096 loopback HTTP -> /api/tools + /mcp). R0 of the MCP-benchmark-and-beyond 358// arc: exposes nx_tool_registry as an MCP-compatible + superset surface. Reached as <domain>/api/tools + /mcp via 359// proxy_routes.conf. ADDITIVE isolated service -- backs off until nx_tools_api_serve.elf is deployed (fail-safe). 360const HC_TAPI_CMD: *u8 = "cd /volume1/homes/elderwesto/nishihost && chmod +x ./nx_tools_api_serve.elf 2>/dev/null; mkdir -p knowledge 2>/dev/null; exec ./nx_tools_api_serve.elf serve 18096 >>/tmp/tools_api.log 2>&1" as *u8 361// Finance dashboard server (:18097 loopback HTTP -> serves fin_stack.html). Reached as <domain>/finance (gated) 362// via proxy_routes.conf. ADDITIVE isolated service -- backs off until nx_fin_serve.elf is deployed (fail-safe). 363const HC_FIN_CMD: *u8 = "cd /volume1/homes/elderwesto/nishihost && chmod +x ./nx_fin_serve.elf 2>/dev/null; exec ./nx_fin_serve.elf serve 18097 >>/tmp/fin_serve.log 2>&1" as *u8 364// /dist content-addressed DOWNLOAD server (:18120 loopback -> sites.elf /dist). ADDITIVE isolated service; backs 365// off until nx_dist_serve.elf is deployed to /volume1/ai/dist -> fail-safe. Makes the download transport DURABLE. 366const HC_DIST_CMD: *u8 = "cd /volume1/ai/dist && chmod +x ./nx_dist_serve.elf 2>/dev/null; exec ./nx_dist_serve.elf serve 18120 >>/tmp/dist_serve.log 2>&1" as *u8 367// BitTorrent SEEDER (:6881, INBOUND peers) -- the UPLOAD/SHARE half of the torrent stack. Serves every torrent 368// registered in /volume1/ai/torrent/seed_index.conf (info_hash -> file). ADDITIVE isolated service; backs off 369// until nx_torrent_seed.elf is deployed -> fail-safe. NEVER-BRICK: writes zero state, only net sends + file reads. 370const HC_SEED_CMD: *u8 = "cd /volume1/ai/torrent && chmod +x ./nx_torrent_seed.elf 2>/dev/null; exec ./nx_torrent_seed.elf serve 6881 /volume1/ai/torrent/seed_index.conf >>/tmp/seed.log 2>&1" as *u8 371// DHT ANNOUNCER: periodically announce_peer every registered info_hash FROM the NAS (BEP-5 infers our IP from 372// the packet source) so strangers DISCOVER the seeder via the DHT. ADDITIVE; backs off until deployed -> fail-safe. 373const HC_SEEDANN_CMD: *u8 = "cd /volume1/ai/torrent && chmod +x ./nx_seed_announce_all.elf 2>/dev/null; exec ./nx_seed_announce_all.elf /volume1/ai/torrent/seed_index.conf >>/tmp/seedann.log 2>&1" as *u8 374// torstat: run nx_torrent_seedeval on EVERY active download (media/<btih>) -> SCARCE vs IGNORING vs COMPLETE 375// per torrent -- diagnoses a stuck DL: are missing pieces held by peers (pull/unchoke bug) or absent (dead swarm)? 376const HC_TORSTAT_CMD: *u8 = "cd /volume1/ai/torrent && set -f; chmod +x ./nx_torrent_seedeval.elf 2>/dev/null; : > /tmp/torstat.out; for area in media gallery; do while read b; do echo === $area/$b === >> /tmp/torstat.out; ./nx_torrent_seedeval.elf $area/$b >> /tmp/torstat.out 2>&1; done < $area/torrents.idx 2>/dev/null; done; echo === SEED REGISTRY - what we SHARE back === >> /tmp/torstat.out; cat seed_index.conf >> /tmp/torstat.out 2>/dev/null; echo === seeding $(wc -l < seed_index.conf 2>/dev/null) torrents === >> /tmp/torstat.out" as *u8 377// MULTIPLAYER game relay (:7702 loopback HTTP -> /mineworld-mp page + /pstate POST + /roster GET; shared-mmap 378// rooms, fork-per-connection). Reached via proxy_routes.conf. ADDITIVE isolated service -- backs off until 379// nx_mp_serve.elf is deployed (fail-safe). Worst failure = multiplayer down; touches nothing else (never-brick). 380const HC_MP_CMD: *u8 = "cd /volume1/homes/elderwesto/nishihost && chmod +x ./nx_mp_serve.elf 2>/dev/null; mkdir -p web_assets/_game_build 2>/dev/null; exec ./nx_mp_serve.elf >>/tmp/mp_serve.log 2>&1" as *u8 381 382func hc_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 383func hc_putn(v: i64) -> i64 { 384 let t: *u8=sys_mmap(28); var m: i64=v; var neg9: i64=0; if m<0{m=0-m;neg9=1} var k: i64=0; if m==0{t[0]=48 as u8;k=1} 385 while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} if neg9==1{t[k]=45 as u8;k=k+1} 386 let b: *u8=sys_mmap(28); var i: i64=0; while i<k{b[i]=t[k-1-i];i=i+1} sys_write(1,b,k) 387 sys_munmap(t,28); sys_munmap(b,28); return 0 388} 389func hc_streq(a: *u8, b: *u8) -> i64 { var i: i64=0; while a[i]!=(0 as u8) { if a[i]!=b[i] { return 0 } i=i+1 } if b[i]!=(0 as u8) { return 0 } return 1 } 390 391// write a decimal pid to a path 392func hc_write_pid(path: *u8, pid: i64) -> i64 { 393 let buf: *u8 = sys_mmap(28); var m: i64=pid; let t: *u8=sys_mmap(28); var k: i64=0 394 if m==0 { t[0]=48; k=1 } 395 while m>0 { t[k]=(48+(m%10)) as u8; m=m/10; k=k+1 } 396 var i: i64=0; while i<k { buf[i]=t[k-1-i]; i=i+1 } 397 let fd: i64 = sys_openat_wr(path, 0x1a4) 398 if fd<0 { return 0-1 } 399 sys_write(fd, buf, k); sys_close(fd); return 0 400} 401// read a decimal pid from a path (0 if absent/empty) 402func hc_read_pid(path: *u8) -> i64 { 403 let box: *i64 = sys_mmap(16) as *i64; box[0]=0 404 let d: *u8 = sys_read_file(path, box) 405 if (d as i64)==0 { return 0 } 406 let n: i64 = box[0]; var v: i64=0; var i: i64=0 407 while i<n { let c: u8=d[i]; if c>=(48 as u8) { if c<=(57 as u8) { v=v*10+((c-(48 as u8)) as i64) } } i=i+1 } 408 return v 409} 410 411// SINGLE-SUPERVISOR LEASE (getpid is BROKEN here -> can't pid-exclude self in a /proc scan, so use an EPOCH 412// HEARTBEAT): the live supervisor writes `now` to HC_SUP_LOCK every poll. A supervisor starting up that sees a 413// FRESH heartbeat (<90s = 6x the 15s poll, tolerates slow polls) DEFERS instead of racing -- the by-construction 414// prevention of the dueling-supervisor outage. reconcile/selfswap CLEAR the lease (write 0) AFTER killing the old 415// one, so the INTENDED relaunch always claims (never false-defers on a dead supervisor's stale heartbeat). 416func hc_sup_touch() -> i64 { return hc_write_pid(HC_SUP_LOCK, sys_now_realtime_sec()) } 417func hc_sup_clear() -> i64 { return hc_write_pid(HC_SUP_LOCK, 0) } // mark stale -> the next supervisor claims 418func hc_sup_fresh() -> i64 { 419 let e: i64 = hc_read_pid(HC_SUP_LOCK) 420 if e <= 0 { return 0 } // absent / cleared -> free to claim 421 let age: i64 = sys_now_realtime_sec() - e 422 if age < 0 { return 0 } // clock skew -> treat stale, never falsely defer (zero-supervisor is the worst outcome) 423 if age < 90 { return 1 } // a live supervisor heartbeat -> defer 424 return 0 // stale (holder crashed >90s ago) -> claim 425} 426 427// spawn the sites daemon; return its pid 428func hc_spawn() -> i64 { 429 let pid: i64 = sys_fork() 430 if pid==0 { 431 let argv: *i64 = sys_mmap(16) as *i64; argv[0]=HC_ELF as i64; argv[1]=0 432 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 433 sys_execve_clean(HC_ELF, argv, envp) 434 sys_exit(127) 435 } 436 return pid 437} 438 439func hc_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n } 440func hc_atoi(s: *u8) -> i64 { var v: i64=0; var i: i64=0; while s[i]!=(0 as u8) { let c: u8=s[i]; if c<(48 as u8){return v} if c>(57 as u8){return v} v=v*10+((c-(48 as u8)) as i64); i=i+1 } return v } 441func hc_contains(hay: *u8, hn: i64, needle: *u8, nn: i64) -> i64 { 442 if nn==0 { return 1 } 443 var i: i64=0 444 while i+nn<=hn { var k: i64=0; var ok: i64=1; while k<nn { if hay[i+k]!=needle[k]{ok=0;k=nn} k=k+1 } if ok==1 { return 1 } i=i+1 } 445 return 0 446} 447// BOUNDED raw read of /proc/<pid>/cmdline into a caller-owned buf (reused across PIDs) -> total bytes. CRITICAL: 448// the proc scanners MUST NOT use sys_read_file here -- it mmaps 4 GiB PER CALL and never frees, so scanning 449// ~200 /proc entries exhausts virtual memory and the next fork fails ENOMEM (the supervisor then can't respawn 450// daemons + dies = the F-class root). This reader allocates nothing; the caller's fixed buffer is reused. 451func hc_read_cmdline(path: *u8, buf: *u8, cap: i64) -> i64 { 452 let fd: i64 = sys_openat_rd(path) 453 if fd < 0 { return 0 } 454 var total: i64 = 0 455 var n: i64 = sys_read(fd, buf, cap) 456 while n > 0 { total = total + n; if total >= cap { n = 0 } else { n = sys_read(fd, (buf as i64 + total) as *u8, cap - total) } } 457 sys_close(fd) 458 return total 459} 460 461// Sovereign replacement for pkill: scan /proc, kill every process whose /proc/<pid>/cmdline contains 462// `needle`. No shell. Uses getdents64 + read_file + nx_kill (the now-working sovereign kill). 463func proc_kill_by_name(needle: *u8, sig: i64) -> i64 { 464 let nn: i64 = hc_slen(needle) 465 let fd: i64 = sys_openat_rd("/proc" as *u8) 466 if fd < 0 { return 0 } 467 let buf: *u8 = sys_mmap(HC_MAGIC_65536) 468 let path: *u8 = sys_mmap(256) 469 let clbox: *i64 = sys_mmap(16) as *i64; let clbuf: *u8 = sys_mmap(HC_MAGIC_8192) 470 var killed: i64 = 0 471 var run: i64 = 1 472 while run == 1 { 473 let n: i64 = sys_getdents64(fd, buf, HC_MAGIC_65536) 474 if n <= 0 { run = 0 } else { 475 var off: i64 = 0 476 while off < n { 477 let rec: *u8 = ((buf as i64 + off) as *u8) 478 let reclen: i64 = dirent_reclen(rec) 479 if reclen <= 0 { off = n } else { 480 let name: *u8 = dirent_name(rec) 481 if name[0] >= (48 as u8) { if name[0] <= (57 as u8) { 482 var p: i64 = 0 483 let pre: *u8 = "/proc/" as *u8 484 var a: i64 = 0; while pre[a]!=(0 as u8){path[p]=pre[a];p=p+1;a=a+1} 485 a = 0; while name[a]!=(0 as u8){path[p]=name[a];p=p+1;a=a+1} 486 let suf: *u8 = "/cmdline" as *u8 487 a = 0; while suf[a]!=(0 as u8){path[p]=suf[a];p=p+1;a=a+1} 488 path[p] = 0 as u8 489 let cln: i64 = hc_read_cmdline(path, clbuf, HC_MAGIC_8192) 490 if cln > 0 { if hc_contains(clbuf, cln, needle, nn) == 1 { 491 nx_kill(hc_atoi(name), sig) 492 killed = killed + 1 493 } } 494 } } 495 off = off + reclen 496 } 497 } 498 } 499 } 500 sys_close(fd) 501 sys_munmap(buf, HC_MAGIC_65536); sys_munmap(path, 256); sys_munmap(clbox as *u8, 16); sys_munmap(clbuf, HC_MAGIC_8192) // FREE scan buffers (no leak) 502 return killed 503} 504 505// Kill every process whose /proc/<pid>/cmdline contains BOTH needles. CRITICAL: a single broad needle like 506// "supervise" also matches the OS's s6-supervise service processes (openssh/nginx/docker/gitea/calibre-web/ 507// lanraragi/redis/...) -- killing those would take down SSH and every container. Requiring "nx_hostctl" AND 508// "supervise" TOGETHER uniquely targets our own supervisor ("./nx_hostctl supervise") and nothing else: the 509// "selfswap" process has nx_hostctl but not supervise; s6-supervise has supervise but not nx_hostctl. 510func proc_kill_by_two(n1: *u8, n2: *u8, sig: i64) -> i64 { 511 let l1: i64 = hc_slen(n1); let l2: i64 = hc_slen(n2) 512 let fd: i64 = sys_openat_rd("/proc" as *u8) 513 if fd < 0 { return 0 } 514 let buf: *u8 = sys_mmap(HC_MAGIC_65536); let path: *u8 = sys_mmap(256); let clbox: *i64 = sys_mmap(16) as *i64; let clbuf: *u8 = sys_mmap(HC_MAGIC_8192) 515 var killed: i64 = 0; var run: i64 = 1 516 while run == 1 { 517 let n: i64 = sys_getdents64(fd, buf, HC_MAGIC_65536) 518 if n <= 0 { run = 0 } else { 519 var off: i64 = 0 520 while off < n { 521 let rec: *u8 = ((buf as i64 + off) as *u8) 522 let reclen: i64 = dirent_reclen(rec) 523 if reclen <= 0 { off = n } else { 524 let name: *u8 = dirent_name(rec) 525 if name[0] >= (48 as u8) { if name[0] <= (57 as u8) { 526 var p: i64 = 0; let pre: *u8 = "/proc/" as *u8 527 var a: i64 = 0; while pre[a]!=(0 as u8){path[p]=pre[a];p=p+1;a=a+1} 528 a = 0; while name[a]!=(0 as u8){path[p]=name[a];p=p+1;a=a+1} 529 let suf: *u8 = "/cmdline" as *u8 530 a = 0; while suf[a]!=(0 as u8){path[p]=suf[a];p=p+1;a=a+1} 531 path[p] = 0 as u8 532 let cln: i64 = hc_read_cmdline(path, clbuf, HC_MAGIC_8192) 533 if cln > 0 { 534 if hc_contains(clbuf, cln, n1, l1) == 1 { if hc_contains(clbuf, cln, n2, l2) == 1 { 535 nx_kill(hc_atoi(name), sig) 536 killed = killed + 1 537 } } 538 } 539 } } 540 off = off + reclen 541 } 542 } 543 } 544 } 545 sys_close(fd) 546 sys_munmap(buf, HC_MAGIC_65536); sys_munmap(path, 256); sys_munmap(clbox as *u8, 16); sys_munmap(clbuf, HC_MAGIC_8192) // FREE scan buffers (no leak) 547 return killed 548} 549 550// READ-ONLY: 1 if any process cmdline contains `needle` (no kill). Lets the supervisor ADOPT a running 551// daemon instead of blindly re-spawning it (which would EADDRINUSE on a live :8443). 552// seq1318 (RE-APPLIED 4th time): ONE /proc walk per poll, shared by every guard, instead of a full 553// /proc walk PER GUARD PER POLL (~25 x ~950 = ~23,775 cmdline reads/poll). Measured 180 -> 11 554// jiffies/60s. ps_alive_or_direct is ASYMMETRIC: ALIVE is free from the snapshot; a NOT-ALIVE 555// verdict -- the one that makes this supervisor kill and spawn -- is re-verified against a 556// snapshot <=250ms old, so a stale negative can never double-spawn a just-started daemon. 557// The old inline scan lives in nx_proc_snapshot.nx:ps_alive_direct as the fallback AND the gate oracle. 558func proc_alive_by_name(needle: *u8) -> i64 { 559 return ps_alive_or_direct(needle) 560} 561 562// Respawn a daemon (sites.elf/redirect/vroom/translate) via SINGLE fork + nx_setsid (the daemon becomes a 563// SESSION LEADER, no controlling terminal) + execve -- the exact property the durable `setsid ./sites.elf` 564// has, which PROVABLY survives. The old plain fork+execve left the daemon in the supervisor's session with a 565// ctty and it DIED on the next poll ("restarted (was dead)" forever). The daemon stays this supervisor's CHILD 566// (ppid=supervisor) so the poll-loop reap (wait4 WNOHANG) still collects it if it dies -- we do NOT wait4 here 567// (the daemon runs forever). Returns the daemon pid for hc_write_pid. 568func hc_spawn_path(pth: *u8) -> i64 { 569 let pid: i64 = sys_fork() 570 if pid==0 { 571 nx_setsid() 572 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 573 if dn >= 0 { sys_dup3(dn,0,0); sys_dup3(dn,1,0); sys_dup3(dn,2,0) } 574 let argv: *i64 = sys_mmap(16) as *i64; argv[0]=pth as i64; argv[1]=0 575 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 576 sys_execve_clean(pth, argv, envp) 577 sys_exit(127) 578 return 0 579 } 580 return pid 581} 582 583// CrashLoopBackOff guard params, passed POSITIONALLY to rg_should_restart(ws,cnt,now,cap_ms,base_ms) -- see 584// nx_restart_guard.nx (S-class containment: K8s CrashLoopBackOff + OTP intensity + capped exponential backoff). 585// HC_GUARD_INTERVAL_MS = backoff CAP (max wait between restarts of a hard loop); HC_GUARD_BURST = backoff BASE 586// (first step, doubling each repeat crash). With the ~15s poll: the first ~4 crashes restart at poll rate, then the 587// wait escalates 16s->32s->...->5min cap = a hard crash-loop is CONTAINED to ~1 restart/5min (was: 5 per 2min, 588// reset each window = a SLOW INFINITE loop that spammed logs + starved CPU). A daemon that ran healthy >= 589// RG_HEALTH_RESET_MS(60s) before dying resets -> immediate restart (auto-recovery). (Names kept to avoid touching 590// the 12 call sites; reinterpreted per this comment -- TODO rename to HC_BACKOFF_CAP_MS/HC_BACKOFF_BASE_MS.) 591const HC_GUARD_INTERVAL_MS: i64 = 300000 // cap_ms: 5 min (K8s CrashLoopBackOff ceiling) 592const HC_GUARD_BURST: i64 = 1000 // base_ms: 1s first backoff step (doubles each repeated crash) 593 594// Does this daemon need a restart? DEAD (PID gone) -> yes. ALIVE but its PLAINTEXT port probes HUNG (up but 595// not serving) -> yes. port<=0 = PID-liveness only (TLS / unprobeable daemon) so alive is sufficient. 596func hc_needs_restart(alive: i64, port: i64) -> i64 { 597 if alive == 0 { return 1 } 598 if port <= 0 { return 0 } 599 if hp_probe(port, 3) == HP_HUNG { return 1 } 600 return 0 601} 602 603// Guard one daemon: liveness + (plaintext) serving-probe -> crash-loop-guarded restart. ws/cnt = caller-owned 604// 2-word guard state. write_pid=1 records the new pid (sites.elf). Returns 1 if it restarted, else 0. 605func hc_guard_one(name: *u8, path: *u8, port: i64, ws: *i64, cnt: *i64, pidpath: *u8, write_pid: i64) -> i64 { 606 let alive: i64 = proc_alive_by_name(name) 607 if hc_needs_restart(alive, port) == 0 { return 0 } 608 let now: i64 = sys_now_realtime_ms() 609 if rg_should_restart(ws, cnt, now, HC_GUARD_INTERVAL_MS, HC_GUARD_BURST) == 0 { 610 hc_puts(" [guard] " as *u8); hc_puts(name); hc_puts(" crash-looping -> BACKING OFF (not restarting this window)\n" as *u8) 611 return 0 612 } 613 if alive == 1 { proc_kill_by_name(name, 9) } // wedged husk: kill it so the respawn binds the port cleanly 614 let p: i64 = hc_spawn_path(path) 615 if write_pid == 1 { hc_write_pid(pidpath, p) } 616 hc_puts(" [guard] restarted " as *u8); hc_puts(name) 617 if alive == 1 { hc_puts(" (was HUNG)\n" as *u8) } else { hc_puts(" (was dead)\n" as *u8) } 618 return 1 619} 620 621// Spawn the auth'd library reader WITH its args (keysfile storefile realm port) + the correct CWD (its 622// relative knowledge/_offc paths). hc_spawn_path can't -- it execs bare, no args, wrong CWD. 623func hc_spawn_reader() -> i64 { 624 let pid: i64 = sys_fork() 625 if pid==0 { 626 let argv: *i64 = sys_mmap(32) as *i64 627 argv[0]=HC_SH as i64; argv[1]=HC_DASHC as i64; argv[2]=HC_READER_CMD as i64; argv[3]=0 628 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 629 sys_execve_clean(HC_SH, argv, envp) 630 hc_puts(" [spawn_reader] EXECVE /bin/sh FAILED\n" as *u8) 631 sys_exit(127) 632 } 633 return pid 634} 635// Spawn the gallery serve (:18090) / gateway (:18190) WITH cwd+args via sh -c exec (same robust pattern as the 636// reader; hc_spawn_path execs bare/no-args/wrong-cwd). The gateway needs its OPAQUE realm keys+store + backend. 637func hc_spawn_galx_serve() -> i64 { 638 let pid: i64 = sys_fork() 639 if pid==0 { 640 nx_setsid() // SESSION LEADER + detach ctty -> survives the poll loop (the property hc_spawn_path has) 641 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 642 if dn >= 0 { sys_dup3(dn,0,0); sys_dup3(dn,1,0); sys_dup3(dn,2,0) } 643 let argv: *i64 = sys_mmap(32) as *i64 644 argv[0]=HC_SH as i64; argv[1]=HC_DASHC as i64; argv[2]=HC_GALX_SERVE_CMD as i64; argv[3]=0 645 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 646 sys_execve_clean(HC_SH, argv, envp) 647 sys_exit(127) 648 } 649 return pid 650} 651// archive server :18801 -- byte-for-byte the hc_spawn_galx_serve pattern, only the command differs. 652// setsid + /dev/null on 0,1,2 + sh -c (exec) with the right cwd: the three properties hc_spawn_path lacks. 653func hc_spawn_arcsrv() -> i64 { 654 let pid: i64 = sys_fork() 655 if pid==0 { 656 nx_setsid() // SESSION LEADER + detach ctty -> survives the poll loop 657 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 658 if dn >= 0 { sys_dup3(dn,0,0); sys_dup3(dn,1,0); sys_dup3(dn,2,0) } 659 let argv: *i64 = sys_mmap(32) as *i64 660 argv[0]=HC_SH as i64; argv[1]=HC_DASHC as i64; argv[2]=HC_ARCSRV_CMD as i64; argv[3]=0 661 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 662 sys_execve_clean(HC_SH, argv, envp) 663 sys_exit(127) 664 } 665 return pid 666} 667func hc_spawn_galx_gw() -> i64 { 668 let pid: i64 = sys_fork() 669 if pid==0 { 670 nx_setsid() // SESSION LEADER + detach ctty -> survives the poll loop (the property hc_spawn_path has) 671 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 672 if dn >= 0 { sys_dup3(dn,0,0); sys_dup3(dn,1,0); sys_dup3(dn,2,0) } 673 let argv: *i64 = sys_mmap(32) as *i64 674 argv[0]=HC_SH as i64; argv[1]=HC_DASHC as i64; argv[2]=HC_GALX_GW_CMD as i64; argv[3]=0 675 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 676 sys_execve_clean(HC_SH, argv, envp) 677 sys_exit(127) 678 } 679 return pid 680} 681// spawn the wiki OPAQUE gateway (:18791) -- same sh -c (exec) detach pattern as the gallery gateway, so the 682// exec'd nx_wiki_gw.elf is THIS supervisor's direct child + proc_alive_by_name("nx_wiki_gw.elf") finds it. 683func hc_spawn_wiki_gw() -> i64 { 684 let pid: i64 = sys_fork() 685 if pid==0 { 686 nx_setsid() 687 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 688 if dn >= 0 { sys_dup3(dn,0,0); sys_dup3(dn,1,0); sys_dup3(dn,2,0) } 689 let argv: *i64 = sys_mmap(32) as *i64 690 argv[0]=HC_SH as i64; argv[1]=HC_DASHC as i64; argv[2]=HC_WIKI_GW_CMD as i64; argv[3]=0 691 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 692 sys_execve_clean(HC_SH, argv, envp) 693 sys_exit(127) 694 } 695 return pid 696} 697// spawn the hub OPAQUE gateway (:18792) -- mirrors hc_spawn_wiki_gw (sh -c exec detach; direct child of this supervisor). 698func hc_spawn_hub_gw() -> i64 { 699 let pid: i64 = sys_fork() 700 if pid==0 { 701 nx_setsid() 702 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 703 if dn >= 0 { sys_dup3(dn,0,0); sys_dup3(dn,1,0); sys_dup3(dn,2,0) } 704 let argv: *i64 = sys_mmap(32) as *i64 705 argv[0]=HC_SH as i64; argv[1]=HC_DASHC as i64; argv[2]=HC_HUB_GW_CMD as i64; argv[3]=0 706 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 707 sys_execve_clean(HC_SH, argv, envp) 708 sys_exit(127) 709 } 710 return pid 711} 712// spawn the gen orchestrator (:18795) + gateway (:18794) -- mirror hc_spawn_hub_gw (sh -c exec detach; 713// direct child of this supervisor so proc_alive_by_name finds them). 714func hc_spawn_gen_orch() -> i64 { 715 let pid: i64 = sys_fork() 716 if pid==0 { 717 nx_setsid() 718 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 719 if dn >= 0 { sys_dup3(dn,0,0); sys_dup3(dn,1,0); sys_dup3(dn,2,0) } 720 let argv: *i64 = sys_mmap(32) as *i64 721 argv[0]=HC_SH as i64; argv[1]=HC_DASHC as i64; argv[2]=HC_GEN_ORCH_CMD as i64; argv[3]=0 722 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 723 sys_execve_clean(HC_SH, argv, envp) 724 sys_exit(127) 725 } 726 return pid 727} 728func hc_spawn_gen_gw() -> i64 { 729 let pid: i64 = sys_fork() 730 if pid==0 { 731 nx_setsid() 732 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 733 if dn >= 0 { sys_dup3(dn,0,0); sys_dup3(dn,1,0); sys_dup3(dn,2,0) } 734 let argv: *i64 = sys_mmap(32) as *i64 735 argv[0]=HC_SH as i64; argv[1]=HC_DASHC as i64; argv[2]=HC_GEN_GW_CMD as i64; argv[3]=0 736 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 737 sys_execve_clean(HC_SH, argv, envp) 738 sys_exit(127) 739 } 740 return pid 741} 742// spawn the SYNTH STUDIO daemon (:18796) -- mirror hc_spawn_gen_orch (sh -c exec detach). 743func hc_spawn_synth() -> i64 { 744 let pid: i64 = sys_fork() 745 if pid==0 { 746 nx_setsid() 747 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 748 if dn >= 0 { sys_dup3(dn,0,0); sys_dup3(dn,1,0); sys_dup3(dn,2,0) } 749 let argv: *i64 = sys_mmap(32) as *i64 750 argv[0]=HC_SH as i64; argv[1]=HC_DASHC as i64; argv[2]=HC_SYNTH_CMD as i64; argv[3]=0 751 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 752 sys_execve_clean(HC_SH, argv, envp) 753 sys_exit(127) 754 } 755 return pid 756} 757// spawn the DEV/CI API daemon (:18797) -- mirror hc_spawn_synth (sh -c exec detach). 758func hc_spawn_devapi() -> i64 { 759 let pid: i64 = sys_fork() 760 if pid==0 { 761 nx_setsid() 762 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 763 if dn >= 0 { sys_dup3(dn,0,0); sys_dup3(dn,1,0); sys_dup3(dn,2,0) } 764 let argv: *i64 = sys_mmap(32) as *i64 765 argv[0]=HC_SH as i64; argv[1]=HC_DASHC as i64; argv[2]=HC_DEVAPI_CMD as i64; argv[3]=0 766 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 767 sys_execve_clean(HC_SH, argv, envp) 768 sys_exit(127) 769 } 770 return pid 771} 772// spawn the media/torrent daemon (:8097) via the portable launcher nx_media_env hub -- mirrors hc_spawn_hub_gw. 773func hc_spawn_torrent() -> i64 { 774 let pid: i64 = sys_fork() 775 if pid==0 { 776 nx_setsid() 777 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 778 if dn >= 0 { sys_dup3(dn,0,0); sys_dup3(dn,1,0); sys_dup3(dn,2,0) } 779 let argv: *i64 = sys_mmap(32) as *i64 780 argv[0]=HC_SH as i64; argv[1]=HC_DASHC as i64; argv[2]=HC_TORRENT_CMD as i64; argv[3]=0 781 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 782 sys_execve_clean(HC_SH, argv, envp) 783 sys_exit(127) 784 } 785 return pid 786} 787// spawn the torrent OPAQUE gateway (:18793) -- mirrors hc_spawn_torrent / hc_spawn_hub_gw. 788func hc_spawn_torrent_gw() -> i64 { 789 let pid: i64 = sys_fork() 790 if pid==0 { 791 nx_setsid() 792 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 793 if dn >= 0 { sys_dup3(dn,0,0); sys_dup3(dn,1,0); sys_dup3(dn,2,0) } 794 let argv: *i64 = sys_mmap(32) as *i64 795 argv[0]=HC_SH as i64; argv[1]=HC_DASHC as i64; argv[2]=HC_TORRENT_GW_CMD as i64; argv[3]=0 796 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 797 sys_execve_clean(HC_SH, argv, envp) 798 sys_exit(127) 799 } 800 return pid 801} 802// Guard the library reader: liveness + plaintext serving-probe (it serves the login shell on GET / => 200, 803// so hp_probe sees it serving) -> crash-loop-guarded restart-WITH-ARGS. hc_guard_one can't pass args. 804// PID-ONLY (port 0), NOT a serving-probe: ROOT-CAUSE (spawntest-proven) -- hp_probe connects, sends /healthz, 805// reads only 4096 bytes, then CLOSES; the reader writes the (larger) login-shell response into that closed 806// socket -> SIGPIPE -> the reader (no SIGPIPE handler) is TERMINATED. So the supervisor's own health-probe was 807// killing the reader every poll (a curl that reads fully never trips it -- which is why manual launch worked). 808// Liveness-only avoids touching the reader's socket. (The deeper fix = reader ignores SIGPIPE; tracked.) 809func hc_guard_reader(ws: *i64, cnt: *i64) -> i64 { 810 let alive: i64 = proc_alive_by_name("nx_media_server_auth.elf" as *u8) 811 if hc_needs_restart(alive, 0) == 0 { return 0 } 812 let now: i64 = sys_now_realtime_ms() 813 if rg_should_restart(ws, cnt, now, HC_GUARD_INTERVAL_MS, HC_GUARD_BURST) == 0 { 814 hc_puts(" [guard] nx_media_server_auth.elf crash-looping -> BACKING OFF (this window)\n" as *u8); return 0 815 } 816 if alive == 1 { proc_kill_by_name("nx_media_server_auth.elf" as *u8, 9) } 817 hc_spawn_reader() 818 hc_puts(" [guard] restarted nx_media_server_auth.elf (library reader, :8791)") 819 if alive == 1 { hc_puts(" (was HUNG)\n" as *u8) } else { hc_puts(" (was dead)\n" as *u8) } 820 return 1 821} 822// Guard the gallery serve/gateway: liveness-only (the listener is fork-per-request; a serving-probe could 823// SIGPIPE a request child, so we trust PID-liveness like sites.elf:8443) -> crash-loop-guarded respawn-with-args. 824func hc_guard_galx_serve(ws: *i64, cnt: *i64) -> i64 { 825 if proc_alive_by_name("nx_gallery_serve.elf" as *u8) == 1 { return 0 } 826 let now: i64 = sys_now_realtime_ms() 827 if rg_should_restart(ws, cnt, now, HC_GUARD_INTERVAL_MS, HC_GUARD_BURST) == 0 { 828 hc_puts(" [guard] nx_gallery_serve.elf crash-looping -> BACKING OFF\n" as *u8); return 0 829 } 830 hc_spawn_galx_serve() 831 hc_puts(" [guard] restarted nx_gallery_serve.elf :18090 (was dead)\n" as *u8) 832 return 1 833} 834// archive server :18801 -- same shape as hc_guard_galx_serve. PID-liveness only, no serving probe: 835// hp_probe sends /healthz, reads, then CLOSES, which is exactly what SIGPIPE-killed redirect.elf and the 836// reader every poll. Verify this one is serving with an external GET, never with the probe. 837func hc_guard_arcsrv(ws: *i64, cnt: *i64) -> i64 { 838 if proc_alive_by_name("nx_archive_server.elf" as *u8) == 1 { return 0 } 839 let now: i64 = sys_now_realtime_ms() 840 if rg_should_restart(ws, cnt, now, HC_GUARD_INTERVAL_MS, HC_GUARD_BURST) == 0 { 841 hc_puts(" [guard] nx_archive_server.elf crash-looping -> BACKING OFF\n" as *u8); return 0 842 } 843 hc_spawn_arcsrv() 844 hc_puts(" [guard] restarted nx_archive_server.elf :18801 (was dead)\n" as *u8) 845 return 1 846} 847func hc_guard_galx_gw(ws: *i64, cnt: *i64) -> i64 { 848 if proc_alive_by_name("nx_gallery_gateway.elf" as *u8) == 1 { return 0 } 849 let now: i64 = sys_now_realtime_ms() 850 if rg_should_restart(ws, cnt, now, HC_GUARD_INTERVAL_MS, HC_GUARD_BURST) == 0 { 851 hc_puts(" [guard] nx_gallery_gateway.elf crash-looping -> BACKING OFF\n" as *u8); return 0 852 } 853 hc_spawn_galx_gw() 854 hc_puts(" [guard] restarted nx_gallery_gateway.elf :18190 (was dead)\n" as *u8) 855 return 1 856} 857// Guard the wiki OPAQUE gateway (:18791): liveness-only (fork-per-request listener; a serving-probe could 858// SIGPIPE a request child, like the gallery) -> crash-loop-guarded respawn. This + the poll loop = the wiki 859// gateway is now permanently supervised: it auto-restarts on crash/kill forever, no manual setsid ever again. 860func hc_guard_wiki_gw(ws: *i64, cnt: *i64) -> i64 { 861 if proc_alive_by_name("nx_wiki_gw.elf" as *u8) == 1 { return 0 } 862 let now: i64 = sys_now_realtime_ms() 863 if rg_should_restart(ws, cnt, now, HC_GUARD_INTERVAL_MS, HC_GUARD_BURST) == 0 { 864 hc_puts(" [guard] nx_wiki_gw.elf crash-looping -> BACKING OFF\n" as *u8); return 0 865 } 866 hc_spawn_wiki_gw() 867 hc_puts(" [guard] restarted nx_wiki_gw.elf :18791 (was dead)\n" as *u8) 868 return 1 869} 870// Guard the hub OPAQUE gateway (:18792): liveness-only, crash-loop-guarded respawn (mirrors hc_guard_wiki_gw). 871// Until the binary is deployed to HC_HUB_GW it simply backs off (exec fails -> child exits) -- fail-safe, no harm. 872func hc_guard_hub_gw(ws: *i64, cnt: *i64) -> i64 { 873 if proc_alive_by_name("nx_hub_gw.elf" as *u8) == 1 { return 0 } 874 let now: i64 = sys_now_realtime_ms() 875 if rg_should_restart(ws, cnt, now, HC_GUARD_INTERVAL_MS, HC_GUARD_BURST) == 0 { 876 hc_puts(" [guard] nx_hub_gw.elf crash-looping -> BACKING OFF\n" as *u8); return 0 877 } 878 hc_spawn_hub_gw() 879 hc_puts(" [guard] restarted nx_hub_gw.elf :18792 (was dead)\n" as *u8) 880 return 1 881} 882// spawn the Relationship OS daemon (:8027 loopback) -- mirrors hc_spawn_hub_gw. 883func hc_spawn_relate() -> i64 { 884 let pid: i64 = sys_fork() 885 if pid==0 { 886 nx_setsid() 887 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 888 if dn >= 0 { sys_dup3(dn,0,0); sys_dup3(dn,1,0); sys_dup3(dn,2,0) } 889 let argv: *i64 = sys_mmap(32) as *i64 890 argv[0]=HC_SH as i64; argv[1]=HC_DASHC as i64; argv[2]=HC_RELATE_CMD as i64; argv[3]=0 891 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 892 sys_execve_clean(HC_SH, argv, envp) 893 sys_exit(127) 894 } 895 return pid 896} 897// Guard the Relationship OS daemon (:8027). liveness-only, crash-loop-guarded (mirrors hc_guard_mgmt). 898// Backs off until nx_relate_daemon.elf is promoted (exec fails -> child exits) -- fail-safe, no harm pre-deploy. 899func hc_guard_relate(ws: *i64, cnt: *i64) -> i64 { 900 if proc_alive_by_name("nx_relate_daemon.elf" as *u8) == 1 { return 0 } 901 let now: i64 = sys_now_realtime_ms() 902 if rg_should_restart(ws, cnt, now, HC_GUARD_INTERVAL_MS, HC_GUARD_BURST) == 0 { 903 hc_puts(" [guard] nx_relate_daemon.elf crash-looping -> BACKING OFF\n" as *u8); return 0 904 } 905 hc_spawn_relate() 906 hc_puts(" [guard] restarted nx_relate_daemon.elf :8027 (was dead)\n" as *u8) 907 return 1 908} 909// spawn the ethical CLEAN-SERVE daemon (:8102 -- /clean) -- mirrors hc_spawn_relate. 910func hc_spawn_cleanserve() -> i64 { 911 let pid: i64 = sys_fork() 912 if pid==0 { 913 nx_setsid() 914 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 915 if dn >= 0 { sys_dup3(dn,0,0); sys_dup3(dn,1,0); sys_dup3(dn,2,0) } 916 let argv: *i64 = sys_mmap(32) as *i64 917 argv[0]=HC_SH as i64; argv[1]=HC_DASHC as i64; argv[2]=HC_CLEANSERVE_CMD as i64; argv[3]=0 918 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 919 sys_execve_clean(HC_SH, argv, envp) 920 sys_exit(127) 921 } 922 return pid 923} 924// Guard the CLEAN-SERVE daemon (:8102). liveness-only, crash-loop-guarded (mirrors hc_guard_relate). 925// Backs off until nx_clean_serve_daemon.elf is promoted (exec fails -> child exits) -- fail-safe, no harm pre-deploy. 926func hc_guard_cleanserve(ws: *i64, cnt: *i64) -> i64 { 927 if proc_alive_by_name("nx_clean_serve_daemon.elf" as *u8) == 1 { return 0 } 928 let now: i64 = sys_now_realtime_ms() 929 if rg_should_restart(ws, cnt, now, HC_GUARD_INTERVAL_MS, HC_GUARD_BURST) == 0 { 930 hc_puts(" [guard] nx_clean_serve_daemon.elf crash-looping -> BACKING OFF\n" as *u8); return 0 931 } 932 hc_spawn_cleanserve() 933 hc_puts(" [guard] restarted nx_clean_serve_daemon.elf :8102 (was dead)\n" as *u8) 934 return 1 935} 936// spawn the WORKER MESH serve backend (:8029 -- console + SSE gen + gated image) -- mirrors hc_spawn_relate. 937func hc_spawn_mesh_serve() -> i64 { 938 let pid: i64 = sys_fork() 939 if pid==0 { 940 nx_setsid() 941 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 942 if dn >= 0 { sys_dup3(dn,0,0); sys_dup3(dn,1,0); sys_dup3(dn,2,0) } 943 let argv: *i64 = sys_mmap(32) as *i64 944 argv[0]=HC_SH as i64; argv[1]=HC_DASHC as i64; argv[2]=HC_MESH_SERVE_CMD as i64; argv[3]=0 945 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 946 sys_execve_clean(HC_SH, argv, envp) 947 sys_exit(127) 948 } 949 return pid 950} 951// Guard the mesh serve (:8029). liveness-only, crash-loop-guarded (mirrors hc_guard_relate). Backs off until 952// nx_mesh_serve.elf is present -- fail-safe, no harm pre-deploy. ADOPTS the running setsid daemon (no double-spawn). 953func hc_guard_mesh_serve(ws: *i64, cnt: *i64) -> i64 { 954 if proc_alive_by_name("nx_mesh_serve.elf" as *u8) == 1 { return 0 } 955 let now: i64 = sys_now_realtime_ms() 956 if rg_should_restart(ws, cnt, now, HC_GUARD_INTERVAL_MS, HC_GUARD_BURST) == 0 { 957 hc_puts(" [guard] nx_mesh_serve.elf crash-looping -> BACKING OFF\n" as *u8); return 0 958 } 959 hc_spawn_mesh_serve() 960 hc_puts(" [guard] restarted nx_mesh_serve.elf :8029 (was dead)\n" as *u8) 961 return 1 962} 963// spawn the WORKER MESH OPAQUE gateway (:8055 -- gated /mesh console) -- mirrors hc_spawn_relate. 964func hc_spawn_mesh_gw() -> i64 { 965 let pid: i64 = sys_fork() 966 if pid==0 { 967 nx_setsid() 968 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 969 if dn >= 0 { sys_dup3(dn,0,0); sys_dup3(dn,1,0); sys_dup3(dn,2,0) } 970 let argv: *i64 = sys_mmap(32) as *i64 971 argv[0]=HC_SH as i64; argv[1]=HC_DASHC as i64; argv[2]=HC_MESH_GW_CMD as i64; argv[3]=0 972 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 973 sys_execve_clean(HC_SH, argv, envp) 974 sys_exit(127) 975 } 976 return pid 977} 978// Guard the mesh gateway (:8055). liveness-only, crash-loop-guarded (mirrors hc_guard_relate). ADOPTS the running 979// setsid gateway; respawns it after crash/reboot -- this is the durability rung "run it like gallery" implies. 980func hc_guard_mesh_gw(ws: *i64, cnt: *i64) -> i64 { 981 if proc_alive_by_name("nx_mesh_gateway.elf" as *u8) == 1 { return 0 } 982 let now: i64 = sys_now_realtime_ms() 983 if rg_should_restart(ws, cnt, now, HC_GUARD_INTERVAL_MS, HC_GUARD_BURST) == 0 { 984 hc_puts(" [guard] nx_mesh_gateway.elf crash-looping -> BACKING OFF\n" as *u8); return 0 985 } 986 hc_spawn_mesh_gw() 987 hc_puts(" [guard] restarted nx_mesh_gateway.elf :8055 (was dead)\n" as *u8) 988 return 1 989} 990// spawn the /project OPAQUE gateway (:8028) -- mirrors hc_spawn_relate (sh -c exec detach). 991func hc_spawn_project() -> i64 { 992 let pid: i64 = sys_fork() 993 if pid==0 { 994 nx_setsid() 995 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 996 if dn >= 0 { sys_dup3(dn,0,0); sys_dup3(dn,1,0); sys_dup3(dn,2,0) } 997 let argv: *i64 = sys_mmap(32) as *i64 998 argv[0]=HC_SH as i64; argv[1]=HC_DASHC as i64; argv[2]=HC_PROJECT_CMD as i64; argv[3]=0 999 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 1000 sys_execve_clean(HC_SH, argv, envp) 1001 sys_exit(127) 1002 } 1003 return pid 1004} 1005// Guard the /project OPAQUE gateway (:8028; sites gated row /project). liveness-only, crash-loop-guarded 1006// (mirrors hc_guard_relate). Backs off until nx_project_serve.elf is deployed -- fail-safe, no harm; ADOPTS 1007// an already-running detached daemon (liveness by name) exactly like the synth/devapi guards. 1008func hc_guard_project(ws: *i64, cnt: *i64) -> i64 { 1009 if proc_alive_by_name("nx_project_serve.elf" as *u8) == 1 { return 0 } 1010 let now: i64 = sys_now_realtime_ms() 1011 if rg_should_restart(ws, cnt, now, HC_GUARD_INTERVAL_MS, HC_GUARD_BURST) == 0 { 1012 hc_puts(" [guard] nx_project_serve.elf crash-looping -> BACKING OFF\n" as *u8); return 0 1013 } 1014 hc_spawn_project() 1015 hc_puts(" [guard] restarted nx_project_serve.elf :8028 (was dead)\n" as *u8) 1016 return 1 1017} 1018// spawn the sovereign management API (:18099) -- mirrors hc_spawn_hub_gw. 1019func hc_spawn_mgmt() -> i64 { 1020 let pid: i64 = sys_fork() 1021 if pid==0 { 1022 nx_setsid() 1023 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 1024 if dn >= 0 { sys_dup3(dn,0,0); sys_dup3(dn,1,0); sys_dup3(dn,2,0) } 1025 let argv: *i64 = sys_mmap(32) as *i64 1026 argv[0]=HC_SH as i64; argv[1]=HC_DASHC as i64; argv[2]=HC_MGMT_CMD as i64; argv[3]=0 1027 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 1028 sys_execve_clean(HC_SH, argv, envp) 1029 sys_exit(127) 1030 } 1031 return pid 1032} 1033// Guard the sovereign MANAGEMENT API (:18099 loopback HTTP -> /api). liveness-only, crash-loop-guarded 1034// (mirrors hc_guard_hub_gw). Backs off until nx_mgmt_api.elf is deployed -- fail-safe, no harm. 1035func hc_guard_mgmt(ws: *i64, cnt: *i64) -> i64 { 1036 if proc_alive_by_name("nx_mgmt_api.elf" as *u8) == 1 { return 0 } 1037 let now: i64 = sys_now_realtime_ms() 1038 if rg_should_restart(ws, cnt, now, HC_GUARD_INTERVAL_MS, HC_GUARD_BURST) == 0 { 1039 hc_puts(" [guard] nx_mgmt_api.elf crash-looping -> BACKING OFF\n" as *u8); return 0 1040 } 1041 hc_spawn_mgmt() 1042 hc_puts(" [guard] restarted nx_mgmt_api.elf :18099 (was dead)\n" as *u8) 1043 return 1 1044} 1045// spawn the doc-portal ADMIN daemon (:8456) -- mirrors hc_spawn_mgmt. 1046func hc_spawn_admin() -> i64 { 1047 let pid: i64 = sys_fork() 1048 if pid==0 { 1049 nx_setsid() 1050 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 1051 if dn >= 0 { sys_dup3(dn,0,0); sys_dup3(dn,1,0); sys_dup3(dn,2,0) } 1052 let argv: *i64 = sys_mmap(32) as *i64 1053 argv[0]=HC_SH as i64; argv[1]=HC_DASHC as i64; argv[2]=HC_ADMIN_CMD as i64; argv[3]=0 1054 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 1055 sys_execve_clean(HC_SH, argv, envp) 1056 sys_exit(127) 1057 } 1058 return pid 1059} 1060// Guard the doc-portal ADMIN daemon (:8456 loopback HTTP -> admin.<d>/). liveness-only, crash-loop-guarded 1061// (mirrors hc_guard_mgmt). Backs off until nx_docportal_admin_daemon.elf is deployed -- fail-safe, no harm. 1062// LISTEN DEADLINE FOR THE ADMIN DAEMON (2026-09-02, debt 1788361379). MEASURED: after a SIGSEGV this guard respawned 1063// nx_docportal_admin_daemon and the respawn SPUN 15.9h of user CPU inside its shard pre-warm with :18456 NEVER opened -- 1064// and the name-only check below was satisfied the whole time, so nishifamily.com/search answered 503 for ~16h while 1065// /api/services and nx_health (both fed by the same proc_alive_by_name) read UP. A live process that never binds is 1066// the one state PID-liveness cannot see, and it is exactly the state a wedged start produces. 1067// WHY A GRACE AND NOT A PROBE-ON-EVERY-POLL: this daemon deliberately opens its port AFTER a warm-before-listen phase 1068// (10-15s measured at load 20) that can also wait on the tool-registry lock (6 min measured on 2026-09-01), so one 1069// refusal is not evidence and killing on it would manufacture the crash loop this file records for the reader. A 1070// refusal that PERSISTS past the grace is evidence. hp_socket_dead is connect-only (it never writes a byte -- the 1071// SIGPIPE class recorded twice in this file) and demands two refusals HP_CONFIRM_GAP_MS apart, paid only on the 1072// refused path. DERIVED, NOT GUESSED: 40 polls x the ~15s supervise cadence = 600s, the measured worst legitimate 1073// cold start (6 min lock wait + 13s warm) with margin; every waiting poll is ANNOUNCED so the bound is visible. 1074const HC_ADMIN_BIND_GRACE_POLLS: i64 = 40 1075static hc_adm_dead_polls: i64 1076func hc_guard_admin(ws: *i64, cnt: *i64) -> i64 { 1077 if proc_alive_by_name("nx_docportal_admin_daemon.elf" as *u8) == 1 { 1078 if hp_socket_dead(HC_MAGIC_18456) == 0 { hc_adm_dead_polls = 0; return 0 } 1079 hc_adm_dead_polls = hc_adm_dead_polls + 1 1080 hc_puts(" [guard] nx_docportal_admin_daemon.elf ALIVE but :18456 not listening -- bind-grace poll " as *u8); hc_putn(hc_adm_dead_polls) 1081 hc_puts(" of " as *u8); hc_putn(HC_ADMIN_BIND_GRACE_POLLS); hc_puts("\n" as *u8) 1082 if rg_bind_grace_expired(1, 1, hc_adm_dead_polls, HC_ADMIN_BIND_GRACE_POLLS) == 0 { return 0 } // PURE policy in nx_restart_guard, refereed by nx_restart_guard_gate T6-T10 1083 let now2: i64 = sys_now_realtime_ms() 1084 if rg_should_restart(ws, cnt, now2, HC_GUARD_INTERVAL_MS, HC_GUARD_BURST) == 0 { 1085 hc_puts(" [guard] nx_docportal_admin_daemon.elf crash-looping -> BACKING OFF\n" as *u8); return 0 1086 } 1087 let hk: i64 = proc_kill_by_name("nx_docportal_admin_daemon.elf" as *u8, 9) 1088 hc_adm_dead_polls = 0 1089 hc_spawn_admin() 1090 hc_puts(" [guard] restarted nx_docportal_admin_daemon.elf (:18456 never listened inside the bind grace; killed wedged procs=" as *u8); hc_putn(hk); hc_puts(") (was HUNG-AT-START)\n" as *u8) 1091 return 1 1092 } 1093 hc_adm_dead_polls = 0 1094 let now: i64 = sys_now_realtime_ms() 1095 if rg_should_restart(ws, cnt, now, HC_GUARD_INTERVAL_MS, HC_GUARD_BURST) == 0 { 1096 hc_puts(" [guard] nx_docportal_admin_daemon.elf crash-looping -> BACKING OFF\n" as *u8); return 0 1097 } 1098 hc_spawn_admin() 1099 // ⚠PORT CORRECTED 8456 -> 18456 AND THE CHECK NAMED (2026-07-30). This line claimed :8456 while 1100 // HC_ADMIN_CMD launches the daemon on 18456 -- and the guard above does not probe a port AT ALL, 1101 // it tests proc_alive_by_name. A message that names a port it never checks invites exactly the 1102 // wrong diagnosis: I read this line, saw the live daemon on :18456, and formed a port-table 1103 // hypothesis (seq1314 class) that was simply false. The guard was right; the message lied. 1104 // ★LAW: a diagnostic must describe the check that was ACTUALLY performed. Naming a port beside a 1105 // name-based liveness test is not a harmless typo -- it is a false lead with a plausible story. 1106 hc_puts(" [guard] restarted nx_docportal_admin_daemon.elf (:18456, liveness by PROCESS NAME not port) (was dead)\n" as *u8) 1107 return 1 1108} 1109// spawn the mail.<d> EMAIL-PORTAL daemon (:18465) -- mirrors hc_spawn_admin. 1110func hc_spawn_mailportal() -> i64 { 1111 let pid: i64 = sys_fork() 1112 if pid==0 { 1113 nx_setsid() 1114 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 1115 if dn >= 0 { sys_dup3(dn,0,0); sys_dup3(dn,1,0); sys_dup3(dn,2,0) } 1116 let argv: *i64 = sys_mmap(32) as *i64 1117 argv[0]=HC_SH as i64; argv[1]=HC_DASHC as i64; argv[2]=HC_MAILPORTAL_CMD as i64; argv[3]=0 1118 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 1119 sys_execve_clean(HC_SH, argv, envp) 1120 sys_exit(127) 1121 } 1122 return pid 1123} 1124// Guard the mail.<d> EMAIL-PORTAL daemon (:18465 loopback HTTP -> mail.<d>/). liveness-only, 1125// crash-loop-guarded; backs off until nx_email_portal_daemon.elf is deployed -- fail-safe, no harm. 1126func hc_guard_mailportal(ws: *i64, cnt: *i64) -> i64 { 1127 if proc_alive_by_name("nx_email_portal_daemon.elf" as *u8) == 1 { return 0 } 1128 let now: i64 = sys_now_realtime_ms() 1129 if rg_should_restart(ws, cnt, now, HC_GUARD_INTERVAL_MS, HC_GUARD_BURST) == 0 { 1130 hc_puts(" [guard] nx_email_portal_daemon.elf crash-looping -> BACKING OFF\n" as *u8); return 0 1131 } 1132 hc_spawn_mailportal() 1133 hc_puts(" [guard] restarted nx_email_portal_daemon.elf :18465 (was dead)\n" as *u8) 1134 return 1 1135} 1136// spawn the admin.<d>/site SITE-VISUALS editor daemon (:18466) -- mirrors hc_spawn_admin. 1137func hc_spawn_siteedit() -> i64 { 1138 let pid: i64 = sys_fork() 1139 if pid==0 { 1140 nx_setsid() 1141 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 1142 if dn >= 0 { sys_dup3(dn,0,0); sys_dup3(dn,1,0); sys_dup3(dn,2,0) } 1143 let argv: *i64 = sys_mmap(32) as *i64 1144 argv[0]=HC_SH as i64; argv[1]=HC_DASHC as i64; argv[2]=HC_SITEEDIT_CMD as i64; argv[3]=0 1145 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 1146 sys_execve_clean(HC_SH, argv, envp) 1147 sys_exit(127) 1148 } 1149 return pid 1150} 1151// Guard the site-visuals editor (:18466 loopback HTTP -> admin.<d>/site). liveness-only, 1152// crash-loop-guarded; backs off until nx_siteedit_daemon.elf is deployed -- fail-safe, no harm. 1153func hc_guard_siteedit(ws: *i64, cnt: *i64) -> i64 { 1154 if proc_alive_by_name("nx_siteedit_daemon.elf" as *u8) == 1 { return 0 } 1155 let now: i64 = sys_now_realtime_ms() 1156 if rg_should_restart(ws, cnt, now, HC_GUARD_INTERVAL_MS, HC_GUARD_BURST) == 0 { 1157 hc_puts(" [guard] nx_siteedit_daemon.elf crash-looping -> BACKING OFF\n" as *u8); return 0 1158 } 1159 hc_spawn_siteedit() 1160 hc_puts(" [guard] restarted nx_siteedit_daemon.elf :18466 (was dead)\n" as *u8) 1161 return 1 1162} 1163func hc_spawn_inventory() -> i64 { 1164 let pid: i64 = sys_fork() 1165 if pid==0 { 1166 nx_setsid() 1167 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 1168 if dn >= 0 { sys_dup3(dn,0,0); sys_dup3(dn,1,0); sys_dup3(dn,2,0) } 1169 let argv: *i64 = sys_mmap(32) as *i64 1170 argv[0]=HC_SH as i64; argv[1]=HC_DASHC as i64; argv[2]=HC_INVENTORY_CMD as i64; argv[3]=0 1171 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 1172 sys_execve_clean(HC_SH, argv, envp) 1173 sys_exit(127) 1174 } 1175 return pid 1176} 1177// Guard the inventory/ownership checker (:18122 LOOPBACK-ONLY -> /inventory via the proxy). 1178// Liveness-only + crash-loop-guarded, and it BACKS OFF rather than spinning if the elf is absent -- 1179// fail-safe by construction: a service that is not deployed must not be able to wedge the supervisor 1180// that every other service depends on. 1181func hc_guard_inventory(ws: *i64, cnt: *i64) -> i64 { 1182 if proc_alive_by_name("nx_inventory_serve.elf" as *u8) == 1 { return 0 } 1183 let now: i64 = sys_now_realtime_ms() 1184 if rg_should_restart(ws, cnt, now, HC_GUARD_INTERVAL_MS, HC_GUARD_BURST) == 0 { 1185 hc_puts(" [guard] nx_inventory_serve.elf crash-looping -> BACKING OFF\n" as *u8); return 0 1186 } 1187 hc_spawn_inventory() 1188 hc_puts(" [guard] restarted nx_inventory_serve.elf :18122 (was dead)\n" as *u8) 1189 return 1 1190} 1191// spawn the sovereign agent-facing TOOLS API (:18096) -- mirrors hc_spawn_mgmt. 1192func hc_spawn_tapi() -> i64 { 1193 let pid: i64 = sys_fork() 1194 if pid==0 { 1195 nx_setsid() 1196 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 1197 if dn >= 0 { sys_dup3(dn,0,0); sys_dup3(dn,1,0); sys_dup3(dn,2,0) } 1198 let argv: *i64 = sys_mmap(32) as *i64 1199 argv[0]=HC_SH as i64; argv[1]=HC_DASHC as i64; argv[2]=HC_TAPI_CMD as i64; argv[3]=0 1200 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 1201 sys_execve_clean(HC_SH, argv, envp) 1202 sys_exit(127) 1203 } 1204 return pid 1205} 1206// Guard the sovereign TOOLS API (:18096 loopback HTTP -> /api/tools + /mcp). liveness-only, crash-loop-guarded 1207// (mirrors hc_guard_mgmt). Backs off until nx_tools_api_serve.elf is deployed -- fail-safe, no harm. 1208func hc_guard_tapi(ws: *i64, cnt: *i64) -> i64 { 1209 if proc_alive_by_name("nx_tools_api_serve.elf" as *u8) == 1 { return 0 } 1210 let now: i64 = sys_now_realtime_ms() 1211 if rg_should_restart(ws, cnt, now, HC_GUARD_INTERVAL_MS, HC_GUARD_BURST) == 0 { 1212 hc_puts(" [guard] nx_tools_api_serve.elf crash-looping -> BACKING OFF\n" as *u8); return 0 1213 } 1214 hc_spawn_tapi() 1215 hc_puts(" [guard] restarted nx_tools_api_serve.elf :18096 (was dead)\n" as *u8) 1216 return 1 1217} 1218// spawn the finance dashboard server (:18097) -- mirrors hc_spawn_tapi exactly. 1219func hc_spawn_fin() -> i64 { 1220 let pid: i64 = sys_fork() 1221 if pid==0 { 1222 nx_setsid() 1223 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 1224 if dn >= 0 { sys_dup3(dn,0,0); sys_dup3(dn,1,0); sys_dup3(dn,2,0) } 1225 let argv: *i64 = sys_mmap(32) as *i64 1226 argv[0]=HC_SH as i64; argv[1]=HC_DASHC as i64; argv[2]=HC_FIN_CMD as i64; argv[3]=0 1227 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 1228 sys_execve_clean(HC_SH, argv, envp) 1229 sys_exit(127) 1230 } 1231 return pid 1232} 1233// Guard the finance dashboard server (:18097 loopback HTTP -> /finance). liveness-only, crash-loop-guarded 1234// (mirrors hc_guard_tapi). Backs off until nx_fin_serve.elf is deployed -- fail-safe, no harm. 1235func hc_guard_fin(ws: *i64, cnt: *i64) -> i64 { 1236 if proc_alive_by_name("nx_fin_serve.elf" as *u8) == 1 { return 0 } 1237 let now: i64 = sys_now_realtime_ms() 1238 if rg_should_restart(ws, cnt, now, HC_GUARD_INTERVAL_MS, HC_GUARD_BURST) == 0 { 1239 hc_puts(" [guard] nx_fin_serve.elf crash-looping -> BACKING OFF\n" as *u8); return 0 1240 } 1241 hc_spawn_fin() 1242 hc_puts(" [guard] restarted nx_fin_serve.elf :18097 (was dead)\n" as *u8) 1243 return 1 1244} 1245// spawn the CONTINUOUS M&E evaluator (nx_health_eval loop) -- mirrors hc_spawn_fin exactly (sh -c exec 1246// detach; direct child so proc_alive_by_name finds it). It loops forever; if it ever dies, respawned. 1247func hc_spawn_health() -> i64 { 1248 let pid: i64 = sys_fork() 1249 if pid==0 { 1250 nx_setsid() 1251 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 1252 if dn >= 0 { sys_dup3(dn,0,0); sys_dup3(dn,1,0); sys_dup3(dn,2,0) } 1253 let argv: *i64 = sys_mmap(32) as *i64 1254 argv[0]=HC_SH as i64; argv[1]=HC_DASHC as i64; argv[2]=HC_HEALTH_CMD as i64; argv[3]=0 1255 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 1256 sys_execve_clean(HC_SH, argv, envp) 1257 sys_exit(127) 1258 } 1259 return pid 1260} 1261// Guard the M&E evaluator loop. PID-liveness only (no port -- outbound TLS, listens on nothing), crash-loop 1262// guarded (mirrors hc_guard_fin). Backs off until nx_health_eval.elf + data/mozilla_certdata.txt are 1263// deployed -- fail-safe, no harm to anything else (a missing binary just means no respawn, never a brick). 1264func hc_guard_health(ws: *i64, cnt: *i64) -> i64 { 1265 if proc_alive_by_name("nx_health_eval.elf" as *u8) == 1 { return 0 } 1266 let now: i64 = sys_now_realtime_ms() 1267 if rg_should_restart(ws, cnt, now, HC_GUARD_INTERVAL_MS, HC_GUARD_BURST) == 0 { 1268 hc_puts(" [guard] nx_health_eval.elf crash-looping -> BACKING OFF\n" as *u8); return 0 1269 } 1270 hc_spawn_health() 1271 hc_puts(" [guard] restarted nx_health_eval.elf (continuous M&E loop, was dead)\n" as *u8) 1272 return 1 1273} 1274// spawn the video signaling/media relay (:8445) -- mirrors hc_spawn_health exactly. 1275func hc_spawn_sig2() -> i64 { 1276 let pid: i64 = sys_fork() 1277 if pid==0 { 1278 nx_setsid() 1279 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 1280 if dn >= 0 { sys_dup3(dn,0,0); sys_dup3(dn,1,0); sys_dup3(dn,2,0) } 1281 let argv: *i64 = sys_mmap(32) as *i64 1282 argv[0]=HC_SH as i64; argv[1]=HC_DASHC as i64; argv[2]=HC_SIG2_CMD as i64; argv[3]=0 1283 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 1284 sys_execve_clean(HC_SH, argv, envp) 1285 sys_exit(127) 1286 } 1287 return pid 1288} 1289// Guard the signaling relay (task #26). PID-liveness ("nx_signaling_v2" needle ADOPTS one already running 1290// from any path), crash-loop guarded. Backs off until the ELF is deployed to the canonical path -- fail-safe. 1291func hc_guard_sig2(ws: *i64, cnt: *i64) -> i64 { 1292 if proc_alive_by_name("nx_signaling_v2" as *u8) == 1 { return 0 } 1293 let now: i64 = sys_now_realtime_ms() 1294 if rg_should_restart(ws, cnt, now, HC_GUARD_INTERVAL_MS, HC_GUARD_BURST) == 0 { 1295 hc_puts(" [guard] nx_signaling_v2 crash-looping -> BACKING OFF\n" as *u8); return 0 1296 } 1297 hc_spawn_sig2() 1298 hc_puts(" [guard] restarted nx_signaling_v2 :8445 (video signaling relay, was dead)\n" as *u8) 1299 return 1 1300} 1301// ---- FLEET SUPERVISOR UNDER THE HOST SUPERVISOR (2026-09-02) ------------------------------------------- 1302// MEASURED: nx_execsurface's census row for nx_daemon_supervisor reads clock=0 cron=0 daemon=0 roster=0 1303// actlog=0 fork=- : NOTHING in the estate launches the fleet supervisor. Its running instance was started 1304// by a one-shot operator shell, so it could not be recycled (5.9 GB swapped, EXEMPT-TERM in resgov because 1305// nothing would revive it) and no deploy row could name-kill it. hostctl is the natural parent: the cron 1306// sentinel keeps exactly one hostctl supervise alive, and hostctl already parents the clock the same way. 1307// ADOPTS a running instance by name (PID-liveness only, no port probe: its /status is a courtesy, not a 1308// contract), respawns after death or a deploy-kill, crash-loop guarded. This is NOT a second supervisor 1309// for the daemons.reg fleet -- it supervises the supervisor, one level up, and touches none of its rows. 1310// SOVEREIGN SPAWN (2026-09-02): this used to exec `/bin/sh -c "cd ... && chmod +x ./nx_daemon_supervisor.elf; exec 1311// ./nx_daemon_supervisor.elf >>/tmp/..."` -- a shell in the run path of the ROOT guard (the estate's own law: NishiLang 1312// for everything, shell is break-glass), and a fork literal opening with "cd " that the execution-surface census cannot 1313// credit, so `nx_execsurface who nx_daemon_supervisor` answered NO-SURFACE for the very organ this guard respawns. 1314// Direct execve now: chdir + chmod + the same stdout log, argv[0] = "./nx_daemon_supervisor.elf" (the literal the census 1315// keys on). Behaviour is otherwise byte-for-byte: setsid, stdin on /dev/null, cwd nishihost, no inherited fds. 1316const HC_DSUP_ELF: *u8 = "/volume1/homes/elderwesto/nishihost/nx_daemon_supervisor.elf" as *u8 1317const HC_DSUP_ARGV0: *u8 = "./nx_daemon_supervisor.elf" as *u8 1318const HC_DSUP_STDOUT: *u8 = "/tmp/daemon_supervisor_stdout.log" as *u8 1319func hc_spawn_dsup() -> i64 { 1320 let pid: i64 = sys_fork() 1321 if pid==0 { 1322 nx_setsid() 1323 sys_chdir("/volume1/homes/elderwesto/nishihost" as *u8) 1324 nx_chmod(HC_DSUP_ELF, 0x1ed) 1325 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 1326 if dn >= 0 { sys_dup3(dn,0,0) } 1327 let lf: i64 = sys_openat_append(HC_DSUP_STDOUT, 420) 1328 if lf >= 0 { sys_dup3(lf,1,0); sys_dup3(lf,2,0) } 1329 let argv: *i64 = sys_mmap(32) as *i64 1330 argv[0]=HC_DSUP_ARGV0 as i64; argv[1]=0 1331 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 1332 sys_execve_clean(HC_DSUP_ELF, argv, envp) 1333 sys_exit(127) 1334 } 1335 return pid 1336} 1337// LIVENESS BY BEHAVIOUR, NOT BY NAME (2026-09-02, second cut). The first cut adopted the supervisor by PID 1338// name alone -- and its /status server is a FORKED CHILD of the same binary that outlives its parent, so a 1339// dead fleet supervisor with a living orphan child read ALIVE by name forever: the guard that never fires, 1340// measured the day it shipped (a sink deploy rolled back NEVER-LISTENED because the fleet it relied on had 1341// no living supervisor while this guard said it did). The discriminator is the cycle counter the PARENT 1342// writes into the fork-shared /status page every loop: an orphan can only ever serve the last value. The 1343// DECISION is rg_stale_cycle in nx_restart_guard (pure, gate-pinned): alive means name-alive AND the cycle 1344// advanced within HC_DSUP_STALE_POLLS polls. Stale that long = dead or wedged parent -> kill BY NAME (takes 1345// the orphan too, freeing :18095 for the respawn) -> respawn under the crash-loop guard. A /status that does 1346// not ANSWER is not treated as dead (its own header calls it a courtesy); only a cycle that answers and never 1347// moves is. st: [0]=last cycle seen, [1]=consecutive polls without an advance. 1348// DERIVED, not picked (2026-09-02, after a 4-poll bound was measured to be tighter than one honest cycle under an I/O 1349// storm): the supervisor's worst-case cycle is DS_MAX (64) rows x DS_PROBE_TMO (3 s) plus the 8 s registry verify, 1350// about 200 s, so the bound is the first poll count whose 15 s loop clears that with a full cycle of margin. 1351const HC_DSUP_STALE_POLLS: i64 = 20 // 20 polls x 15 s = 300 s with no advance 1352// SILENCE WITH A NAME: an orphaned /status child answers with no counter forever; after this many consecutive 1353// silent polls with the name alive, kill by name and respawn (rg_silent_stale, measured 2026-09-02). Ten minutes: 1354// longer than any honest first cycle, shorter than the hours the fleet went unsupervised that day. 1355const HC_DSUP_SILENT_POLLS: i64 = 40 1356const HC_DSUP_READ_TMO_MS: i64 = 3000 1357const HC_DSUP_STATUS_REQ: *u8 = "GET /status HTTP/1.1\r\nHost: 127.0.0.1:18095\r\nConnection: close\r\n\r\n" 1358const HC_DSUP_CYCLE_KEY: *u8 = "cycle=" 1359// PROGRESS TICK (2026-09-02): the supervisor now publishes a MONOTONIC `tick=` after EVERY row (nx_daemon_supervisor 1360// ds_trailer), so the guard measures row progress, not cycle progress. Measured the same day: `cycle=` sat at 1 for 14 min 1361// under an I/O storm while the bound below was derived from a 200 s worst-case CYCLE -- the guard would have killed a 1362// supervisor that was alive and working, i.e. converted a slow start into an unrecoverable one (the wedge-kill class). 1363// `tick=` is read FIRST; a body without it (an older supervisor) falls back to `cycle=`, so the guard never goes blind. 1364const HC_DSUP_TICK_KEY: *u8 = "tick=" 1365// the integer after the FIRST `key` in buf[bs..n); -1 when the key is absent 1366func hc_dsup_scan(buf: *u8, bs: i64, n: i64, key: *u8) -> i64 { 1367 var v: i64 = 0 - 1 1368 let kn: i64 = hc_slen(key) 1369 var i: i64 = bs 1370 while i + kn <= n { 1371 var k: i64 = 0; var hit: i64 = 1 1372 while k < kn { if buf[i+k] != key[k] { hit=0; k=kn } else { k=k+1 } } 1373 if hit == 1 { 1374 v = 0 1375 var p: i64 = i + kn 1376 while p < n { let c: i64 = buf[p] as i64; if c >= 48 { if c <= 57 { v = v*10 + (c-48); p=p+1 } else { p=n } } else { p=n } } 1377 i = n 1378 } 1379 i = i + 1 1380 } 1381 return v 1382} 1383// the supervisor's progress counter from the /status body: `tick=` (row-granular, 2026-09-02) else `cycle=` (older 1384// supervisors); -1 when nothing answered or neither key is present 1385func hc_dsup_cycle() -> i64 { 1386 let buf: *u8 = sys_mmap(HC_MAGIC_16384); let tp: *i64 = sys_mmap(16) as *i64 1387 let bs: i64 = hc_loopback_req_port(HC_MAGIC_18095, HC_DSUP_STATUS_REQ, buf, HC_MAGIC_16384, tp, HC_DSUP_READ_TMO_MS) 1388 var v: i64 = 0 - 1 1389 if bs >= 0 { 1390 v = hc_dsup_scan(buf, bs, tp[0], HC_DSUP_TICK_KEY) 1391 if v < 0 { v = hc_dsup_scan(buf, bs, tp[0], HC_DSUP_CYCLE_KEY) } 1392 } 1393 sys_munmap(buf, HC_MAGIC_16384); sys_munmap(tp as *u8, 16) 1394 return v 1395} 1396func hc_guard_dsup(ws: *i64, cnt: *i64, st: *i64) -> i64 { 1397 if proc_alive_by_name("nx_daemon_supervisor" as *u8) == 1 { 1398 let cyc: i64 = hc_dsup_cycle() 1399 let stale: i64 = rg_stale_cycle(st, cyc, HC_DSUP_STALE_POLLS) 1400 let silent: i64 = rg_silent_stale(st, cyc, HC_DSUP_SILENT_POLLS) 1401 if stale + silent == 0 { return 0 } 1402 if stale == 1 { 1403 hc_puts(" [guard] nx_daemon_supervisor: name-alive but /status cycle=" as *u8); hc_putn(cyc) 1404 hc_puts(" has not advanced for " as *u8); hc_putn(HC_DSUP_STALE_POLLS) 1405 hc_puts(" polls -- an orphan /status child serving a dead parent's snapshot, or a wedged parent; killing by name and respawning\n" as *u8) 1406 } 1407 if silent == 1 { 1408 hc_puts(" [guard] nx_daemon_supervisor: name-alive but /status has carried NO cycle counter for " as *u8); hc_putn(HC_DSUP_SILENT_POLLS) 1409 hc_puts(" polls -- the orphaned status child of a parent that died before its first publish (measured 2026-09-02); killing by name and respawning\n" as *u8) 1410 } 1411 proc_kill_by_name("nx_daemon_supervisor" as *u8, 9) 1412 } 1413 let now: i64 = sys_now_realtime_ms() 1414 if rg_should_restart(ws, cnt, now, HC_GUARD_INTERVAL_MS, HC_GUARD_BURST) == 0 { 1415 hc_puts(" [guard] nx_daemon_supervisor crash-looping -> BACKING OFF\n" as *u8); return 0 1416 } 1417 hc_spawn_dsup() 1418 hc_puts(" [guard] restarted nx_daemon_supervisor (fleet supervisor over daemons.reg; it was dead, or stale by the line above -- it had no parent before 2026-09-02)\n" as *u8) 1419 return 1 1420} 1421// spawn the /dist download server (:18120) -- mirrors hc_spawn_fin exactly. 1422func hc_spawn_dist() -> i64 { 1423 let pid: i64 = sys_fork() 1424 if pid==0 { 1425 nx_setsid() 1426 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 1427 if dn >= 0 { sys_dup3(dn,0,0); sys_dup3(dn,1,0); sys_dup3(dn,2,0) } 1428 let argv: *i64 = sys_mmap(32) as *i64 1429 argv[0]=HC_SH as i64; argv[1]=HC_DASHC as i64; argv[2]=HC_DIST_CMD as i64; argv[3]=0 1430 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 1431 sys_execve_clean(HC_SH, argv, envp) 1432 sys_exit(127) 1433 } 1434 return pid 1435} 1436// Guard the /dist download server (:18120 -> /dist). liveness-only, crash-loop-guarded (mirrors hc_guard_fin). 1437// Backs off until nx_dist_serve.elf is deployed -- fail-safe. proc_alive_by_name matches the full cmdline (long name safe). 1438func hc_guard_dist(ws: *i64, cnt: *i64) -> i64 { 1439 if proc_alive_by_name("nx_dist_serve.elf" as *u8) == 1 { return 0 } 1440 let now: i64 = sys_now_realtime_ms() 1441 if rg_should_restart(ws, cnt, now, HC_GUARD_INTERVAL_MS, HC_GUARD_BURST) == 0 { 1442 hc_puts(" [guard] nx_dist_serve.elf crash-looping -> BACKING OFF\n" as *u8); return 0 1443 } 1444 hc_spawn_dist() 1445 hc_puts(" [guard] restarted nx_dist_serve.elf :18120 (was dead)\n" as *u8) 1446 return 1 1447} 1448// spawn the BitTorrent SEEDER (:6881) -- mirrors hc_spawn_dist exactly. 1449func hc_spawn_seed() -> i64 { 1450 let pid: i64 = sys_fork() 1451 if pid==0 { 1452 nx_setsid() 1453 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 1454 if dn >= 0 { sys_dup3(dn,0,0); sys_dup3(dn,1,0); sys_dup3(dn,2,0) } 1455 let argv: *i64 = sys_mmap(32) as *i64 1456 argv[0]=HC_SH as i64; argv[1]=HC_DASHC as i64; argv[2]=HC_SEED_CMD as i64; argv[3]=0 1457 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 1458 sys_execve_clean(HC_SH, argv, envp) 1459 sys_exit(127) 1460 } 1461 return pid 1462} 1463// Guard the BitTorrent seeder (:6881, inbound peers). liveness-only, crash-loop-guarded (mirrors hc_guard_dist). 1464// Backs off until nx_torrent_seed.elf is deployed -- fail-safe. proc_alive_by_name matches the full cmdline. 1465func hc_guard_seed(ws: *i64, cnt: *i64) -> i64 { 1466 if proc_alive_by_name("nx_torrent_seed.elf" as *u8) == 1 { return 0 } 1467 let now: i64 = sys_now_realtime_ms() 1468 if rg_should_restart(ws, cnt, now, HC_GUARD_INTERVAL_MS, HC_GUARD_BURST) == 0 { 1469 hc_puts(" [guard] nx_torrent_seed.elf crash-looping -> BACKING OFF\n" as *u8); return 0 1470 } 1471 hc_spawn_seed() 1472 hc_puts(" [guard] restarted nx_torrent_seed.elf :6881 (was dead)\n" as *u8) 1473 return 1 1474} 1475// spawn the DHT announcer -- mirrors hc_spawn_seed exactly. 1476func hc_spawn_seedann() -> i64 { 1477 let pid: i64 = sys_fork() 1478 if pid==0 { 1479 nx_setsid() 1480 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 1481 if dn >= 0 { sys_dup3(dn,0,0); sys_dup3(dn,1,0); sys_dup3(dn,2,0) } 1482 let argv: *i64 = sys_mmap(32) as *i64 1483 argv[0]=HC_SH as i64; argv[1]=HC_DASHC as i64; argv[2]=HC_SEEDANN_CMD as i64; argv[3]=0 1484 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 1485 sys_execve_clean(HC_SH, argv, envp) 1486 sys_exit(127) 1487 } 1488 return pid 1489} 1490// Guard the DHT announcer (makes the seeder discoverable). liveness-only, crash-loop-guarded (mirrors hc_guard_seed). 1491func hc_guard_seedann(ws: *i64, cnt: *i64) -> i64 { 1492 if proc_alive_by_name("nx_seed_announce_all.elf" as *u8) == 1 { return 0 } 1493 let now: i64 = sys_now_realtime_ms() 1494 if rg_should_restart(ws, cnt, now, HC_GUARD_INTERVAL_MS, HC_GUARD_BURST) == 0 { 1495 hc_puts(" [guard] nx_seed_announce_all.elf crash-looping -> BACKING OFF\n" as *u8); return 0 1496 } 1497 hc_spawn_seedann() 1498 hc_puts(" [guard] restarted nx_seed_announce_all.elf (DHT announcer, was dead)\n" as *u8) 1499 return 1 1500} 1501// spawn the multiplayer game relay (:7702) -- mirrors hc_spawn_fin exactly. 1502func hc_spawn_mp() -> i64 { 1503 let pid: i64 = sys_fork() 1504 if pid==0 { 1505 nx_setsid() 1506 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 1507 if dn >= 0 { sys_dup3(dn,0,0); sys_dup3(dn,1,0); sys_dup3(dn,2,0) } 1508 let argv: *i64 = sys_mmap(32) as *i64 1509 argv[0]=HC_SH as i64; argv[1]=HC_DASHC as i64; argv[2]=HC_MP_CMD as i64; argv[3]=0 1510 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 1511 sys_execve_clean(HC_SH, argv, envp) 1512 sys_exit(127) 1513 } 1514 return pid 1515} 1516// Guard the multiplayer game relay (:7702 loopback HTTP -> /mineworld-mp + /pstate + /roster). liveness-only, 1517// crash-loop-guarded (mirrors hc_guard_fin). Backs off until nx_mp_serve.elf is deployed -- fail-safe, no harm. 1518func hc_guard_mp(ws: *i64, cnt: *i64) -> i64 { 1519 if proc_alive_by_name("nx_mp_serve.elf" as *u8) == 1 { return 0 } 1520 let now: i64 = sys_now_realtime_ms() 1521 if rg_should_restart(ws, cnt, now, HC_GUARD_INTERVAL_MS, HC_GUARD_BURST) == 0 { 1522 hc_puts(" [guard] nx_mp_serve.elf crash-looping -> BACKING OFF\n" as *u8); return 0 1523 } 1524 hc_spawn_mp() 1525 hc_puts(" [guard] restarted nx_mp_serve.elf :7702 (was dead)\n" as *u8) 1526 return 1 1527} 1528// Guard the gen orchestrator (:18795) + gateway (:18794): liveness-only, crash-loop-guarded respawn 1529// (mirrors hc_guard_hub_gw). Fail-safe: backs off until the elfs land in /volume1/ai/gen (gendeploy). 1530func hc_guard_gen_orch(ws: *i64, cnt: *i64) -> i64 { 1531 if proc_alive_by_name("nx_gen_orchestrator_daemon.elf" as *u8) == 1 { return 0 } 1532 let now: i64 = sys_now_realtime_ms() 1533 if rg_should_restart(ws, cnt, now, HC_GUARD_INTERVAL_MS, HC_GUARD_BURST) == 0 { 1534 hc_puts(" [guard] nx_gen_orchestrator_daemon.elf crash-looping -> BACKING OFF\n" as *u8); return 0 1535 } 1536 hc_spawn_gen_orch() 1537 hc_puts(" [guard] restarted nx_gen_orchestrator_daemon.elf :18795 (was dead)\n" as *u8) 1538 return 1 1539} 1540func hc_guard_gen_gw(ws: *i64, cnt: *i64) -> i64 { 1541 if proc_alive_by_name("nx_gen_gateway_daemon.elf" as *u8) == 1 { return 0 } 1542 let now: i64 = sys_now_realtime_ms() 1543 if rg_should_restart(ws, cnt, now, HC_GUARD_INTERVAL_MS, HC_GUARD_BURST) == 0 { 1544 hc_puts(" [guard] nx_gen_gateway_daemon.elf crash-looping -> BACKING OFF\n" as *u8); return 0 1545 } 1546 hc_spawn_gen_gw() 1547 hc_puts(" [guard] restarted nx_gen_gateway_daemon.elf :18794 (was dead)\n" as *u8) 1548 return 1 1549} 1550// Guard the SYNTH STUDIO daemon (:18796): liveness-only, crash-loop-guarded respawn (mirrors hc_guard_gen_orch). 1551// Fail-safe: backs off until nx_synth_serve_daemon.elf is in /volume1/ai/synth. ADOPTS the already-running 1552// detached daemon (proc_alive -> no double-spawn); respawns it after a crash or NAS reboot = durability. 1553func hc_guard_synth(ws: *i64, cnt: *i64) -> i64 { 1554 if proc_alive_by_name("nx_synth_serve_daemon.elf" as *u8) == 1 { return 0 } 1555 let now: i64 = sys_now_realtime_ms() 1556 if rg_should_restart(ws, cnt, now, HC_GUARD_INTERVAL_MS, HC_GUARD_BURST) == 0 { 1557 hc_puts(" [guard] nx_synth_serve_daemon.elf crash-looping -> BACKING OFF\n" as *u8); return 0 1558 } 1559 hc_spawn_synth() 1560 hc_puts(" [guard] restarted nx_synth_serve_daemon.elf :18796 (was dead)\n" as *u8) 1561 return 1 1562} 1563// Guard the DEV/CI API daemon (:18797): liveness-only, crash-loop-guarded respawn (mirrors hc_guard_synth). 1564// Fail-safe: backs off until nx_dev_api_serve.elf is in /volume1/ai/devapi. ADOPTS the already-running daemon. 1565func hc_guard_devapi(ws: *i64, cnt: *i64) -> i64 { 1566 if proc_alive_by_name("nx_dev_api_serve.elf" as *u8) == 1 { return 0 } 1567 let now: i64 = sys_now_realtime_ms() 1568 if rg_should_restart(ws, cnt, now, HC_GUARD_INTERVAL_MS, HC_GUARD_BURST) == 0 { 1569 hc_puts(" [guard] nx_dev_api_serve.elf crash-looping -> BACKING OFF\n" as *u8); return 0 1570 } 1571 hc_spawn_devapi() 1572 hc_puts(" [guard] restarted nx_dev_api_serve.elf :18797 (was dead)\n" as *u8) 1573 return 1 1574} 1575// kickdevapi: kill the running dev-API daemon so the guard respawns the freshly-shipped binary (the update path). 1576func cmd_kickdevapi() -> i64 { 1577 let pk: i64 = proc_kill_by_name("nx_dev_api_serve.elf" as *u8, 9) 1578 hc_puts(" kickdevapi: killed nx_dev_api_serve.elf procs=" as *u8); hc_putn(pk) 1579 hc_puts(" -> guard respawns the NEW binary\n" as *u8) 1580 return 0 1581} 1582// devapitoolchain: extract the shipped nx_cc toolchain tarball in the dev-API dir so /api/dev/build+/gate compile ON 1583// the hub (the hub becomes a self-sufficient build host). Idempotent; chmods the binaries. Prints the tar exit code. 1584func cmd_devapitoolchain() -> i64 { 1585 let pid: i64 = sys_fork() 1586 if pid == 0 { 1587 let argv: *i64 = sys_mmap(32) as *i64 1588 argv[0]=HC_SH as i64; argv[1]=HC_DASHC as i64 1589 argv[2]="cd /volume1/ai/devapi && tar xzf nx_toolchain.tar.gz && chmod +x _offc/*.elf && echo TOOLCHAIN-EXTRACTED-OK && ./_offc/nx_sov_build_run.elf nx_bytes_eq 2>&1 | tail -1" as *u8 as i64; argv[3]=0 1590 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin:/usr/local/bin:/opt/bin" as *u8 as i64; envp[1]=0 1591 sys_execve_clean(HC_SH, argv, envp) 1592 sys_exit(127) 1593 } 1594 let st: *i64 = sys_mmap(16) as *i64 1595 sys_wait4(pid, st, 0) 1596 hc_puts(" devapitoolchain: tar xzf nx_toolchain.tar.gz -> /volume1/ai/devapi/ (exit=" as *u8); hc_putn((st[0]>>8)&255); hc_puts(")\n" as *u8) 1597 return 0 1598} 1599// Guard the media/torrent daemon (:8097): liveness-only (PID by name), crash-loop-guarded respawn via the 1600// portable launcher. Until the bundle is deployed to /volume1/ai/torrent the exec fails -> child exits -> 1601// it simply backs off (fail-safe, no harm to the rest of the fleet -- each guard is independent). 1602func hc_guard_torrent(ws: *i64, cnt: *i64) -> i64 { 1603 if proc_alive_by_name("nx_torrent_daemon" as *u8) == 1 { return 0 } 1604 let now: i64 = sys_now_realtime_ms() 1605 if rg_should_restart(ws, cnt, now, HC_GUARD_INTERVAL_MS, HC_GUARD_BURST) == 0 { 1606 hc_puts(" [guard] nx_torrent_daemon crash-looping -> BACKING OFF\n" as *u8); return 0 1607 } 1608 hc_spawn_torrent() 1609 hc_puts(" [guard] restarted nx_torrent_daemon :8097 (was dead)\n" as *u8) 1610 return 1 1611} 1612// Guard the torrent OPAQUE gateway (:18793): liveness-only, crash-loop-guarded respawn. Fail-safe: backs off 1613// until the gateway elf is deployed to /volume1/ai/torrent. 1614func hc_guard_torrent_gw(ws: *i64, cnt: *i64) -> i64 { 1615 if proc_alive_by_name("nx_torrent_gw.elf" as *u8) == 1 { return 0 } 1616 let now: i64 = sys_now_realtime_ms() 1617 if rg_should_restart(ws, cnt, now, HC_GUARD_INTERVAL_MS, HC_GUARD_BURST) == 0 { 1618 hc_puts(" [guard] nx_torrent_gw.elf crash-looping -> BACKING OFF\n" as *u8); return 0 1619 } 1620 hc_spawn_torrent_gw() 1621 hc_puts(" [guard] restarted nx_torrent_gw.elf :18793 (was dead)\n" as *u8) 1622 return 1 1623} 1624// Guard the login OPAQUE daemon (:9091, the token minter): liveness-only respawn. Was UNSUPERVISED (a real 1625// fragility -- a crash = site-wide login outage); now auto-restarts forever. Adopts the running one (proc_alive). 1626func hc_guard_login(ws: *i64, cnt: *i64) -> i64 { 1627 if proc_alive_by_name("nx_opaque_login.elf" as *u8) == 1 { return 0 } 1628 let now: i64 = sys_now_realtime_ms() 1629 if rg_should_restart(ws, cnt, now, HC_GUARD_INTERVAL_MS, HC_GUARD_BURST) == 0 { 1630 hc_puts(" [guard] nx_opaque_login.elf crash-looping -> BACKING OFF\n" as *u8); return 0 1631 } 1632 hc_spawn_login() 1633 hc_puts(" [guard] restarted nx_opaque_login.elf :9091 (was dead)\n" as *u8) 1634 return 1 1635} 1636 1637// DEDICATED READER-KEEPER: fork+exec the reader and BLOCKING-wait4 it, respawn on death, forever. This is 1638// EXACTLY the spawntest pattern that PROVABLY keeps the reader alive -- whereas spawning it from the poll loop 1639// (which then nanosleeps + rescans /proc) reliably kills it ~15s later (cause still unidentified, but the 1640// blocking-wait parent keeps it up; spawntest-proven). The reader is THIS keeper's child, so the main poll 1641// loop never touches it. The keeper logs each death's sig/code. Run as a forked child of cmd_supervise. 1642// PURE respawn-delay policy, extracted so it can be reasoned about and gated independently. 1643// Returns ms to wait before the next respawn attempt. 1644// WHY IT EXISTS (defect found 2026-07-20 while diagnosing a wedged NAS): the original breaker 1645// counted ONLY fast (<5s) code=1 bind-fails -- but this very function's own header documents the 1646// reader's real failure mode as "reliably kills it ~15s later". 15s > 5s, so isfast was never set, 1647// fastfails never reached 8, and THE CIRCUIT-BREAKER WAS DEAD CODE FOR THE ONLY FAILURE THAT 1648// ACTUALLY HAPPENS: an unbounded ~1-per-16s respawn that never escalates and never gives up. 1649// Now ANY repeatedly short-lived death escalates. Still never stops retrying and never deletes 1650// anything, so it self-heals the instant the underlying cause clears (never-brick preserved). 1651func hc_keep_delay(fastfails: i64, shortfails: i64) -> i64 { 1652 if fastfails >= 8 { return HC_MAGIC_60000 } 1653 if shortfails >= 20 { return HC_MAGIC_300000 } 1654 if shortfails >= 8 { return HC_MAGIC_60000 } 1655 return 1000 1656} 1657 1658// where the keeper publishes its monotonic spawn count so `status` can read it in ONE call (seq1623). 1659// Deliberately in the daemon cwd, not /tmp: /tmp is tmpfs and this must survive across samples; a 1660// counter that vanishes is exactly the kind of instrument this session kept catching. 1661const HC_RK_COUNT_PATH: *u8 = "reader_keep.count" as *u8 1662func cmd_reader_keep() -> i64 { 1663 hc_puts("[reader-keep] dedicated blocking-wait keeper for the library reader (:8791)\n" as *u8) 1664 // BUILD-IDENTITY PROBE (2026-08-06). NXA_SMALL_MAX is defined ONLY in nx_syscalls.nx, so a binary 1665 // that can print it PROVES its compile included that file. Four separate experiments could not 1666 // settle that from the outside: this supervisor rebuilds to the same 247434 bytes and never gains 1667 // the ~1052-byte small-allocation-arena delta that two control organs both gain, one of them a 1668 // sibling in this very directory. It also gives an operator something binary size cannot -- whether 1669 // a RUNNING supervisor actually has the arena, printed by the process itself. 1670 hc_puts("[reader-keep] arena_threshold=" as *u8); hc_putn(NXA_SMALL_MAX); hc_puts("\n" as *u8) 1671 let st: *i64 = sys_mmap(16) as *i64 1672 var run: i64 = 1 1673 var rk_spawns: i64 = 0 // published to HC_RK_COUNT_PATH each spawn; 0 on a fresh supervisor 1674 var shortfails: i64 = 0 // consecutive deaths inside 5 MINUTES (any sig/code) -> the real-mode breaker 1675 var fastfails: i64 = 0 // consecutive FAST code=1 exits (bind-fail => :HC_MAGIC_8791 already held) -> throttle the hopeless hot-spin 1676 while run == 1 { 1677 // ROOT FIX (was the CPU sink): the poll-loop guard hc_guard_reader ALSO keeps :8791 alive, so if a reader is 1678 // already up, spawning another just fork/exec/bind-fails (code=1) forever. Skip when one is already alive; 1679 // re-check every 5s. Self-heals the instant :8791 frees; never deletes anything (never-brick). 1680 if proc_alive_by_name("nx_media_server_auth.elf" as *u8) == 1 { 1681 sys_sleep_ms(HC_MAGIC_5000) 1682 } else { 1683 let t0: i64 = sys_now_us() 1684 rk_spawns = rk_spawns + 1 1685 // hc_write_pid is a GENERIC decimal writer that happens to be pid-named -- reused, not cloned. 1686 hc_write_pid(HC_RK_COUNT_PATH, rk_spawns) 1687 let pid: i64 = hc_spawn_reader() 1688 hc_puts(" [reader-keep] spawned reader pid=" as *u8); hc_putn(pid); hc_puts("\n" as *u8) 1689 st[0]=0 1690 sys_wait4(pid, st, 0) // BLOCKING -- the survival pattern; returns only when the reader actually dies 1691 let sig: i64 = st[0]&0x7f 1692 let code: i64 = (st[0]>>8)&0xff 1693 let life_us: i64 = sys_now_us() - t0 1694 hc_puts(" [reader-keep] reader pid=" as *u8); hc_putn(pid); hc_puts(" died sig=" as *u8); hc_putn(sig); hc_puts(" code=" as *u8); hc_putn(code); hc_puts(" -> respawn\n" as *u8) 1695 // CIRCUIT-BREAKER (never-brick PREVENT): a fast code=1 exit means the reader could not bind :8791 (a live 1696 // duplicate already holds it) -- retrying every 1s just churns fork/exec/bind-fail forever (the measured CPU 1697 // sink). After 8 consecutive fast bind-fails, back off to once/min: 60x less churn, and it STILL self-heals 1698 // the instant :8791 frees (we never stop retrying, never delete anything). 1699 var isfast: i64 = 0 1700 if sig == 0 { if code == 1 { if life_us < HC_MAGIC_5000000 { isfast = 1 } } } 1701 if isfast == 1 { fastfails = fastfails + 1 } else { fastfails = 0 } 1702 // REAL-MODE BREAKER: a reader that dies inside 5 MINUTES is failing, not completing a lifecycle -- 1703 // regardless of sig/code and regardless of whether it was "fast". This is the counter that 1704 // actually moves for the observed ~15s death mode; fastfails alone never did. 1705 var isshort: i64 = 0 1706 if life_us < HC_MAGIC_300000000 { isshort = 1 } 1707 if isshort == 1 { shortfails = shortfails + 1 } else { shortfails = 0 } 1708 if fastfails == 8 { hc_puts(" [reader-keep] 8 consecutive fast bind-fails (:8791 held elsewhere) -> backoff 60s (self-heals when the port frees)\n" as *u8) } 1709 if shortfails == 8 { hc_puts(" [reader-keep] 8 consecutive short-lived deaths -> backoff 60s (breaker ENGAGED; was dead code before 07-20)\n" as *u8) } 1710 if shortfails == 20 { hc_puts(" [reader-keep] 20 consecutive short-lived deaths -> backoff 300s (still retrying, still self-healing)\n" as *u8) } 1711 let delay: i64 = hc_keep_delay(fastfails, shortfails) 1712 // OBSERVABILITY FIX 2026-07-30: the two backoff notices above fire at EXACTLY ==8 and ==20, 1713 // once each, and these lines carry NO TIMESTAMP -- so a tail read past that point shows an 1714 // unbroken wall of spawn/die and looks IDENTICAL whether the breaker is engaged (1 retry per 1715 // minute) or dead (1 per second). I misread exactly that today and called it a fork storm. 1716 // Emit the CHOSEN DELAY on every respawn: the delay IS the rate, so the log states its own 1717 // throttle instead of making a reader infer it from wall-clock it does not have. 1718 hc_puts(" [reader-keep] shortfails=" as *u8); hc_putn(shortfails) 1719 hc_puts(" fastfails=" as *u8); hc_putn(fastfails) 1720 hc_puts(" next-retry-ms=" as *u8); hc_putn(delay); hc_puts("\n" as *u8) 1721 sys_sleep_ms(delay) 1722 } 1723 } 1724 return 0 1725} 1726 1727// ---- LIVE STATUS PAGE: the supervisor already knows every daemon's state each poll, so it emits a fresh 1728// status.html into the nishifamily doc-root every cycle. Browser auto-refreshes (meta refresh) -> a truly 1729// live nishifamily.com/status with zero extra moving parts. The /status monitoring foundation. 1730func hc_cat(dst: *u8, off: i64, s: *u8) -> i64 { var i: i64=0; while s[i]!=(0 as u8){dst[off+i]=s[i];i=i+1} return off+i } 1731func hc_catn(dst: *u8, off: i64, v: i64) -> i64 { 1732 var m: i64=v; if m<0 {m=0-m} 1733 let t: *u8=sys_mmap(28); var k: i64=0; if m==0 {t[0]=48 as u8;k=1} 1734 while m>0 {t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} 1735 var i: i64=0; while i<k {dst[off+i]=t[k-1-i];i=i+1} 1736 return off+k 1737} 1738func hc_status_row(dst: *u8, off: i64, name: *u8, label: *u8) -> i64 { 1739 var o: i64=off 1740 o=hc_cat(dst,o,"<div class=row><span class=svc>" as *u8); o=hc_cat(dst,o,label); o=hc_cat(dst,o,"</span>" as *u8) 1741 if proc_alive_by_name(name)==1 { o=hc_cat(dst,o,"<span class='pill up'>UP</span>" as *u8) } else { o=hc_cat(dst,o,"<span class='pill down'>DOWN</span>" as *u8) } 1742 o=hc_cat(dst,o,"</div>" as *u8) 1743 return o 1744} 1745// hc_snap_svc: one "SVC <name> <port> <UP|DOWN> <procs> <rwin> <rtot>" line for the mgmt-API health snapshot 1746// (read by /api/health + /api/services -> rendered at nishifamily.com/health). procs = BOOLEAN liveness (1=UP / 1747// 0=DOWN) ON PURPOSE: a raw proc count would false-positive mc_is_dup "duplicate-instance" on sites.elf's 1748// per-request fork children -- the exact false +/- to avoid. state=="DOWN" drives the honest down reason. 1749// hc_rwin: the TRUE windowed crash-loop signal for the snapshot -- the guard's own restart counter (cnt) with the 1750// SAME 60s health-reset rg_should_restart applies (nx_restart_guard.nx:44): a daemon that ran healthy >= 1751// RG_HEALTH_RESET_MS since its last restart reads 0, so a service that crash-looped then RECOVERED is NOT falsely 1752// flagged (no false +/-). A genuinely crash-looping daemon (dying repeatedly within the window) shows cnt>0, and 1753// mc_is_loop trips at >=5. nx_clock_tickless is passed 0 by the caller (its by-design tickless respawn is not a 1754// crash-loop) -- belt-and-suspenders against a false flag on the one daemon that respawns on purpose. 1755func hc_rwin(cnt: *i64, ws: *i64, now: i64) -> i64 { 1756 if now - ws[0] >= RG_HEALTH_RESET_MS { return 0 } 1757 return cnt[0] 1758} 1759func hc_snap_svc(buf: *u8, o: i64, name: *u8, port: i64, rwin: i64) -> i64 { 1760 var p: i64 = o 1761 p = hc_cat(buf, p, "SVC " as *u8); p = hc_cat(buf, p, name); p = hc_cat(buf, p, " " as *u8); p = hc_catn(buf, p, port); p = hc_cat(buf, p, " " as *u8) 1762 // Fields: <state> <procs> <rwin> <rtot> <holder>. procs = BOOLEAN liveness (1/0) ON PURPOSE (a raw count false- 1763 // positives mc_is_dup on fork children). rwin = the TRUE windowed restart count (crash-loop signal) from the 1764 // guard state -- the gap this closes. 8th token (holder) = port-holder PID, computed ONLY on the DOWN path. 1765 if proc_alive_by_name(name) == 1 { 1766 p = hc_cat(buf, p, "UP 1 " as *u8); p = hc_catn(buf, p, rwin); p = hc_cat(buf, p, " " as *u8); p = hc_catn(buf, p, rwin); p = hc_cat(buf, p, " 0\n" as *u8) 1767 } else { 1768 var holder: i64 = 0 1769 if port > 0 { holder = pon_port_owner_pid(port) } 1770 p = hc_cat(buf, p, "DOWN 0 " as *u8); p = hc_catn(buf, p, rwin); p = hc_cat(buf, p, " " as *u8); p = hc_catn(buf, p, rwin); p = hc_cat(buf, p, " " as *u8); p = hc_catn(buf, p, holder); p = hc_cat(buf, p, "\n" as *u8) 1771 } 1772 return p 1773} 1774// hc_emit_snapshot: write the mgmt-API health snapshot. SUP 1 = single-supervisor BY CONSTRUCTION (this proc holds 1775// the heartbeat lease; a 2nd supervisor DEFERS, see cmd_supervise) -> mc_is_duel(1)=false, NO false "dueling- 1776// supervisors" (the operator's exact past misdiagnosis). Atomic write to the path nx_mgmt_api reads (HC_MGMT_CMD). 1777func hc_emit_snapshot(rw_sites: i64, rw_wiki: i64, rw_ggw: i64, rw_gserve: i64, rw_reader: i64, rw_redirect: i64, rw_tgw: i64, rw_mgmt: i64, rw_adm: i64, rw_vroom: i64, rw_tapi: i64, rw_mail: i64) -> i64 { 1778 let buf: *u8 = sys_mmap(HC_MAGIC_4096); var o: i64 = 0 1779 o = hc_cat(buf, o, "SUP 1\n" as *u8) 1780 o = hc_snap_svc(buf, o, "sites.elf" as *u8, HC_MAGIC_8443, rw_sites) 1781 o = hc_snap_svc(buf, o, "nx_wiki_gw.elf" as *u8, HC_MAGIC_18791, rw_wiki) 1782 o = hc_snap_svc(buf, o, "nx_gallery_gateway.elf" as *u8, HC_MAGIC_18190, rw_ggw) 1783 o = hc_snap_svc(buf, o, "nx_gallery_serve.elf" as *u8, HC_MAGIC_18090, rw_gserve) 1784 o = hc_snap_svc(buf, o, "nx_media_server_auth.elf" as *u8, HC_MAGIC_8791, rw_reader) 1785 o = hc_snap_svc(buf, o, "redirect.elf" as *u8, HC_MAGIC_8080, rw_redirect) 1786 o = hc_snap_svc(buf, o, "nx_torrent_gw.elf" as *u8, HC_MAGIC_18793, rw_tgw) 1787 o = hc_snap_svc(buf, o, "nx_clock_tickless.elf" as *u8, 0, 0) // rwin=0 BY CONSTRUCTION: tickless respawn is by-design, NOT a crash-loop (no false +/-) 1788 o = hc_snap_svc(buf, o, "nx_mgmt_api.elf" as *u8, HC_MAGIC_18098, rw_mgmt) 1789 o = hc_snap_svc(buf, o, "nx_docportal_admin_daemon.elf" as *u8, HC_MAGIC_18456, rw_adm) // admin.<d>/ (relocated :HC_MAGIC_8456 -> :HC_MAGIC_18456 to dodge PID HC_MAGIC_15274's squat; matches HC_ADMIN_CMD + proxy_routes) -- 8th token = its real :HC_MAGIC_18456 holder 1790 o = hc_snap_svc(buf, o, "nx_email_portal_daemon.elf" as *u8, HC_MAGIC_18465, rw_mail) // mail.<d>/ email portal (matches HC_MAILPORTAL_CMD + proxy_routes) 1791 o = hc_snap_svc(buf, o, "nx_siteedit_daemon.elf" as *u8, HC_MAGIC_18466, 0) 1792 o = hc_snap_svc(buf, o, "nx_inventory_serve.elf" as *u8, HC_MAGIC_18122, 0) // inventory/ownership checker (loopback-only) // admin.<d>/site visuals editor (rw window not tracked yet) 1793 o = hc_snap_svc(buf, o, "nx_vroom_daemon.elf" as *u8, HC_MAGIC_8446, rw_vroom) // video room backend (nginx-fronted :HC_MAGIC_8446) -- ADDITIVE: surfaces the guarded (poll @890) daemon in /api/health so a dead room is VISIBLE, not silent 1794 o = hc_snap_svc(buf, o, "nx_tools_api_serve.elf" as *u8, HC_MAGIC_18096, rw_tapi) // agent-facing tools API (/api/tools + /mcp) -- R0 MCP-compatible + superset; surfaces in /api/services + /health 1795 // THE TOKEN MINTER (:9091), 2026-07-30 -- same ADDITIVE fix the vroom row above records, for the one 1796 // service it matters most on. hc_guard_login has supervised it for a while, but it appeared in NONE of 1797 // /api/health, /api/services or mgmt_snap.json, so overall:"OK" did not cover the surface that mints 1798 // EVERY session: if login died, health still read OK and an operator watching it learned nothing. 1799 // The guard protects AVAILABILITY; this snapshot protects ATTENTION. They must not disagree about which 1800 // services exist. rwin=0 like the siteedit row -- its restart window is not threaded into this emitter yet 1801 // (an honest 0, not a fabricated number). 1802 o = hc_snap_svc(buf, o, "nx_opaque_login.elf" as *u8, HC_MAGIC_9091, 0) 1803 let tmp: *u8 = "/volume1/homes/elderwesto/nishihost/mgmt_snap.json.tmp" as *u8 1804 let dst: *u8 = "/volume1/homes/elderwesto/nishihost/mgmt_snap.json" as *u8 1805 let fd: i64 = sys_openat_wr(tmp, 0x1a4) 1806 if fd >= 0 { sys_write(fd, buf, o); sys_close(fd); sys_renameat(tmp, dst) } 1807 sys_munmap(buf, HC_MAGIC_4096) 1808 return 0 1809} 1810 1811func hc_emit_status_html(now: i64) -> i64 { 1812 let buf: *u8 = sys_mmap(HC_MAGIC_16384); var o: i64=0 1813 o=hc_cat(buf,o,"<!doctype html><html lang=en><head><meta charset=utf-8><meta http-equiv=refresh content=15><meta name=viewport content='width=device-width,initial-scale=1'><title>Nishi Systems Status</title><style>body{margin:0;background:#0b1020;color:#e6ecf5;font:15px/1.6 -apple-system,Segoe UI,Roboto,sans-serif}.wrap{max-width:680px;margin:0 auto;padding:32px 20px}h1{font-size:1.5rem;margin:0 0 2px}.sub{color:#8794ad;font-size:.9rem;margin:0 0 24px}.row{display:flex;justify-content:space-between;align-items:center;padding:13px 16px;margin:7px 0;background:#141b30;border:1px solid #1f2942;border-radius:9px}.svc{font-weight:500}.pill{font-size:.8rem;font-weight:700;padding:3px 11px;border-radius:20px;letter-spacing:.04em}.pill.up{background:#0f3d24;color:#3ad77f}.pill.down{background:#451620;color:#ff6b81}.ts{color:#5f6d88;font-size:.8rem;margin-top:22px;text-align:center}</style></head><body><div class=wrap>" as *u8) 1814 o=hc_cat(buf,o,"<h1>Nishi Systems Status</h1><p class=sub>live &middot; self-refreshing every 15s &middot; emitted by the sovereign nx_hostctl supervisor</p>" as *u8) 1815 o=hc_status_row(buf,o,"sites.elf" as *u8,"Web &mdash; nishifamily.com (:8443 TLS)" as *u8) 1816 o=hc_status_row(buf,o,"nx_wiki_gw.elf" as *u8,"Wiki OPAQUE gateway (:18791)" as *u8) 1817 o=hc_status_row(buf,o,"nx_hub_gw.elf" as *u8,"Hub OPAQUE gateway (:18792)" as *u8) 1818 o=hc_status_row(buf,o,"nx_gen_gateway_daemon.elf" as *u8,"Gen OPAQUE gateway (:18794)" as *u8) 1819 o=hc_status_row(buf,o,"nx_gen_orchestrator_daemon.elf" as *u8,"Gen orchestrator (:18795)" as *u8) 1820 o=hc_status_row(buf,o,"nx_opaque_login.elf" as *u8,"Login OPAQUE daemon (:9091)" as *u8) 1821 o=hc_status_row(buf,o,"nx_gallery_gateway.elf" as *u8,"Gallery OPAQUE gateway (:18190)" as *u8) 1822 o=hc_status_row(buf,o,"nx_gallery_serve.elf" as *u8,"Gallery media serve (:18090)" as *u8) 1823 o=hc_status_row(buf,o,"nx_media_server_auth.elf" as *u8,"Library reader (:8791)" as *u8) 1824 o=hc_status_row(buf,o,"redirect.elf" as *u8,"HTTP&rarr;HTTPS redirect (:8080)" as *u8) 1825 o=hc_status_row(buf,o,"nx_vroom_daemon.elf" as *u8,"Video room backend (:8446, nginx-fronted)" as *u8) 1826 o=hc_cat(buf,o,"<p class=ts>supervisor poll @ epoch " as *u8); o=hc_catn(buf,o,now); o=hc_cat(buf,o,"s</p></div></body></html>" as *u8) 1827 let tmp: *u8 = "/volume1/homes/elderwesto/nishihost/sites/nishifamily/status.html.tmp" as *u8 1828 let dst: *u8 = "/volume1/homes/elderwesto/nishihost/sites/nishifamily/status.html" as *u8 1829 let fd: i64 = sys_openat_wr(tmp, 0x1a4) 1830 if fd >= 0 { sys_write(fd, buf, o); sys_close(fd); sys_renameat(tmp, dst) } 1831 sys_munmap(buf, HC_MAGIC_16384) 1832 return 0 1833} 1834 1835// POLL supervisor: every 15s, ensure each daemon is alive AND (for plaintext daemons) actually SERVING; 1836// restart any that died or wedged, crash-loop-guarded. ADOPTS already-running daemons (no double-spawn). 1837// Sovereign replacement for nishi_supervisor.sh -- now HUNG-aware, the gap systemd/proc-scan both miss. 1838// assert vm.overcommit_memory=1 (the 2026-07-03 commit-leak outage fix: fork-per-connection daemons with 1839// large anon maps get fork() refused under the mode-0 heuristic once Committed_AS piles up -- the sovereign 1840// spine re-asserts the knob on EVERY supervise start, so a NAS reboot can never silently re-arm the wedge). 1841// RAM-only kernel knob: reversible, never-brick by construction; a write failure logs and continues. 1842func hc_assert_overcommit() -> i64 { 1843 let fd: i64 = sys_openat_wr("/proc/sys/vm/overcommit_memory" as *u8, 0x1a4) 1844 if fd < 0 { hc_puts("[hostctl] WARN: cannot open vm.overcommit_memory (fork-wedge guard NOT asserted)\n" as *u8); return 0 } 1845 sys_write(fd, "1" as *u8, 1) 1846 sys_close(fd) 1847 hc_puts("[hostctl] vm.overcommit_memory=1 asserted (commit-leak fork-wedge guard)\n" as *u8) 1848 return 0 1849} 1850 1851func cmd_supervise() -> i64 { 1852 // FD-LEAK CURE (2026-07-17): drop any INHERITED fd >= 3 before doing anything else. A dirty launch chain 1853 // (a selfswap/boot that still held mgmt's :18098 listen socket) would otherwise propagate that socket to 1854 // EVERY daemon this supervisor forks -- children inherit the supervisor's fd table -- so a redeployed mgmt 1855 // hits EADDRINUSE and cannot rebind :18098 (the ~10min control-plane outage of 2026-07-17). One scrub here 1856 // cleans the supervisor AND, by inheritance, every child it spawns. [[nx_fd_hygiene]] 1857 nx_fd_scrub_inherited() 1858 hc_puts("[hostctl] fd-scrub at supervisor entry (inherited fds dropped -> no listen-socket leak into children)\n" as *u8) 1859 hc_puts("[hostctl] sovereign supervisor: PID-liveness + SERVING health-probe (HUNG-aware) + crash-loop guard\n" as *u8) 1860 hc_assert_overcommit() 1861 // SINGLE-SUPERVISOR by construction: if another supervisor is already heartbeating, DEFER (don't start a 1862 // competing poll loop -- that race was the nishifamily/andelinwest outage). Never zero: the deferring 1863 // instance just exits, the live one keeps running. reconcile/selfswap clear the lease so they always claim. 1864 if hc_sup_fresh() == 1 { 1865 hc_puts("[hostctl] another supervisor is heartbeating (lease fresh <90s) -- DEFERRING, not competing\n" as *u8) 1866 return 0 1867 } 1868 hc_sup_touch() // claim the lease NOW so any later/racing launch sees the fresh heartbeat and defers 1869 let kpid: i64 = sys_fork() // dedicated reader-keeper (blocking-wait pattern); poll loop below handles the rest 1870 if kpid == 0 { cmd_reader_keep(); sys_exit(0); return 0 } 1871 hc_puts("[hostctl] forked reader-keeper pid=" as *u8); hc_putn(kpid); hc_puts("\n" as *u8) 1872 // retire the bash gallery while-loop supervisor (unique: its cmdline has BOTH "while" and the gateway elf; 1873 // the real daemons + the sites supervisor do not) so this becomes the ONE supervisor for the gallery too. 1874 let bk: i64 = proc_kill_by_two("while" as *u8, "nx_gallery_gateway.elf" as *u8, 9) 1875 hc_puts("[hostctl] retired bash gallery supervisor procs=" as *u8); hc_putn(bk); hc_puts("\n" as *u8) 1876 let s_ws: *i64 = sys_mmap(16) as *i64; let s_cnt: *i64 = sys_mmap(16) as *i64 // sites.elf 1877 let r_ws: *i64 = sys_mmap(16) as *i64; let r_cnt: *i64 = sys_mmap(16) as *i64 // redirect.elf 1878 let v_ws: *i64 = sys_mmap(16) as *i64; let v_cnt: *i64 = sys_mmap(16) as *i64 // vroom 1879 let sr_ws: *i64 = sys_mmap(16) as *i64; let sr_cnt: *i64 = sys_mmap(16) as *i64 // sni_router.elf :HC_MAGIC_7443 (sovereign public edge) 1880 let x_ws: *i64 = sys_mmap(16) as *i64; let x_cnt: *i64 = sys_mmap(16) as *i64 // translate 1881 let lib_ws: *i64 = sys_mmap(16) as *i64; let lib_cnt: *i64 = sys_mmap(16) as *i64 // library reader :HC_MAGIC_8791 1882 let dsup_ws: *i64 = sys_mmap(16) as *i64; let dsup_cnt: *i64 = sys_mmap(16) as *i64 // FLEET SUPERVISOR nx_daemon_supervisor (:18095 status; had NO parent until 2026-09-02 -- adopted if running, respawned if dead) 1883 let dsup_st: *i64 = sys_mmap(32) as *i64 // stale-cycle state for hc_guard_dsup: [0]=last /status cycle seen, [1]=polls without an advance (zero-filled by mmap) 1884 let reap_st: *i64 = sys_mmap(16) as *i64 // reaped-child status (alloc ONCE, reused -- no per-poll leak) 1885 let gs_ws: *i64 = sys_mmap(16) as *i64; let gs_cnt: *i64 = sys_mmap(16) as *i64 // gallery serve :HC_MAGIC_18090 1886 let gw_ws: *i64 = sys_mmap(16) as *i64; let gw_cnt: *i64 = sys_mmap(16) as *i64 // gallery gateway :HC_MAGIC_18190 1887 let wgw_ws: *i64 = sys_mmap(16) as *i64; let wgw_cnt: *i64 = sys_mmap(16) as *i64 // wiki gateway :HC_MAGIC_18791 1888 let hgw_ws: *i64 = sys_mmap(16) as *i64; let hgw_cnt: *i64 = sys_mmap(16) as *i64 // hub gateway :HC_MAGIC_18792 1889 let lgn_ws: *i64 = sys_mmap(16) as *i64; let lgn_cnt: *i64 = sys_mmap(16) as *i64 // login daemon :HC_MAGIC_9091 1890 let tor_ws: *i64 = sys_mmap(16) as *i64; let tor_cnt: *i64 = sys_mmap(16) as *i64 // media/torrent daemon :HC_MAGIC_8097 1891 let tgw_ws: *i64 = sys_mmap(16) as *i64; let tgw_cnt: *i64 = sys_mmap(16) as *i64 // torrent OPAQUE gateway :HC_MAGIC_18793 1892 let gor_ws: *i64 = sys_mmap(16) as *i64; let gor_cnt: *i64 = sys_mmap(16) as *i64 // gen orchestrator :HC_MAGIC_18795 1893 let ggw_ws: *i64 = sys_mmap(16) as *i64; let ggw_cnt: *i64 = sys_mmap(16) as *i64 // gen OPAQUE gateway :HC_MAGIC_18794 1894 let syn_ws: *i64 = sys_mmap(16) as *i64; let syn_cnt: *i64 = sys_mmap(16) as *i64 // SYNTH STUDIO daemon :HC_MAGIC_18796 (sites /synth/api -> here; renders motion/creatures on demand) 1895 let devapi_ws: *i64 = sys_mmap(16) as *i64; let devapi_cnt: *i64 = sys_mmap(16) as *i64 // DEV/CI API daemon :HC_MAGIC_18797 (sites /api/dev -> here; build/gate/run organs over HTTP, cap-gated) 1896 let clk_ws: *i64 = sys_mmap(16) as *i64; let clk_cnt: *i64 = sys_mmap(16) as *i64 // nx_clock_tickless: the ONE consolidated clock (runs a bounded tickless window then exits -> respawned = continuous) 1897 let mgmt_ws: *i64 = sys_mmap(16) as *i64; let mgmt_cnt: *i64 = sys_mmap(16) as *i64 // mgmt API :HC_MAGIC_18099 (HTTP /api) 1898 let adm_ws: *i64 = sys_mmap(16) as *i64; let adm_cnt: *i64 = sys_mmap(16) as *i64 // doc-portal admin daemon :HC_MAGIC_8456 (admin.<d>/ owner self-service) 1899 let mail_ws: *i64 = sys_mmap(16) as *i64; let mail_cnt: *i64 = sys_mmap(16) as *i64 // email-portal daemon :HC_MAGIC_18465 (mail.<d>/ webmail) 1900 let inv_ws: *i64 = sys_mmap(16) as *i64; let inv_cnt: *i64 = sys_mmap(16) as *i64 // inventory checker :HC_MAGIC_18122 (loopback-only, proxied at /inventory) 1901 let sed_ws: *i64 = sys_mmap(16) as *i64; let sed_cnt: *i64 = sys_mmap(16) as *i64 // site-visuals editor :HC_MAGIC_18466 (admin.<d>/site) 1902 let tapi_ws: *i64 = sys_mmap(16) as *i64; let tapi_cnt: *i64 = sys_mmap(16) as *i64 // tools API :HC_MAGIC_18096 (HTTP /api/tools + /mcp -- R0 agent-facing API) 1903 let fin_ws: *i64 = sys_mmap(16) as *i64; let fin_cnt: *i64 = sys_mmap(16) as *i64 // finance dashboard :HC_MAGIC_18097 (HTTP /finance) 1904 let dist_ws: *i64 = sys_mmap(16) as *i64; let dist_cnt: *i64 = sys_mmap(16) as *i64 // /dist download server :HC_MAGIC_18120 (HTTP /dist -- content-addressed) 1905 let mp_ws: *i64 = sys_mmap(16) as *i64; let mp_cnt: *i64 = sys_mmap(16) as *i64 // multiplayer game relay :HC_MAGIC_7702 (HTTP /mineworld-mp + /pstate + /roster) 1906 let seed_ws: *i64 = sys_mmap(16) as *i64; let seed_cnt: *i64 = sys_mmap(16) as *i64 // BitTorrent seeder :HC_MAGIC_6881 (inbound peers -- the UPLOAD/SHARE half) 1907 let sann_ws: *i64 = sys_mmap(16) as *i64; let sann_cnt: *i64 = sys_mmap(16) as *i64 // DHT announcer (makes the seeder discoverable via BEP-5) 1908 let he_ws: *i64 = sys_mmap(16) as *i64; let he_cnt: *i64 = sys_mmap(16) as *i64 // continuous M&E evaluator (nx_health_eval loop -- functional/quality health of the whole surface, every 300s) 1909 let sg_ws: *i64 = sys_mmap(16) as *i64; let sg_cnt: *i64 = sys_mmap(16) as *i64 // video signaling/media relay :HC_MAGIC_8445 (task #26: was UNSUPERVISED -- the fire-and-pray near-miss) 1910 let rel_ws: *i64 = sys_mmap(16) as *i64; let rel_cnt: *i64 = sys_mmap(16) as *i64 // Relationship OS daemon :HC_MAGIC_8027 (the /relate app; backs off until relatedeploy'd) 1911 let pjw_ws: *i64 = sys_mmap(16) as *i64; let pjw_cnt: *i64 = sys_mmap(16) as *i64 // /project OPAQUE gateway :HC_MAGIC_8028 (contractor portal; adopts the running daemon) 1912 let cs_ws: *i64 = sys_mmap(16) as *i64; let cs_cnt: *i64 = sys_mmap(16) as *i64 // ethical CLEAN-SERVE daemon :HC_MAGIC_8102 (/clean; backs off until cleanservedeploy'd) 1913 let meshsrv_ws: *i64 = sys_mmap(16) as *i64; let meshsrv_cnt: *i64 = sys_mmap(16) as *i64 // WORKER MESH serve :HC_MAGIC_8029 (console + SSE gen + gated image backend) 1914 let meshgw_ws: *i64 = sys_mmap(16) as *i64; let meshgw_cnt: *i64 = sys_mmap(16) as *i64 // WORKER MESH OPAQUE gateway :HC_MAGIC_8055 (gated /mesh console) 1915 let arc_ws: *i64 = sys_mmap(16) as *i64; let arc_cnt: *i64 = sys_mmap(16) as *i64 // time-machine archive server :18801 (static serve of web_assets/archive) 1916 var run: i64 = 1 1917 while run == 1 { 1918 // seq1318: ONE /proc walk feeds EVERY liveness question below; all guards read the SAME instant. 1919 ps_refresh() 1920 hc_guard_one("sites.elf" as *u8, HC_ELF, 0, s_ws, s_cnt, HC_PID, 1) // TLS:HC_MAGIC_8443 -> PID-only 1921 hc_guard_one("redirect.elf" as *u8, HC_REDIRECT, 0, r_ws, r_cnt, HC_PID, 0) // PID-only: the :HC_MAGIC_8080 serving-probe (hp_probe sends /healthz, reads HC_MAGIC_4096, closes) SIGPIPE'd/HUNG-killed this working daemon every poll -> crash-loop (SAME root cause as the reader, see hostctl:~409). PID-liveness like sites.elf. (verify post-deploy: external probe :80 -> 301 https) 1922 hc_guard_one("nx_vroom_daemon.elf" as *u8, HC_VROOM, HC_MAGIC_8446, v_ws, v_cnt, HC_PID, 0) // plaintext -> probe 1923 // 2026-09-02 RETIRED FROM THIS ROSTER: hc_guard_one("/sni_router.elf", HC_SNIROUTER, 0, sr_ws, sr_cnt, HC_PID, 0). 1924 // daemons.reg retired the :7443 legacy router to `watch` on 2026-08-04 (DSM nginx owns :443; the serving path is 1925 // :443 -> NAT -> :8443 sites.elf, measured 20/20 from a LAN client) -- but THIS hand-coded roster kept guarding 1926 // it. MEASURED in supervisor.log's TAIL today: `[guard] restarted /sni_router.elf (was dead)` then `[reap] sig=9` 1927 // on a loop for hours, the crash-loop breaker engaging every few cycles, on a box at load 22-33 whose fork rate 1928 // read 37/s. A GUARD WHOSE PROBE PORT IS DEAD MANUFACTURES THE CHURN IT REPORTS. sr_ws/sr_cnt stay allocated 1929 // above so no other slot moves; re-add the call only after the router binds a port that is actually its own. 1930 hc_guard_one("nx_translate_daemon.elf" as *u8, HC_XLATE, HC_MAGIC_8447, x_ws, x_cnt, HC_PID, 0) // plaintext -> probe 1931 hc_guard_galx_serve(gs_ws, gs_cnt) // gallery serve :HC_MAGIC_18090 (NSFW gallery backend) 1932 hc_guard_galx_gw(gw_ws, gw_cnt) // gallery OPAQUE gateway :HC_MAGIC_18190 (login + media proxy) 1933 hc_guard_wiki_gw(wgw_ws, wgw_cnt) // wiki OPAQUE gateway :HC_MAGIC_18791 (login + the gated HC_MAGIC_1195-page wiki) 1934 hc_guard_hub_gw(hgw_ws, hgw_cnt) // hub OPAQUE gateway :HC_MAGIC_18792 (leveled /hub via the maturity registry) 1935 hc_guard_login(lgn_ws, lgn_cnt) // login OPAQUE daemon :HC_MAGIC_9091 (the token minter -- was unsupervised) 1936 hc_guard_torrent(tor_ws, tor_cnt) // media/torrent daemon :HC_MAGIC_8097 (the acquirer, migrated dev -> NAS hub; backs off until deployed) 1937 hc_guard_torrent_gw(tgw_ws, tgw_cnt) // torrent OPAQUE gateway :HC_MAGIC_18793 (sites /torrent -> here -> daemon; the live nishifamily front) 1938 hc_guard_gen_orch(gor_ws, gor_cnt) // gen orchestrator :HC_MAGIC_18795 (UI+batch->HC_MAGIC_5080+gallery; backs off until gendeploy'd) 1939 hc_guard_gen_gw(ggw_ws, ggw_cnt) // gen OPAQUE gateway :HC_MAGIC_18794 (sites /gen -> here -> orchestrator; backs off until gendeploy'd) 1940 hc_guard_synth(syn_ws, syn_cnt) // SYNTH STUDIO daemon :HC_MAGIC_18796 (sites /synth/api -> here; adopts the running detached daemon, respawns after crash/reboot) 1941 hc_guard_devapi(devapi_ws, devapi_cnt) // DEV/CI API daemon :HC_MAGIC_18797 (sites /api/dev -> here; adopts running daemon, respawns after crash/reboot) 1942 hc_guard_one("nx_clock_tickless.elf" as *u8, HC_CLOCK, 0, clk_ws, clk_cnt, HC_PID, 0) // the ONE consolidated scheduler: PID-only (alive during its tickless window, dead after -> respawn = continuous, no per-job daemon). ADDITIVE: nothing above changes. 1943 hc_guard_arcsrv(arc_ws, arc_cnt) // time-machine archive :18801. WAS hc_guard_one(..HC_ARCSRV..), which spawns via hc_spawn_path -- MEASURED DEAD: nx_netobs bench gave ok=0 err=1 in 203us (instant REFUSAL, not a timeout) with the elf promoted and the guard entry provably correct, i.e. spawning and dying. hc_spawn_path execs bare and inherits the supervisor's stdout; this daemon WRITES to stdout, so its first write to a dead pipe SIGPIPEs it -- the identical failure recorded twice in this file. hc_spawn_arcsrv adds /dev/null on 0,1,2 + the cwd its web_assets/ and seg_store paths need. Still PID-liveness only, no serving probe. ADDITIVE: nothing above changes. 1944 hc_guard_mgmt(mgmt_ws, mgmt_cnt) // sovereign management API :HC_MAGIC_18099 (HTTP /api -- operate the ecosystem over the wire, not the shell dance) 1945 hc_guard_relate(rel_ws, rel_cnt) // Relationship OS daemon :HC_MAGIC_8027 (/relate; fail-safe backoff until relatedeploy promotes the binary) 1946 hc_guard_project(pjw_ws, pjw_cnt) // /project OPAQUE gateway :HC_MAGIC_8028 (contractor portal; fail-safe backoff until the elf is deployed) 1947 hc_guard_cleanserve(cs_ws, cs_cnt) // ethical CLEAN-SERVE daemon :HC_MAGIC_8102 (/clean; fail-safe backoff until cleanservedeploy promotes the binary) 1948 hc_guard_mesh_serve(meshsrv_ws, meshsrv_cnt) // WORKER MESH serve :HC_MAGIC_8029 (sovereign job console backend; adopts the running setsid daemon, respawns after crash/reboot) 1949 hc_guard_mesh_gw(meshgw_ws, meshgw_cnt) // WORKER MESH OPAQUE gateway :HC_MAGIC_8055 (sites /mesh -> here; gated console, respawns after crash/reboot) 1950 hc_guard_admin(adm_ws, adm_cnt) // doc-portal ADMIN daemon :HC_MAGIC_8456 (admin.<d>/ -- owner self-service: invite-gated self-register + upload) 1951 hc_guard_mailportal(mail_ws, mail_cnt) // EMAIL-PORTAL daemon :HC_MAGIC_18465 (mail.<d>/ -- Modern-Auth webmail: invite register + inbox + local delivery) 1952 hc_guard_siteedit(sed_ws, sed_cnt) // SITE-VISUALS editor :HC_MAGIC_18466 (admin.<d>/site -- blueprint save/preview/publish, same realm as the doc portal) 1953 hc_guard_inventory(inv_ws, inv_cnt) // INVENTORY/ownership checker :HC_MAGIC_18122 (loopback-only; published at /inventory by proxy_routes.conf) 1954 hc_guard_tapi(tapi_ws, tapi_cnt) // agent-facing tools API :HC_MAGIC_18096 (HTTP /api/tools + /mcp -- R0 MCP-compatible + superset) 1955 hc_guard_fin(fin_ws, fin_cnt) // finance dashboard :HC_MAGIC_18097 (HTTP /finance -- gated login surface) 1956 hc_guard_dist(dist_ws, dist_cnt) // /dist content-addressed download server :HC_MAGIC_18120 (durable -- auto-restarts) 1957 hc_guard_seed(seed_ws, seed_cnt) // BitTorrent seeder :HC_MAGIC_6881 (inbound peers; serves seed_index.conf -- the UPLOAD/SHARE half, durable) 1958 hc_guard_seedann(sann_ws, sann_cnt) // DHT announcer: periodically announce_peer every registered info_hash -> discoverable (durable) 1959 hc_guard_mp(mp_ws, mp_cnt) // multiplayer game relay :HC_MAGIC_7702 (/mineworld-mp + /pstate + /roster -- shared-world games) 1960 hc_guard_health(he_ws, he_cnt) // CONTINUOUS M&E: nx_health_eval loop -- functional/quality health of the whole surface every 300s (kills fire-and-pray). ADDITIVE: nothing above changes. 1961 hc_guard_sig2(sg_ws, sg_cnt) 1962 hc_guard_dsup(dsup_ws, dsup_cnt, dsup_st) // FLEET SUPERVISOR nx_daemon_supervisor: the census said nothing launched it; now hostctl does (adopt-if-CYCLING, kill-and-respawn-if-stale, respawn-if-dead). ADDITIVE. // video signaling/media relay :HC_MAGIC_8445 (task #26 closed: supervised + reconcile-cycled). ADDITIVE. 1963 hc_emit_status_html(sys_now_realtime_sec()) // LIVE nishifamily.com/status.html -- refreshed every poll 1964 let snap_now: i64 = sys_now_realtime_ms() // real windowed rwin per service (guard cnt + 60s health-reset) 1965 hc_emit_snapshot(hc_rwin(s_cnt,s_ws,snap_now), hc_rwin(wgw_cnt,wgw_ws,snap_now), hc_rwin(gw_cnt,gw_ws,snap_now), hc_rwin(gs_cnt,gs_ws,snap_now), hc_rwin(lib_cnt,lib_ws,snap_now), hc_rwin(r_cnt,r_ws,snap_now), hc_rwin(tgw_cnt,tgw_ws,snap_now), hc_rwin(mgmt_cnt,mgmt_ws,snap_now), hc_rwin(adm_cnt,adm_ws,snap_now), hc_rwin(v_cnt,v_ws,snap_now), hc_rwin(tapi_cnt,tapi_ws,snap_now), hc_rwin(mail_cnt,mail_ws,snap_now)) // LIVE mgmt_snap.json -> /api/health + /api/services -> nishifamily.com/health dashboard (TRUE state, no false +/-) 1966 // reader handled by the forked cmd_reader_keep() (blocking-wait), NOT polled here -- polling kills it 1967 var rr: i64 = sys_wait4(0-1, reap_st, WNOHANG) // reap dead children so zombies don't pile toward RLIMIT_NPROC 1968 while rr > 0 { 1969 hc_puts(" [reap] pid=" as *u8); hc_putn(rr); hc_puts(" sig=" as *u8); hc_putn(reap_st[0]&0x7f); hc_puts(" code=" as *u8); hc_putn((reap_st[0]>>8)&0xff); hc_puts("\n" as *u8) // INSTRUMENT: why does the supervised reader exit? 1970 rr = sys_wait4(0-1, reap_st, WNOHANG) 1971 } 1972 hc_sup_touch() // heartbeat the single-supervisor lease every poll (a competing launch sees this and defers) 1973 sys_sleep_ms(HC_MAGIC_15000) 1974 } 1975 return 0 1976} 1977 1978func cmd_deploy() -> i64 { 1979 hc_puts("[hostctl] deploy: atomic-publish staged artifacts + restart (pure syscalls, no shell)\n" as *u8) 1980 sys_mkdir(HC_DOCROOT, 0x1ed) 1981 let r1: i64 = sys_renameat(HC_INDEX_NEW, HC_INDEX) 1982 let r2: i64 = sys_renameat(HC_CONF_NEW, HC_CONF) 1983 // seq1097 (2026-07-27 outage): REFUSE the elf swap unless a REAL artifact is staged. The old 1984 // unconditional live->prev rename meant a deploy with nothing staged (e.g. a retry after an 1985 // aborted-response /api/deploy that DID execute server-side) renamed live away and installed 1986 // NOTHING -- guard spins exit-127 on a missing file. stat-gate: exists AND st_size>0 (st_size @ 1987 // +48, same struct-stat offsets hc_file_age_s relies on). Fail-safe: refuse loudly, touch nothing. 1988 let stb: *u8 = sys_mmap(160) 1989 var stok: i64 = 0 1990 if sys_fstatat(HC_ELF_NEW, stb) == 0 { 1991 let szp: *i64 = ((stb as i64) + 48) as *i64 1992 if szp[0] > 0 { stok = 1 } 1993 } 1994 if stok == 0 { 1995 hc_puts(" REFUSED elf swap: sites.elf.new absent or 0 bytes -- live binary left untouched\n" as *u8) 1996 return 1 1997 } 1998 sys_renameat(HC_ELF, HC_ELF_PREV) // never-brick: keep the current live binary as .prev for rollback 1999 let r3: i64 = sys_renameat(HC_ELF_NEW, HC_ELF) 2000 nx_chmod(HC_ELF, 0x1ed) 2001 // REAL return codes, NOT (rX & 1). rename returns 0 on success and -errno on failure, and ENOENT is -2 2002 // whose low bit is 0 -- so the masked form printed EVERY EVEN ERRNO AS SUCCESS. That is exactly how a 2003 // promote that installed NOTHING reported `rc=0` in cmd_mgmtdeploy on 2026-07-30, and it was caught only 2004 // by comparing artifact bytes. Print what the kernel actually said. 2005 hc_puts(" rename rc index=" as *u8); hc_putn(r1) 2006 hc_puts(" conf=" as *u8); hc_putn(r2) 2007 hc_puts(" elf=" as *u8); hc_putn(r3) 2008 hc_puts(" (0=ok, negative=-errno, NOT bit-masked)\n" as *u8) 2009 // ROBUST name-kill (matches cmd_wikideploy/galxdeploy): the pidfile is only written when the supervisor 2010 // ITSELF restarts sites.elf (hc_guard_one write_pid=1). An ADOPTED sites.elf (running before the supervisor 2011 // came up) leaves the pidfile stale -> the old pidfile-kill could MISS the restart (new binary never loads) 2012 // or SIGKILL a reused PID. Kill by name so the supervisor's name-guard respawns the NEW binary deterministically. 2013 let pk: i64 = proc_kill_by_name("sites.elf" as *u8, 9) 2014 hc_puts(" killed sites.elf procs=" as *u8); hc_putn(pk); hc_puts(" -> supervisor respawns the NEW binary (name-guarded restart, <=15s)\n" as *u8) 2015 return 0 2016} 2017 2018// mgmtdeploy: promote nx_mgmt_api.elf.new -> live (.prev kept, never-brick) + kill by name -> hc_guard_mgmt 2019// respawns the NEW binary (<=15s). Closes the publish-from-anywhere bootstrap: /api/upload stages the mgmt 2020// binary itself, /api/deploy (conf row `mgmtapi` -> this sub) promotes it -- after which ALL future mgmt 2021// updates AND static-content publishes flow over the authenticated public edge, no SSH/LAN. 2022func cmd_mgmtdeploy() -> i64 { 2023 hc_puts("[hostctl] mgmtdeploy: promote the staged mgmt binary -> live (.prev kept) + kick -> guard respawns\n" as *u8) 2024 // ---- REFUSE BEFORE DESTROY (2026-07-30). THREE COMPOUNDING DEFECTS LIVED IN THESE FIVE LINES: 2025 // (1) the live -> .prev rename ran UNCONDITIONALLY, BEFORE anything checked a staged binary existed. With 2026 // no .new present that MOVES THE LIVE BINARY AWAY and the install rename then fails, leaving NO live 2027 // mgmt at all. Only the never-brick auto-rollback put it back -- MEASURED TODAY: live and .prev both 2028 // ended at 578789 while the real 578865 build sat on disk unused. 2029 // (2) the rc was printed as (rr & 1). rename returns 0 on success and -errno on failure, and ENOENT is 2030 // -2, whose low bit is 0 -- so THE FAILURE PRINTED rc=0, IDENTICAL TO SUCCESS. Every even errno 2031 // displayed as success, so the operator reads a green promote that never happened. 2032 // (3) /api/build stages `<t>.sov.elf.new` but this promoter only ever read `<t>.elf.new` -- the seq1525 2033 // class, staged where the promoter cannot see it. Two names for one artifact is two sources of truth. 2034 // FIX: accept EITHER staged name, and REFUSE WITHOUT TOUCHING LIVE when neither is present. 2035 let mlive: *u8 = "/volume1/homes/elderwesto/nishihost/nx_mgmt_api.elf" as *u8 2036 let mprev: *u8 = "/volume1/homes/elderwesto/nishihost/nx_mgmt_api.elf.prev" as *u8 2037 let mnew1: *u8 = "/volume1/homes/elderwesto/nishihost/nx_mgmt_api.elf.new" as *u8 2038 let mnew2: *u8 = "/volume1/homes/elderwesto/nishihost/nx_mgmt_api.sov.elf.new" as *u8 2039 var stg: *u8 = 0 as *u8 2040 // ★PRECEDENCE BUG FIXED 2026-07-30 -- THIS IS HOW A "SUCCESSFUL" DEPLOY SHIPS A STALE BINARY. 2041 // This preferred mnew1 (.elf.new, the legacy UPLOAD name) whenever it existed, so ONE stale leftover 2042 // PERMANENTLY SHADOWED every fresh /api/build artifact. MEASURED: both names sat on disk; /api/deploy 2043 // answered PROMOTED and hc_promote_to genuinely promoted -- the STALE one -- while nx_mgmt_api.sov.elf.new 2044 // (581942B, built minutes earlier) was left untouched. Nothing lied; the wrong artifact was chosen. The 2045 // leftover persists BY DESIGN because hc_promote_to COPIES rather than renames (EXDEV safety), so nothing 2046 // ever consumed it. Prefer the sanctioned build artifact, and consume BOTH on success (below) so a 2047 // leftover can never shadow a build again. Rollback is unaffected -- it lives in .prev, not in .new. 2048 if hc_exists(mnew2) == 1 { stg = mnew2 } 2049 if stg == (0 as *u8) { if hc_exists(mnew1) == 1 { stg = mnew1 } } 2050 if stg == (0 as *u8) { 2051 hc_puts(" REFUSED: no staged binary -- looked for BOTH nx_mgmt_api.elf.new and nx_mgmt_api.sov.elf.new.\n" as *u8) 2052 hc_puts(" LIVE IS UNTOUCHED. The old code would have moved it to .prev and left the host with nothing.\n" as *u8) 2053 return 1 2054 } 2055 // USE THE SHARED PROMOTER, DO NOT HAND-ROLL ANOTHER ONE. This exact defect has now been found and fixed 2056 // THREE TIMES in three functions -- hc_promote_to first, then cmd_selfswap at seq992 after its own outage, 2057 // then here. hc_promote_to is also STRICTLY SAFER than a local renameat: it COPIES, because sys_renameat 2058 // across btrfs subvolumes returns EXDEV, which once moved a live binary to .prev and installed NOTHING. 2059 // A fourth site that hand-rolls this will rediscover the same outage; call this instead. 2060 let ok: i64 = hc_promote_to(stg, mlive, mprev) 2061 if ok == 0 { 2062 hc_puts(" INSTALL FAILED -- hc_promote_to reported no-op/short-write; live left as it was.\n" as *u8) 2063 return 1 2064 } 2065 hc_puts(" installed via hc_promote_to (EXDEV-safe copy, .prev banked, refuses when nothing is staged)\n" as *u8) 2066 // CONSUME BOTH STAGED NAMES. hc_promote_to COPIES, so without this the source survives and the next 2067 // deploy re-promotes an artifact that has already shipped -- which is exactly the shadowing bug above. 2068 // Safe: these are SPENT staging files, the live binary is installed and .prev holds the rollback copy. 2069 // Unlinking both (not just the one chosen) is the point -- leaving the loser is what created the trap. 2070 sys_unlinkat(mnew2) 2071 sys_unlinkat(mnew1) 2072 hc_puts(" consumed staged artifacts (both names) -- a leftover can no longer shadow the next build\n" as *u8) 2073 // ---- R5 SEQUENCING: LET MGMT FINISH ITS REPLY BEFORE WE KILL IT ------------------------------------ 2074 // THE DEFECT THIS REMOVES: this sub is invoked BY the mgmt daemon (POST /api/deploy target=mgmtapi -> 2075 // md_exec_hostctl -> here), so SIGKILLing nx_mgmt_api.elf right now destroys the process that is still 2076 // writing the caller's HTTP response. That is the FETCH-FAIL every /api/deploy has ever returned (~14x in 2077 // one session) -- and we built DOCTRINE around it ("503 = EXPECTED, do NOT retry-hammer") instead of 2078 // fixing it. It is also why the seq1563 deploy lease strands: mgmt dies before reaching its own release, 2079 // so the lane stays closed until the TTL and the NEXT deploy is refused as lease-busy. 2080 // ⚠ THE KILLER IS A DIFFERENT PROCESS: hostctl is forked by mgmt, so mgmt is not "self" from here and the 2081 // md_kill_by_name self-guard cannot fire. The delay has to live at the site that actually kills. 2082 // FIX: detach a child that waits, THEN signals. hostctl returns immediately, mgmt finishes and flushes its 2083 // response, and only then does it go -- guard respawns the already-promoted binary. SIGTERM not SIGKILL so 2084 // a daemon that later grows a drain handler gets to use it; the guard still respawns either way. 2085 let dpid: i64 = sys_fork() 2086 if dpid == 0 { 2087 nx_setsid() 2088 sys_sleep_ms(HC_MAGIC_1500) // hoisted 2026-09-02: the ratchet refused the whole file's build for this one inline literal (const already existed) 2089 proc_kill_by_name("nx_mgmt_api.elf" as *u8, 15) 2090 sys_exit(0) 2091 } 2092 hc_puts(" scheduled SIGTERM of nx_mgmt_api.elf in 1500ms (reply-flush window) -> hc_guard_mgmt respawns the NEW binary (<=15s)\n" as *u8) 2093 return 0 2094} 2095// never-brick mgmt ROLLBACK: restore nx_mgmt_api.elf.prev -> live + kick (reverse a bad mgmtdeploy). 2096func cmd_mgmtrollback() -> i64 { 2097 hc_puts("[hostctl] mgmtrollback: restore nx_mgmt_api.elf.prev -> live + kick\n" as *u8) 2098 let rr: i64 = sys_renameat("/volume1/homes/elderwesto/nishihost/nx_mgmt_api.elf.prev" as *u8, "/volume1/homes/elderwesto/nishihost/nx_mgmt_api.elf" as *u8) 2099 nx_chmod("/volume1/homes/elderwesto/nishihost/nx_mgmt_api.elf" as *u8, 0x1ed) 2100 hc_put_rc(" restore rename rc=" as *u8, rr) 2101 let pk: i64 = proc_kill_by_name("nx_mgmt_api.elf" as *u8, 9) 2102 hc_puts(" killed nx_mgmt_api.elf procs=" as *u8); hc_putn(pk); hc_puts(" -> guard respawns the RESTORED binary\n" as *u8) 2103 return 0 2104} 2105 2106const HC_COPYBUF: i64 = 262144 // chunked-copy buffer for cross-subvolume promote (hc_copy_file) 2107// ---- P1 off-LAN parity: promote/rollback the TORRENT stack (lives in /volume1/ai/torrent/, a DIFFERENT dir 2108// than the nishihost staging area). /api/upload stages <name>.new in nishihost cwd; this cross-dir promotes it 2109// into the torrent dir (SAME volume1 => sys_renameat works). GUARDED per-binary: only promote if the staged 2110// .new exists, so deploying one torrent binary never destroys the other's live copy. .prev kept (never-brick). 2111func hc_exists(p: *u8) -> i64 { let fd: i64 = sys_openat_rd(p); if fd < 0 { return 0 } sys_close(fd); return 1 } 2112// ---- HONEST RETURN CODES (2026-07-30) ------------------------------------------------------------------ 2113// THE IDIOM THIS REPLACES, WHICH WAS AT 14 SITES IN THIS FILE: `b[0] = (48 + (rc & 1))` printed only the LOW 2114// BIT of a syscall result. rename/unlink return 0 on success and -errno on failure, and ENOENT is -2 -- low 2115// bit 0 -- so EVERY EVEN ERRNO RENDERED AS `0`, INDISTINGUISHABLE FROM SUCCESS. That is precisely how 2116// cmd_mgmtdeploy reported `install rename rc=0` while installing NOTHING and briefly leaving the host with no 2117// mgmt binary; it was caught only by comparing artifact BYTES, never by the message. 2118// ★★★★★LAW: A SUCCESS MESSAGE DERIVED FROM A MASKED ERROR CODE IS WORSE THAN NO MESSAGE -- it does not 2119// merely fail to inform, it actively asserts the opposite of what happened. One helper, so no site can get 2120// this wrong again (the same discipline as binding SIGPIPE and FD_CLOEXEC to the listen primitive). 2121func hc_put_rc(label: *u8, rc: i64) -> i64 { 2122 hc_puts(label) 2123 hc_putn(rc) 2124 hc_puts(" (0=ok, negative=-errno, NOT bit-masked)\n" as *u8) 2125 return 0 2126} 2127// chunked copy src -> dst (0777); 0 = clean full copy, -1 = short write. REQUIRED for the torrent promote: 2128// /volume1/ai/torrent is a DIFFERENT btrfs subvolume than /volume1/homes (nishihost), so sys_renameat ACROSS 2129// them returns EXDEV. The old hc_promote_to swallowed that (rename live->prev same-dir OK, then rename 2130// new->live cross-subvol EXDEV FAILED) -> it moved the live binary to .prev and installed NOTHING -> the 2131// daemon could not respawn (missing binary) = a self-inflicted outage. Copy is cross-subvolume-safe. 2132func hc_copy_file(src: *u8, dst: *u8) -> i64 { 2133 let sf: i64 = sys_openat_rd(src); if sf < 0 { return 0 - 1 } 2134 let df: i64 = sys_openat_wr(dst, 0x1ff); if df < 0 { sys_close(sf); return 0 - 1 } 2135 let buf: *u8 = sys_mmap(HC_COPYBUF); var bad: i64 = 0; var go: i64 = 1 2136 while go == 1 { 2137 let r: i64 = sys_read(sf, buf, HC_COPYBUF) 2138 if r <= 0 { go = 0 } else { let w: i64 = sys_write(df, buf, r); if w != r { bad = 1; go = 0 } } 2139 } 2140 sys_close(sf); sys_close(df) 2141 return 0 - bad 2142} 2143// Byte length of a path, or -1 if unreadable. Streamed, so an arbitrarily large elf is never held whole. 2144func hc_file_size(p: *u8) -> i64 { 2145 let fd: i64 = sys_openat_rd(p) 2146 if fd < 0 { return 0 - 1 } 2147 let sb: *u8 = sys_mmap(HC_COPYBUF) 2148 var tot: i64 = 0 2149 var go: i64 = 1 2150 while go == 1 { 2151 let r: i64 = sys_read(fd, sb, HC_COPYBUF) 2152 if r <= 0 { go = 0 } 2153 if r > 0 { tot = tot + r } 2154 } 2155 sys_close(fd) 2156 return tot 2157} 2158// ---- EXPECTED-IDENTITY PROMOTE (2026-07-30) -- deploy a DIGEST, not a TAG ------------------------------- 2159// THE RACE THIS CLOSES, MEASURED THE SAME DAY: nx_sov_build_run writes _build/<t>.sov.elf and a LATER step 2160// copies that PATH and promotes it. Any sibling rebuilding the same target in between OVERWRITES the file. 2161// I built nx_mgmt_api at 578865, verified it, and by promote time _build held a sibling's 579263 -- WHICH IS 2162// WHAT WENT LIVE. I verified one artifact and shipped another and nothing detected it. mgmt then walked 2163// 578789 -> 578865 -> 579263 -> 580657 inside one hour, so this is the normal condition, not a freak. 2164// A PATH IS A PROMISE ABOUT A LOCATION, NOT ABOUT CONTENT. The `build-<target>` lease does not help: it is 2165// released when the COMPILE ends, so it locks the wrong interval -- the hazard lives AFTER it. 2166// expect_size > 0 makes the caller state WHICH artifact it verified; a mismatch REFUSES with both numbers 2167// instead of silently shipping bytes nobody tested. 0 keeps the old behaviour so every existing caller is 2168// unchanged and this is purely additive. 2169// ⚠SIZE IS A WEAKER CHECK THAN A CONTENT HASH -- a same-size rewrite passes (the seq1477 law). It is used 2170// here because it needs NO new import in a file 52 organs depend on, and it catches the measured failure. 2171// The strong check is `nx_artifact_id verify <path> <size> <fnv>` run by the caller before invoking this. 2172func hc_promote_to_expect(nhnew: *u8, live: *u8, prev: *u8, expect_size: i64) -> i64 { 2173 if hc_exists(nhnew) == 0 { return 0 } 2174 let asz: i64 = hc_file_size(nhnew) 2175 if expect_size > 0 { 2176 if asz != expect_size { 2177 hc_puts(" REFUSED: the staged artifact is NOT the one you built -- expected bytes=" as *u8) 2178 hc_putn(expect_size) 2179 hc_puts(" actual bytes=" as *u8); hc_putn(asz); hc_puts("\n" as *u8) 2180 hc_puts(" A sibling almost certainly rebuilt this target between your build and this promote.\n" as *u8) 2181 hc_puts(" LIVE IS UNTOUCHED. Rebuild, re-read the size, and promote the artifact you verified.\n" as *u8) 2182 return 0 2183 } 2184 } 2185 // DURABLE ROLLBACK CHAIN (2026-08-21). MEASURED THIS SESSION: two deploys of ONE target inside a 2186 // single session left BOTH live and .prev holding the new build -- the pre-change binary was GONE, 2187 // and an older-generation binary CANNOT be rebuilt. This slot holds "the PREVIOUS DEPLOY", never 2188 // "the last known good", so the SECOND deploy is the one that leaves you with no way back. Until now 2189 // the only defence was the operator remembering to copy the artifact aside first, and 2190 // *A SAFETY PROPERTY THAT DEPENDS ON SOMEONE REMEMBERING IS AN ADOPTION GAP WITH EXTRA STEPS. 2191 // One generation deeper is kept here, and the generation that DOES fall off is ANNOUNCED with its 2192 // size -- a bounded chain whose loss is VISIBLE rather than silent (the no-silent-caps law). 2193 // Bounded deliberately: one extra file per target, no directory, and no new import into a file 52 2194 // organs depend on. It does NOT make the chain infinite -- it converts the measured two-deploy loss 2195 // into a kept copy and makes every deeper loss say so out loud. 2196 let bufG: *u8 = sys_mmap(512) 2197 let prev2: *u8 = hc_pathsuf(bufG, prev, "2" as *u8) 2198 if hc_exists(prev) == 1 { 2199 if hc_exists(prev2) == 1 { 2200 hc_puts(" ROLLBACK-CHAIN dropping generation " as *u8); hc_puts(prev2) 2201 hc_puts(" bytes=" as *u8); hc_putn(hc_file_size(prev2)) 2202 hc_puts(" -- if that artifact cannot be rebuilt, bank it BEFORE the next deploy\n" as *u8) 2203 } 2204 sys_renameat(prev, prev2) // rotate: the outgoing .prev survives one more deploy 2205 } 2206 sys_renameat(live, prev) // keep current live as .prev (same-dir rename; a running proc keeps its old inode) 2207 let rr: i64 = hc_copy_file(nhnew, live) // promote staged .new -> live via COPY (cross-subvolume EXDEV-safe; sys_renameat here WAS the outage) 2208 nx_chmod(live, 0x1ed) 2209 if rr == 0 { 2210 // A RECEIPT ON EVERY PROMOTE, not only when asked: a deploy verb that cannot name what it deployed 2211 // is not a receipt, and a lost response then reads as a stale binary. 2212 hc_puts(" promoted bytes=" as *u8); hc_putn(asz); hc_puts(" (verify with: nx_artifact_id.elf id <live-path>)\n" as *u8) 2213 return 1 2214 } 2215 return 0 2216} 2217func hc_promote_to(nhnew: *u8, live: *u8, prev: *u8) -> i64 { 2218 return hc_promote_to_expect(nhnew, live, prev, 0) 2219} 2220// ---- GENERIC DEPLOY (R2 of the SOTA rung plan) --------------------------------------------------- 2221// THE DEFECT THIS RETIRES: cmd_deploy() takes NO PARAMETERS. It is hardcoded to the HC_ELF constants 2222// and to proc_kill_by_name("sites.elf") -- a GENERIC NAME on a SINGLE-SERVICE deployer. That is why 2223// every new deployable needed its own function, and the cost was counted 2026-07-31: 2224// 15 cmd_*deploy functions + 12 cmd_*rollback twins = 27 near-duplicate copies of ONE pattern. 2225// 2226// THE REAL COST IS NOT DUPLICATION, IT IS DIVERGENCE. Four hard-won lessons live in cmd_mgmtdeploy -- 2227// refuse-before-destroy, print the REAL rc, accept EITHER staged name, consume BOTH on success -- and 2228// cmd_deploy has only the first. Each lesson had to be re-applied 27 times and demonstrably was not. 2229// ★★★★★★ EVERY COPY OF A PATTERN IS A PLACE A FIX CAN FAIL TO ARRIVE. 2230// 2231// With this, adding a deployable is a ROW in deploy_targets.conf naming sub=gdeploy plus the live path 2232// and process name -- a DATA edit, not new code. That is the whole of R2. 2233 2234// Build <live><suffix> into dst. Returns dst for call-site brevity. 2235func hc_pathsuf(dst: *u8, live: *u8, suf: *u8) -> *u8 { 2236 var o: i64 = hc_cat(dst, 0, live) 2237 o = hc_cat(dst, o, suf) 2238 dst[o] = 0 as u8 2239 return dst 2240} 2241 2242// Derive the SECOND staged name: /path/nx_foo.elf -> /path/nx_foo.sov.elf.new 2243// /api/build stages <target>.sov.elf.new while /api/upload stages <artifact>.new. TWO NAMES FOR ONE 2244// ARTIFACT IS TWO SOURCES OF TRUTH, and a promoter that reads only one silently ignores the other -- 2245// the seq1525 class, staged where the promoter cannot see it. 2246func hc_sovname(dst: *u8, live: *u8) -> *u8 { 2247 var n: i64 = 0 2248 while live[n] != (0 as u8) { n = n + 1 } 2249 // strip a trailing ".elf" if present, then append ".sov.elf.new" 2250 var cut: i64 = n 2251 if n > 4 { 2252 if live[n-4] == (46 as u8) { if live[n-3] == (101 as u8) { if live[n-2] == (108 as u8) { if live[n-1] == (102 as u8) { cut = n - 4 } } } } 2253 } 2254 var i: i64 = 0 2255 while i < cut { dst[i] = live[i]; i = i + 1 } 2256 var o: i64 = hc_cat(dst, cut, ".sov.elf.new" as *u8) 2257 dst[o] = 0 as u8 2258 return dst 2259} 2260 2261// Promote a staged artifact over <live> and restart <killname>. ALL FOUR LESSONS IN ONE PLACE. 2262func hc_deploy_one(live: *u8, killname: *u8) -> i64 { 2263 let bufA: *u8 = sys_mmap(512) 2264 let bufB: *u8 = sys_mmap(512) 2265 let bufP: *u8 = sys_mmap(512) 2266 let new1: *u8 = hc_pathsuf(bufA, live, ".new" as *u8) 2267 let new2: *u8 = hc_sovname(bufB, live) 2268 let prev: *u8 = hc_pathsuf(bufP, live, ".prev" as *u8) 2269 2270 // LESSON 3 + PRECEDENCE: prefer the SANCTIONED BUILD artifact (.sov.elf.new). Preferring the legacy 2271 // upload name let ONE stale leftover permanently shadow every fresh build -- a promote that reported 2272 // success and shipped the wrong binary. Nothing lied; the wrong artifact was chosen. 2273 var stg: *u8 = 0 as *u8 2274 if hc_exists(new2) == 1 { stg = new2 } 2275 if stg == (0 as *u8) { if hc_exists(new1) == 1 { stg = new1 } } 2276 2277 // LESSON 1 -- REFUSE BEFORE DESTROY. The original unconditional live->prev rename moved the live 2278 // binary away BEFORE checking anything was staged, leaving the host with NOTHING when the install 2279 // then failed. Refuse loudly and touch nothing. 2280 if stg == (0 as *u8) { 2281 hc_puts(" REFUSED: nothing staged for " as *u8); hc_puts(live) 2282 hc_puts(" (looked for BOTH .new and .sov.elf.new) -- LIVE UNTOUCHED\n" as *u8) 2283 return 1 2284 } 2285 2286 // hc_promote_to banks .prev, COPIES (EXDEV-safe across subvolumes), and chmods 0755. 2287 // ⚠⚠ IT RETURNS 1 ON SUCCESS AND 0 ON FAILURE -- the INVERSE of the syscall convention used three 2288 // lines below it. The first version of this function assumed 0=ok and therefore printed 2289 // "PROMOTE FAILED" over a promote that had DEMONSTRABLY SUCCEEDED (live=NEW, .prev=OLD on disk), 2290 // then skipped the consume and the restart. Caught by a sandbox positive-path test, not by review. 2291 // ★★★★★★ A RETURN CONVENTION IS PART OF AN API -- READ IT, NEVER INFER IT FROM THE NEIGHBOURS. 2292 // This file mixes both conventions, which is exactly why the mistake is available to make. 2293 let ok: i64 = hc_promote_to(stg, live, prev) 2294 hc_puts(" promote ok=" as *u8); hc_putn(ok); hc_puts(" (hc_promote_to: 1=SUCCESS, 0=failure)\n" as *u8) 2295 if ok != 1 { 2296 hc_puts(" PROMOTE FAILED -- live left as-is, .prev intact\n" as *u8) 2297 return 1 2298 } 2299 2300 // LESSON 4 -- consume BOTH staged names. hc_promote_to copies rather than renames, so nothing 2301 // consumes the source; a leftover would shadow the next build forever. 2302 sys_unlinkat(new1) 2303 sys_unlinkat(new2) 2304 2305 let pk: i64 = proc_kill_by_name(killname, 9) 2306 hc_puts(" killed " as *u8); hc_puts(killname) 2307 hc_puts(" procs=" as *u8); hc_putn(pk) 2308 hc_puts(" -> guard respawns the NEW binary (name-guarded restart, <=15s)\n" as *u8) 2309 return 0 2310} 2311 2312// Does s start with p? Needed for the gdeploy@<row> form; hc_streq is exact-match only. 2313func hc_prefix(s: *u8, p: *u8) -> i64 { 2314 var i: i64 = 0 2315 while p[i] != (0 as u8) { 2316 if s[i] == (0 as u8) { return 0 } 2317 if s[i] != p[i] { return 0 } 2318 i = i + 1 2319 } 2320 return 1 2321} 2322 2323// Read field `want` (0-based) of the deploy_targets.conf row whose FIRST field == rowname, into out. 2324// Returns 1 if found. ONE SOURCE OF TRUTH ON PURPOSE: the paths live in the SAME row /api/deploy already 2325// reads, not a second file. A second mapping file would be a second thing to forget to update, which is 2326// the declared-vs-real divergence this whole arc is about. 2327func hc_row_field(rowname: *u8, want: i64, out: *u8) -> i64 { 2328 let lp: *i64 = sys_mmap(16) as *i64 2329 lp[0] = 0 2330 let b: *u8 = sys_read_file("deploy_targets.conf" as *u8, lp) 2331 let n: i64 = lp[0] 2332 if n <= 0 { return 0 } 2333 var line: i64 = 0 2334 while line < n { 2335 var eol: i64 = line 2336 var es: i64 = 0 2337 while es == 0 { if eol >= n { es = 1 } else { if b[eol] == (10 as u8) { es = 1 } else { eol = eol + 1 } } } 2338 var skip: i64 = 0 2339 if line < eol { if b[line] == (35 as u8) { skip = 1 } } 2340 if skip == 0 { 2341 // field 0 must equal rowname 2342 var p: i64 = line 2343 var fe: i64 = 0 2344 while fe == 0 { if p < eol { if b[p] == (32 as u8) { fe = 1 } else { p = p + 1 } } else { fe = 1 } } 2345 var hit: i64 = 1 2346 var k: i64 = 0 2347 while rowname[k] != (0 as u8) { 2348 if line + k >= p { hit = 0; k = k + 1 } else { 2349 if b[line + k] != rowname[k] { hit = 0 } 2350 k = k + 1 2351 } 2352 } 2353 if p - line != k { hit = 0 } 2354 if hit == 1 { 2355 var f: i64 = 1 2356 var q: i64 = p 2357 var done: i64 = 0 2358 while done == 0 { 2359 var ws: i64 = 0 2360 while ws == 0 { if q < eol { if b[q] == (32 as u8) { q = q + 1 } else { ws = 1 } } else { ws = 1 } } 2361 if q >= eol { done = 1 } else { 2362 let fs: i64 = q 2363 var f2: i64 = 0 2364 while f2 == 0 { if q < eol { if b[q] == (32 as u8) { f2 = 1 } else { q = q + 1 } } else { f2 = 1 } } 2365 if f == want { 2366 var z: i64 = 0 2367 while fs + z < q { out[z] = b[fs + z]; z = z + 1 } 2368 out[z] = 0 as u8 2369 return 1 2370 } 2371 f = f + 1 2372 } 2373 } 2374 return 0 2375 } 2376 } 2377 line = eol + 1 2378 } 2379 return 0 2380} 2381 2382// gdeploy <live-path> <kill-name> -- the data-driven deploy verb. 2383func cmd_gdeploy(live: *u8, killname: *u8) -> i64 { 2384 hc_puts("[hostctl] gdeploy: generic atomic promote + name-guarded restart\n" as *u8) 2385 hc_puts(" live=" as *u8); hc_puts(live); hc_puts(" kill=" as *u8); hc_puts(killname); hc_puts("\n" as *u8) 2386 return hc_deploy_one(live, killname) 2387} 2388 2389// ---- THE PER-ROW REVERSE GEAR (2026-09-03) ------------------------------------------------------- 2390// WHY THIS EXISTS, MEASURED: every gdeploy@ row in deploy_targets.conf named `rollback` in field 5, and 2391// cmd_rollback() TAKES NO TARGET ARGUMENT -- it unconditionally renames sites.elf.prev over sites.elf and 2392// kills sites.elf. So the declared reverse gear of 16 daemons REVERSED THE FRONT DOOR and left the bad 2393// daemon live; and because /api/deploy's health watchdog invokes field 5, that wrong-subject destruction 2394// was AUTOMATIC, not merely a manual footgun. This file already recorded the hazard twice by hand 2395// (docportal corrected 2026-09-02, comparegw dodging it with a deliberately unknown verb, and the words 2396// "every gdeploy row above carries that word") without generalising it. 2397// ★A DEFECT NAMED IN A COMMENT BESIDE THE ROWS THAT STILL HAVE IT IS NOT DOCUMENTED, IT IS ADVERTISED. 2398// Nothing new is needed to do this right: hc_deploy_one already banks <live>.prev per row, and the row 2399// itself carries live-path (field 6) and kill-name (field 7). There was simply never a generic reverse. 2400// 2401// TWO CONJUNCTS cmd_rollback DOES NOT HAVE, both deliberate and both about failing toward the live binary: 2402// 1. REFUSE BEFORE DESTROY (hc_deploy_one lesson 1): with no banked .prev there is nothing to restore and 2403// a rename of an absent file would leave the target with NOTHING. Refuse loudly, touch nothing. 2404// 2. DO NOT KILL AFTER A FAILED RESTORE. cmd_rollback chmods and kills regardless of the rename rc; if the 2405// restore failed, killing the process only respawns THE BAD BINARY while reporting a rollback. A reverse 2406// gear that cannot prove it restored must not also stop the thing it failed to fix. 2407// The rc is PROPAGATED (cmd_gdeploy discards hc_deploy_one's rc and exits 0, so a REFUSED deploy reads as 2408// success -- an asymmetry deliberately NOT copied here, because a refused rollback the caller reads as 2409// success is how a bad binary stays live with everyone believing it was reverted). 2410func cmd_gdeploy_rollback(live: *u8, killname: *u8) -> i64 { 2411 hc_puts("[hostctl] gdeployrb: restore <live>.prev -> live + name-guarded restart (PER-ROW reverse gear)\n" as *u8) 2412 hc_puts(" live=" as *u8); hc_puts(live); hc_puts(" kill=" as *u8); hc_puts(killname); hc_puts("\n" as *u8) 2413 let bufP: *u8 = sys_mmap(512) 2414 let prev: *u8 = hc_pathsuf(bufP, live, ".prev" as *u8) 2415 if hc_exists(prev) != 1 { 2416 hc_puts(" REFUSED: no banked " as *u8); hc_puts(prev) 2417 hc_puts(" -- LIVE UNTOUCHED (there is nothing to roll back to)\n" as *u8) 2418 return 1 2419 } 2420 let rr: i64 = sys_renameat(prev, live) 2421 hc_put_rc(" restore rename rc=" as *u8, rr) 2422 if rr != 0 { 2423 hc_puts(" REFUSED: restore FAILED -- live left as it was and the process NOT killed (a kill here would respawn the bad binary)\n" as *u8) 2424 return 1 2425 } 2426 nx_chmod(live, 0x1ed) 2427 let pk: i64 = proc_kill_by_name(killname, 9) 2428 hc_puts(" killed " as *u8); hc_puts(killname) 2429 hc_puts(" procs=" as *u8); hc_putn(pk) 2430 hc_puts(" -> supervisor respawns the RESTORED binary\n" as *u8) 2431 return 0 2432} 2433 2434// ---- TOOLCHAIN DEPLOY (seq891) ------------------------------------------------------------------- 2435// THE GAP THIS CLOSES: the ecosystem could build and deploy every SERVICE over its own API but could 2436// NOT update the COMPILER that builds them. /api/upload gates on a service-basename allowlist and 2437// /api/build stages into nishihost/, with no allowlisted route into buildroot/_offc -- so a PROVEN 2438// compiler fix could not be landed API-first (rule 27) and needed on-LAN/ssh. 2439// 2440// NEVER-BRICK BY CONSTRUCTION: this reuses hc_promote_to, the same primitive the torrent and gallery 2441// cross-dir promotes already use. It no-ops when nothing is staged, banks the current binary as .prev, 2442// COPIES rather than renames (cross-subvolume EXDEV is what caused a real outage here once), and 2443// chmods 0755 so the promoted compiler stays EXECUTABLE -- the detail that makes the tempting 2444// /api/unpack shortcut unsafe (nx_treepack writes 0644 and unlinks; see debt seq903). 2445// 2446// Each binary is INDEPENDENT: a missing stage is a no-op, not a failure, so a partial ship can never 2447// half-install a toolchain. The toolchain is NOT a daemon -- there is nothing to kill or respawn, and 2448// the next /api/build simply picks up the new binary. Verify a landing by BEHAVIOUR (build a probe), 2449// never by the promote returning 1. 2450const HC_TC_CC: *u8 = "/volume1/homes/elderwesto/nishihost/buildroot/_offc/nx_cc_sovereign.elf" as *u8 2451const HC_TC_CC_NEW: *u8 = "/volume1/homes/elderwesto/nishihost/buildroot/runtime/nx_cc_sovereign.elf.new" as *u8 2452const HC_TC_CC_PREV: *u8 = "/volume1/homes/elderwesto/nishihost/buildroot/_offc/nx_cc_sovereign.elf.prev" as *u8 2453const HC_TC_CC_BAD: *u8 = "/volume1/homes/elderwesto/nishihost/buildroot/_offc/nx_cc_sovereign.elf.bad" as *u8 2454const HC_TC_ASM: *u8 = "/volume1/homes/elderwesto/nishihost/buildroot/_offc/nxasm_x86_main.elf" as *u8 2455const HC_TC_ASM_NEW: *u8 = "/volume1/homes/elderwesto/nishihost/buildroot/runtime/nxasm_x86_main.elf.new" as *u8 2456const HC_TC_ASM_PREV: *u8 = "/volume1/homes/elderwesto/nishihost/buildroot/_offc/nxasm_x86_main.elf.prev" as *u8 2457const HC_TC_ASM_BAD: *u8 = "/volume1/homes/elderwesto/nishihost/buildroot/_offc/nxasm_x86_main.elf.bad" as *u8 2458const HC_TC_SBR: *u8 = "/volume1/homes/elderwesto/nishihost/buildroot/_offc/nx_sov_build_run.elf" as *u8 2459const HC_TC_SBR_NEW: *u8 = "/volume1/homes/elderwesto/nishihost/buildroot/runtime/nx_sov_build_run.elf.new" as *u8 2460const HC_TC_SBR_PREV: *u8 = "/volume1/homes/elderwesto/nishihost/buildroot/_offc/nx_sov_build_run.elf.prev" as *u8 2461const HC_TC_SBR_BAD: *u8 = "/volume1/homes/elderwesto/nishihost/buildroot/_offc/nx_sov_build_run.elf.bad" as *u8 2462func cmd_toolchaindeploy() -> i64 { 2463 hc_puts("[hostctl] toolchaindeploy: promote staged toolchain -> buildroot/_offc/ (.prev kept, chmod 0755)\n" as *u8) 2464 let c: i64 = hc_promote_to(HC_TC_CC_NEW, HC_TC_CC, HC_TC_CC_PREV) 2465 let a: i64 = hc_promote_to(HC_TC_ASM_NEW, HC_TC_ASM, HC_TC_ASM_PREV) 2466 let b: i64 = hc_promote_to(HC_TC_SBR_NEW, HC_TC_SBR, HC_TC_SBR_PREV) 2467 hc_puts("[hostctl] toolchaindeploy: cc=" as *u8); hc_putn(c) 2468 hc_puts(" nxasm=" as *u8); hc_putn(a) 2469 hc_puts(" sovbuildrun=" as *u8); hc_putn(b) 2470 hc_puts(" (1=promoted, 0=nothing staged) -- VERIFY BY BUILDING A PROBE, not by these counts\n" as *u8) 2471 return 0 2472} 2473func cmd_toolchainrollback() -> i64 { 2474 hc_puts("[hostctl] toolchainrollback: restore the .prev toolchain binaries\n" as *u8) 2475 let c: i64 = hc_promote_to(HC_TC_CC_PREV, HC_TC_CC, HC_TC_CC_BAD) 2476 let a: i64 = hc_promote_to(HC_TC_ASM_PREV, HC_TC_ASM, HC_TC_ASM_BAD) 2477 let b: i64 = hc_promote_to(HC_TC_SBR_PREV, HC_TC_SBR, HC_TC_SBR_BAD) 2478 hc_puts("[hostctl] toolchainrollback: cc=" as *u8); hc_putn(c) 2479 hc_puts(" nxasm=" as *u8); hc_putn(a) 2480 hc_puts(" sovbuildrun=" as *u8); hc_putn(b) 2481 hc_puts("\n" as *u8) 2482 return 0 2483} 2484func cmd_torrentdeploy() -> i64 { 2485 hc_puts("[hostctl] torrentdeploy: promote staged torrent binaries -> /volume1/ai/torrent/ (.prev kept) + kicktorrent\n" as *u8) 2486 let d: i64 = hc_promote_to("/volume1/homes/elderwesto/nishihost/nx_torrent_daemon.sov.elf.new" as *u8, "/volume1/ai/torrent/nx_torrent_daemon.sov.elf" as *u8, "/volume1/ai/torrent/nx_torrent_daemon.sov.elf.prev" as *u8) 2487 let s: i64 = hc_promote_to("/volume1/homes/elderwesto/nishihost/nx_torrent_seedeval.elf.new" as *u8, "/volume1/ai/torrent/nx_torrent_seedeval.elf" as *u8, "/volume1/ai/torrent/nx_torrent_seedeval.elf.prev" as *u8) 2488 // ★THE SEEDER ITSELF (2026-07-30) -- the one torrent binary in this directory with NO promotion line. 2489 // torrentdeploy promoted daemon, seedeval, worker, mp4faststart, extract and vidindex, but never 2490 // nx_torrent_seed, so a seeder fix could not ship by ANY sanctioned route: /api/promote is refused now 2491 // that it is correctly declared a daemon, and /api/deploy had nothing to call. This ONE LINE is the 2492 // whole adoption gap -- the primitives (hc_promote_to), the guard (restarts it when dead) and the 2493 // restart verb (kickseed) all already existed and were simply unreachable for this artefact. 2494 // ⚠NAME TRAP: the siblings promote to `<name>.sov.elf`, but HC_SEED_CMD execs `./nx_torrent_seed.elf` 2495 // and the guard matches that exact name -- so the LIVE target is `.elf`, NOT `.sov.elf`. Promoting to 2496 // the sibling-style name would place a binary nothing ever runs and read as a successful deploy. 2497 // hc_promote_to is guarded on .new existence, so this is a NO-OP whenever no seeder is staged. 2498 let sd: i64 = hc_promote_to("/volume1/homes/elderwesto/nishihost/nx_torrent_seed.sov.elf.new" as *u8, "/volume1/ai/torrent/nx_torrent_seed.elf" as *u8, "/volume1/ai/torrent/nx_torrent_seed.elf.prev" as *u8) 2499 // WORKER (nx_torrent_get.sov.elf) parity: it lives in /volume1/ai/torrent/ as a daemon SIBLING (d_self_dir 2500 // execs it there) but had NO API deploy path -- so a worker fix (e.g. the "IGNORING: seeders unchoke us, 2501 // have=0" data-timeout fix) could not ship without shell. Built by /api/build -> nishihost/<name>.sov.elf.new, 2502 // cross-dir promoted here (same volume1 => rename), .prev kept (never-brick). Guarded on .new existence, so a 2503 // daemon-only or seedeval-only deploy never touches the live worker. Running workers double-fork (reparent to 2504 // init) so they survive; NEW spawns use the new elf -> pause/resume a stuck torrent to force it onto the fix. 2505 let w: i64 = hc_promote_to("/volume1/homes/elderwesto/nishihost/nx_torrent_get.sov.elf.new" as *u8, "/volume1/ai/torrent/nx_torrent_get.sov.elf" as *u8, "/volume1/ai/torrent/nx_torrent_get.sov.elf.prev" as *u8) 2506 // nx_mp4_faststart: daemon sibling (self-dir) the /stream handler execve's to relocate moov to the front of a 2507 // non-faststart mp4 so a browser <video> can play it. Same cross-subvolume copy-promote as the worker. 2508 let mf: i64 = hc_promote_to("/volume1/homes/elderwesto/nishihost/nx_mp4_faststart.sov.elf.new" as *u8, "/volume1/ai/torrent/nx_mp4_faststart.sov.elf" as *u8, "/volume1/ai/torrent/nx_mp4_faststart.sov.elf.prev" as *u8) 2509 // GALLERY BRIDGE (2026-07-24): EXTRACTOR (download.part -> real .mp4 on complete) + fixed NAS-path INDEXER 2510 // (scans /volume1/ai/torrent -> gallery catalog). Worker-spawned siblings; absent-.new promotes = no-ops (:1759). 2511 let ex: i64 = hc_promote_to("/volume1/homes/elderwesto/nishihost/nx_torrent_extract.sov.elf.new" as *u8, "/volume1/ai/torrent/nx_torrent_extract.sov.elf" as *u8, "/volume1/ai/torrent/nx_torrent_extract.sov.elf.prev" as *u8) 2512 let gv: i64 = hc_promote_to("/volume1/homes/elderwesto/nishihost/nx_galx_vidindex.sov.elf.new" as *u8, "/volume1/ai/torrent/nx_galx_vidindex.sov.elf" as *u8, "/volume1/ai/torrent/nx_galx_vidindex.sov.elf.prev" as *u8) 2513 hc_puts(" promoted daemon=" as *u8); hc_putn(d); hc_puts(" seedeval=" as *u8); hc_putn(s); hc_puts(" SEEDER=" as *u8); hc_putn(sd); hc_puts(" worker=" as *u8); hc_putn(w); hc_puts(" mp4faststart=" as *u8); hc_putn(mf); hc_puts(" extract=" as *u8); hc_putn(ex); hc_puts(" vidindex=" as *u8); hc_putn(gv); hc_puts("\n" as *u8) 2514 // 2026-07-24: daemon auto-kill REMOVED here -- torrentdeploy now only PLACES binaries (incl. the new 2515 // gallery-bridge extractor + indexer siblings); restart the daemon EXPLICITLY via kicktorrent only when 2516 // the daemon binary itself changed, so placing sibling tools never forces a needless daemon restart. 2517 hc_puts(" (daemon not auto-restarted; run kicktorrent explicitly if the daemon binary changed)\n" as *u8) 2518 return 0 2519} 2520func cmd_torrentrollback() -> i64 { 2521 hc_puts("[hostctl] torrentrollback: restore torrent binaries .prev -> live + kicktorrent (reverse a bad torrentdeploy)\n" as *u8) 2522 let d: i64 = hc_promote_to("/volume1/ai/torrent/nx_torrent_daemon.sov.elf.prev" as *u8, "/volume1/ai/torrent/nx_torrent_daemon.sov.elf" as *u8, "/volume1/ai/torrent/nx_torrent_daemon.sov.elf.bad" as *u8) 2523 let s: i64 = hc_promote_to("/volume1/ai/torrent/nx_torrent_seedeval.elf.prev" as *u8, "/volume1/ai/torrent/nx_torrent_seedeval.elf" as *u8, "/volume1/ai/torrent/nx_torrent_seedeval.elf.bad" as *u8) 2524 // ★ROLLBACK MUST COVER EXACTLY WHAT DEPLOY PLACES. A deploy line without a matching rollback line is a 2525 // one-way door: the .prev is banked and unreachable, so a bad seeder could be shipped but not reversed. 2526 let sd: i64 = hc_promote_to("/volume1/ai/torrent/nx_torrent_seed.elf.prev" as *u8, "/volume1/ai/torrent/nx_torrent_seed.elf" as *u8, "/volume1/ai/torrent/nx_torrent_seed.elf.bad" as *u8) 2527 hc_puts(" restored daemon=" as *u8); hc_putn(d); hc_puts(" seedeval=" as *u8); hc_putn(s); hc_puts(" SEEDER=" as *u8); hc_putn(sd); hc_puts("\n" as *u8) 2528 if d == 1 { proc_kill_by_name("nx_torrent_daemon" as *u8, 9) } 2529 // Restart the seeder only if its binary actually changed, mirroring the daemon rule above. 2530 if sd == 1 { proc_kill_by_name("nx_torrent_seed.elf" as *u8, 9) } 2531 return 0 2532} 2533 2534// promote/rollback the Relationship OS daemon (:8027). Same-dir promote in nishihost cwd (hc_promote_to = keep 2535// live as .prev, mv .new -> live, chmod; guarded on .new existence -> deploying without a staged .new is a no-op). 2536func cmd_relatedeploy() -> i64 { 2537 hc_puts("[hostctl] relatedeploy: promote nx_relate_daemon.elf.new -> live (.prev kept) + restart :8027\n" as *u8) 2538 let d: i64 = hc_promote_to(HC_RELATE_NEW, HC_RELATE, HC_RELATE_PREV) 2539 hc_puts(" promoted=" as *u8); hc_putn(d); hc_puts("\n" as *u8) 2540 if d == 1 { let pk: i64 = proc_kill_by_name("nx_relate_daemon.elf" as *u8, 9); hc_puts(" killed relate procs=" as *u8); hc_putn(pk); hc_puts(" -> guard respawns the NEW :8027 daemon\n" as *u8) } 2541 return 0 2542} 2543func cmd_relaterollback() -> i64 { 2544 hc_puts("[hostctl] relaterollback: restore nx_relate_daemon.elf.prev -> live + restart (reverse a bad relatedeploy)\n" as *u8) 2545 let d: i64 = hc_promote_to(HC_RELATE_PREV, HC_RELATE, "/volume1/homes/elderwesto/nishihost/nx_relate_daemon.elf.bad" as *u8) 2546 hc_puts(" restored=" as *u8); hc_putn(d); hc_puts("\n" as *u8) 2547 if d == 1 { proc_kill_by_name("nx_relate_daemon.elf" as *u8, 9) } 2548 return 0 2549} 2550// promote/rollback the SOVEREIGN RU<->EN translate daemon (:8447, /translate). Same-dir promote in nishihost cwd 2551// (mirrors cmd_docportaldeploy). Staged binary is nx_translate_daemon.sov.elf.new (nx_ship/build-over-API output). 2552func cmd_xlatedeploy() -> i64 { 2553 hc_puts("[hostctl] xlatedeploy: promote nx_translate_daemon.sov.elf.new -> live (.prev kept) + restart :8447\n" as *u8) 2554 let d: i64 = hc_promote_to(HC_XLATE_NEW, HC_XLATE, HC_XLATE_PREV) 2555 hc_puts(" promoted=" as *u8); hc_putn(d); hc_puts("\n" as *u8) 2556 if d == 1 { let pk: i64 = proc_kill_by_name("nx_translate_daemon.elf" as *u8, HC_SIGKILL); hc_puts(" killed xlate procs=" as *u8); hc_putn(pk); hc_puts(" -> guard respawns the NEW :8447 daemon\n" as *u8) } 2557 return 0 2558} 2559func cmd_xlaterollback() -> i64 { 2560 hc_puts("[hostctl] xlaterollback: restore nx_translate_daemon.elf.prev -> live + restart (reverse a bad xlatedeploy)\n" as *u8) 2561 let d: i64 = hc_promote_to(HC_XLATE_PREV, HC_XLATE, "/volume1/homes/elderwesto/nishihost/nx_translate_daemon.elf.bad" as *u8) 2562 hc_puts(" restored=" as *u8); hc_putn(d); hc_puts("\n" as *u8) 2563 if d == 1 { proc_kill_by_name("nx_translate_daemon.elf" as *u8, HC_SIGKILL) } 2564 return 0 2565} 2566 2567// promote/rollback the ethical CLEAN-SERVE daemon (:8102). Same-dir promote in nishihost cwd (mirrors cmd_relatedeploy). 2568func cmd_cleanservedeploy() -> i64 { 2569 hc_puts("[hostctl] cleanservedeploy: promote nx_clean_serve_daemon.elf.new -> live (.prev kept) + restart :8102\n" as *u8) 2570 let d: i64 = hc_promote_to(HC_CLEANSERVE_NEW, HC_CLEANSERVE, HC_CLEANSERVE_PREV) 2571 hc_puts(" promoted=" as *u8); hc_putn(d); hc_puts("\n" as *u8) 2572 if d == 1 { let pk: i64 = proc_kill_by_name("nx_clean_serve_daemon.elf" as *u8, 9); hc_puts(" killed cleanserve procs=" as *u8); hc_putn(pk); hc_puts(" -> guard respawns the NEW :8102 daemon\n" as *u8) } 2573 return 0 2574} 2575// promote/rollback the DOCPORTAL admin daemon (:HC_MAGIC_18456, /search + /doc + /api). Same-dir promote in nishihost cwd 2576// (mirrors cmd_cleanservedeploy). The staged binary is <name>.SOV.elf.new (nx_ship/build-over-API output) -> the 2577// deploy sub consumes it directly, so /api/deploy target=docportal needs NO manual .sov rename. .prev = rollback. 2578func cmd_docportaldeploy() -> i64 { 2579 hc_puts("[hostctl] docportaldeploy: promote nx_docportal_admin_daemon.sov.elf.new -> live (.prev kept) + kick :18456\n" as *u8) 2580 let d: i64 = hc_promote_to(HC_DOCPORTAL_NEW, HC_DOCPORTAL, HC_DOCPORTAL_PREV) 2581 hc_puts(" promoted=" as *u8); hc_putn(d); hc_puts("\n" as *u8) 2582 if d == 1 { let pk: i64 = proc_kill_by_name("nx_docportal_admin_daemon.elf" as *u8, 9); hc_puts(" killed docportal procs=" as *u8); hc_putn(pk); hc_puts(" -> guard respawns the NEW :18456 daemon\n" as *u8) } 2583 return 0 2584} 2585func cmd_docportalrollback() -> i64 { 2586 hc_puts("[hostctl] docportalrollback: restore nx_docportal_admin_daemon.elf.prev -> live + kick (reverse a bad docportaldeploy)\n" as *u8) 2587 let d: i64 = hc_promote_to(HC_DOCPORTAL_PREV, HC_DOCPORTAL, "/volume1/homes/elderwesto/nishihost/nx_docportal_admin_daemon.elf.bad" as *u8) 2588 hc_puts(" restored=" as *u8); hc_putn(d); hc_puts("\n" as *u8) 2589 if d == 1 { proc_kill_by_name("nx_docportal_admin_daemon.elf" as *u8, 9) } 2590 return 0 2591} 2592func cmd_cleanserverollback() -> i64 { 2593 hc_puts("[hostctl] cleanserverollback: restore nx_clean_serve_daemon.elf.prev -> live + restart (reverse a bad cleanservedeploy)\n" as *u8) 2594 let d: i64 = hc_promote_to(HC_CLEANSERVE_PREV, HC_CLEANSERVE, "/volume1/homes/elderwesto/nishihost/nx_clean_serve_daemon.elf.bad" as *u8) 2595 hc_puts(" restored=" as *u8); hc_putn(d); hc_puts("\n" as *u8) 2596 if d == 1 { proc_kill_by_name("nx_clean_serve_daemon.elf" as *u8, 9) } 2597 return 0 2598} 2599 2600// never-brick ROLLBACK: restore the previous live sites.elf (saved by cmd_deploy as .prev) + respawn. Reverses a bad deploy. 2601func cmd_rollback() -> i64 { 2602 hc_puts("[hostctl] rollback: restore sites.elf.prev -> live + respawn (reverse a bad deploy)\n" as *u8) 2603 let rr: i64 = sys_renameat(HC_ELF_PREV, HC_ELF) 2604 nx_chmod(HC_ELF, 0x1ed) 2605 hc_put_rc(" restore rename rc=" as *u8, rr) 2606 let pk: i64 = proc_kill_by_name("sites.elf" as *u8, 9) 2607 hc_puts(" killed sites.elf procs=" as *u8); hc_putn(pk); hc_puts(" -> supervisor respawns the RESTORED binary\n" as *u8) 2608 return 0 2609} 2610 2611// CERT-ONLY deploy: backup the live cert+key as .bak, atomically install the staged .new, then kill 2612// sites.elf so the supervisor respawns the LIVE binary (which reloads the cert at startup). NO binary 2613// swap -- safe even with a broken staged sites.elf. never-brick: .bak enables cmd_certrollback. 2614func cmd_certswap() -> i64 { 2615 hc_puts("[hostctl] certswap: backup live cert -> .bak, install .new -> live, restart sites.elf (no binary swap)\n" as *u8) 2616 sys_renameat(HC_CERT_LIVE, HC_CERT_BAK) 2617 sys_renameat(HC_KEY_LIVE, HC_KEY_BAK) 2618 let r1: i64 = sys_renameat(HC_CERT_NEW, HC_CERT_LIVE) 2619 let r2: i64 = sys_renameat(HC_KEY_NEW, HC_KEY_LIVE) 2620 hc_put_rc(" install rc cert=" as *u8, r1) 2621 hc_put_rc(" install rc key=" as *u8, r2) 2622 let pk: i64 = proc_kill_by_name("sites.elf" as *u8, 9) 2623 hc_puts(" killed sites.elf procs=" as *u8); hc_putn(pk); hc_puts(" -> supervisor respawns the LIVE binary (reloads new cert, <=15s)\n" as *u8) 2624 return 0 2625} 2626 2627// never-brick cert ROLLBACK: restore the previous cert+key (.bak) + respawn. Reverses a bad certswap. 2628func cmd_certrollback() -> i64 { 2629 hc_puts("[hostctl] certrollback: restore cert/key .bak -> live + restart sites.elf\n" as *u8) 2630 sys_renameat(HC_CERT_BAK, HC_CERT_LIVE) 2631 sys_renameat(HC_KEY_BAK, HC_KEY_LIVE) 2632 let pk: i64 = proc_kill_by_name("sites.elf" as *u8, 9) 2633 hc_puts(" killed sites.elf procs=" as *u8); hc_putn(pk); hc_puts(" -> supervisor respawns (restored cert)\n" as *u8) 2634 return 0 2635} 2636 2637// spawn the live login daemon detached (mirrors hc_spawn_wiki_gw). 2638func hc_spawn_login() -> i64 { 2639 let pid: i64 = sys_fork() 2640 if pid==0 { 2641 nx_setsid() 2642 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 2643 if dn >= 0 { sys_dup3(dn,0,0); sys_dup3(dn,1,0); sys_dup3(dn,2,0) } 2644 let argv: *i64 = sys_mmap(32) as *i64 2645 argv[0]=HC_SH as i64; argv[1]=HC_DASHC as i64; argv[2]=HC_LOGIN_CMD as i64; argv[3]=0 2646 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 2647 sys_execve_clean(HC_SH, argv, envp) 2648 sys_exit(127) 2649 } 2650 return pid 2651} 2652// PRE-FLIGHT: spawn the staged login .new on throwaway :9099 (NOT :9091); print its startup. OPAQUE-LOGIN-UP = 2653// safe to logindeploy; CTX-INIT-FAIL/BIND-FAIL = bad keys/args, do NOT deploy. Then kill the test (frees :9099). 2654func cmd_logintest() -> i64 { 2655 hc_puts("=== logintest: spawn nx_opaque_login.elf.new on :9099 (live :9091 untouched) ===\n" as *u8) 2656 let pid: i64 = sys_fork() 2657 if pid==0 { 2658 nx_setsid() 2659 let argv: *i64 = sys_mmap(32) as *i64 2660 argv[0]=HC_SH as i64; argv[1]=HC_DASHC as i64; argv[2]=HC_LOGIN_TEST_CMD as i64; argv[3]=0 2661 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 2662 sys_execve_clean(HC_SH, argv, envp) 2663 sys_exit(127) 2664 } 2665 sys_sleep_ms(HC_MAGIC_1500) 2666 let lb: *u8 = sys_mmap(HC_MAGIC_8192) 2667 let n: i64 = hc_read_cmdline("/tmp/logintest.out" as *u8, lb, HC_MAGIC_8192) 2668 hc_puts("--- /tmp/logintest.out ---\n" as *u8) 2669 if n > 0 { sys_write(1, lb, n) } else { hc_puts("(empty -- did not start?)\n" as *u8) } 2670 let k: i64 = proc_kill_by_name("nx_opaque_login.elf.new" as *u8, 9) 2671 hc_puts("\n killed test procs=" as *u8); hc_putn(k); hc_puts(" (:9099 freed)\n" as *u8) 2672 return 0 2673} 2674 2675// runmtlsproxy: SHALLOW execve (REPLACE this fresh nx_hostctl image with the proxy) -- reached ONLY via 2676// hc_dfork_exec(HC_RUNMTLSPROXY), which already fork+setsid-detached. The SECOND execve from this NON-forked 2677// main-dispatch context dodges the deep-forked-child codegen edge -- FULLY SOVEREIGN, no /bin/sh (cf. cmd_runsite). 2678// stdout/stderr -> /tmp/mtlsproxy.out so mtlsproxytest can report the cert-load + bind result. main() already chdir'd. 2679func cmd_runmtlsproxy() -> i64 { 2680 let lf: i64 = sys_openat_wr("/tmp/mtlsproxy.out" as *u8, 0x1a4) 2681 if lf >= 0 { sys_dup3(lf,1,0); sys_dup3(lf,2,0) } 2682 let argv: *i64 = sys_mmap(32) as *i64 2683 argv[0]=HC_MTLS_PROXY_BIN as i64; argv[1]=HC_MP_PORT as i64; argv[2]=HC_MP_BACKEND as i64; argv[3]=0 2684 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 2685 sys_execve_clean(HC_MTLS_PROXY_BIN, argv, envp) 2686 sys_exit(127) 2687 return 0 2688} 2689// mtlsproxytest: sovereign-launch the mTLS proxy on :9444, print its startup log (cert load + bind), and leave it 2690// RUNNING for a browser test. Touches NOTHING live -- new port, unsupervised, no DSM/:443 change. 2691func cmd_mtlsproxytest() -> i64 { 2692 hc_puts("=== mtlsproxytest: sovereign launch nx_aw_mtls_proxy.elf :9444 -> wiki_gw :18791 (no /bin/sh) ===\n" as *u8) 2693 hc_dfork_exec(HC_RUNMTLSPROXY) 2694 sys_sleep_ms(HC_MAGIC_1500) 2695 let lb: *u8 = sys_mmap(HC_MAGIC_8192) 2696 let n: i64 = hc_read_cmdline("/tmp/mtlsproxy.out" as *u8, lb, HC_MAGIC_8192) 2697 hc_puts("--- /tmp/mtlsproxy.out ---\n" as *u8) 2698 if n > 0 { sys_write(1, lb, n) } else { hc_puts("(empty -- did not start? cert path / bind :9444)\n" as *u8) } 2699 if proc_alive_by_name("nx_aw_mtls_proxy.elf" as *u8) == 1 { hc_puts(" nx_aw_mtls_proxy ALIVE on :9444 (sovereign, unsupervised test)\n" as *u8) } else { hc_puts(" nx_aw_mtls_proxy NOT alive -- see log above\n" as *u8) } 2700 return 0 2701} 2702 2703// SHALLOW execve targets (reached via hc_dfork_exec -> fresh main-dispatch image, dodges the codegen edge, no sh). 2704// NAS-resident Apertus/model mirror: shallow-execve the download organ (absolute /volume1 paths -> CWD-independent), 2705// log to /tmp/apertus_mirror.out. Reached ONLY via hc_dfork_exec(HC_RUNMIRROR) (fork+setsid-detached), so it survives 2706// the SSH channel teardown. Mirrors cmd_runmtlsproxy/cmd_runandelin exactly. 2707func cmd_runmirror() -> i64 { 2708 let lf: i64 = sys_openat_wr("/tmp/apertus_mirror.out" as *u8, 0x1a4) 2709 if lf >= 0 { sys_dup3(lf,1,0); sys_dup3(lf,2,0) } 2710 let argv: *i64 = sys_mmap(32) as *i64 2711 argv[0]=HC_MIRROR_BIN as i64; argv[1]=0 2712 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 2713 sys_execve_clean(HC_MIRROR_BIN, argv, envp) 2714 sys_exit(127) 2715 return 0 2716} 2717// mirror: fire-and-forget launch the NAS-resident mirror (detached), then show the first log bytes as confirmation. 2718func cmd_mirror() -> i64 { 2719 hc_puts("=== mirror: sovereign-launch nx_apertus_mirror_nas.elf -> /volume1/ai/apertus (NAS-local, detached) ===\n" as *u8) 2720 hc_dfork_exec(HC_RUNMIRROR) 2721 sys_sleep_ms(HC_MAGIC_1500) 2722 let lb: *u8 = sys_mmap(HC_MAGIC_8192) 2723 let n: i64 = hc_read_cmdline("/tmp/apertus_mirror.out" as *u8, lb, HC_MAGIC_8192) 2724 hc_puts("--- /tmp/apertus_mirror.out (first bytes) ---\n" as *u8) 2725 if n>0 { sys_write(1, lb, n) } else { hc_puts("(empty -- starting)\n" as *u8) } 2726 return 0 2727} 2728// PARALLEL: shallow-execve the concurrent-fork downloader (proves what crashed WSL runs fine on the NAS). 2729func cmd_runmirrorpar() -> i64 { 2730 let lf: i64 = sys_openat_wr("/tmp/apertus_par.out" as *u8, 0x1a4) 2731 if lf >= 0 { sys_dup3(lf,1,0); sys_dup3(lf,2,0) } 2732 let argv: *i64 = sys_mmap(32) as *i64 2733 argv[0]=HC_MIRRORPAR_BIN as i64; argv[1]=0 2734 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 2735 sys_execve_clean(HC_MIRRORPAR_BIN, argv, envp) 2736 sys_exit(127) 2737 return 0 2738} 2739func cmd_mirrorpar() -> i64 { 2740 hc_puts("=== mirrorpar: sovereign-launch nx_par_pull_nas.elf (PARALLEL concurrent, NAS-native, detached) ===\n" as *u8) 2741 hc_dfork_exec(HC_RUNMIRRORPAR) 2742 sys_sleep_ms(HC_MAGIC_2500) 2743 let lb: *u8 = sys_mmap(HC_MAGIC_8192) 2744 let n: i64 = hc_read_cmdline("/tmp/apertus_par.out" as *u8, lb, HC_MAGIC_8192) 2745 hc_puts("--- /tmp/apertus_par.out (first bytes) ---\n" as *u8) 2746 if n>0 { sys_write(1, lb, n) } else { hc_puts("(empty -- starting)\n" as *u8) } 2747 return 0 2748} 2749// stop the NAS mirror/parallel pulls -- FULL-cmdline match (hc_kill_by_cmdline), NOT the 15-char-truncated comm. 2750func cmd_mirrorstop() -> i64 { 2751 hc_puts("=== mirrorstop: kill NAS mirror + parallel pulls (full-cmdline match) ===\n" as *u8) 2752 let k1: i64 = hc_kill_by_cmdline("nx_apertus_mirror_nas.elf" as *u8) 2753 let k2: i64 = hc_kill_by_cmdline("nx_par_pull_nas.elf" as *u8) 2754 hc_puts(" killed mirror=" as *u8); hc_putn(k1); hc_puts(" par=" as *u8); hc_putn(k2); hc_puts("\n" as *u8) 2755 return 0 2756} 2757// rundistpub: SHALLOW execve of the publisher -- reached ONLY via hc_dfork_exec, dodging the deep-forked-child 2758// codegen edge (a DIRECT fork+execve here silently no-ops, exactly what the mirror sidesteps). stdout -> the log. 2759func cmd_rundistpub() -> i64 { 2760 let lf: i64 = sys_openat_wr("/tmp/distpub.out" as *u8, 0x1a4) 2761 if lf >= 0 { sys_dup3(lf,1,0); sys_dup3(lf,2,0) } 2762 let argv: *i64 = sys_mmap(32) as *i64; argv[0]=HC_DISTPUB_BIN as i64; argv[1]=0 2763 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 2764 sys_execve_clean(HC_DISTPUB_BIN, argv, envp) 2765 sys_exit(127) 2766 return 0 2767} 2768// distpub: launch the publisher detached (survives SSH teardown), give it time to hash, then emit its output. 2769// Idempotent + additive, so re-running is always safe if the first emit is early. 2770func cmd_distpub() -> i64 { 2771 hc_puts("=== distpub: publish a real NAS file to the hub distribution index ===\n" as *u8) 2772 hc_dfork_exec(HC_RUNDISTPUB) 2773 sys_sleep_ms(HC_MAGIC_4000) 2774 let lb: *u8 = sys_mmap(HC_MAGIC_8192); let n: i64 = hc_read_cmdline("/tmp/distpub.out" as *u8, lb, HC_MAGIC_8192) 2775 hc_puts("--- /tmp/distpub.out ---\n" as *u8) 2776 if n>0 { sys_write(1, lb, n) } else { hc_puts("(empty -- still hashing? re-run distpub; it is idempotent)\n" as *u8) } 2777 return 0 2778} 2779// rundistserve: SHALLOW execve of the /dist download server (via hc_dfork_exec). argv = [bin, "serve", "18099"]. 2780func cmd_rundistserve() -> i64 { 2781 let lf: i64 = sys_openat_wr("/tmp/distserve.out" as *u8, 0x1a4) 2782 if lf >= 0 { sys_dup3(lf,1,0); sys_dup3(lf,2,0) } 2783 let argv: *i64 = sys_mmap(64) as *i64; argv[0]=HC_DISTSERVE_BIN as i64; argv[1]=HC_A_SERVE as i64; argv[2]=HC_A_PORT as i64; argv[3]=0 2784 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 2785 sys_execve_clean(HC_DISTSERVE_BIN, argv, envp) 2786 sys_exit(127) 2787 return 0 2788} 2789// rundistprobe: SHALLOW execve of the loopback probe. argv = [bin, "probe", "18099", "/dist/<cid>/<name>"]. 2790func cmd_rundistprobe() -> i64 { 2791 let lf: i64 = sys_openat_wr("/tmp/distprobe.out" as *u8, 0x1a4) 2792 if lf >= 0 { sys_dup3(lf,1,0); sys_dup3(lf,2,0) } 2793 let argv: *i64 = sys_mmap(64) as *i64; argv[0]=HC_DISTSERVE_BIN as i64; argv[1]=HC_A_PROBE as i64; argv[2]=HC_A_PORT as i64; argv[3]=HC_DIST_PROBE_PATH as i64; argv[4]=0 2794 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 2795 sys_execve_clean(HC_DISTSERVE_BIN, argv, envp) 2796 sys_exit(127) 2797 return 0 2798} 2799// distserve: launch the /dist download server detached, show its LIVE banner. 2800func cmd_distserve() -> i64 { 2801 hc_puts("=== distserve: start the /dist download server on 127.0.0.1:18099 ===\n" as *u8) 2802 hc_dfork_exec(HC_RUNDISTSERVE) 2803 sys_sleep_ms(HC_MAGIC_1200) 2804 let lb: *u8 = sys_mmap(HC_MAGIC_4096); let n: i64 = hc_read_cmdline("/tmp/distserve.out" as *u8, lb, HC_MAGIC_4096) 2805 if n>0 { sys_write(1, lb, n) } else { hc_puts("(no banner yet)\n" as *u8) } 2806 return 0 2807} 2808// distprobe: loopback GET a published cid -> proves the download transport returns the real bytes. 2809func cmd_distprobe() -> i64 { 2810 hc_puts("=== distprobe: loopback GET a published cid from :18099 ===\n" as *u8) 2811 hc_dfork_exec(HC_RUNDISTPROBE) 2812 sys_sleep_ms(HC_MAGIC_2000) 2813 let lb: *u8 = sys_mmap(HC_MAGIC_8192); let n: i64 = hc_read_cmdline("/tmp/distprobe.out" as *u8, lb, HC_MAGIC_8192) 2814 if n>0 { sys_write(1, lb, n) } else { hc_puts("(no probe output -- server not up?)\n" as *u8) } 2815 return 0 2816} 2817func cmd_runandelin() -> i64 { 2818 let lf: i64 = sys_openat_wr("/tmp/andelin.out" as *u8, 0x1a4) 2819 if lf >= 0 { sys_dup3(lf,1,0); sys_dup3(lf,2,0) } 2820 let argv: *i64 = sys_mmap(32) as *i64 2821 argv[0]=HC_ANDELIN_BIN as i64; argv[1]=0 2822 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 2823 sys_execve_clean(HC_ANDELIN_BIN, argv, envp) 2824 sys_exit(127) 2825 return 0 2826} 2827func cmd_runsnir() -> i64 { 2828 let lf: i64 = sys_openat_wr("/tmp/snir.out" as *u8, 0x1a4) 2829 if lf >= 0 { sys_dup3(lf,1,0); sys_dup3(lf,2,0) } 2830 let argv: *i64 = sys_mmap(32) as *i64 2831 argv[0]=HC_SNIR_BIN as i64; argv[1]=HC_SNIR_PORT as i64; argv[2]=HC_SNIR_BACKEND as i64; argv[3]=0 2832 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 2833 sys_execve_clean(HC_SNIR_BIN, argv, envp) 2834 sys_exit(127) 2835 return 0 2836} 2837// nftread: SOVEREIGN NETFILTER R0 grounding. Direct (non-forked main-dispatch) shallow execve of nx_nft_read -> 2838// READS the kernel nat-table info via getsockopt(IPT_SO_GET_INFO). Runs as root (this nx_hostctl was sudo'd). 2839// READ-ONLY: cannot disturb DSM. stdout (the table info, or the rc telling us legacy-vs-nft) flows back up the SSH channel. 2840func cmd_nftread() -> i64 { 2841 let argv: *i64 = sys_mmap(16) as *i64; argv[0]=HC_NFTREAD_BIN as i64; argv[1]=0 2842 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 2843 sys_execve_clean(HC_NFTREAD_BIN, argv, envp) 2844 sys_exit(127) 2845 return 0 2846} 2847// nettap: the sovereign WIRESHARK-in-the-API (operator 2026-07-04 "build that capability into the apis"). 2848// Shallow-execve of nx_netscope_capture (AF_PACKET raw socket -> nx_packet_dissect; root here = CAP_NET_RAW OK). 2849// READ-ONLY on the wire + HARD-BOUNDED (dport filter, 60s wall-clock, 200-row cap) -- observes, never disturbs. 2850// Optional argv[2] = dest port (default 443). Output (SYN src -> dst rows) flows back up the SSH channel. 2851func cmd_nettap(port: *u8) -> i64 { 2852 let argv: *i64 = sys_mmap(48) as *i64 2853 argv[0] = HC_NETTAP_BIN as i64 2854 if (port as i64) != 0 { argv[1] = port as i64 } else { argv[1] = "443" as *u8 as i64 } 2855 argv[2] = "60" as *u8 as i64 2856 argv[3] = "200" as *u8 as i64 2857 argv[4] = 0 2858 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 2859 sys_execve_clean(HC_NETTAP_BIN, argv, envp) 2860 sys_exit(127) 2861 return 0 2862} 2863// platformtest: launch the andelinwest CLIENT daemon (:8453) + the SNI router (:8444) on INTERNAL ports, print 2864// each startup, leave both RUNNING for verification. Touches NOTHING live (no :443, no DSM). 2865func cmd_platformtest() -> i64 { 2866 hc_puts("=== platformtest: sovereign-launch andelinwest :8453 (LE ECDSA) + SNI router :8444 (no /bin/sh) ===\n" as *u8) 2867 let lb: *u8 = sys_mmap(HC_MAGIC_8192) 2868 hc_dfork_exec(HC_RUNANDELIN) 2869 sys_sleep_ms(HC_MAGIC_1200) 2870 let n1: i64 = hc_read_cmdline("/tmp/andelin.out" as *u8, lb, HC_MAGIC_8192) 2871 hc_puts("--- /tmp/andelin.out ---\n" as *u8); if n1>0 { sys_write(1, lb, n1) } else { hc_puts("(empty)\n" as *u8) } 2872 if proc_alive_by_name("nx_andelinwest_daemon.elf" as *u8) == 1 { hc_puts(" andelinwest ALIVE :8453\n" as *u8) } else { hc_puts(" andelinwest NOT alive\n" as *u8) } 2873 hc_dfork_exec(HC_RUNSNIR) 2874 sys_sleep_ms(HC_MAGIC_1200) 2875 let n2: i64 = hc_read_cmdline("/tmp/snir.out" as *u8, lb, HC_MAGIC_8192) 2876 hc_puts("--- /tmp/snir.out ---\n" as *u8); if n2>0 { sys_write(1, lb, n2) } else { hc_puts("(empty)\n" as *u8) } 2877 if proc_alive_by_name("nx_sni_router.elf" as *u8) == 1 { hc_puts(" sni_router ALIVE :8444\n" as *u8) } else { hc_puts(" sni_router NOT alive\n" as *u8) } 2878 return 0 2879} 2880// DEPLOY the new login daemon: save .prev, atomic-swap .new->live, kill+respawn :9091 (24h sessions). Existing 2881// tokens keep working (stateless); only /login is briefly down during respawn. Reversible: loginrollback. 2882func cmd_logindeploy() -> i64 { 2883 hc_puts("[hostctl] logindeploy: .prev save + atomic-swap nx_opaque_login.elf + restart :9091 (24h sessions)\n" as *u8) 2884 sys_renameat(HC_LOGIN, HC_LOGIN_PREV) 2885 let r: i64 = sys_renameat(HC_LOGIN_NEW, HC_LOGIN) 2886 nx_chmod(HC_LOGIN, 0x1ed) 2887 hc_put_rc(" swap rc=" as *u8, r) 2888 let pk: i64 = proc_kill_by_name("nx_opaque_login.elf" as *u8, 9) 2889 sys_sleep_ms(400) 2890 let np: i64 = hc_spawn_login() 2891 hc_puts(" killed old login procs=" as *u8); hc_putn(pk); hc_puts(" respawned pid=" as *u8); hc_putn(np); hc_puts(" :9091\n" as *u8) 2892 return 0 2893} 2894// ROLLBACK the login daemon: restore .prev -> live + restart :9091. Reverses a bad logindeploy. 2895func cmd_loginrollback() -> i64 { 2896 hc_puts("[hostctl] loginrollback: restore nx_opaque_login.elf.prev -> live + restart :9091\n" as *u8) 2897 let r: i64 = sys_renameat(HC_LOGIN_PREV, HC_LOGIN) 2898 nx_chmod(HC_LOGIN, 0x1ed) 2899 hc_put_rc(" restore rc=" as *u8, r) 2900 proc_kill_by_name("nx_opaque_login.elf" as *u8, 9) 2901 sys_sleep_ms(400) 2902 hc_spawn_login() 2903 hc_puts(" restored + restarted :9091\n" as *u8) 2904 return 0 2905} 2906 2907// HUB deploy prep: create the hub content dirs with pure syscalls (no shell mkdir) + chmod the gateway 2908// binary executable if it has already been recv'd. The supervisor's HC_HUB_GW guard then auto-launches it. 2909// Idempotent (sys_mkdir on an existing dir is harmless; nx_chmod on an absent file is harmless). 2910func cmd_hubdeploy() -> i64 { 2911 hc_puts("[hostctl] hubdeploy: mkdir /volume1/ai/hub{,/pages} + chmod nx_hub_gw.elf +x (sovereign sys_mkdir)\n" as *u8) 2912 sys_mkdir("/volume1/ai/hub" as *u8, 0x1ff) // 0777 so the elderwesto recv can write content here 2913 sys_mkdir("/volume1/ai/hub/pages" as *u8, 0x1ff) 2914 nx_chmod("/volume1/ai/hub/nx_hub_gw.elf" as *u8, 0x1ed) // 0755 if already landed (harmless if absent) 2915 let hk: i64 = proc_kill_by_name("nx_hub_gw.elf" as *u8, 9) // restart so a re-deployed binary takes effect (HC_HUB_GW guard respawns it) 2916 hc_puts(" hub dirs ready + killed nx_hub_gw procs=" as *u8); hc_putn(hk); hc_puts(" -> HC_HUB_GW guard respawns the new :18792 gateway\n" as *u8) 2917 return 0 2918} 2919 2920// GEN deploy prep: create /volume1/ai/gen (0777 so the elderwesto recv can write the elfs + the daemons can 2921// write the gallery store/blob/sidecar) + chmod the gen daemons +x. The HC_GEN_* guards then auto-launch them. 2922// Idempotent (sys_mkdir on an existing dir is harmless; nx_chmod on an absent file is harmless). 2923func cmd_gendeploy() -> i64 { 2924 hc_puts("[hostctl] gendeploy: mkdir /volume1/ai/gen + chmod gen daemons +x -> guards launch :18794/:18795\n" as *u8) 2925 sys_mkdir("/volume1/ai/gen" as *u8, 0x1ff) 2926 nx_chmod("/volume1/ai/gen/nx_gen_gateway_daemon.elf" as *u8, 0x1ed) 2927 nx_chmod("/volume1/ai/gen/nx_gen_orchestrator_daemon.elf" as *u8, 0x1ed) 2928 let g1: i64 = proc_kill_by_name("nx_gen_gateway_daemon.elf" as *u8, 9) 2929 let g2: i64 = proc_kill_by_name("nx_gen_orchestrator_daemon.elf" as *u8, 9) 2930 hc_puts(" gen dir ready + killed gw=" as *u8); hc_putn(g1); hc_puts(" orch=" as *u8); hc_putn(g2); hc_puts(" -> HC_GEN guards respawn :18794/:18795\n" as *u8) 2931 return 0 2932} 2933// genprobe: one-shot LIVE proof. Run nx_gen_live_probe.elf (sh -c exec, like cmd_logintest) -> it registers a temp 2934// owner in the live store, enrolls it OWNER, logs in, and drives the deployed gateway @127.0.0.1:18794 asserting 2935// owner->200 + no-cred->401, then demotes the temp owner. Read its output back and print it (relayed to operator). 2936func cmd_genprobe() -> i64 { 2937 // The live probe now drives a REAL generation (~15s GPU) which outlasts the nx_aw_hostctl relay's read window. 2938 // So: PRINT the previous run's COMPLETE result (from the file), then FIRE a new run DETACHED (stdout->file + 2939 // setsid so it survives the SSH close and finishes the generation). Re-run genprobe in ~25s to read this run. 2940 hc_puts("[hostctl] genprobe: last result below, then firing a fresh live-probe run (detached)\n" as *u8) 2941 let lb: *u8 = sys_mmap(HC_MAGIC_131072) 2942 let n: i64 = hc_read_cmdline("/volume1/ai/gen/genprobe.out" as *u8, lb, HC_MAGIC_131072) 2943 hc_puts("--- last nx_gen_live_probe result ---\n" as *u8) 2944 if n > 0 { sys_write(1, lb, n) } else { hc_puts("(no previous result yet)\n" as *u8) } 2945 nx_chmod("/volume1/ai/gen/nx_gen_live_probe.elf" as *u8, 0x1ed) 2946 let pid: i64 = sys_fork() 2947 if pid==0 { 2948 nx_setsid() 2949 let argv: *i64 = sys_mmap(32) as *i64 2950 argv[0]=HC_SH as i64; argv[1]=HC_DASHC as i64; argv[2]=HC_GENPROBE_CMD as i64; argv[3]=0 2951 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 2952 sys_execve_clean(HC_SH, argv, envp) 2953 sys_exit(127) 2954 } 2955 hc_puts("--- fired a new run (detached); re-run `genprobe` in ~25s to read its result ---\n" as *u8) 2956 return 0 2957} 2958// First-swap TAKEOVER: stop the old .sh supervisor + old daemon (sovereign /proc kill), atomic-publish 2959// the staged artifacts, then BECOME the sovereign supervisor (spawn + monitor + respawn the new daemon). 2960// Run detached (setsid) on the NAS -- retires nishi_supervisor.sh. 2961func cmd_takeover() -> i64 { 2962 hc_puts("[hostctl] TAKEOVER: stop old .sh supervisor + daemon, atomic-publish, become sovereign supervisor\n" as *u8) 2963 proc_kill_by_name("nishi_supervisor" as *u8, 9) 2964 proc_kill_by_name("sites.elf" as *u8, 9) 2965 sys_sleep_ms(600) 2966 sys_mkdir(HC_DOCROOT, 0x1ed) 2967 sys_renameat(HC_INDEX_NEW, HC_INDEX) 2968 sys_renameat(HC_CONF_NEW, HC_CONF) 2969 // seq1097 twin: never install a 0-byte staged elf (a failed upload can stage one; it sat for days 2970 // in 2026-07). Absent .new already fails the rename harmlessly; this closes the 0-byte case. 2971 let tkstb: *u8 = sys_mmap(160) 2972 var tkok: i64 = 0 2973 if sys_fstatat(HC_ELF_NEW, tkstb) == 0 { 2974 let tkszp: *i64 = ((tkstb as i64) + 48) as *i64 2975 if tkszp[0] > 0 { tkok = 1 } 2976 } 2977 if tkok == 1 { 2978 sys_renameat(HC_ELF_NEW, HC_ELF) 2979 nx_chmod(HC_ELF, 0x1ed) 2980 } else { 2981 hc_puts("[hostctl] takeover: no real staged sites.elf.new (absent or 0B) -- keeping current live\n" as *u8) 2982 } 2983 hc_puts("[hostctl] artifacts published; daemonizing sovereign supervisor (setsid + detach stdio)\n" as *u8) 2984 let pid: i64 = sys_fork() 2985 if pid == 0 { 2986 nx_setsid() // new session -> survives SSH/parent close 2987 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 2988 if dn >= 0 { sys_dup3(dn, 1, 0); sys_dup3(dn, 2, 0) } // detach stdout/stderr from the SSH channel 2989 cmd_supervise() // become the detached sovereign supervisor 2990 sys_exit(0) 2991 return 0 2992 } 2993 hc_puts("[hostctl] supervisor daemonized; takeover returns (ssh_exec gets channel EOF)\n" as *u8) 2994 return 0 2995} 2996 2997// LOCAL Engineer gate: prove atomic-rename publish + pidfile + kill + /proc-scan-kill on /tmp (no NAS). 2998func cmd_selftest() -> i64 { 2999 var pass: i64=0; var total: i64=0 3000 // 1. atomic publish 3001 let fda: i64 = sys_openat_wr("/tmp/hc_live" as *u8, 0x1a4); sys_write(fda, "OLD" as *u8, 3); sys_close(fda) 3002 let fdb: i64 = sys_openat_wr("/tmp/hc_live.new" as *u8, 0x1a4); sys_write(fdb, "NEWCONTENT" as *u8, 10); sys_close(fdb) 3003 sys_renameat("/tmp/hc_live.new" as *u8, "/tmp/hc_live" as *u8) 3004 let box: *i64 = sys_mmap(16) as *i64; box[0]=0 3005 let got: *u8 = sys_read_file("/tmp/hc_live" as *u8, box) 3006 total=total+1 3007 if (got as i64)!=0 { if box[0]==10 { hc_puts(" PASS atomic-publish: live flipped to NEWCONTENT\n" as *u8); pass=pass+1 } else { hc_puts(" FAIL publish len\n" as *u8) } } else { hc_puts(" FAIL publish read\n" as *u8) } 3008 // 2. pidfile round-trip + kill 3009 let kid: i64 = sys_fork() 3010 if kid==0 { sys_sleep_ms(HC_MAGIC_4000); sys_exit(0); return 0 } 3011 hc_write_pid("/tmp/hc_test.pid" as *u8, kid) 3012 let rp: i64 = hc_read_pid("/tmp/hc_test.pid" as *u8) 3013 sys_sleep_ms(300) 3014 let kr: i64 = nx_kill(rp, 9) 3015 let st: *i64 = sys_mmap(16) as *i64; st[0]=0 3016 let reaped: i64 = sys_wait4(kid, st, 0) 3017 total=total+1 3018 if rp==kid { if kr==0 { if (st[0]&0x7f)==9 { hc_puts(" PASS pidfile+kill: round-tripped pid, SIGKILLed daemon\n" as *u8); pass=pass+1 } else { hc_puts(" FAIL not signal-killed\n" as *u8) } } else { hc_puts(" FAIL kill rc\n" as *u8) } } else { hc_puts(" FAIL pidfile round-trip\n" as *u8) } 3019 // 3. proc_kill_by_name -- fork a child that execs /bin/sleep with a unique marker, /proc-kill it 3020 let kid2: i64 = sys_fork() 3021 if kid2 == 0 { 3022 let av: *i64 = sys_mmap(32) as *i64 3023 av[0] = "/bin/sleep" as *u8 as i64; av[1] = "31337" as *u8 as i64; av[2] = 0 3024 let ev: *i64 = sys_mmap(16) as *i64; ev[0] = 0 3025 sys_execve_clean("/bin/sleep" as *u8, av, ev) 3026 sys_exit(0) 3027 return 0 3028 } 3029 sys_sleep_ms(400) 3030 let nk: i64 = proc_kill_by_name("31337" as *u8, 9) 3031 let st2: *i64 = sys_mmap(16) as *i64; st2[0]=0 3032 let reaped2: i64 = sys_wait4(kid2, st2, 0) 3033 total=total+1 3034 if nk >= 1 { if reaped2 == kid2 { if (st2[0]&0x7f)==9 { hc_puts(" PASS proc_kill_by_name: found by /proc cmdline + SIGKILLed\n" as *u8); pass=pass+1 } else { hc_puts(" FAIL proc_kill: not signal-killed\n" as *u8) } } else { hc_puts(" FAIL proc_kill: wait4\n" as *u8) } } else { hc_puts(" FAIL proc_kill: found 0\n" as *u8) } 3035 3036 hc_puts("---- hostctl selftest passed " as *u8); let nb: *u8=sys_mmap(4); nb[0]=(48+pass) as u8; sys_write(1,nb,1); hc_puts(" / " as *u8); let tb: *u8=sys_mmap(4); tb[0]=(48+total) as u8; sys_write(1,tb,1); hc_puts(" ----\n" as *u8) 3037 if pass==total { return 0 } 3038 return 1 3039} 3040 3041// SOVEREIGN supervisor self-update (operator: "no sh"). nx_aw_push streams the new binary to nishihost as 3042// nx_hostctl.new, then execs THIS (run as nx_hostctl.new selfswap). We: atomic-rename .new over the live 3043// supervisor path; kill ONLY the old supervisor (matched by its "supervise" cmdline -- this "selfswap" 3044// process and sites.elf/redirect/vroom do NOT contain that token, so they're untouched); then re-exec the 3045// LIVE (now-new) binary as a detached `supervise`. The new supervisor ADOPTS every running daemon (proc_alive, 3046// no double-spawn) and adds the library reader -> the live site is never interrupted, only the supervisor 3047// process is swapped. Re-exec (not a bare cmd_supervise call) gives it the canonical "nx_hostctl supervise" 3048// cmdline so the NEXT selfswap can find it. 3049func cmd_selfswap() -> i64 { 3050 hc_puts("[hostctl] selfswap: atomic-install new supervisor + restart supervisor only (sites.elf untouched)\n" as *u8) 3051 // *** FAIL-SAFE GUARD (seq992, 2026-07-25) -- RESOLVE THE STAGED BINARY BEFORE TOUCHING THE LIVE ONE. 3052 // This function used to rename the live supervisor to .prev and only THEN rename the staged binary 3053 // over it, with NO existence check. If nothing was staged under HC_SELF_NEW, the live supervisor was 3054 // moved away, NOTHING was installed, the running supervisor was SIGKILLed, and the re-exec targeted a 3055 // path that no longer existed => no supervisor, unable to respawn itself. That is the EXACT outage 3056 // hc_promote_to was rewritten to avoid ("it moved the live binary to .prev and installed NOTHING"), 3057 // and hc_promote_to got the hc_exists guard while this path did not. 3058 // It was reachable by the DOCUMENTED sequence: /api/build stages <t>.sov.elf.new, but the deploy row 3059 // names nx_hostctl.new, and no API-reachable rename bridges them (seq983) -- so build-then-deploy ran 3060 // selfswap with HC_SELF_NEW absent. Accepting the .sov.elf.new name here closes the naming trap too. 3061 var srcnew: *u8 = HC_SELF_NEW 3062 if hc_exists(HC_SELF_NEW) == 0 { 3063 if hc_exists(HC_SELF_NEW_SOV) == 1 { 3064 srcnew = HC_SELF_NEW_SOV 3065 hc_puts(" staged binary found under the /api/build name (nx_hostctl.sov.elf.new)\n" as *u8) 3066 } else { 3067 hc_puts("[hostctl] selfswap: REFUSED -- no staged supervisor at nx_hostctl.new or nx_hostctl.sov.elf.new; live binary UNTOUCHED\n" as *u8) 3068 return 1 3069 } 3070 } 3071 sys_renameat(HC_SELF, HC_SELF_PREV) // never-brick (#26): save the OLD supervisor binary for superrollback (selfswap had NO rollback) 3072 let rr: i64 = sys_renameat(srcnew, HC_SELF) 3073 nx_chmod(HC_SELF, 0x1ed) 3074 hc_put_rc(" install rename rc=" as *u8, rr) 3075 let k: i64 = proc_kill_by_two("nx_hostctl" as *u8, "supervise" as *u8, 9) 3076 hc_puts(" killed old supervisor procs=" as *u8); let kb: *u8=sys_mmap(4); kb[0]=(48+(k%10)) as u8; sys_write(1,kb,1); hc_puts(" (matched nx_hostctl+supervise only -- s6-supervise untouched)\n" as *u8) 3077 sys_sleep_ms(400) 3078 hc_sup_clear() // clear the lease so the re-exec'd new supervisor claims (the killed old one's heartbeat is stale) 3079 // ONE launcher (2026-09-02): this used to hand-roll the fork with stdout AND stderr on /dev/null, so every `[guard]` 3080 // verdict of a selfswap'd supervisor -- including each kill+respawn of nx_daemon_supervisor -- was discarded, while 3081 // superrollback's hc_daemonize_supervise logged the same lines to supervisor.log. Measured that day: 13 fleet-supervisor 3082 // LIVE banners with not one guard line behind them. hc_dfork_exec keeps stdin on /dev/null (the SSH-channel lesson) and 3083 // routes stdout/stderr to supervisor.log; selfswap and superrollback now share it, so the two cannot disagree. 3084 hc_daemonize_supervise() 3085 hc_puts(" new supervisor daemonized (re-exec'd live binary as `supervise`, stdout/stderr -> supervisor.log)\n" as *u8) 3086 return 0 3087} 3088 3089// superrollback: restore the PREVIOUS supervisor binary (saved by selfswap as .prev) + reconcile to it. The 3090// never-brick (#26) rollback path selfswap lacked -- if a freshly selfswap'd supervisor misbehaves, this puts the 3091// proven binary back and relaunches it (kill duplicates, clear the lease, daemonize the one). The recovery net. 3092func cmd_superrollback() -> i64 { 3093 hc_puts("[hostctl] superrollback: restore nx_hostctl.prev -> live supervisor binary, then reconcile to it\n" as *u8) 3094 let rr: i64 = sys_renameat(HC_SELF_PREV, HC_SELF) 3095 nx_chmod(HC_SELF, 0x1ed) 3096 hc_puts(" restore rename rc=" as *u8); hc_putn(rr); hc_puts("\n" as *u8) 3097 let k: i64 = proc_kill_by_two("nx_hostctl" as *u8, "supervise" as *u8, 9) 3098 hc_puts(" killed supervisors=" as *u8); hc_putn(k); hc_puts("\n" as *u8) 3099 sys_sleep_ms(800) 3100 hc_sup_clear() 3101 hc_daemonize_supervise() 3102 hc_puts(" restored (prev) supervisor daemonized\n" as *u8) 3103 return 0 3104} 3105 3106// supervisetest: READ-ONLY mechanical proof of the single-supervisor guarantee (#26: prove, don't assert). Reads 3107// the live lease + reports its age and what a 2nd `supervise` launch WOULD do -- WITHOUT starting a competing 3108// loop. fresh(<90s) => a duplicate launch defers (guaranteed one supervisor); stale => the heartbeat stopped. 3109func cmd_supervisetest() -> i64 { 3110 hc_puts("[hostctl] supervisetest: READ-ONLY single-supervisor lease check (no competing loop started)\n" as *u8) 3111 let e: i64 = hc_read_pid(HC_SUP_LOCK) 3112 let now: i64 = sys_now_realtime_sec() 3113 hc_puts(" lease_epoch=" as *u8); hc_putn(e); hc_puts(" now=" as *u8); hc_putn(now); hc_puts(" age_s=" as *u8); hc_putn(now - e); hc_puts("\n" as *u8) 3114 let fresh: i64 = hc_sup_fresh() 3115 hc_puts(" hc_sup_fresh=" as *u8); hc_putn(fresh) 3116 if fresh == 1 { hc_puts(" -> a 2nd `supervise` launch WOULD DEFER = single-supervisor GUARANTEED (mechanically proven)\n" as *u8) } 3117 if fresh == 0 { hc_puts(" -> lease NOT fresh (no live heartbeat) -> a launch would CLAIM (no live supervisor to defer to)\n" as *u8) } 3118 return 0 3119} 3120 3121// DIAGNOSTIC: spawn the reader exactly as the supervisor does (fork + sh -c exec, direct child), then BLOCK in 3122// wait4 on it and report lifetime + sig/code. Run standalone (nx_hostctl spawntest) -> isolates the spawn from 3123// the poll loop. If it never returns, the reader stays up when nx_hostctl spawns it (so the loop/probe is the 3124// real culprit); if it returns, we finally see WHY the reader dies (signal vs exit code, and how long it lived). 3125func cmd_spawntest() -> i64 { 3126 hc_puts("[spawntest] fork+exec reader (direct child), blocking wait4 for its fate\n" as *u8) 3127 let t0: i64 = sys_now_realtime_sec() 3128 let pid: i64 = hc_spawn_reader() 3129 hc_puts(" spawned pid=" as *u8); hc_putn(pid); hc_puts("\n" as *u8) 3130 let st: *i64 = sys_mmap(16) as *i64; st[0]=0 3131 let reaped: i64 = sys_wait4(pid, st, 0) 3132 let t1: i64 = sys_now_realtime_sec() 3133 hc_puts(" REAPED=" as *u8); hc_putn(reaped); hc_puts(" lived_s=" as *u8); hc_putn(t1-t0) 3134 hc_puts(" sig=" as *u8); hc_putn(st[0]&0x7f); hc_puts(" code=" as *u8); hc_putn((st[0]>>8)&0xff); hc_puts("\n" as *u8) 3135 return 0 3136} 3137 3138// hc_dfork_exec: the SOVEREIGN daemonizer -- pure .nx, NO OS setsid/sh. SINGLE fork -> the CHILD does nx_setsid 3139// (the setsid SYSCALL -> SESSION LEADER, no controlling terminal) -> execve's a fresh `nx_hostctl <arg>` image, 3140// which runs forever and reparents to init when this (parent) process exits. The parent does NOT wait4 (the 3141// child never returns). This is EXACTLY the structure of `setsid nx_hostctl supervise &` (live PID 16331, which 3142// SURVIVES) and the durable `setsid ./sites.elf` -- both SESSION LEADERS. The earlier DOUBLE fork made the 3143// daemon a non-leader grandchild, which cmd_supervise did NOT survive (the trivial sleeper did) -- session- 3144// leadership is the property that matters. stdio -> supervisor.log + /dev/null. arg = "supervise"|"sleeper". 3145func hc_dfork_exec(arg: *u8) -> i64 { 3146 let pid: i64 = sys_fork() 3147 if pid == 0 { 3148 nx_setsid() 3149 let lf: i64 = sys_openat_append("/volume1/homes/elderwesto/nishihost/supervisor.log" as *u8, 420) 3150 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 3151 if dn >= 0 { sys_dup3(dn, 0, 0) } 3152 if lf >= 0 { sys_dup3(lf, 1, 0); sys_dup3(lf, 2, 0) } 3153 let argv: *i64 = sys_mmap(32) as *i64 3154 argv[0]=HC_SELF as i64; argv[1]=arg as i64; argv[2]=0 3155 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 3156 sys_execve_clean(HC_SELF, argv, envp) // child (session leader) becomes nx_hostctl <arg>, runs forever 3157 sys_exit(127) 3158 return 0 3159 } 3160 return 0 // parent returns immediately; child reparents to init on exit 3161} 3162 3163func hc_daemonize_supervise() -> i64 { return hc_dfork_exec(HC_SUPERVISE) } 3164 3165// A sovereign idle daemon (NO OS /bin/sleep) -- the daemontest payload only: loops sleeping forever so I can 3166// daemonize it and confirm it survived the channel teardown without restarting the real supervisor. 3167func cmd_sleeper() -> i64 { 3168 var run: i64 = 1 3169 while run == 1 { sys_sleep_ms(HC_MAGIC_3600000) } 3170 return 0 3171} 3172 3173// SAFE proof that the SOVEREIGN hc_dfork_exec survives the teardown, WITHOUT restarting the real supervisor: 3174// daemonize `nx_hostctl sleeper` (pure .nx, no OS tools), then check it is still running after the channel closed. 3175func cmd_daemontest() -> i64 { 3176 hc_puts("[daemontest] SOVEREIGN hc_dfork_exec -> nx_hostctl sleeper (no OS tools); check it survives\n" as *u8) 3177 hc_dfork_exec(HC_SLEEPER) 3178 return 0 3179} 3180 3181// S-CLASS OPS RECONCILE (operator: "fix this bullshit rooted owned stuff -- s-class exceed at ops/mgmt"). 3182// THE ROOT-CAUSE of the recurring pain: a SPLIT ownership model -- the boot rc.d launches nx_hostctl as ROOT 3183// (so sites.elf/vroom/translate/redirect are root), while ad-hoc SSH-launched supervisors + the reader ran as 3184// elderwesto. Neither could manage the other's daemons (EPERM), deploys couldn't restart root daemons, and 3185// duplicate supervisors competed. FIX = ONE supervisor, run as root (consistent with the boot context), 3186// owning EVERYTHING. Run via `sudo nx_hostctl reconcile` (nx_aw_sudo): kill every supervisor/keeper (matched 3187// nx_hostctl+supervise, NOT this "reconcile" proc), the reader, and sites.elf (so the freshly-deployed binary 3188// loads); then fork+setsid+detach and BECOME the one root supervisor (cmd_supervise forks the keeper -> reader 3189// and adopts/respawns the rest). Direct cmd_supervise() call (NOT execve) -- the keeper-proven survival path. 3190func cmd_reconcile() -> i64 { 3191 hc_puts("[hostctl] reconcile: ONE sovereign session-leader supervisor; ADOPT running sites.elf (zero website downtime)\n" as *u8) 3192 let k1: i64 = proc_kill_by_two("nx_hostctl" as *u8, "supervise" as *u8, 9) 3193 let k2: i64 = proc_kill_by_name("nx_media_server_auth.elf" as *u8, 9) 3194 let k3: i64 = proc_kill_by_name("nx_health_eval.elf" as *u8, 9) // cycle the M&E loop so the fresh supervisor respawns the latest ELF (no long-lived orphan on an update) 3195 let k4: i64 = proc_kill_by_name("nx_signaling_v2" as *u8, 9) // cycle the signaling relay onto the latest ELF (clients auto-reconnect lanes ~1.2s + re-hello presence) 3196 hc_puts(" killed supervisors+keepers=" as *u8); hc_putn(k1); hc_puts(" readers=" as *u8); hc_putn(k2); hc_puts(" health=" as *u8); hc_putn(k3); hc_puts(" sig2=" as *u8); hc_putn(k4); hc_puts(" (sites.elf KEPT -> adopted)\n" as *u8) 3197 sys_sleep_ms(800) 3198 hc_sup_clear() // clear the lease so the fresh supervisor claims (the killed old one's heartbeat is stale-by-fiat) 3199 hc_daemonize_supervise() 3200 hc_puts(" ONE sovereign supervisor daemonized (single-fork session leader) -> keeper(reader) + adopts sites.elf\n" as *u8) 3201 return 0 3202} 3203 3204// kickreader: SURGICAL one-shot wedge-clear. Kill ONLY the library reader (:8791) by name; the long-running 3205// supervisor's blocking-wait keeper (cmd_reader_keep) detects the death and respawns a FRESH reader. Does NOT 3206// touch the supervisor/keeper/sites.elf/gallery/wiki -> minimal blast radius (the reader blips ~1s, website 3207// untouched). This is the surgical primitive a wedge needs: a wedge = alive-but-slow, which the keeper can't 3208// see (it only respawns on DEATH). Never-brick #26: the kill is reversible BY the keeper (proven UP at runtime). 3209// The one-shot's own cmdline is "nx_hostctl kickreader" (never contains the reader needle) -> no self-match. 3210func cmd_kickreader() -> i64 { 3211 hc_puts("[hostctl] kickreader: SIGKILL the library reader (:8791) -> the keeper respawns a fresh one\n" as *u8) 3212 let k: i64 = proc_kill_by_name("nx_media_server_auth.elf" as *u8, 9) 3213 hc_puts(" killed reader procs=" as *u8); hc_putn(k); hc_puts(" (keeper blocking-wait4 respawns a FRESH reader)\n" as *u8) 3214 if k == 0 { hc_puts(" NOTE: no reader matched -- nothing to kick (reader not running here?)\n" as *u8) } 3215 return 0 3216} 3217 3218// kicktorrent: SURGICAL one-shot for the media/torrent daemon (:8097). Kill ONLY the daemon by name; the 3219// supervise-loop guard (hc_guard_torrent, proc_alive "nx_torrent_daemon") detects the death and respawns it 3220// via the hub profile (nx_media_env hub) -> the NEW binary. The one-shot's cmdline is "nx_hostctl kicktorrent" 3221// (never contains "nx_torrent_daemon"; "nx_torrent_gw" is a different name) -> no self/gateway match. Use after 3222// staging a new daemon binary. Never-brick #26: reversible BY the guard (proven UP at runtime). 3223func cmd_kicktorrent() -> i64 { 3224 hc_puts("[hostctl] kicktorrent: SIGKILL the media/torrent daemon (:8097) -> the guard respawns the NEW binary\n" as *u8) 3225 let k: i64 = proc_kill_by_name("nx_torrent_daemon" as *u8, 9) 3226 hc_puts(" killed daemon procs=" as *u8); hc_putn(k); hc_puts(" (supervisor guard respawns via the hub profile)\n" as *u8) 3227 if k == 0 { hc_puts(" NOTE: no daemon matched -- nothing to kick (not running here?)\n" as *u8) } 3228 return 0 3229} 3230 3231// kickseed: SURGICAL one-shot for the BitTorrent SEEDER (:6881). Kill it by name; hc_guard_seed respawns the 3232// NEW binary on the next poll. The one-shot cmdline "nx_hostctl kickseed" never contains "nx_torrent_seed.elf" 3233// -> no self-match. Use after staging a new seeder binary or editing seed_index.conf (fresh registry read). 3234func cmd_kickseed() -> i64 { 3235 hc_puts("[hostctl] kickseed: SIGKILL the BitTorrent seeder (:6881) -> hc_guard_seed respawns the NEW binary\n" as *u8) 3236 let k: i64 = proc_kill_by_name("nx_torrent_seed.elf" as *u8, 9) 3237 hc_puts(" killed seeder procs=" as *u8); hc_putn(k); hc_puts(" (supervisor guard respawns from seed_index.conf)\n" as *u8) 3238 if k == 0 { hc_puts(" NOTE: no seeder matched -- guard will start it on the next poll (fail-safe)\n" as *u8) } 3239 return 0 3240} 3241// portmap: auto-open :6881 on the router via NAT-PMP (nx_natpmp) -> WAN-reachable seeder + more peers. Fail-safe. 3242// routerctl: sovereign router dashboard (nx_router_ctl dash over our own SSH -> GL.iNet uci: model/wan-ip/forwards/clients). 3243const HC_ROUTERCTL_CMD: *u8 = "cd /volume1/ai/torrent && chmod +x ./nx_router_ctl.elf 2>/dev/null; : > /tmp/routerctl.out; ./nx_router_ctl.elf dash >> /tmp/routerctl.out 2>&1" as *u8 3244func cmd_routerctl() -> i64 { 3245 let pid: i64 = sys_fork() 3246 if pid == 0 { 3247 let argv: *i64 = sys_mmap(32) as *i64; argv[0]=HC_SH as i64; argv[1]=HC_DASHC as i64; argv[2]=HC_ROUTERCTL_CMD as i64; argv[3]=0 3248 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 3249 sys_execve_clean(HC_SH, argv, envp); sys_exit(127) 3250 } 3251 let st: *i64 = sys_mmap(16) as *i64; sys_wait4(pid, st, 0) 3252 hc_puts("=== routerctl: GL.iNet dashboard (sovereign SSH+uci) ===\n" as *u8) 3253 let lb: *u8 = sys_mmap(HC_MAGIC_16384); let n: i64 = hc_read_cmdline("/tmp/routerctl.out" as *u8, lb, HC_MAGIC_16384) 3254 if n > 0 { sys_write(1, lb, n) } else { hc_puts("(no output)\n" as *u8) } 3255 return 0 3256} 3257const HC_PORTMAP_CMD: *u8 = "cd /volume1/ai/torrent && chmod +x ./nx_natpmp.elf ./nx_upnp_igd.elf 2>/dev/null; : > /tmp/portmap.out; echo === NAT-PMP === >> /tmp/portmap.out; ./nx_natpmp.elf 6881 >> /tmp/portmap.out 2>&1; echo === UPnP-IGD === >> /tmp/portmap.out; ./nx_upnp_igd.elf 6881 >> /tmp/portmap.out 2>&1" as *u8 3258func cmd_portmap() -> i64 { 3259 // The SSDP->desc->SOAP flow can exceed the control-plane relay timeout, so run it DETACHED and show the 3260 // PREVIOUS run's result. Each call: print /tmp/portmap.out (last result) + launch a fresh background run. 3261 hc_puts("=== portmap: last NAT-PMP + UPnP result (/tmp/portmap.out) ===\n" as *u8) 3262 let lb: *u8 = sys_mmap(HC_MAGIC_16384); let n: i64 = hc_read_cmdline("/tmp/portmap.out" as *u8, lb, HC_MAGIC_16384) 3263 if n > 0 { sys_write(1, lb, n) } else { hc_puts("(no prior result yet -- fresh run launching now)\n" as *u8) } 3264 let pid: i64 = sys_fork() // detached (double-fork) so it survives the relay close 3265 if pid == 0 { 3266 let pid2: i64 = sys_fork() 3267 if pid2 == 0 { 3268 let argv: *i64 = sys_mmap(32) as *i64; argv[0]=HC_SH as i64; argv[1]=HC_DASHC as i64; argv[2]=HC_PORTMAP_CMD as i64; argv[3]=0 3269 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 3270 sys_execve_clean(HC_SH, argv, envp); sys_exit(127) 3271 } 3272 sys_exit(0) 3273 } 3274 let st: *i64 = sys_mmap(16) as *i64; sys_wait4(pid, st, 0) 3275 hc_puts(" (fresh portmap launched in background; call `portmap` again in ~15s to read the result)\n" as *u8) 3276 return 0 3277} 3278// trackerrefresh: GROW the tracker list from live public sources (nx_tracker_refresh over sovereign TLS, 3279// merged+deduped into trackers.txt). "grow, not static" -- schedule daily; workers read trackers.txt on spawn. 3280const HC_TRACKERREFRESH_CMD: *u8 = "cd /volume1/ai/torrent && mkdir -p data && chmod +x ./nx_tracker_refresh.elf 2>/dev/null; : > /tmp/trackerrefresh.out; ./nx_tracker_refresh.elf /volume1/ai/torrent/trackers.txt >> /tmp/trackerrefresh.out 2>&1" as *u8 3281func cmd_trackerrefresh() -> i64 { 3282 let pid: i64 = sys_fork() 3283 if pid == 0 { 3284 let argv: *i64 = sys_mmap(32) as *i64; argv[0]=HC_SH as i64; argv[1]=HC_DASHC as i64; argv[2]=HC_TRACKERREFRESH_CMD as i64; argv[3]=0 3285 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 3286 sys_execve_clean(HC_SH, argv, envp); sys_exit(127) 3287 } 3288 let st: *i64 = sys_mmap(16) as *i64; sys_wait4(pid, st, 0) 3289 hc_puts("=== trackerrefresh: grow trackers.txt from live public lists ===\n" as *u8) 3290 let lb: *u8 = sys_mmap(HC_MAGIC_65536); let n: i64 = hc_read_cmdline("/tmp/trackerrefresh.out" as *u8, lb, HC_MAGIC_65536) 3291 if n > 0 { sys_write(1, lb, n) } else { hc_puts("(no output)\n" as *u8) } 3292 return 0 3293} 3294// reseed: register EVERY completed download (both areas) into the seed registry so we SHARE our whole 3295// library back (nx_torrent_reseed computes info_hash=SHA1(meta), plen, total=.part-size; idempotent). 3296const HC_RESEED_CMD: *u8 = "cd /volume1/ai/torrent && chmod +x ./nx_torrent_reseed.elf 2>/dev/null; : > /tmp/reseed.out; ./nx_torrent_reseed.elf /volume1/ai/torrent/media /volume1/ai/torrent/seed_index.conf >> /tmp/reseed.out 2>&1; ./nx_torrent_reseed.elf /volume1/ai/torrent/gallery /volume1/ai/torrent/seed_index.conf >> /tmp/reseed.out 2>&1" as *u8 3297func cmd_reseed() -> i64 { 3298 let pid: i64 = sys_fork() 3299 if pid == 0 { 3300 let argv: *i64 = sys_mmap(32) as *i64; argv[0]=HC_SH as i64; argv[1]=HC_DASHC as i64; argv[2]=HC_RESEED_CMD as i64; argv[3]=0 3301 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 3302 sys_execve_clean(HC_SH, argv, envp); sys_exit(127) 3303 } 3304 let st: *i64 = sys_mmap(16) as *i64; sys_wait4(pid, st, 0) 3305 hc_puts("=== reseed: register all completed downloads for sharing ===\n" as *u8) 3306 let lb: *u8 = sys_mmap(HC_MAGIC_65536); let n: i64 = hc_read_cmdline("/tmp/reseed.out" as *u8, lb, HC_MAGIC_65536) 3307 if n > 0 { sys_write(1, lb, n) } else { hc_puts("(no output)\n" as *u8) } 3308 return 0 3309} 3310// kickworkers: SIGKILL every torrent download WORKER (nx_torrent_get.sov.elf). kicktorrent only kills the 3311// daemon; workers are double-forked (reparented to init) and survive -> a stale/buggy worker keeps flailing 3312// on a download. After a worker-binary fix, kill the old workers then kicktorrent so autoresume respawns the 3313// NEW binary. NEVER-BRICK: SIGKILL only; the donefile persists progress so the resumed worker continues. 3314func cmd_kickworkers() -> i64 { 3315 hc_puts("[hostctl] kickworkers: SIGKILL all torrent download workers (nx_torrent_get.sov.elf)\n" as *u8) 3316 let k: i64 = proc_kill_by_name("nx_torrent_get.sov.elf" as *u8, 9) 3317 hc_puts(" killed worker procs=" as *u8); hc_putn(k); hc_puts(" (kicktorrent next -> autoresume respawns the NEW worker binary; donefile keeps progress)\n" as *u8) 3318 return 0 3319} 3320// torstat: run nx_torrent_seedeval on every active download -> per-torrent SCARCE/IGNORING/COMPLETE verdict. 3321// Reads root-owned media/<btih>/download.* (hostctl is root), so it works where SFTP-as-elderwesto can't. 3322func cmd_torstat() -> i64 { 3323 let pid: i64 = sys_fork() 3324 if pid == 0 { 3325 let argv: *i64 = sys_mmap(32) as *i64; argv[0]=HC_SH as i64; argv[1]=HC_DASHC as i64; argv[2]=HC_TORSTAT_CMD as i64; argv[3]=0 3326 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0 3327 sys_execve_clean(HC_SH, argv, envp); sys_exit(127) 3328 } 3329 let st: *i64 = sys_mmap(16) as *i64; sys_wait4(pid, st, 0) 3330 hc_puts("=== torstat: seedeval per active download (media/<btih>) ===\n" as *u8) 3331 let lb: *u8 = sys_mmap(HC_MAGIC_262144); let n: i64 = hc_read_cmdline("/tmp/torstat.out" as *u8, lb, HC_MAGIC_262144) 3332 if n > 0 { sys_write(1, lb, n) } else { hc_puts("(no output -- media/torrents.idx empty or missing?)\n" as *u8) } 3333 return 0 3334} 3335// kickseedann: SIGKILL the DHT announcer -> hc_guard_seedann respawns it (fresh registry read + immediate announce sweep). 3336func cmd_kickseedann() -> i64 { 3337 hc_puts("[hostctl] kickseedann: SIGKILL the DHT announcer -> hc_guard_seedann respawns it (immediate re-announce)\n" as *u8) 3338 let k: i64 = proc_kill_by_name("nx_seed_announce_all.elf" as *u8, 9) 3339 hc_puts(" killed announcer procs=" as *u8); hc_putn(k); hc_puts(" (guard respawns; a sweep announces every registered info_hash)\n" as *u8) 3340 if k == 0 { hc_puts(" NOTE: no announcer matched -- guard will start it on the next poll (fail-safe)\n" as *u8) } 3341 return 0 3342} 3343 3344// kicktorrentgw: SURGICAL one-shot for the torrent OPAQUE gateway (:18793). Kill it by name; the guard 3345// (hc_guard_torrent_gw) respawns the NEW binary. The one-shot cmdline "nx_hostctl kicktorrentgw" never 3346// contains "nx_torrent_gw.elf" -> no self-match. Use after staging a new gateway binary (.new + mv). 3347func cmd_kicktorrentgw() -> i64 { 3348 hc_puts("[hostctl] kicktorrentgw: SIGKILL the torrent OPAQUE gateway (:18793) -> the guard respawns the NEW binary\n" as *u8) 3349 let k: i64 = proc_kill_by_name("nx_torrent_gw.elf" as *u8, 9) 3350 hc_puts(" killed gateway procs=" as *u8); hc_putn(k); hc_puts(" (supervisor guard respawns nx_torrent_gw)\n" as *u8) 3351 if k == 0 { hc_puts(" NOTE: no gateway matched -- nothing to kick (not running here?)\n" as *u8) } 3352 return 0 3353} 3354 3355// One-shot gallery gateway deploy: atomic-publish the staged .new gateway binary, then kill the running gateway 3356// -> the supervisor (the bash loop, OR this nx_hostctl once it supervises the gallery) respawns the NEW binary. 3357// killdurindex: SIGKILL the duration-index batch (+ its ts_dur child). A boot-spawn (now removed) forked the 3358// durindex WITHOUT close-on-exec, so it inherited the gallery's :18090 LISTENING socket and kept it open after 3359// the gallery was killed -> every new gallery serve hit EADDRINUSE on bind -> exit -> crash-loop. Killing it 3360// frees :18090; the supervisor guard then brings nx_gallery_serve back. Surgical one-shot (mirrors kicktorrent). 3361func cmd_killdurindex() -> i64 { 3362 hc_puts("[hostctl] killdurindex: SIGKILL nx_galx_durindex + nx_ts_dur (frees the :18090 socket fd they inherited)\n" as *u8) 3363 let k1: i64 = proc_kill_by_name("nx_galx_durindex.elf" as *u8, 9) 3364 let k2: i64 = proc_kill_by_name("nx_ts_dur.elf" as *u8, 9) 3365 hc_puts(" killed durindex=" as *u8); hc_putn(k1); hc_puts(" ts_dur=" as *u8); hc_putn(k2); hc_puts(" -> :18090 freed; the guard respawns the gallery serve\n" as *u8) 3366 return 0 3367} 3368// Launch the duration-index batch the PROPER ecosystem way: a detached control-plane one-shot, NOT a serve 3369// boot-spawn. setsid (survives the ssh session) + CLOSE every inherited fd 3..255 (the ssh channel and any 3370// sockets -> the long-lived batch holds NOTHING; this is exactly the hygiene the boot-spawn lacked) + flock 3371// single-instance (in the durindex itself) + resumable. cwd /volume1/ai/galx; chmod the elfs +x (recv lands 0644). 3372func hc_spawn_durindex_job() -> i64 { 3373 let pid: i64 = sys_fork() 3374 if pid == 0 { 3375 nx_setsid() 3376 sys_chdir("/volume1/ai/galx" as *u8) 3377 var f: i64 = 3; while f < 256 { sys_close(f); f = f + 1 } 3378 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 3379 let lg: i64 = sys_openat_append("galx_durindex.log" as *u8, 0x1a4) 3380 if dn >= 0 { sys_dup3(dn, 0, 0); sys_close(dn) } 3381 if lg >= 0 { sys_dup3(lg, 1, 0); sys_dup3(lg, 2, 0); sys_close(lg) } 3382 nx_chmod("./nx_galx_durindex.elf" as *u8, 0x1ed) 3383 nx_chmod("./nx_ts_dur.elf" as *u8, 0x1ed) 3384 let av: *i64 = sys_mmap(64) as *i64 3385 av[0] = "./nx_galx_durindex.elf" as *u8 as i64 3386 av[1] = "knowledge/status/galx_vid_paths.tsv" as *u8 as i64 3387 av[2] = "./nx_ts_dur.elf" as *u8 as i64 3388 av[3] = "knowledge/status/galx_dur.raw" as *u8 as i64 3389 av[4] = 0 3390 let envp: *i64 = sys_mmap(16) as *i64; envp[0] = "PATH=/usr/bin:/bin" as *u8 as i64; envp[1] = 0 3391 sys_execve_clean("./nx_galx_durindex.elf" as *u8, av, envp) 3392 sys_exit(127) 3393 } 3394 return pid 3395} 3396// galxpipeline: detached analysis-on-INGEST pass (operator 2026-07-14, API/MCP-native media derivation). 3397// Runs nx_media_ingest_pipeline (thumbnail prewarm + NXVI duration/keyframe/markers) from /volume1/ai/galx so 3398// newly-found media gets its thumbnail + duration without a manual step. Mirrors hc_spawn_durindex_job exactly. 3399func hc_spawn_pipeline_job() -> i64 { 3400 let pid: i64 = sys_fork() 3401 if pid == 0 { 3402 nx_setsid() 3403 sys_chdir("/volume1/ai/galx" as *u8) 3404 var f: i64 = 3; while f < 256 { sys_close(f); f = f + 1 } 3405 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 3406 let lg: i64 = sys_openat_append("galx_pipeline.log" as *u8, 0x1a4) 3407 if dn >= 0 { sys_dup3(dn, 0, 0); sys_close(dn) } 3408 if lg >= 0 { sys_dup3(lg, 1, 0); sys_dup3(lg, 2, 0); sys_close(lg) } 3409 nx_chmod("./nx_media_ingest_pipeline.elf" as *u8, 0x1ed) 3410 let av: *i64 = sys_mmap(64) as *i64 3411 av[0] = "./nx_media_ingest_pipeline.elf" as *u8 as i64 3412 av[1] = 0 3413 let envp: *i64 = sys_mmap(16) as *i64; envp[0] = "PATH=/usr/bin:/bin" as *u8 as i64; envp[1] = 0 3414 sys_execve_clean("./nx_media_ingest_pipeline.elf" as *u8, av, envp) 3415 sys_exit(127) 3416 } 3417 return pid 3418} 3419// galxidxrun (MV-2; the SEEK half of "seek is broken"): detached NXVI keyframe-index BACKFILL over every 3420// recording lacking one. The index was built LAZILY on first play, so ~98% of recordings had no scrub on 3421// first open -- measured 8,821 of 76,018 indexed while the durindex batch precedent sits at 62,100. Mirrors 3422// hc_spawn_pipeline_job exactly (setsid + fds closed + own log). NEWEST-FIRST by the organ's own design; 3423// sequential fork+wait = bounded IO; idempotent (NXVI v2 skipped) = re-run CONTINUES, never restarts. 3424// DECLARED LIMIT: nx_ts_index parses H.264 only -- HEVC recordings stay unindexed until the MV-4 arc. 3425func hc_spawn_idxbackfill_job() -> i64 { 3426 let pid: i64 = sys_fork() 3427 if pid == 0 { 3428 nx_setsid() 3429 sys_chdir("/volume1/ai/galx" as *u8) 3430 var f: i64 = 3; while f < 256 { sys_close(f); f = f + 1 } 3431 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 3432 let lg: i64 = sys_openat_append("galx_idxbackfill.log" as *u8, 0x1a4) 3433 if dn >= 0 { sys_dup3(dn, 0, 0); sys_close(dn) } 3434 if lg >= 0 { sys_dup3(lg, 1, 0); sys_dup3(lg, 2, 0); sys_close(lg) } 3435 nx_chmod("./nx_galx_idx_backfill.elf" as *u8, 0x1ed) 3436 nx_chmod("./nx_ts_index.elf" as *u8, 0x1ed) 3437 let av: *i64 = sys_mmap(64) as *i64 3438 av[0] = "./nx_galx_idx_backfill.elf" as *u8 as i64 3439 av[1] = 0 3440 let envp: *i64 = sys_mmap(16) as *i64; envp[0] = "PATH=/usr/bin:/bin" as *u8 as i64; envp[1] = 0 3441 sys_execve_clean("./nx_galx_idx_backfill.elf" as *u8, av, envp) 3442 sys_exit(127) 3443 } 3444 return pid 3445} 3446// galxidxkick (seq1065): recover a WEDGED backfill. SIGKILL on a D-state process does NOT land until its 3447// I/O completes (measured on the mvault walker, seq955), so the kick VERIFIES death before doing anything 3448// else -- a blind kill+respawn would put TWO writers on the same galxidx_<id>.idx files. Decision tree: 3449// still alive after kill -> WEDGED-UNKILLABLE: report + do nothing (retry later; nothing else is safe) 3450// dead, pipeline alive -> the pipeline's wait4 just returned; IT resumes iteration (no respawn here, 3451// avoids the dual-writer race between standalone + pipeline-forked backfill) 3452// dead, pipeline dead too -> respawn the standalone backfill via hc_spawn_idxbackfill_job 3453func cmd_galxidxkick() -> i64 { 3454 hc_puts("[hostctl] galxidxkick: kill the (possibly wedged) NXVI backfill, verify death, resume safely\n" as *u8) 3455 let k: i64 = proc_kill_by_name("nx_galx_idx_backfill.elf" as *u8, 9) 3456 hc_puts(" SIGKILL sent to procs=" as *u8); hc_putn(k); hc_puts("\n" as *u8) 3457 sys_sleep_ms(800) 3458 if proc_alive_by_name("nx_galx_idx_backfill.elf" as *u8) == 1 { 3459 hc_puts(" VERDICT=WEDGED-UNKILLABLE: still alive after SIGKILL (D-state; the signal lands when its I/O completes). NOT respawning -- retry galxidxkick later.\n" as *u8) 3460 return 1 3461 } 3462 if proc_alive_by_name("nx_media_ingest_pipeline.elf" as *u8) == 1 { 3463 hc_puts(" VERDICT=KILLED; pipeline is alive and its wait4 just returned -> pipeline resumes iteration itself (no standalone respawn, avoids dual writers)\n" as *u8) 3464 return 0 3465 } 3466 hc_spawn_idxbackfill_job() 3467 hc_puts(" VERDICT=KILLED+RESPAWNED standalone (pipeline not running) -> galx_idxbackfill.log\n" as *u8) 3468 return 0 3469} 3470// galxpipelinekick (seq1065): recover a wedged ANALYSIS PIPELINE (thumbs + NXVI). Kills the pipeline AND its 3471// forked backfill child (killing only the parent would orphan a wedged child that keeps the guard matching), 3472// verifies BOTH deaths, then respawns the pipeline fresh. Same D-state honesty as galxidxkick. 3473func cmd_galxpipelinekick() -> i64 { 3474 hc_puts("[hostctl] galxpipelinekick: kill pipeline + backfill child, verify death, respawn pipeline\n" as *u8) 3475 let kp: i64 = proc_kill_by_name("nx_media_ingest_pipeline.elf" as *u8, 9) 3476 let kb: i64 = proc_kill_by_name("nx_galx_idx_backfill.elf" as *u8, 9) 3477 hc_puts(" SIGKILL sent: pipeline=" as *u8); hc_putn(kp); hc_puts(" backfill=" as *u8); hc_putn(kb); hc_puts("\n" as *u8) 3478 sys_sleep_ms(800) 3479 var still: i64 = 0 3480 if proc_alive_by_name("nx_media_ingest_pipeline.elf" as *u8) == 1 { still = 1 } 3481 if proc_alive_by_name("nx_galx_idx_backfill.elf" as *u8) == 1 { still = 1 } 3482 if still == 1 { 3483 hc_puts(" VERDICT=WEDGED-UNKILLABLE: a member is still alive after SIGKILL (D-state). NOT respawning -- retry later.\n" as *u8) 3484 return 1 3485 } 3486 hc_spawn_pipeline_job() 3487 hc_puts(" VERDICT=KILLED+RESPAWNED pipeline (thumb prewarm + NXVI resume from scratch pass; idempotent per item) -> galx_pipeline.log\n" as *u8) 3488 return 0 3489} 3490func cmd_galxidxrun() -> i64 { 3491 hc_puts("[hostctl] galxidxrun: detached NXVI keyframe-seek backfill (newest-first, idempotent, resumable) -> /volume1/ai/galx/galx_idxbackfill.log\n" as *u8) 3492 if proc_alive_by_name("nx_galx_idx_backfill.elf" as *u8) == 1 { hc_puts(" already running -> no-op (idempotent)\n" as *u8); return 0 } 3493 hc_spawn_idxbackfill_job() 3494 hc_puts(" spawned detached\n" as *u8) 3495 return 0 3496} 3497func cmd_galxpipeline() -> i64 { 3498 hc_puts("[hostctl] galxpipeline: detached analysis-on-ingest pass (thumbnail prewarm + NXVI duration/markers) from /volume1/ai/galx -- idempotent, resumable\n" as *u8) 3499 if proc_alive_by_name("nx_media_ingest_pipeline.elf" as *u8) == 1 { hc_puts(" already running -> no-op (idempotent)\n" as *u8); return 0 } 3500 hc_spawn_pipeline_job() 3501 hc_puts(" spawned -> progress in /volume1/ai/galx/galx_pipeline.log; re-run to resume\n" as *u8) 3502 return 0 3503} 3504// SEARCH PIPELINE ON THE NAS (2026-07-23, operator: "i want the NAS doing these things not the laptop where 3505// we dev"). Detached one-shot run of a search-index pipeline organ against the LIVE web shard at 3506// nishihost/knowledge/store/dp-web-pub-. Mirrors hc_spawn_pipeline_job EXACTLY (the proven setsid pattern; 3507// the deep-fork codegen edge only bites re-forking daemons like sites.elf, not one-shot organs). The organs 3508// are mmap-open (measured pagerank ~4.2GB RSS) so they fit the NAS. Safe-by-construction: pagerank APPENDS 3509// pr: segments (rule-13), compact VERIFIES byte-identical before its atomic manifest swap + keeps superseded 3510// files. a2=0 for a 2-arg organ (compact), else the 3rd argv. Log -> knowledge/status/searchpipe.log. 3511// Address-space ceiling for detached search-pipeline jobs. 16 GiB = ~4x the largest MEASURED legitimate 3512// footprint (pagerank ~4.2GB) and well under the 36GB box, so a normal job never notices and a runaway dies 3513// long before the host is endangered. DATA, not taste: raise it here if a real job ever needs more, and 3514// prefer moving it to config if a second consumer appears. 3515// ⚠RAISED TO 48GiB AND THEN REVERTED, SAME SESSION 2026-08-06 -- BANKED BECAUSE THE RETRACTION IS 3516// THE LESSON. I raised it believing this cap silently killed every searchpipe job at startup: two 3517// `sub=searchcompact` spawns appeared to write NOTHING to searchpipe.log. That absence was 3518// MANUFACTURED BY MY OWN INSTRUMENT -- nx_shelltool had truncated the scan and reported 11 header 3519// matches; the same grep at `per_file_coverage=complete` returned 198, showing compaction running 3520// continuously the whole time (segments 4 -> 10 -> 200 -> 600 -> 896). ***A TRUNCATED SCAN REPORTS 3521// AN ABSENCE, AND AN ABSENCE IS INDISTINGUISHABLE FROM A THING THAT NEVER RAN*** -- so I built a 3522// root cause on top of a measurement artifact and weakened a real OOM protection to fix a defect 3523// that did not exist. 16GiB stands: it is ~4x the largest MEASURED legitimate footprint and it was 3524// added after three organs drove this host to the OOM edge in one session. The REAL defect is that 3525// compaction is LOSING A RACE with the crawler, not that it is dead -- fold budget, not this cap. 3526const HC_JOB_AS_CAP: i64 = 17179869184 3527func hc_spawn_searchpipe_job(organ: *u8, a1: *u8, a2: *u8) -> i64 { 3528 let pid: i64 = sys_fork() 3529 if pid == 0 { 3530 nx_setsid() 3531 sys_chdir("/volume1/homes/elderwesto/nishihost" as *u8) 3532 var f: i64 = 3; while f < 256 { sys_close(f); f = f + 1 } 3533 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 3534 let lg: i64 = sys_openat_append("knowledge/status/searchpipe.log" as *u8, 0x1a4) 3535 if dn >= 0 { sys_dup3(dn, 0, 0); sys_close(dn) } 3536 if lg >= 0 { sys_dup3(lg, 1, 0); sys_dup3(lg, 2, 0); sys_close(lg) } 3537 nx_chmod(organ, 0x1ed) 3538 let av: *i64 = sys_mmap(64) as *i64 3539 av[0] = organ as i64 3540 av[1] = a1 as i64 3541 av[2] = a2 as i64 3542 av[3] = 0 3543 let envp: *i64 = sys_mmap(16) as *i64; envp[0] = "PATH=/usr/bin:/bin" as *u8 as i64; envp[1] = 0 3544 // MEMORY ENVELOPE (2026-07-30). THREE organs drove this host to the OOM edge in ONE session -- 3545 // nx_ssdf ~28GB, nx_mvault ~7.3GB, nx_web_shard_compact ~21GB and still climbing at +88MB/12s with 3546 // 536MB free -- and every time the COLLATERAL was the control plane: mgmt and the tools daemon 3547 // stopped answering, /api/build refused itself, and once sshd could not even complete a handshake. 3548 // The comment above sized these jobs by hope ("~4.2GB RSS so they fit the NAS"); hope is not a limit. 3549 // Cap the CHILD's address space before exec so a runaway JOB dies instead of the HOST. 3550 // ADOPTION, NOT INVENTION: nx_prlimit + RLIMIT_AS already existed in nx_syscalls, and 3551 // nx_supervised_dispatch already proves this exact fork -> cap-self -> exec shape in its gate. It was 3552 // simply never wired at the spawner where production jobs actually run -- this session's recurring 3553 // meta-defect. Applied at the CHOKEPOINT so every searchpipe job inherits it, not per leaf organ. 3554 // Set on SELF (pid 0) after fork, before execve: the limit survives exec and a non-root process 3555 // cannot raise a lowered hard limit back, so the exec'd image cannot opt out. 3556 // LOUD-BY-CONSTRUCTION (2026-08-06). fd 1 is the searchpipe log by now, so this line lands in 3557 // the log BEFORE the address-space cap and the exec. It converts the two silent failure modes 3558 // into readable ones: this marker with NO organ output after it == the child died at startup 3559 // (RLIMIT_AS refusal or a failed exec); no marker at all == the fork/redirect never happened. 3560 // Written because compaction and pagerank were BOTH dead for a week and the only evidence was 3561 // an absence -- and an absence is exactly what a job that was never scheduled also looks like. 3562 hc_puts("[searchpipe] exec " as *u8); hc_puts(organ) 3563 hc_puts(" (if NO organ output follows this line, the child died at STARTUP -- raise HC_JOB_AS_CAP or check the elf, do not assume the job simply had nothing to do)\n" as *u8) 3564 let rlim: *i64 = sys_mmap(16) as *i64 3565 rlim[0] = HC_JOB_AS_CAP 3566 rlim[1] = HC_JOB_AS_CAP 3567 nx_prlimit(0, RLIMIT_AS, rlim as *u8, 0 as *u8) 3568 sys_execve_clean(organ, av, envp) 3569 hc_puts("[searchpipe] EXEC FAILED (127) -- elf missing or not executable: " as *u8); hc_puts(organ); hc_puts("\n" as *u8) 3570 sys_exit(127) 3571 } 3572 return pid 3573} 3574func cmd_searchpagerank() -> i64 { 3575 hc_puts("[hostctl] searchpagerank: detached PageRank build on the NAS live web shard -> pr: priors (mmap-open, ~4.2GB)\n" as *u8) 3576 if proc_alive_by_name("nx_pagerank_build.elf" as *u8) == 1 { hc_puts(" already running -> no-op (idempotent)\n" as *u8); return 0 } 3577 if proc_alive_by_name("nx_web_shard_compact.elf" as *u8) == 1 { hc_puts(" a compaction is running -> defer (no-op)\n" as *u8); return 0 } 3578 hc_spawn_searchpipe_job("./_offc/nx_pagerank_build.elf" as *u8, "web" as *u8, "60" as *u8) 3579 hc_puts(" spawned -> progress in knowledge/status/searchpipe.log\n" as *u8) 3580 return 0 3581} 3582func cmd_searchcompact() -> i64 { 3583 hc_puts("[hostctl] searchcompact: detached web-shard compaction on the NAS -> fold segments (verifies before swap)\n" as *u8) 3584 if proc_alive_by_name("nx_web_shard_compact.elf" as *u8) == 1 { hc_puts(" already running -> no-op (idempotent)\n" as *u8); return 0 } 3585 if proc_alive_by_name("nx_pagerank_build.elf" as *u8) == 1 { hc_puts(" a pagerank build is running -> defer (no-op)\n" as *u8); return 0 } 3586 hc_spawn_searchpipe_job("./_offc/nx_web_shard_compact.elf" as *u8, "web" as *u8, 0 as *u8) 3587 hc_puts(" spawned -> progress in knowledge/status/searchpipe.log\n" as *u8) 3588 return 0 3589} 3590// searchcrawl (2026-07-25, seq912): the COVERAGE half of the search pipeline, alongside searchpagerank 3591// (authority) and searchcompact (storage). Runs the EXISTING nx_web_crawl_step -- a frontier-persisted, 3592// resumable crawler whose frontier lives in the shard itself -- detached via the SAME spawner, so a batch 3593// survives the edge's ~15s backend-read timeout that silently defeats synchronous tools/call ingest. 3594// arg1 = max_pages this run; arg2 = optional seed url to bootstrap/inject into the frontier. 3595// Deliberately NOT a new ingest organ: the crawler, its pacing, dedup and outlink filtering already exist. 3596// FIXED-ARG by design: hostctl subs ignore caller argv (the allowlist pins them), so max_pages and the 3597// bootstrap seed are compiled in rather than passed -- no hostile-arg surface. The seed is idempotent: 3598// an existing fr: row just flips last-wins, so re-running only ever CONTINUES the crawl. 3599// seconds since a file was last written, or -1 if it does not exist. Uses the SAME sys_fstatat + struct 3600// stat offsets the docportal shard-cache signature already relies on (st_mtime sec @ +88 on x86_64) -- 3601// existing primitive, no new mechanism. Absent file => -1 => callers must treat it as 'cannot judge'. 3602func hc_file_age_s(path: *u8) -> i64 { 3603 let stb: *u8 = sys_mmap(160) 3604 if sys_fstatat(path, stb) != 0 { return 0 - 1 } 3605 let mtp: *i64 = ((stb as i64) + 88) as *i64 3606 let now: i64 = sys_now_realtime_sec() 3607 let age: i64 = now - mtp[0] 3608 if age < 0 { return 0 } 3609 return age 3610} 3611func cmd_searchcrawl() -> i64 { 3612 let maxp: *u8 = "400" as *u8 3613 // AIMED 2026-07-26: was a single Wikipedia hub, which filled the frontier with encyclopedia-adjacent 3614 // pages -- 113 of them landed and the judged ruler did not move BY ONE POINT, because the queries ask 3615 // for canonical sites (health agency, tax authority, kernel archive) that hub never links to. Now a 3616 // 44-domain CURATED list, selected by a documented rule that never mentions the benchmark. Re-seeding 3617 // is idempotent (a known frontier url is skipped), so every run just continues the crawl. 3618 let seed: *u8 = "knowledge/status/canonical_seeds.txt" as *u8 3619 hc_puts("[hostctl] searchcrawl: detached resumable web crawl on the NAS (frontier lives in the shard; re-run to continue)\n" as *u8) 3620 // STALENESS ESCAPE (seq961, a defect in my own first cut): a bare proc_alive guard turns ONE hang into 3621 // PERMANENT unavailability -- a stalled crawler holds the lock forever and the leg is wedged with no way 3622 // out over the API. Liveness is judged by PROGRESS (searchpipe.log mtime), not by the process existing: 3623 // alive AND log older than HC_CRAWL_STALE_S => reap it (the same hc_kill_by_cmdline every kick sub uses) 3624 // and fall through to a fresh spawn. Alive AND progressing => genuine no-op, still idempotent. 3625 if proc_alive_by_name("nx_web_crawl_step.elf" as *u8) == 1 { 3626 let age: i64 = hc_file_age_s("knowledge/status/searchpipe.log" as *u8) 3627 if age >= 0 { if age < HC_CRAWL_STALE_S { 3628 hc_puts(" already running and progressing (log age " as *u8); hc_putn(age); hc_puts("s) -> no-op (idempotent)\n" as *u8) 3629 return 0 3630 } } 3631 hc_puts(" STALLED: alive but no log progress for " as *u8); hc_putn(age); hc_puts("s -> reaping and respawning\n" as *u8) 3632 let kk: i64 = hc_kill_by_cmdline("nx_web_crawl_step.elf" as *u8) 3633 hc_puts(" reaped procs=" as *u8); hc_putn(kk); hc_puts("\n" as *u8) 3634 } 3635 if proc_alive_by_name("nx_web_shard_compact.elf" as *u8) == 1 { hc_puts(" a compaction is running -> defer (no-op)\n" as *u8); return 0 } 3636 if proc_alive_by_name("nx_pagerank_build.elf" as *u8) == 1 { hc_puts(" a pagerank build is running -> defer (no-op)\n" as *u8); return 0 } 3637 hc_spawn_searchpipe_job("./nx_web_crawl_step.elf" as *u8, maxp, seed) 3638 hc_puts(" spawned -> progress in knowledge/status/searchpipe.log\n" as *u8) 3639 return 0 3640} 3641func cmd_durindexrun() -> i64 { 3642 hc_puts("[hostctl] durindexrun: detached duration-index batch (setsid + ALL fds closed + flock single-instance) from /volume1/ai/galx -- the proper one-shot, NOT a serve boot-spawn\n" as *u8) 3643 if proc_alive_by_name("nx_galx_durindex.elf" as *u8) == 1 { hc_puts(" already running -> no-op (resumable+idempotent)\n" as *u8); return 0 } 3644 hc_spawn_durindex_job() 3645 hc_puts(" spawned -> appends knowledge/status/galx_dur.raw; progress in /volume1/ai/galx/galx_durindex.log; re-run to resume\n" as *u8) 3646 return 0 3647} 3648func hc_count_lines(path: *u8) -> i64 { 3649 let szp: *i64 = sys_mmap(16) as *i64 3650 let b: *u8 = sys_read_file(path, szp) 3651 if (b as i64) == 0 { return 0 - 1 } 3652 let sz: i64 = szp[0] 3653 var n: i64 = 0; var i: i64 = 0 3654 while i < sz { if b[i] == (10 as u8) { n = n + 1 } i = i + 1 } 3655 return n 3656} 3657func hc_tail(path: *u8, nb: i64) -> i64 { 3658 let szp: *i64 = sys_mmap(16) as *i64 3659 let b: *u8 = sys_read_file(path, szp) 3660 if (b as i64) == 0 { hc_puts(" (log absent)\n" as *u8); return 0 } 3661 let sz: i64 = szp[0] 3662 var start: i64 = sz - nb; if start < 0 { start = 0 } 3663 sys_write(1, ((b as i64) + start) as *u8, sz - start); hc_puts("\n" as *u8) 3664 return 0 3665} 3666// durindexstat: report runtime-sort data readiness = galx_dur.raw durations done vs vid_paths total. 3667func cmd_durindexstat() -> i64 { 3668 let dn: i64 = hc_count_lines("/volume1/ai/galx/knowledge/status/galx_dur.raw" as *u8) 3669 let pn: i64 = hc_count_lines("/volume1/ai/galx/knowledge/status/galx_vid_paths.tsv" as *u8) 3670 hc_puts("[hostctl] durindexstat: galx_dur.raw durations=" as *u8); hc_putn(dn); hc_puts(" / vid_paths total=" as *u8); hc_putn(pn); hc_puts("\n" as *u8) 3671 if dn < 0 { hc_puts(" galx_dur.raw ABSENT -> runtime-sort shows building (run durindexrun)\n" as *u8) } 3672 else { if dn >= pn { hc_puts(" -> COMPLETE: runtime-sort fully populated\n" as *u8) } else { hc_puts(" -> PARTIAL: runtime-sort works on the indexed subset; durindexrun to continue\n" as *u8) } } 3673 hc_puts(" --- galx_durindex.log tail ---\n" as *u8) 3674 hc_tail("/volume1/ai/galx/galx_durindex.log" as *u8, 700) 3675 return 0 3676} 3677// copy the n-th (0-indexed) line of buf into out (null-terminated, CR stripped); returns len or -1. 3678func hc_nth_line(buf: *u8, sz: i64, n: i64, out: *u8) -> i64 { 3679 var line: i64 = 0; var i: i64 = 0; var ls: i64 = 0 3680 while i <= sz { 3681 var nl: i64 = 0; if i == sz { nl = 1 } else { if buf[i] == (10 as u8) { nl = 1 } } 3682 if nl == 1 { 3683 if line == n { var ce: i64 = i; if ce > ls { if buf[ce-1] == (13 as u8) { ce = ce - 1 } } var o: i64 = 0; var k: i64 = ls; while k < ce { out[o] = buf[k]; o = o + 1; k = k + 1 } out[o] = 0 as u8; return o } 3684 line = line + 1; ls = i + 1 3685 } 3686 i = i + 1 3687 } 3688 return 0 - 1 3689} 3690// tsdurtest: run nx_ts_dur on the video at vid_paths line 40302 (first non-done) + show its raw output -- to 3691// diagnose why the batch produced no durations for the non-.ts tail. 3692func cmd_tsdurtest() -> i64 { 3693 let szp: *i64 = sys_mmap(16) as *i64 3694 let b: *u8 = sys_read_file("/volume1/ai/galx/knowledge/status/galx_vid_paths.tsv" as *u8, szp) 3695 if (b as i64) == 0 { hc_puts("no vid_paths\n" as *u8); return 0 } 3696 let path: *u8 = sys_mmap(HC_MAGIC_2048) 3697 if hc_nth_line(b, szp[0], HC_MAGIC_40302, path) < 0 { hc_puts("line 40302 not found\n" as *u8); return 0 } 3698 hc_puts("[tsdurtest] line 40302 path=" as *u8); hc_puts(path); hc_puts("\n" as *u8) 3699 let pid: i64 = sys_fork() 3700 if pid == 0 { 3701 sys_chdir("/volume1/ai/galx" as *u8) 3702 let of: i64 = sys_openat_wr("/tmp/tsdurtest.out" as *u8, 0x1a4) 3703 if of >= 0 { sys_dup3(of, 1, 0); sys_dup3(of, 2, 0) } 3704 nx_chmod("./nx_ts_dur.elf" as *u8, 0x1ed) 3705 let av: *i64 = sys_mmap(32) as *i64; av[0] = "./nx_ts_dur.elf" as *u8 as i64; av[1] = path as i64; av[2] = 0 3706 let envp: *i64 = sys_mmap(16) as *i64; envp[0] = "PATH=/usr/bin:/bin" as *u8 as i64; envp[1] = 0 3707 sys_execve_clean("./nx_ts_dur.elf" as *u8, av, envp); sys_exit(127) 3708 } 3709 let st: *i64 = sys_mmap(16) as *i64; sys_wait4(pid, st, 0) 3710 hc_puts(" --- nx_ts_dur output ---\n" as *u8) 3711 hc_tail("/tmp/tsdurtest.out" as *u8, 400) 3712 return 0 3713} 3714// durbinbuild: compact galx_dur.raw -> galx_dur.bin so the gallery's /vid/<id>/dur seekbar lookup is an 3715// O(1) 8-byte read instead of a 16MB first+last-PCR re-scan. Run after the durindex appends new durations; 3716// the gallery reads the fresh .bin with NO restart (mirrors galx_vid_off.bin). Short-lived: wait + show. 3717func cmd_durbinbuild() -> i64 { 3718 hc_puts("[hostctl] durbinbuild: compacting galx_dur.raw -> galx_dur.bin (O(1) /dur seekbar lookup)\n" as *u8) 3719 let bf: i64 = sys_openat_rd("/volume1/ai/galx/nx_galx_durbin.elf" as *u8) 3720 if bf < 0 { hc_puts(" ERROR nx_galx_durbin.elf MISSING -> re-send it\n" as *u8); return 0 } 3721 let bsz: i64 = sys_lseek(bf, 0, 2); sys_close(bf) 3722 hc_puts(" binary present, size=" as *u8); hc_putn(bsz); hc_puts("\n" as *u8) 3723 let pid: i64 = sys_fork() 3724 if pid == 0 { 3725 sys_chdir("/volume1/ai/galx" as *u8) 3726 let of: i64 = sys_openat_wr("/tmp/durbin.out" as *u8, 0x1a4) 3727 if of >= 0 { sys_dup3(of, 1, 0); sys_dup3(of, 2, 0) } 3728 nx_chmod("./nx_galx_durbin.elf" as *u8, 0x1ed) 3729 let av: *i64 = sys_mmap(32) as *i64; av[0] = "./nx_galx_durbin.elf" as *u8 as i64; av[1] = 0 3730 let envp: *i64 = sys_mmap(16) as *i64; envp[0] = "PATH=/usr/bin:/bin" as *u8 as i64; envp[1] = 0 3731 sys_execve_clean("./nx_galx_durbin.elf" as *u8, av, envp); sys_exit(127) 3732 } 3733 let st: *i64 = sys_mmap(16) as *i64; sys_wait4(pid, st, 0) 3734 let raw: i64 = st[0] 3735 hc_puts(" child status raw=" as *u8); hc_putn(raw); hc_puts(" exit=" as *u8); hc_putn((raw>>8)&0xff); hc_puts(" sig=" as *u8); hc_putn(raw&0x7f); hc_puts("\n" as *u8) 3736 hc_tail("/tmp/durbin.out" as *u8, 400) 3737 let cf: i64 = sys_openat_rd("/volume1/ai/galx/knowledge/status/galx_dur.bin" as *u8) 3738 if cf < 0 { hc_puts(" galx_dur.bin NOT created\n" as *u8) } else { let csz: i64 = sys_lseek(cf, 0, 2); sys_close(cf); hc_puts(" galx_dur.bin size=" as *u8); hc_putn(csz); hc_puts(" bytes (= 16 + 8*n)\n" as *u8) } 3739 return 0 3740} 3741// streaming copy src->dst (0755). Returns 0 ok, <0 fail. (staging the freshly-built elf for /api/deploy) 3742func hc_copyfile(src: *u8, dst: *u8) -> i64 { 3743 let sfd: i64 = sys_openat_rd(src); if sfd < 0 { return 0 - 1 } 3744 let dfd: i64 = sys_openat_wr(dst, 0x1ed); if dfd < 0 { sys_close(sfd); return 0 - 2 } 3745 let buf: *u8 = sys_mmap(HC_MAGIC_1048576); var go: i64 = 1 3746 while go == 1 { let nr: i64 = sys_read(sfd, buf, HC_MAGIC_1048576); if nr <= 0 { go = 0 } else { sys_write(dfd, buf, nr) } } 3747 sys_close(sfd); sys_close(dfd); return 0 3748} 3749// buildrun <name>: COMPILE a target ON THE NAS. cd buildroot (has _offc/ toolchain + runtime/ synced source), 3750// run _offc/nx_sov_build_run.elf <name> --build-only -> buildroot/_build/<name>.sov.elf (or, for 3751// pre-flock builder versions, /tmp/