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