code wiki / _hdl_build / nx_mgmt_data.nx
nx_mgmt_data.nx source
↩ module page · 2142 lines · 114172 B
1// nx_mgmt_data.nx -- the DATA / ADAPTER layer of the management plane (the OUTER ring; secondary adapters).
2// The ONLY layer that touches the outside world for STATE: it parses snapshot bytes, reads the data-driven
3// config allowlists, and drives the secondary adapters (the hostctl exec + the real-HTTP health probe). It has
4// NO knowledge of transport (no HTTP/socket/auth); the IO ring depends on IT, never the reverse (ports &
5// adapters / dependency inversion). Grounded in knowledge/library/arch_* (three-tier DATA tier, hexagonal
6// secondary adapters, loose coupling). Reuses the SOTA-gated nx_deploy_lib (validate/exec) + nx_http_health_lib
7// (probe) -- DRY. license_tier: ORIGINAL
8import "nx_syscalls.nx"
9import "nx_connect.nx" // bounded connect: a raw sys_connect hangs ~127s on a black-holed host
10import "_hdl_build/nx_adnet_invoice.nx"
11import "_hdl_build/nx_adnet_creative.nx"
12import "nx_deploy_lib.nx"
13import "nx_http_health_lib.nx"
14import "nx_tool_run.nx" // seq1443: tr_run_capture_to -- the GATE-PROVEN bounded exec (see md_exec_gate_capture)
15
16// ---- parse primitives over a buffer (slices, no null terminators) -----------------------------------
17func md_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
18
19func md_cat_slice(d: *u8, o: i64, src: *u8, off: i64, len: i64) -> i64 {
20 var i: i64 = 0
21 while i < len { d[o] = src[off + i]; o = o + 1; i = i + 1 }
22 return o
23}
24
25func md_tok_eq(src: *u8, off: i64, len: i64, s: *u8) -> i64 {
26 let sl: i64 = md_len(s)
27 if sl != len { return 0 }
28 var i: i64 = 0
29 while i < len { if (src[off + i] as i64) != (s[i] as i64) { return 0 } i = i + 1 }
30 return 1
31}
32
33func md_slice_atoi(src: *u8, off: i64, len: i64) -> i64 {
34 var v: i64 = 0
35 var i: i64 = 0
36 while i < len {
37 let c: i64 = src[off + i] as i64
38 if c >= 48 { if c <= 57 { v = v * 10 + (c - 48) } }
39 i = i + 1
40 }
41 return v
42}
43
44// index of '\n' at-or-after start, or n.
45func md_eol(snap: *u8, n: i64, start: i64) -> i64 {
46 var i: i64 = start
47 var f: i64 = 0
48 while f == 0 {
49 if i >= n { f = 1 } else { if (snap[i] as i64) == 10 { f = 1 } else { i = i + 1 } }
50 }
51 return i
52}
53
54// split snap[ls..le) on spaces into up-to-maxf (offs,lens) absolute slices. returns field count.
55func md_split(snap: *u8, ls: i64, le: i64, offs: *i64, lens: *i64, maxf: i64) -> i64 {
56 var nf: i64 = 0
57 var i: i64 = ls
58 while i < le {
59 var sk: i64 = 1
60 while sk == 1 { if i >= le { sk = 0 } else { if (snap[i] as i64) == 32 { i = i + 1 } else { sk = 0 } } }
61 if i < le {
62 let st: i64 = i
63 var sc: i64 = 1
64 while sc == 1 { if i >= le { sc = 0 } else { if (snap[i] as i64) == 32 { sc = 0 } else { i = i + 1 } } }
65 if nf < maxf { offs[nf] = st; lens[nf] = i - st; nf = nf + 1 }
66 }
67 }
68 return nf
69}
70
71func md_slice_eq(a: *u8, ao: i64, al: i64, b: *u8, bo: i64, bl: i64) -> i64 {
72 if al != bl { return 0 }
73 var i: i64 = 0
74 while i < al { if (a[ao + i] as i64) != (b[bo + i] as i64) { return 0 } i = i + 1 }
75 return 1
76}
77
78func md_copy_slice_z(dst: *u8, src: *u8, off: i64, len: i64, cap: i64) -> i64 {
79 var n: i64 = len
80 if n > cap - 1 { n = cap - 1 }
81 var i: i64 = 0
82 while i < n { dst[i] = src[off + i]; i = i + 1 }
83 dst[n] = 0 as u8
84 return n
85}
86
87// ---- file / config adapters -------------------------------------------------------------------------
88func md_read_file(path: *u8, szbox: *i64) -> *u8 {
89 szbox[0] = 0
90 return sys_read_file(path, szbox)
91}
92
93// ---- ADNET BILLING (debt 1785513943): the outside-world half of the invoice route ----------------
94// Lives HERE, in the DATA ring, not in nx_mgmt_api: that ring owns transport only (see the api header).
95// The money math stays in nx_adnet_bill and the join in nx_adnet_invoice -- this function is purely the
96// file access those two are deliberately free of.
97// FAIL-CLOSED: an unreadable INVENTORY or RATE CARD returns 0 (the route answers 503) rather than an
98// empty invoice -- "no rows" and "could not read the rows" must never look alike to a biller. An absent
99// JOURNAL is different and legitimate: it means zero events, so it degrades to an empty count.
100const MD_ADNET_INV: *u8 = "/volume1/homes/elderwesto/nishihost/sites/nishifamily/synth/adnet_inventory.txt" as *u8
101const MD_ADNET_RATES: *u8 = "/volume1/homes/elderwesto/nishihost/knowledge/status/adnet_rates.conf" as *u8
102const MD_ADNET_SERVED: *u8 = "/volume1/homes/elderwesto/nishihost/adnet_impressions.log" as *u8
103const MD_ADNET_CLICKS: *u8 = "/volume1/homes/elderwesto/nishihost/adnet_clicks.log" as *u8
104const MD_ADNET_VIEW: *u8 = "/volume1/homes/elderwesto/nishihost/knowledge/status/adnet_viewable.log" as *u8
105
106// ---- ADNET CREATIVE INTAKE (debt 1785512202): the outside-world half of the upload route --------
107// Validation and naming live in nx_adnet_creative (pure, gated 13/13); this is only the file write.
108// CONTENT-ADDRESSED, so the write is IDEMPOTENT by construction (rule 10): re-uploading identical bytes
109// lands on the identical path. No overwrite hazard, no version skew, and the URL doubles as a cache key.
110// Returns the ACR_* verdict; urlout receives the first-party url ONLY on ACR_OK.
111const MD_ADNET_SYNTH: *u8 = "/volume1/homes/elderwesto/nishihost/sites/nishifamily/synth/" as *u8
112
113func md_adnet_creative_store(b: *u8, n: i64, urlout: *u8, urlcap: i64) -> i64 {
114 urlout[0] = 0 as u8
115 let v: i64 = acr_validate(b, n)
116 if v != ACR_OK { return v }
117 let nm: *u8 = sys_mmap(64)
118 if acr_name(b, n, nm, 64) == 0 { return ACR_NOT_PNG }
119 let path: *u8 = sys_mmap(512)
120 var o: i64 = 0
121 var i: i64 = 0
122 while MD_ADNET_SYNTH[i] != (0 as u8) { path[o] = MD_ADNET_SYNTH[i]; o = o + 1; i = i + 1 }
123 i = 0
124 while nm[i] != (0 as u8) { path[o] = nm[i]; o = o + 1; i = i + 1 }
125 path[o] = 0 as u8
126 let fd: i64 = sys_openat_wr(path, 420)
127 if fd < 0 { return 0 - 1 }
128 let w: i64 = sys_write(fd, b, n)
129 sys_close(fd)
130 if w != n { return 0 - 1 }
131 if acr_url(b, n, urlout, urlcap) == 0 { return 0 - 1 }
132 return ACR_OK
133}
134
135func md_adnet_invoice_report(out: *u8, cap: i64) -> i64 {
136 let bx: *i64 = sys_mmap(16) as *i64
137 let inv: *u8 = md_read_file(MD_ADNET_INV, bx)
138 if (inv as i64) == 0 { return 0 }
139 let iln: i64 = bx[0]
140 let bx2: *i64 = sys_mmap(16) as *i64
141 let rates: *u8 = md_read_file(MD_ADNET_RATES, bx2)
142 if (rates as i64) == 0 { return 0 }
143 let rln: i64 = bx2[0]
144 let bx3: *i64 = sys_mmap(16) as *i64
145 var served: *u8 = md_read_file(MD_ADNET_SERVED, bx3)
146 var sln: i64 = bx3[0]
147 if (served as i64) == 0 { served = "" as *u8; sln = 0 }
148 let bx4: *i64 = sys_mmap(16) as *i64
149 var view: *u8 = md_read_file(MD_ADNET_VIEW, bx4)
150 var vln: i64 = bx4[0]
151 if (view as i64) == 0 { view = "" as *u8; vln = 0 }
152 let bx5: *i64 = sys_mmap(16) as *i64
153 var clk: *u8 = md_read_file(MD_ADNET_CLICKS, bx5)
154 var cln: i64 = bx5[0]
155 if (clk as i64) == 0 { clk = "" as *u8; cln = 0 }
156 return ainv_report(inv, iln, rates, rln, served, sln, view, vln, clk, cln, out, cap)
157}
158
159// resolve a deploy target NAME (slice nm[off..off+len)) against the allowlist file -> kind + src/sub/url
160// copied null-terminated into caller buffers. 1 = resolved, 0 = unknown (fail-closed). '#' = comment line.
161// COMPILED-IN fallback for the deploy plane's OWN bootstrap targets, so a freshly-deployed mgmt API can deploy
162// mgmtapi/hostctl/torrentstack off-LAN WITHOUT first getting an updated deploy_targets.conf onto the NAS (the
163// file isn't upload-able off-LAN). The file (md_resolve_target) still WINS when present -> it stays the
164// extensible SSOT; this only covers the plane's self-knowledge. Each: kind, src(staged .new), sub(promote), url(health), rb(rollback).
165func md_builtin_target(nm: *u8, off: i64, len: i64, kindb: *i64, srcbuf: *u8, subbuf: *u8, urlbuf: *u8, rbbuf: *u8) -> i64 {
166 if md_slice_eq(nm, off, len, "mgmtapi" as *u8, 0, 7) == 1 {
167 // health = LOCAL TCP-connect to the mgmt API's own port :18098 (robust). The old "https://.../api/" HTTP
168 // probe ran nx_research_fetch from the NAS -> nishifamily.com, which hits DSM's loopback nginx (coin-flip)
169 // -> flaky false-rollback. The new mgmt respawns on :18098 within the 30s retry window -> port-connect greens.
170 kindb[0]=0; md_copy_slice_z(srcbuf, "nx_mgmt_api.elf.new" as *u8, 0, 19, 512); md_copy_slice_z(subbuf, "mgmtdeploy" as *u8, 0, 10, 64); md_copy_slice_z(urlbuf, "port:18098" as *u8, 0, 10, 256); md_copy_slice_z(rbbuf, "mgmtrollback" as *u8, 0, 12, 64); return 1 }
171 if md_slice_eq(nm, off, len, "hostctl" as *u8, 0, 7) == 1 {
172 // health = LOCAL TCP-connect to sites.elf :8443 (robust, like torrentstack). The old "https://.../api/"
173 // HTTP-fetch probe needed nx_research_fetch+CA from the mgmt cwd + hit the :443 DSM-nginx coin-flip + raced
174 // the self-swap -> it ALWAYS false-rolled-back (why no hostctl deploy landed since 07-09). sites.elf stays
175 // up across a self-swap (only the supervisor re-execs), so the port-connect greens reliably.
176 kindb[0]=0; md_copy_slice_z(srcbuf, "nx_hostctl.new" as *u8, 0, 14, 512); md_copy_slice_z(subbuf, "selfswap" as *u8, 0, 8, 64); md_copy_slice_z(urlbuf, "port:8443" as *u8, 0, 9, 256); md_copy_slice_z(rbbuf, "superrollback" as *u8, 0, 13, 64); return 1 }
177 if md_slice_eq(nm, off, len, "torrentstack" as *u8, 0, 12) == 1 {
178 kindb[0]=0; md_copy_slice_z(srcbuf, "nx_torrent_daemon.sov.elf.new" as *u8, 0, 29, 512); md_copy_slice_z(subbuf, "torrentdeploy" as *u8, 0, 13, 64); md_copy_slice_z(urlbuf, "port:8097" as *u8, 0, 9, 256); md_copy_slice_z(rbbuf, "torrentrollback" as *u8, 0, 15, 64); return 1 }
179 // ethical CLEAN-SERVE daemon (:8102, /clean) -- first-class builtin so it deploys purely over the API (no NAS
180 // deploy_targets.conf write). hostctl cleanservedeploy promotes the .new + guard respawns; health = TCP :8102.
181 if md_slice_eq(nm, off, len, "cleanserve" as *u8, 0, 10) == 1 {
182 kindb[0]=0; md_copy_slice_z(srcbuf, "nx_clean_serve_daemon.elf.new" as *u8, 0, 29, 512); md_copy_slice_z(subbuf, "cleanservedeploy" as *u8, 0, 16, 64); md_copy_slice_z(urlbuf, "port:8102" as *u8, 0, 9, 256); md_copy_slice_z(rbbuf, "cleanserverollback" as *u8, 0, 18, 64); return 1 }
183 // DOCPORTAL admin daemon (:18456, /search + /doc + /api) -- first-class builtin so the SEARCH daemon deploys
184 // purely over the API (no more manual .sov.elf.new swap). hostctl docportaldeploy promotes the .sov.elf.new the
185 // build stages + guard respawns; health = local TCP :18456; rollback = docportalrollback (.prev -> live).
186 if md_slice_eq(nm, off, len, "docportal" as *u8, 0, 9) == 1 {
187 kindb[0]=0; md_copy_slice_z(srcbuf, "nx_docportal_admin_daemon.sov.elf.new" as *u8, 0, 37, 512); md_copy_slice_z(subbuf, "docportaldeploy" as *u8, 0, 15, 64); md_copy_slice_z(urlbuf, "port:18456" as *u8, 0, 10, 256); md_copy_slice_z(rbbuf, "docportalrollback" as *u8, 0, 17, 64); return 1 }
188 return 0
189}
190func md_resolve_target(cfgpath: *u8, nm: *u8, off: i64, len: i64, kindb: *i64, srcbuf: *u8, subbuf: *u8, urlbuf: *u8, rbbuf: *u8) -> i64 {
191 let szp: *i64 = sys_mmap(16) as *i64
192 var buf: *u8 = md_read_file(cfgpath, szp)
193 // primary path (knowledge/hosting/, the root-owned data plane) ABSENT -> fall back to the operator-writable
194 // bootstrap conf in the daemon cwd (nishihost/deploy_targets.conf). knowledge/ is root-owned (the root mgmt
195 // daemon created it), so the elderwesto bootstrap that REGISTERS deploy targets can only write the cwd -- this
196 // fallback is what lets a new target (e.g. relate) be registered WITHOUT root. Primary still WINS when present.
197 if (buf as i64) == 0 { buf = md_read_file("deploy_targets.conf" as *u8, szp) }
198 // both configs ABSENT -> still honor the compiled-in bootstrap targets, else the whole deploy plane is dead
199 // off-LAN when the NAS lacks the files (the live-400 that caught this).
200 if (buf as i64) == 0 { return md_builtin_target(nm, off, len, kindb, srcbuf, subbuf, urlbuf, rbbuf) }
201 let n: i64 = szp[0]
202 let offs: *i64 = sys_mmap(64) as *i64
203 let lens: *i64 = sys_mmap(64) as *i64
204 var cur: i64 = 0
205 while cur < n {
206 let le: i64 = md_eol(buf, n, cur)
207 var isc: i64 = 0
208 if le > cur { if (buf[cur] as i64) == 35 { isc = 1 } }
209 if isc == 0 {
210 let nf: i64 = md_split(buf, cur, le, offs, lens, 8)
211 if nf >= 5 {
212 if md_slice_eq(buf, offs[0], lens[0], nm, off, len) == 1 {
213 kindb[0] = md_slice_atoi(buf, offs[1], lens[1])
214 md_copy_slice_z(srcbuf, buf, offs[2], lens[2], 512)
215 md_copy_slice_z(subbuf, buf, offs[3], lens[3], 64)
216 md_copy_slice_z(urlbuf, buf, offs[4], lens[4], 256)
217 // OPTIONAL 6th field = per-target rollback sub (generalized deploy: a torrent target must
218 // roll back the TORRENT binary, not sites.elf). Absent (5-field legacy rows) -> "rollback".
219 if nf >= 6 { md_copy_slice_z(rbbuf, buf, offs[5], lens[5], 64) } else { md_copy_slice_z(rbbuf, "rollback" as *u8, 0, 8, 64) }
220 return 1
221 }
222 }
223 }
224 cur = le + 1
225 }
226 // not in the file -> try the compiled-in bootstrap targets (off-LAN self-enable). Fail-closed if neither.
227 return md_builtin_target(nm, off, len, kindb, srcbuf, subbuf, urlbuf, rbbuf)
228}
229
230// FAIL-CLOSED allowlist of artifact names that /api/upload may STAGE (write <name>.upload -> <name>.new). These
231// are the deployable binaries the on-NAS supervisor promotes from *.new (HC_*_NEW in nx_hostctl). 1 = allowed,
232// 0 = refused (unknown target -> 400, NOTHING written). Names checked as a slice (nm[off..off+len)) so the caller
233// can hand a query-string slice without copying. Data lives HERE (the DATA ring), not buried in the transport layer.
234// NOTE the deliberate absence of directory separators in every entry -- an upload target is a BARE basename, so a
235// caller can never traverse ('/' or '..' would fail every md_slice_eq below), which keeps the staging write pinned
236// to the mgmt daemon's cwd by construction (defense-in-depth over the allowlist itself).
237func md_upload_target_ok(nm: *u8, off: i64, len: i64) -> i64 {
238 if md_slice_eq(nm, off, len, "nx_mgmt_api.elf" as *u8, 0, 15) == 1 { return 1 }
239 if md_slice_eq(nm, off, len, "sites.elf" as *u8, 0, 9) == 1 { return 1 }
240 if md_slice_eq(nm, off, len, "nx_gallery_serve.elf" as *u8, 0, 20) == 1 { return 1 }
241 if md_slice_eq(nm, off, len, "nx_gallery_gateway.elf" as *u8, 0, 22) == 1 { return 1 }
242 if md_slice_eq(nm, off, len, "nx_docportal_admin_daemon.elf" as *u8, 0, 29) == 1 { return 1 }
243 if md_slice_eq(nm, off, len, "nx_hostctl" as *u8, 0, 10) == 1 { return 1 }
244 if md_slice_eq(nm, off, len, "nx_wiki_gw.elf" as *u8, 0, 14) == 1 { return 1 }
245 if md_slice_eq(nm, off, len, "nx_hub_gw.elf" as *u8, 0, 13) == 1 { return 1 }
246 if md_slice_eq(nm, off, len, "nx_torrent_gw.elf" as *u8, 0, 17) == 1 { return 1 }
247 // P1 off-LAN parity: the torrent STACK binaries (deployed cross-dir into /volume1/ai/torrent/ by the
248 // torrentdeploy hostctl sub). Staged as <name>.new in nishihost cwd like every other target.
249 if md_slice_eq(nm, off, len, "nx_torrent_daemon.sov.elf" as *u8, 0, 25) == 1 { return 1 }
250 if md_slice_eq(nm, off, len, "nx_torrent_seedeval.elf" as *u8, 0, 23) == 1 { return 1 }
251 // build-over-API: the tree-pack primitive elf + the source-tree blob (unpacked by /api/unpack via nx_treepack).
252 if md_slice_eq(nm, off, len, "nx_treepack.elf" as *u8, 0, 15) == 1 { return 1 }
253 if md_slice_eq(nm, off, len, "buildsrc.pack" as *u8, 0, 13) == 1 { return 1 }
254 if md_slice_eq(nm, off, len, "buildknow.pack" as *u8, 0, 14) == 1 { return 1 }
255 // /api/compare server-side regen: the hub generator elf (exec'd by md_cmp_regen; updatable over the API).
256 if md_slice_eq(nm, off, len, "nx_swcompare_hub.elf" as *u8, 0, 20) == 1 { return 1 }
257 // /api/compare/publish staging slot: page bytes arrive chunked here, then publish pins them by sha256.
258 if md_slice_eq(nm, off, len, "compare.page" as *u8, 0, 12) == 1 { return 1 }
259 // the Relationship OS daemon (binds loopback :8027; conf row `relate` promotes it once hostctl ships relatedeploy).
260 if md_slice_eq(nm, off, len, "nx_relate_daemon.elf" as *u8, 0, 20) == 1 { return 1 }
261 // the site-visuals editor (loopback :18466; cut over API-pure via /api/restart service=siteedit which
262 // promotes the staged .new -> the FULL editor deploy loop is upload+restart, zero ssh).
263 if md_slice_eq(nm, off, len, "nx_siteedit_daemon.elf" as *u8, 0, 22) == 1 { return 1 }
264 // seq1433 HALF-WIRED DEPLOY LOOP FIXED: md_direct_restart_ok mapped service=toolsapi -> nx_tools_api_serve.elf
265 // and hc_restart_ok/hc_guard_tapi allowed+respawned it, but there was NO upload row -- so the staging slot
266 // nx_tools_api_serve.elf.new could never be written over the API and /api/restart toolsapi could only ever
267 // re-promote a STALE artifact. A restart verb without a staging slot is not a deploy loop. This is the daemon
268 // that gates EVERY agent capability = the one binary the ecosystem could not update API-first (cf. galxgw seq1049).
269 if md_slice_eq(nm, off, len, "nx_tools_api_serve.elf" as *u8, 0, 22) == 1 { return 1 }
270 // the Nishi Pulse survey/insights daemon (:8031, cron-reconciled; /api/restart service=survey promotes
271 // the staged .new -> the survey deploy loop is upload+restart, zero ssh/scp).
272 if md_slice_eq(nm, off, len, "nx_survey_daemon.elf" as *u8, 0, 20) == 1 { return 1 }
273 // the ETHICAL CLEAN-SERVE daemon (:8102, /clean -- neutralize attacks + PRESERVE safe ads + safety receipt;
274 // SSRF-guarded public fetch proxy). Deploy loop = /api/upload + /api/deploy target=cleanserve (hostctl supervise).
275 if md_slice_eq(nm, off, len, "nx_clean_serve_daemon.elf" as *u8, 0, 25) == 1 { return 1 }
276 // the Nishi Office daemon (:8030, cron-reconciled) + its client JS. Deploy loop = /api/upload + /api/restart
277 // service=office (daemon: promote .new + kill -> nx_office_reconcile respawns) / officejs (JS: promote only,
278 // the daemon reads office_app.js per-request). Zero ssh -- matches the survey pattern.
279 if md_slice_eq(nm, off, len, "nx_office_daemon.elf" as *u8, 0, 20) == 1 { return 1 }
280 if md_slice_eq(nm, off, len, "office_app.js" as *u8, 0, 13) == 1 { return 1 }
281 // THE BUILD TOOLCHAIN ITSELF (seq891/903). Staged as <name>.new in nishihost cwd like every other
282 // target, then promoted into buildroot/_offc by /api/promote_toolchain -- which validates the ELF,
283 // banks .prev, chmod +x, CANARY-COMPILES and auto-rolls-back. Uploading merely STAGES; it can never
284 // touch the live compiler, so these rows are safe on their own. Closes the gap where the ecosystem
285 // could deploy every service over its own API but not the compiler that builds them.
286 if md_slice_eq(nm, off, len, "nx_cc_sovereign.elf" as *u8, 0, 19) == 1 { return 1 }
287 if md_slice_eq(nm, off, len, "nxasm_x86_main.elf" as *u8, 0, 18) == 1 { return 1 }
288 if md_slice_eq(nm, off, len, "nx_sov_build_run.elf" as *u8, 0, 20) == 1 { return 1 }
289 return 0
290}
291
292// ---- /api/unpack: resolve a fail-closed unpack destination (dest-key -> staged .pack + abs NAS dir) ----------
293// NEVER-BRICK (#26): only allowlisted dest keys resolve; an unknown key -> 400, nothing written. Each key maps to
294// the STAGED pack (<key>.pack.new, from /api/upload) + a FIXED abs dir under nishihost (nx_treepack writes only
295// under it). Extend by adding a row. `buildsrc` = the runtime source tree for build-over-API.
296func md_unpack_resolve(nm: *u8, off: i64, len: i64, packbuf: *u8, destbuf: *u8) -> i64 {
297 if md_slice_eq(nm, off, len, "buildsrc" as *u8, 0, 8) == 1 {
298 md_copy_slice_z(packbuf, "buildsrc.pack.new" as *u8, 0, 17, 128)
299 md_copy_slice_z(destbuf, "/volume1/homes/elderwesto/nishihost/buildroot/runtime" as *u8, 0, 53, 256)
300 return 1
301 }
302 // `buildknow` = buildroot/knowledge DATA ring (2026-08-05, debt 1785937893): compare .q/.axes corpus banks
303 // and other knowledge data the buildroot-CWD generators (gapmap frontier) read; same staged-pack discipline.
304 if md_slice_eq(nm, off, len, "buildknow" as *u8, 0, 9) == 1 {
305 md_copy_slice_z(packbuf, "buildknow.pack.new" as *u8, 0, 18, 128)
306 md_copy_slice_z(destbuf, "/volume1/homes/elderwesto/nishihost/buildroot/knowledge" as *u8, 0, 55, 256)
307 return 1
308 }
309 return 0
310}
311// fork+exec the on-NAS nx_treepack (unpack mode) with (packpath, destpath); capture stdout -> outpath; exit code.
312func md_exec_treepack(packpath: *u8, destpath: *u8, outpath: *u8) -> i64 {
313 let helf: *u8 = "/volume1/homes/elderwesto/nishihost/nx_treepack.elf" as *u8
314 let args: *i64 = sys_mmap(32) as *i64
315 args[0] = "unpack" as *u8 as i64
316 args[1] = packpath as i64
317 args[2] = destpath as i64
318 return dep_run_capture(helf, args, 3, outpath)
319}
320
321// ---- CONTENT-PUBLISH namespace (publish-from-anywhere for STATIC site files, 2026-07-06) ------------------
322// A content target is `sites/nishifamily/synth/<basename>`: FIXED directory prefix (extend = add a prefix row
323// here, data-ring) + strict basename charset [a-z0-9_.-] (first char alphanumeric, no ".." run, bounded) +
324// extension in {.html, .png, .stl}. '/' is impossible inside the basename by charset, and the prefix is fixed,
325// so path traversal is impossible BY CONSTRUCTION. Services (.elf) stay on md_upload_target_ok + /api/deploy
326// (health-checked promotion); this namespace is static files promoted by /api/promote_content (atomic
327// .prev-backed swap -- no health-check needed, and it can never touch a binary or leave the site down).
328func md_content_ext_ok(nm: *u8, off: i64, len: i64) -> i64 {
329 if len > 5 {
330 var m: i64 = 1
331 let e1: *u8 = ".html" as *u8
332 var i: i64 = 0
333 while i < 5 { if (nm[off + len - 5 + i] as i64) != (e1[i] as i64) { m = 0; i = 5 } else { i = i + 1 } }
334 if m == 1 { return 1 }
335 }
336 if len > 4 {
337 var m2: i64 = 1
338 let e2: *u8 = ".png" as *u8
339 var j: i64 = 0
340 while j < 4 { if (nm[off + len - 4 + j] as i64) != (e2[j] as i64) { m2 = 0; j = 4 } else { j = j + 1 } }
341 if m2 == 1 { return 1 }
342 var m3: i64 = 1
343 let e3: *u8 = ".stl" as *u8
344 var k: i64 = 0
345 while k < 4 { if (nm[off + len - 4 + k] as i64) != (e3[k] as i64) { m3 = 0; k = 4 } else { k = k + 1 } }
346 if m3 == 1 { return 1 }
347 // whole-site static set (site-factory publish): the self-emitted sitemap.xml + robots.txt
348 var m4: i64 = 1
349 let e4: *u8 = ".xml" as *u8
350 var k4: i64 = 0
351 while k4 < 4 { if (nm[off + len - 4 + k4] as i64) != (e4[k4] as i64) { m4 = 0; k4 = 4 } else { k4 = k4 + 1 } }
352 if m4 == 1 { return 1 }
353 var m5: i64 = 1
354 let e5: *u8 = ".txt" as *u8
355 var k5: i64 = 0
356 while k5 < 4 { if (nm[off + len - 4 + k5] as i64) != (e5[k5] as i64) { m5 = 0; k5 = 4 } else { k5 = k5 + 1 } }
357 if m5 == 1 { return 1 }
358 }
359 // the sovereign video-client set (2026-07-11): app.v2.js + nx_video_client.wasm ride /api/upload ->
360 // /api/promote_content like every other static file (retires the nx_aw_send ssh push). Same trust rank
361 // as .html (which can carry <script> anyway); binaries (.elf) stay OUT of this namespace by construction.
362 if len > 3 {
363 var m6: i64 = 1
364 let e6: *u8 = ".js" as *u8
365 var k6: i64 = 0
366 while k6 < 3 { if (nm[off + len - 3 + k6] as i64) != (e6[k6] as i64) { m6 = 0; k6 = 3 } else { k6 = k6 + 1 } }
367 if m6 == 1 { return 1 }
368 }
369 if len > 5 {
370 var m7: i64 = 1
371 let e7: *u8 = ".wasm" as *u8
372 var k7: i64 = 0
373 while k7 < 5 { if (nm[off + len - 5 + k7] as i64) != (e7[k7] as i64) { m7 = 0; k7 = 5 } else { k7 = k7 + 1 } }
374 if m7 == 1 { return 1 }
375 }
376 // the EVIDENCE workstream (2026-07-16): every published evidence run carries api.json machine detail
377 // beside its index.html (dashboards speak plain english; machines get JSON). Same trust rank as .txt.
378 if len > 5 {
379 var m8: i64 = 1
380 let e8: *u8 = ".json" as *u8
381 var k8: i64 = 0
382 while k8 < 5 { if (nm[off + len - 5 + k8] as i64) != (e8[k8] as i64) { m8 = 0; k8 = 5 } else { k8 = k8 + 1 } }
383 if m8 == 1 { return 1 }
384 }
385 return 0
386}
387// prefix TABLE (the data ring this namespace was designed to grow by): returns the matched prefix length,
388// or -1. Each row is a FIXED site subdirectory; extend = add a row.
389func md_content_pfx(nm: *u8, off: i64, len: i64) -> i64 {
390 let p1: *u8 = "sites/nishifamily/synth/" as *u8
391 let l1: i64 = 24
392 if len > l1 {
393 var i: i64 = 0
394 var m: i64 = 1
395 while i < l1 { if (nm[off + i] as i64) != (p1[i] as i64) { m = 0; i = l1 } else { i = i + 1 } }
396 if m == 1 { return l1 }
397 }
398 let p2: *u8 = "sites/nishifamily/swgpu/" as *u8
399 let l2: i64 = 24
400 if len > l2 {
401 var i2: i64 = 0
402 var m2: i64 = 1
403 while i2 < l2 { if (nm[off + i2] as i64) != (p2[i2] as i64) { m2 = 0; i2 = l2 } else { i2 = i2 + 1 } }
404 if m2 == 1 { return l2 }
405 }
406 // the SITE-FACTORY showcase (generated archetype gallery) -- publishes via upload+promote_content
407 let p3: *u8 = "sites/nishifamily/factory/" as *u8
408 let l3: i64 = 26
409 if len > l3 {
410 var i3: i64 = 0
411 var m3: i64 = 1
412 while i3 < l3 { if (nm[off + i3] as i64) != (p3[i3] as i64) { m3 = 0; i3 = l3 } else { i3 = i3 + 1 } }
413 if m3 == 1 { return l3 }
414 }
415 // the SOVEREIGN-INFINIGEN showcases (/world, /gsplat) -- retires the flaky ssh-cat push (2026-07-09):
416 // publish = /api/upload (chunked+staged) -> /api/promote_content (atomic .prev-backed swap)
417 let p4: *u8 = "sites/nishifamily/world/" as *u8
418 let l4: i64 = 24
419 if len > l4 {
420 var i4: i64 = 0
421 var m4: i64 = 1
422 while i4 < l4 { if (nm[off + i4] as i64) != (p4[i4] as i64) { m4 = 0; i4 = l4 } else { i4 = i4 + 1 } }
423 if m4 == 1 { return l4 }
424 }
425 let p5: *u8 = "sites/nishifamily/gsplat/" as *u8
426 let l5: i64 = 25
427 if len > l5 {
428 var i5: i64 = 0
429 var m5: i64 = 1
430 while i5 < l5 { if (nm[off + i5] as i64) != (p5[i5] as i64) { m5 = 0; i5 = l5 } else { i5 = i5 + 1 } }
431 if m5 == 1 { return l5 }
432 }
433 // WHOLESALE-emitted multi-page sites (site-factory R-SITESHAPE): subdir paths allowed under this
434 // prefix via the guarded '/' rule in md_content_target_ok (never doubled, ".." runs still refused).
435 let p6: *u8 = "sites/nishifamily/wholesale/" as *u8
436 let l6: i64 = 28
437 if len > l6 {
438 var i6: i64 = 0
439 var m6: i64 = 1
440 while i6 < l6 { if (nm[off + i6] as i64) != (p6[i6] as i64) { m6 = 0; i6 = l6 } else { i6 = i6 + 1 } }
441 if m6 == 1 { return l6 }
442 }
443 // the public generate-UI over nx_gen (R10 of the Infinigen ladder)
444 let p7: *u8 = "sites/nishifamily/generate/" as *u8
445 let l7: i64 = 27
446 if len > l7 {
447 var i7: i64 = 0
448 var m7: i64 = 1
449 while i7 < l7 { if (nm[off + i7] as i64) != (p7[i7] as i64) { m7 = 0; i7 = l7 } else { i7 = i7 + 1 } }
450 if m7 == 1 { return l7 }
451 }
452 // the sovereign VIDEO CODEC client set (2026-07-11): index.html + app.v2.js + nx_video_client.wasm +
453 // ver.txt. Retires the last ssh (nx_aw_send) in the codec ship loop -- deploy8XX becomes /api/upload ->
454 // /api/promote_content, and the ship gate verifies the :8443 sovereign edge.
455 let p8: *u8 = "sites/nishifamily/video/" as *u8
456 let l8: i64 = 24
457 if len > l8 {
458 var i8: i64 = 0
459 var m8: i64 = 1
460 while i8 < l8 { if (nm[off + i8] as i64) != (p8[i8] as i64) { m8 = 0; i8 = l8 } else { i8 = i8 + 1 } }
461 if m8 == 1 { return l8 }
462 }
463 // the EVIDENCE workstream namespace (2026-07-16, operator: "publish evidence consistent workstream"):
464 // /evidence/<run>/ = nx_evidence_pack output (index.html + api.json + screenshots/recordings),
465 // published via the proven upload->promote_content lane (nx_content_ship ship.manifest). Subdir runs
466 // ride the same guarded '/' rule as wholesale/.
467 let p9: *u8 = "sites/nishifamily/evidence/" as *u8
468 let l9: i64 = 27
469 if len > l9 {
470 var i9: i64 = 0
471 var m9: i64 = 1
472 while i9 < l9 { if (nm[off + i9] as i64) != (p9[i9] as i64) { m9 = 0; i9 = l9 } else { i9 = i9 + 1 } }
473 if m9 == 1 { return l9 }
474 }
475 // the EXPERIENTIAL census page (2026-07-16): EMITTED by nx_s21_census (sync-by-construction) and
476 // republished through this lane on every census re-run -- the page can never drift from disk truth.
477 let p10: *u8 = "sites/nishifamily/experiential/" as *u8
478 let l10: i64 = 31
479 if len > l10 {
480 var i10: i64 = 0
481 var m10: i64 = 1
482 while i10 < l10 { if (nm[off + i10] as i64) != (p10[i10] as i64) { m10 = 0; i10 = l10 } else { i10 = i10 + 1 } }
483 if m10 == 1 { return l10 }
484 }
485 // the COMPARE hub artifacts (2026-08-05, debt 1785937233): /compare api.json + index.html + openapi.json
486 // are laptop-generated (registry is laptop-owned by design) and ship through the proven
487 // upload -> promote_content lane; per-domain spoke pages stay NAS-regen-owned (nx_compare_regen).
488 // This row closes the hub-vs-spoke drift class: the hub gets a DOOR instead of a frozen snapshot.
489 let p11: *u8 = "sites/nishifamily/compare/" as *u8
490 let l11: i64 = 26
491 if len > l11 {
492 var i11: i64 = 0
493 var m11: i64 = 1
494 while i11 < l11 { if (nm[off + i11] as i64) != (p11[i11] as i64) { m11 = 0; i11 = l11 } else { i11 = i11 + 1 } }
495 if m11 == 1 { return l11 }
496 }
497 return 0 - 1
498}
499func md_content_target_ok(nm: *u8, off: i64, len: i64) -> i64 {
500 let pl: i64 = md_content_pfx(nm, off, len)
501 if pl < 0 { return 0 }
502 if len <= pl + 4 { return 0 } // needs prefix + at least an "a.png"-sized basename
503 if len > pl + 64 { return 0 } // bounded basename
504 let c0: i64 = nm[off + pl] as i64 // first basename char: alphanumeric only (blocks ".x" "-x" "..")
505 var ok0: i64 = 0
506 if c0 >= 97 { if c0 <= 122 { ok0 = 1 } }
507 if c0 >= 48 { if c0 <= 57 { ok0 = 1 } }
508 if ok0 == 0 { return 0 }
509 var j: i64 = pl
510 var prevdot: i64 = 0
511 var prevslash: i64 = 0
512 while j < len {
513 let c: i64 = nm[off + j] as i64
514 var okc: i64 = 0
515 if c >= 97 { if c <= 122 { okc = 1 } }
516 if c >= 48 { if c <= 57 { okc = 1 } }
517 if c == 95 { okc = 1 }
518 if c == 45 { okc = 1 }
519 // subdir separator for multi-page sites: never doubled, never after a dot (with the ".."-run
520 // refusal below and the pinned prefix, traversal stays impossible by construction).
521 if c == 47 {
522 if prevslash == 1 { return 0 }
523 if prevdot == 1 { return 0 }
524 okc = 1
525 prevslash = 1
526 } else { prevslash = 0 }
527 if c == 46 {
528 if prevdot == 1 { return 0 } // ".." run -> refuse
529 okc = 1
530 prevdot = 1
531 } else { prevdot = 0 }
532 if okc == 0 { return 0 }
533 j = j + 1
534 }
535 return md_content_ext_ok(nm, off, len)
536}
537
538// ---- COMPARE namespace (Nishi Compare registry SSOT + server-side hub regen, 2026-07-09) --------------------
539// The CONCURRENT-WORK coordination plane for /compare: many sessions publish comparisons, so the shared registry
540// + hub are mutated through THIS one serialized daemon instead of racing raw file writes. The unit of mutation is
541// the COMPARISON RECORD keyed by its /compare/<domain> href segment: different-domain upserts are commutative
542// (merge, no clobber possible); same-domain upserts replace, with the previous line preserved in registry.hist
543// (additive-only). After a mutation the hub index.html + api.json are regenerated SERVER-SIDE from the SSOT by the
544// on-NAS nx_swcompare_hub.elf, so the published surface can never reflect a session's stale partial registry.
545// Installs are sanity-gated + .prev-backed atomic renames (never-brick: a failed regen leaves live files untouched).
546
547// extract the /compare/<domain> merge key from a registry line (field 3 of title|kind|href|radar|stat).
548// Returns domain length copied into domb (NUL-terminated), or 0 if the line/href is malformed. Charset [a-z0-9_-].
549func md_cmp_domain_of(src: *u8, off: i64, len: i64, domb: *u8, cap: i64) -> i64 {
550 var p: i64 = 0
551 var f: i64 = 0
552 while p < len {
553 if (src[off + p] as i64) == 124 { f = f + 1; if f == 2 { p = p + 1; break } }
554 p = p + 1
555 }
556 if f != 2 { return 0 }
557 let pfx: *u8 = "/compare/" as *u8
558 var k: i64 = 0
559 while k < 9 {
560 if p + k >= len { return 0 }
561 if (src[off + p + k] as i64) != (pfx[k] as i64) { return 0 }
562 k = k + 1
563 }
564 var q: i64 = p + 9
565 var o: i64 = 0
566 while q < len {
567 let c: i64 = src[off + q] as i64
568 if c == 124 { break }
569 if c == 47 { break }
570 var okc: i64 = 0
571 if c >= 97 { if c <= 122 { okc = 1 } }
572 if c >= 48 { if c <= 57 { okc = 1 } }
573 if c == 95 { okc = 1 }
574 if c == 45 { okc = 1 }
575 if okc == 0 { return 0 }
576 if o < cap - 1 { domb[o] = src[off + q]; o = o + 1 }
577 q = q + 1
578 }
579 domb[o] = 0 as u8
580 if o < 1 { return 0 }
581 return o
582}
583
584func md_cmp_ws(fd: i64, s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(fd, s, n); return 0 }
585func md_cmp_wn(fd: i64, v: i64) -> i64 {
586 var m: i64 = v
587 if m < 0 { md_cmp_ws(fd, "-" as *u8); m = 0 - m }
588 let t: *u8 = sys_mmap(24)
589 var k: i64 = 0
590 if m == 0 { t[0] = 48 as u8; k = 1 }
591 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
592 let o2: *u8 = sys_mmap(24)
593 var i: i64 = 0
594 while i < k { o2[i] = t[k - 1 - i]; i = i + 1 }
595 sys_write(fd, o2, k)
596 return 0
597}
598
599// merge ONE registry line into the SSOT by domain key: replace the matching entry (old line -> .hist) or append.
600// Atomic (write registry.tmp -> rename); additive history appended AFTER the rename lands. Returns the new entry
601// count, or -1 on write failure (registry untouched -- the tmp+rename never half-writes the live file).
602func md_cmp_upsert(line: *u8, ln: i64, domb: *u8, domn: i64, replacedb: *i64) -> i64 {
603 let szp: *i64 = sys_mmap(16) as *i64
604 let old: *u8 = md_read_file("knowledge/compare/registry" as *u8, szp)
605 let on: i64 = szp[0]
606 let nb: *u8 = sys_mmap(262144)
607 let d2: *u8 = sys_mmap(128)
608 let oldline: *u8 = sys_mmap(4096)
609 var oldn: i64 = 0
610 var o: i64 = 0
611 var entries: i64 = 0
612 var replaced: i64 = 0
613 if (old as i64) != 0 {
614 var i: i64 = 0
615 while i < on {
616 let le: i64 = md_eol(old, on, i)
617 var wrote: i64 = 0
618 if le > i {
619 if (old[i] as i64) != 35 {
620 let dl2: i64 = md_cmp_domain_of(old, i, le - i, d2, 120)
621 if dl2 > 0 {
622 entries = entries + 1
623 if md_slice_eq(d2, 0, dl2, domb, 0, domn) == 1 {
624 replaced = 1
625 wrote = 1
626 oldn = 0
627 var c: i64 = 0
628 while c < (le - i) { if c < 4090 { oldline[c] = old[i + c]; oldn = c + 1 } c = c + 1 }
629 var w2: i64 = 0
630 while w2 < ln { nb[o] = line[w2]; o = o + 1; w2 = w2 + 1 }
631 nb[o] = 10 as u8
632 o = o + 1
633 }
634 }
635 }
636 }
637 if wrote == 0 {
638 var c2: i64 = i
639 while c2 < le { nb[o] = old[c2]; o = o + 1; c2 = c2 + 1 }
640 nb[o] = 10 as u8
641 o = o + 1
642 }
643 i = le + 1
644 }
645 }
646 if replaced == 0 {
647 var w3: i64 = 0
648 while w3 < ln { nb[o] = line[w3]; o = o + 1; w3 = w3 + 1 }
649 nb[o] = 10 as u8
650 o = o + 1
651 entries = entries + 1
652 }
653 let fd: i64 = sys_openat_wr("knowledge/compare/registry.tmp" as *u8, 0x1a4)
654 if fd < 0 { replacedb[0] = replaced; return 0 - 1 }
655 sys_write(fd, nb, o)
656 sys_close(fd)
657 if sys_renameat("knowledge/compare/registry.tmp" as *u8, "knowledge/compare/registry" as *u8) != 0 {
658 replacedb[0] = replaced
659 return 0 - 1
660 }
661 let hf: i64 = sys_openat_append("knowledge/compare/registry.hist" as *u8, 0x1a4)
662 if hf >= 0 {
663 md_cmp_ws(hf, "ts=" as *u8)
664 md_cmp_wn(hf, sys_now_realtime_sec())
665 md_cmp_ws(hf, " op=upsert domain=" as *u8)
666 var hd: i64 = 0
667 while hd < domn { sys_write(hf, ((domb as i64) + hd) as *u8, 1); hd = hd + 1 }
668 md_cmp_ws(hf, " replaced=" as *u8)
669 md_cmp_wn(hf, replaced)
670 md_cmp_ws(hf, "\n" as *u8)
671 if replaced == 1 { if oldn > 0 {
672 md_cmp_ws(hf, " prev: " as *u8)
673 sys_write(hf, oldline, oldn)
674 md_cmp_ws(hf, "\n" as *u8)
675 } }
676 sys_close(hf)
677 }
678 replacedb[0] = replaced
679 return entries
680}
681
682// install a generator-captured output file as a live docroot file: sanity (size + first byte) -> write tmp ->
683// back up live -> rename tmp over live; on failure the previous live file is restored (mirror of promote_content).
684func md_cmp_install(srcp: *u8, tmpp: *u8, prevp: *u8, livep: *u8, firstc: i64) -> i64 {
685 let szp: *i64 = sys_mmap(16) as *i64
686 let b: *u8 = md_read_file(srcp, szp)
687 let n: i64 = szp[0]
688 if (b as i64) == 0 { return 0 }
689 if n < 200 { return 0 }
690 if (b[0] as i64) != firstc { return 0 }
691 let fd: i64 = sys_openat_wr(tmpp, 0x1a4)
692 if fd < 0 { return 0 }
693 sys_write(fd, b, n)
694 sys_close(fd)
695 var had: i64 = 0
696 let pf: i64 = sys_openat_rd(livep)
697 if pf >= 0 { sys_close(pf); had = 1 }
698 if had == 1 { if sys_renameat(livep, prevp) != 0 { return 0 } }
699 if sys_renameat(tmpp, livep) != 0 {
700 if had == 1 { sys_renameat(prevp, livep) }
701 return 0
702 }
703 return 1
704}
705
706// regenerate the /compare hub (index.html + api.json) from the registry SSOT via the on-NAS hub generator elf.
707// Fail-safe: generator output must pass sanity before install; a missing elf / bad output leaves live files alone.
708func md_cmp_regen() -> i64 {
709 let helf: *u8 = "/volume1/homes/elderwesto/nishihost/nx_swcompare_hub.elf" as *u8
710 // Capture the generator output to an ELDERWESTO-OWNED scratch dir (knowledge/compare/, CWD=nishihost), NOT
711 // world-writable /tmp. WHY (2026-07-14 root cause): a root-era mgmt run left /tmp/nx_ma_cmp_*.out root-owned;
712 // after the root->elderwesto guard migration this daemon could no longer OVERWRITE them -> dep_run_capture's
713 // sys_openat_wr failed (EACCES), it ran the generator WITHOUT redirect, and md_cmp_install re-installed the
714 // STALE file every regen (silent "OK", frozen hub). A path this daemon owns always truncates fresh -> correct
715 // install, or an empty capture that fails md_cmp_install's sanity gate -> honest REGEN-FAILED (never stale).
716 let a1: *i64 = sys_mmap(16) as *i64
717 a1[0] = "html" as *u8 as i64
718 dep_run_capture(helf, a1, 1, "knowledge/compare/.regen_html.out" as *u8)
719 let a2: *i64 = sys_mmap(16) as *i64
720 a2[0] = "json" as *u8 as i64
721 dep_run_capture(helf, a2, 1, "knowledge/compare/.regen_json.out" as *u8)
722 let ok1: i64 = md_cmp_install("knowledge/compare/.regen_html.out" as *u8, "sites/nishifamily/compare/index.html.tmp2" as *u8, "sites/nishifamily/compare/index.html.prev" as *u8, "sites/nishifamily/compare/index.html" as *u8, 60)
723 let ok2: i64 = md_cmp_install("knowledge/compare/.regen_json.out" as *u8, "sites/nishifamily/compare/api.json.tmp2" as *u8, "sites/nishifamily/compare/api.json.prev" as *u8, "sites/nishifamily/compare/api.json" as *u8, 123)
724 if ok1 == 1 { if ok2 == 1 { return 1 } }
725 return 0
726}
727
728// validate a bare compare DOMAIN atom: charset [a-z0-9_-], first char alphanumeric, len 1..60 -> copy NUL-terminated.
729// Path segments are built ONLY from this validated atom + fixed literals, so traversal is impossible by construction.
730func md_cmp_dom_ok(src: *u8, off: i64, len: i64, domb: *u8) -> i64 {
731 if len < 1 { return 0 }
732 if len > 60 { return 0 }
733 let c0: i64 = src[off] as i64
734 var ok0: i64 = 0
735 if c0 >= 97 { if c0 <= 122 { ok0 = 1 } }
736 if c0 >= 48 { if c0 <= 57 { ok0 = 1 } }
737 if ok0 == 0 { return 0 }
738 var i: i64 = 0
739 while i < len {
740 let c: i64 = src[off + i] as i64
741 var okc: i64 = 0
742 if c >= 97 { if c <= 122 { okc = 1 } }
743 if c >= 48 { if c <= 57 { okc = 1 } }
744 if c == 95 { okc = 1 }
745 if c == 45 { okc = 1 }
746 if okc == 0 { return 0 }
747 domb[i] = src[off + i]
748 i = i + 1
749 }
750 domb[len] = 0 as u8
751 return 1
752}
753func md_cmp_cat(d: *u8, o: i64, s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { d[o + i] = s[i]; i = i + 1 } d[o + i] = 0 as u8; return o + i }
754
755// publish the STAGED compare.page.new as the live artifact for (domain, kind). kind: 1=page 2=frontier 3=bench 4=api.
756// Server derives the FIXED docroot path from the validated domain atom + a kind enum (no caller-supplied paths at
757// all). Dirs are created as needed; install is sanity-gated + .prev-backed (md_cmp_install). kind=api ALSO refreshes
758// the hub data-link marker knowledge/compare/<domain>-api.json (tmp+rename). Returns 1 ok / 0 fail (live untouched).
759func md_cmp_publish(domb: *u8, kind: i64) -> i64 {
760 let base: *u8 = sys_mmap(512)
761 var o: i64 = md_cmp_cat(base, 0, "sites/nishifamily/compare/" as *u8)
762 o = md_cmp_cat(base, o, domb)
763 sys_mkdir(base, 0x1ed)
764 if kind == 2 { o = md_cmp_cat(base, o, "/frontier" as *u8); sys_mkdir(base, 0x1ed) }
765 if kind == 3 { o = md_cmp_cat(base, o, "/bench" as *u8); sys_mkdir(base, 0x1ed) }
766 let live: *u8 = sys_mmap(512)
767 var lo: i64 = md_cmp_cat(live, 0, base)
768 var fc: i64 = 60
769 if kind == 4 { lo = md_cmp_cat(live, lo, "/api.json" as *u8); fc = 123 } else { lo = md_cmp_cat(live, lo, "/index.html" as *u8) }
770 let tmpp: *u8 = sys_mmap(512)
771 var to: i64 = md_cmp_cat(tmpp, 0, live)
772 to = md_cmp_cat(tmpp, to, ".tmp2" as *u8)
773 let prevp: *u8 = sys_mmap(512)
774 var po: i64 = md_cmp_cat(prevp, 0, live)
775 po = md_cmp_cat(prevp, po, ".prev" as *u8)
776 let oki: i64 = md_cmp_install("compare.page.new" as *u8, tmpp, prevp, live, fc)
777 if oki != 1 { return 0 }
778 if kind == 4 {
779 let mk: *u8 = sys_mmap(512)
780 var mo: i64 = md_cmp_cat(mk, 0, "knowledge/compare/" as *u8)
781 mo = md_cmp_cat(mk, mo, domb)
782 mo = md_cmp_cat(mk, mo, "-api.json" as *u8)
783 let mt: *u8 = sys_mmap(512)
784 var mto: i64 = md_cmp_cat(mt, 0, mk)
785 mto = md_cmp_cat(mt, mto, ".tmp" as *u8)
786 let szp: *i64 = sys_mmap(16) as *i64
787 let b: *u8 = md_read_file("compare.page.new" as *u8, szp)
788 if (b as i64) != 0 { if szp[0] > 0 {
789 let fd: i64 = sys_openat_wr(mt, 0x1a4)
790 if fd >= 0 { sys_write(fd, b, szp[0]); sys_close(fd); sys_renameat(mt, mk) }
791 } }
792 }
793 return 1
794}
795
796// map an allowlisted service name -> the proven nx_aw_hostctl surgical-restart sub (fail-closed: unknown -> 0).
797func md_restart_sub(svc: *u8, off: i64, len: i64, subbuf: *u8) -> i64 {
798 if md_slice_eq(svc, off, len, "reader" as *u8, 0, 6) == 1 { md_copy_slice_z(subbuf, "kickreader" as *u8, 0, 10, 64); return 1 }
799 if md_slice_eq(svc, off, len, "torrent" as *u8, 0, 7) == 1 { md_copy_slice_z(subbuf, "kicktorrent" as *u8, 0, 11, 64); return 1 }
800 if md_slice_eq(svc, off, len, "torrentgw" as *u8, 0, 9) == 1 { md_copy_slice_z(subbuf, "kicktorrentgw" as *u8, 0, 13, 64); return 1 }
801 if md_slice_eq(svc, off, len, "docportal" as *u8, 0, 9) == 1 { md_copy_slice_z(subbuf, "kickdocportal" as *u8, 0, 13, 64); return 1 }
802 return 0
803}
804// DIRECT-restart allowlist (no hostctl sub needed): svc name -> the exact process cmdline needle. The mgmt
805// daemon (root, itself guard-supervised) kills by name; the hostctl supervise guard respawns the on-disk
806// binary <=15s -- so promote-a-staged-.new + /api/restart = the full API-pure editor deploy loop.
807func md_direct_restart_ok(svc: *u8, off: i64, len: i64, namebuf: *u8) -> i64 {
808 if md_slice_eq(svc, off, len, "siteedit" as *u8, 0, 8) == 1 { md_copy_slice_z(namebuf, "nx_siteedit_daemon.elf" as *u8, 0, 22, 64); return 1 }
809 // sites.elf = the EDGE. Restart (kill -> guard respawns) RE-READS proxy_routes.conf, so this doubles as
810 // the API-pure route reload AND an edge redeploy if a sites.elf.new is staged. "sites.elf" substring is
811 // unique to the edge (nx_sites_daemon_v2/nx_sites_reconciled don't contain it).
812 if md_slice_eq(svc, off, len, "sites" as *u8, 0, 5) == 1 { md_copy_slice_z(namebuf, "sites.elf" as *u8, 0, 9, 64); return 1 }
813 // Nishi Pulse survey daemon: kill -> the nx_survey_reconcile cron respawns the on-disk binary <=60s
814 // (not hostctl-guarded; the reconcile row IS its supervisor -- crash+reboot proven 2026-07-10).
815 if md_slice_eq(svc, off, len, "survey" as *u8, 0, 6) == 1 { md_copy_slice_z(namebuf, "nx_survey_daemon.elf" as *u8, 0, 20, 64); return 1 }
816 // Nishi Office daemon: promote nx_office_daemon.elf.new + kill -> nx_office_reconcile cron respawns the
817 // on-disk binary <=60s (SO_REUSEADDR = fast rebind; not hostctl-guarded, the reconcile IS its supervisor).
818 if md_slice_eq(svc, off, len, "office" as *u8, 0, 6) == 1 { md_copy_slice_z(namebuf, "nx_office_daemon.elf" as *u8, 0, 20, 64); return 1 }
819 // Nishi Office client JS: promote office_app.js.new -> live. kill-by-name matches NO process (it is a file,
820 // not a daemon) -> harmless; the office daemon reads office_app.js fresh on the next request.
821 if md_slice_eq(svc, off, len, "officejs" as *u8, 0, 8) == 1 { md_copy_slice_z(namebuf, "office_app.js" as *u8, 0, 13, 64); return 1 }
822 // the R0 agent-facing tools daemon (nx_tools_api_serve.elf :18096, hostctl guard-supervised): kill-by-name
823 // -> the supervise guard respawns the on-disk binary <=15s. Makes the tools/MCP plane API-pure-deployable
824 // (e.g. the fork-per-request concurrency upgrade): SMB/upload the .elf.new -> promote -> /api/restart toolsapi.
825 if md_slice_eq(svc, off, len, "toolsapi" as *u8, 0, 8) == 1 { md_copy_slice_z(namebuf, "nx_tools_api_serve.elf" as *u8, 0, 22, 64); return 1 }
826 // BitTorrent SEEDER :6881 (hostctl guard-supervised): kill-by-name -> the guard respawns the on-disk
827 // binary. THIS WAS THE LAST MISSING LINK IN THE SEEDER DEPLOY LOOP (2026-07-30): the fix could be built,
828 // gate-proven and PLACED by torrentdeploy, and then could not be made to RUN by any sanctioned route --
829 // `torrent` restarts the :8097 DAEMON, not the seeder, and /api/proc_kill needs a fresh M5 session token.
830 // ★NOTE THE SPLIT, IT IS DELIBERATE: md_promote_staged(dname) here would target
831 // nishihost/nx_torrent_seed.elf.new, but the LIVE seeder lives at /volume1/ai/torrent/nx_torrent_seed.elf
832 // and is promoted there by cmd_torrentdeploy. No such .new exists at the nishihost root, so the promote
833 // half cleanly NO-OPS and only the kill does the work. Promoting to the nishihost path instead would
834 // place a binary NOTHING EVER RUNS while reporting a successful restart -- the same name trap that
835 // `.sov.elf` vs `.elf` sets for the deploy half.
836 if md_slice_eq(svc, off, len, "seed" as *u8, 0, 4) == 1 { md_copy_slice_z(namebuf, "nx_torrent_seed.elf" as *u8, 0, 19, 64); return 1 }
837 return 0
838}
839// -- /api/route: validate + append a proxy_routes.conf row (data ring). Format: "<host> <prefix> <port> <mode>".
840func md_catn(d: *u8, o: i64, v: i64) -> i64 {
841 if v == 0 { d[o] = 48 as u8; return o + 1 }
842 var m: i64 = v
843 var oo: i64 = o
844 if m < 0 { d[oo] = 45 as u8; oo = oo + 1; m = 0 - m }
845 var nd: i64 = 1
846 var t: i64 = m
847 while t >= 10 { nd = nd + 1; t = t / 10 }
848 var i: i64 = nd - 1
849 while i >= 0 { d[oo + i] = (48 + (m % 10)) as u8; m = m / 10; i = i - 1 }
850 return oo + nd
851}
852// fail-closed validation of a route (null-terminated host/prefix/mode + numeric port).
853func md_route_valid(host: *u8, prefix: *u8, port: i64, mode: *u8) -> i64 {
854 let hl: i64 = md_len(host)
855 if hl < 3 { return 0 }
856 if hl > 64 { return 0 }
857 if (host[0] as i64) == 46 { return 0 }
858 if (host[0] as i64) == 45 { return 0 }
859 var hasdot: i64 = 0
860 var i: i64 = 0
861 while i < hl {
862 let c: i64 = host[i] as i64
863 var ok: i64 = 0
864 if c >= 97 { if c <= 122 { ok = 1 } }
865 if c >= 48 { if c <= 57 { ok = 1 } }
866 if c == 45 { ok = 1 }
867 if c == 46 { ok = 1; hasdot = 1; if i > 0 { if (host[i - 1] as i64) == 46 { return 0 } } }
868 if ok == 0 { return 0 }
869 i = i + 1
870 }
871 if hasdot == 0 { return 0 }
872 let pl: i64 = md_len(prefix)
873 if pl < 2 { return 0 }
874 if pl > 48 { return 0 }
875 if (prefix[0] as i64) != 47 { return 0 }
876 i = 1
877 while i < pl {
878 let c: i64 = prefix[i] as i64
879 var ok: i64 = 0
880 if c >= 97 { if c <= 122 { ok = 1 } }
881 if c >= 48 { if c <= 57 { ok = 1 } }
882 if c == 95 { ok = 1 }
883 if c == 45 { ok = 1 }
884 if c == 47 { ok = 1; if (prefix[i - 1] as i64) == 47 { return 0 } }
885 if c == 46 { ok = 1; if (prefix[i - 1] as i64) == 46 { return 0 } }
886 if ok == 0 { return 0 }
887 i = i + 1
888 }
889 if port < 1024 { return 0 }
890 if port > 65535 { return 0 }
891 var mok: i64 = 0
892 if md_cstr_eq(mode, "buffered" as *u8) == 1 { mok = 1 }
893 if md_cstr_eq(mode, "stream" as *u8) == 1 { mok = 1 }
894 if md_cstr_eq(mode, "gated" as *u8) == 1 { mok = 1 }
895 if mok == 0 { return 0 }
896 return 1
897}
898// atomically upsert the route row into knowledge/hosting/proxy_routes.conf: preserve every OTHER line, replace
899// any existing "<host> <prefix> ..." row, append the new one; tmp+rename. Returns 1 ok / 0 write-fail.
900func md_route_append(confp: *u8, host: *u8, prefix: *u8, port: i64, mode: *u8) -> i64 {
901 let szp: *i64 = sys_mmap(16) as *i64
902 szp[0] = 0
903 let old: *u8 = md_read_file(confp, szp)
904 let oldn: i64 = szp[0]
905 // NEVER-BRICK: refuse to write when the existing table is unreadable/empty. A transient read failure
906 // (fd exhaustion etc.) with old==0 would otherwise REPLACE the populated edge table with one row ->
907 // every proxied surface incl. /api itself lost = self-lockout. The live table always has rows; a
908 // genuinely fresh bootstrap is an ssh-once operation, not this API's job. Fail-closed.
909 if (old as i64) == 0 { return 0 }
910 if oldn == 0 { return 0 }
911 // build the dedup match key: "<host> <prefix> "
912 let mk: *u8 = sys_mmap(160)
913 var ko: i64 = 0
914 var a: i64 = 0
915 while host[a] != (0 as u8) { mk[ko] = host[a]; ko = ko + 1; a = a + 1 }
916 mk[ko] = 32 as u8; ko = ko + 1
917 a = 0
918 while prefix[a] != (0 as u8) { mk[ko] = prefix[a]; ko = ko + 1; a = a + 1 }
919 mk[ko] = 32 as u8; ko = ko + 1
920 mk[ko] = 0 as u8
921 let mkl: i64 = ko
922 let out: *u8 = sys_mmap(262144)
923 var o: i64 = 0
924 if (old as i64) != 0 {
925 var ls: i64 = 0
926 while ls < oldn {
927 var le: i64 = ls
928 var sc: i64 = 1
929 while sc == 1 { if le >= oldn { sc = 0 } else { if (old[le] as i64) == 10 { sc = 0 } else { le = le + 1 } } }
930 // does this line start with the match key?
931 var m: i64 = 1
932 if ls + mkl > le + 1 { m = 0 }
933 if m == 1 {
934 var j: i64 = 0
935 while j < mkl { if (old[ls + j] as i64) != (mk[j] as i64) { m = 0; j = mkl } else { j = j + 1 } }
936 }
937 if m == 0 {
938 var k: i64 = ls
939 while k <= le { if k < oldn { out[o] = old[k]; o = o + 1 } k = k + 1 }
940 }
941 ls = le + 1
942 }
943 }
944 // append the new row (ensure a trailing newline precedes if the file didn't end in one)
945 if o > 0 { if (out[o - 1] as i64) != 10 { out[o] = 10 as u8; o = o + 1 } }
946 a = 0
947 while host[a] != (0 as u8) { out[o] = host[a]; o = o + 1; a = a + 1 }
948 out[o] = 32 as u8; o = o + 1
949 a = 0
950 while prefix[a] != (0 as u8) { out[o] = prefix[a]; o = o + 1; a = a + 1 }
951 out[o] = 32 as u8; o = o + 1
952 o = md_catn(out, o, port)
953 out[o] = 32 as u8; o = o + 1
954 a = 0
955 while mode[a] != (0 as u8) { out[o] = mode[a]; o = o + 1; a = a + 1 }
956 out[o] = 10 as u8; o = o + 1
957 // NEVER-BRICK: bank the pre-edit table as confp+".prev" FIRST (recovery: cp .prev back), then
958 // atomic write: tmp = confp + ".tmp", rename over confp. Same idiom as binary deploys.
959 let prevp: *u8 = sys_mmap(512)
960 var pj: i64 = 0
961 while confp[pj] != (0 as u8) { prevp[pj] = confp[pj]; pj = pj + 1 }
962 prevp[pj] = 46 as u8; prevp[pj + 1] = 112 as u8; prevp[pj + 2] = 114 as u8; prevp[pj + 3] = 101 as u8; prevp[pj + 4] = 118 as u8; prevp[pj + 5] = 0 as u8
963 let pfd: i64 = sys_openat_wr(prevp, 0x1a4)
964 if pfd >= 0 { sys_write(pfd, old, oldn); sys_close(pfd) }
965 let tmpp: *u8 = sys_mmap(512)
966 var tj: i64 = 0
967 while confp[tj] != (0 as u8) { tmpp[tj] = confp[tj]; tj = tj + 1 }
968 tmpp[tj] = 46 as u8; tmpp[tj + 1] = 116 as u8; tmpp[tj + 2] = 109 as u8; tmpp[tj + 3] = 112 as u8; tmpp[tj + 4] = 0 as u8
969 let fd: i64 = sys_openat_wr(tmpp, 0x1a4)
970 if fd < 0 { return 0 }
971 sys_write(fd, out, o)
972 sys_close(fd)
973 sys_renameat(tmpp, confp)
974 return 1
975}
976// kill every process whose /proc/<pid>/cmdline CONTAINS needle (full-cmdline match -- the 15-char comm
977// truncation trap). Returns processes signalled. Mirrors the proven hostctl proc_kill_by_name.
978func md_pk_contains(hay: *u8, hn: i64, needle: *u8, nl: i64) -> i64 {
979 if nl == 0 { return 0 }
980 var i: i64 = 0
981 while i + nl <= hn {
982 var j: i64 = 0
983 var ok: i64 = 1
984 while j < nl { if (hay[i + j] as i64) != (needle[j] as i64) { ok = 0; j = nl } else { j = j + 1 } }
985 if ok == 1 { return 1 }
986 i = i + 1
987 }
988 return 0
989}
990func md_pk_atoi(s: *u8) -> i64 {
991 var v: i64 = 0
992 var i: i64 = 0
993 while s[i] != (0 as u8) { let c: i64 = s[i] as i64; if c >= 48 { if c <= 57 { v = v * 10 + (c - 48) } } i = i + 1 }
994 return v
995}
996// promote a staged <cwd>/<name>.new -> live <name> (.prev kept), chmod +x. Returns 1 if a .new existed
997// and was promoted, else 0 (restart still valid -- just reloads the same on-disk binary). cwd = nishihost.
998// IDEMPOTENCY for /api/tools/register: is <nm> already the first TAB-field of a line in tool_allowlist.conf?
999// Reads the (small) allowlist raw; matches a line that starts with "<nm>\t". Fail-open to 0 (absent) so a
1000// missing/unreadable allowlist doesn't block a first registration.
1001func md_allow_has_name(nm: *u8) -> i64 {
1002 let fd: i64 = sys_openat_rd("tool_allowlist.conf" as *u8)
1003 if fd < 0 { return 0 }
1004 let cap: i64 = 1 << 18
1005 let buf: *u8 = sys_mmap(cap)
1006 let n: i64 = sys_read(fd, buf, cap - 1)
1007 sys_close(fd)
1008 if n <= 0 { return 0 }
1009 buf[n] = 0 as u8
1010 let nl: i64 = md_len(nm)
1011 var i: i64 = 0
1012 while i + nl < n {
1013 var ls: i64 = 0
1014 if i == 0 { ls = 1 } else { if buf[i-1] == (10 as u8) { ls = 1 } }
1015 if ls == 1 {
1016 var m: i64 = 1
1017 var j: i64 = 0
1018 while j < nl { if buf[i+j] != nm[j] { m = 0; j = nl } else { j = j + 1 } }
1019 if m == 1 { if buf[i+nl] == (9 as u8) { return 1 } }
1020 }
1021 i = i + 1
1022 }
1023 return 0
1024}
1025
1026// seq1281 (RESTORED AGAIN 2026-07-30 -- 3rd backdate, see seq1439/nx_srcguard): read the existing pinned-
1027// args column (4th TAB field .. EOL) of tool <nm>'s allowlist row into dst. Returns copied length; 0 = no
1028// row / no args / unreadable. Lets register-update PRESERVE pinned args when args= is omitted -- an omitted
1029// field must never silently widen a pinned oracle into caller-controlled argv.
1030func md_allow_get_args(nm: *u8, dst: *u8, cap: i64) -> i64 {
1031 let fd: i64 = sys_openat_rd("tool_allowlist.conf" as *u8)
1032 if fd < 0 { return 0 }
1033 let bcap: i64 = 1 << 18
1034 let buf: *u8 = sys_mmap(bcap)
1035 let n: i64 = sys_read(fd, buf, bcap - 1)
1036 sys_close(fd)
1037 if n <= 0 { return 0 }
1038 buf[n] = 0 as u8
1039 let nl: i64 = md_len(nm)
1040 var i: i64 = 0
1041 while i + nl < n {
1042 var ls: i64 = 0
1043 if i == 0 { ls = 1 } else { if buf[i-1] == (10 as u8) { ls = 1 } }
1044 if ls == 1 {
1045 var m: i64 = 1
1046 var j: i64 = 0
1047 while j < nl { if buf[i+j] != nm[j] { m = 0; j = nl } else { j = j + 1 } }
1048 if m == 1 { if buf[i+nl] == (9 as u8) {
1049 var p: i64 = i + nl + 1
1050 var tabs: i64 = 0
1051 var argst: i64 = 0
1052 while p < n {
1053 let c: i64 = buf[p] as i64
1054 if c == 10 { p = n } else {
1055 if c == 9 { tabs = tabs + 1; if tabs == 2 { argst = p + 1; p = n } }
1056 if p < n { p = p + 1 }
1057 }
1058 }
1059 if argst == 0 { return 0 }
1060 var o: i64 = 0
1061 var q: i64 = argst
1062 while q < n {
1063 if buf[q] == (10 as u8) { q = n } else {
1064 if o < cap - 1 { dst[o] = buf[q]; o = o + 1 }
1065 q = q + 1
1066 }
1067 }
1068 dst[o] = 0 as u8
1069 return o
1070 } }
1071 }
1072 i = i + 1
1073 }
1074 return 0
1075}
1076
1077// atomically REPLACE the tool_allowlist.conf row for tool <nm> -- the register-update verb's mutation
1078// (eats the ssh-once row-repoint class: evidence_checkin/mvault/clock repoints). Preserves every OTHER
1079// line byte-exact, drops the existing "<nm>\t..." row(s), appends the replacement
1080// "<nm>\t<elfp>\tGREEN[\t<args>]" row; banks .prev FIRST then tmp+rename (md_route_append idiom).
1081// UPDATE CAN NEVER CREATE: refuses (0) when no row matches. NEVER-BRICK: refuses when the allowlist is
1082// unreadable/empty so a transient read failure cannot truncate the live tool table. 1 ok / 0 refused.
1083func md_allow_update_row(nm: *u8, elfp: *u8, argp: *u8, argn: i64) -> i64 {
1084 let szp: *i64 = sys_mmap(16) as *i64
1085 szp[0] = 0
1086 let old: *u8 = md_read_file("tool_allowlist.conf" as *u8, szp)
1087 let oldn: i64 = szp[0]
1088 if (old as i64) == 0 { return 0 }
1089 if oldn == 0 { return 0 }
1090 let nl: i64 = md_len(nm)
1091 let out: *u8 = sys_mmap(262144)
1092 var o: i64 = 0
1093 var found: i64 = 0
1094 var ls: i64 = 0
1095 while ls < oldn {
1096 var le: i64 = ls
1097 var sc: i64 = 1
1098 while sc == 1 { if le >= oldn { sc = 0 } else { if (old[le] as i64) == 10 { sc = 0 } else { le = le + 1 } } }
1099 var m: i64 = 0
1100 if ls + nl < le {
1101 if (old[ls + nl] as i64) == 9 {
1102 m = 1
1103 var j: i64 = 0
1104 while j < nl { if old[ls + j] != nm[j] { m = 0; j = nl } else { j = j + 1 } }
1105 }
1106 }
1107 if m == 1 { found = 1 } else {
1108 var k: i64 = ls
1109 while k <= le { if k < oldn { out[o] = old[k]; o = o + 1 } k = k + 1 }
1110 }
1111 ls = le + 1
1112 }
1113 if found == 0 { return 0 }
1114 if o > 0 { if (out[o - 1] as i64) != 10 { out[o] = 10 as u8; o = o + 1 } }
1115 var a: i64 = 0
1116 while nm[a] != (0 as u8) { out[o] = nm[a]; o = o + 1; a = a + 1 }
1117 out[o] = 9 as u8; o = o + 1
1118 a = 0
1119 while elfp[a] != (0 as u8) { out[o] = elfp[a]; o = o + 1; a = a + 1 }
1120 out[o] = 9 as u8; o = o + 1
1121 out[o] = 71 as u8; o = o + 1
1122 out[o] = 82 as u8; o = o + 1
1123 out[o] = 69 as u8; o = o + 1
1124 out[o] = 69 as u8; o = o + 1
1125 out[o] = 78 as u8; o = o + 1
1126 if argn > 0 {
1127 out[o] = 9 as u8; o = o + 1
1128 a = 0
1129 while a < argn { out[o] = argp[a]; o = o + 1; a = a + 1 }
1130 }
1131 out[o] = 10 as u8; o = o + 1
1132 let pfd: i64 = sys_openat_wr("tool_allowlist.conf.prev" as *u8, 0x1a4)
1133 if pfd >= 0 { sys_write(pfd, old, oldn); sys_close(pfd) }
1134 let fd: i64 = sys_openat_wr("tool_allowlist.conf.nxtmp" as *u8, 0x1a4)
1135 if fd < 0 { return 0 }
1136 sys_write(fd, out, o)
1137 sys_close(fd)
1138 sys_renameat("tool_allowlist.conf.nxtmp" as *u8, "tool_allowlist.conf" as *u8)
1139 return 1
1140}
1141
1142func md_streq(a: *u8, b: *u8) -> i64 {
1143 var i: i64 = 0
1144 while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 }
1145 if b[i] != (0 as u8) { return 0 }
1146 return 1
1147}
1148
1149// FAIL-CLOSED promote policy for POST /api/promote -- STRUCTURAL, still by-construction (F-210b eaten 07-18):
1150// (1) compiled-in DENY first: daemons + credential oracles (family substrings, so new members inherit the
1151// refusal). Daemons carry live connections -> the health-checked auto-rollback /api/deploy, NEVER a rename.
1152// The deny can never be overridden by any data plane or later rule.
1153// (2) the enumerated one-shot allows (back-compat fast path).
1154// (3) else STAGED-ARTIFACT rule: a name whose <name>.sov.elf.new exists as a real ELF is promotable. Staging is
1155// only reachable via the owner-gated /api/build//api/upload, so this widens promote to OWNER-only actions,
1156// never to a write cap -- the same stance as nx_fs_write denying the tool allowlist, without the
1157// add-a-name-recompile-mgmt treadmill for every new one-shot organ.
1158func md_contains(a: *u8, sub: *u8) -> i64 {
1159 var i: i64 = 0
1160 while a[i] != (0 as u8) {
1161 var j: i64 = 0
1162 var mism: i64 = 0
1163 var run: i64 = 1
1164 while run == 1 {
1165 if sub[j] == (0 as u8) { run = 0 } else {
1166 if a[i+j] == (0 as u8) { mism = 1; run = 0 } else {
1167 if a[i+j] != sub[j] { mism = 1; run = 0 } else { j = j + 1 }
1168 }
1169 }
1170 }
1171 if mism == 0 { return 1 }
1172 i = i + 1
1173 }
1174 return 0
1175}
1176// ---------- organ ROLE, declared (seq1492) ----------
1177//
1178// u2605THE DEFECT THIS REPLACES: promote classified by NAME SUBSTRING, so a
1179// one-shot worker called nx_torrent_get was refused as a "daemon" while
1180// /api/deploy refused it as an unknown target. **Two verbs disagreeing about an
1181// artefact's KIND leave it unshippable** -- and the only remaining way to update
1182// it was the raw scp/ssh path that seq1439 identified as the WORK-DESTROYER. A
1183// substring is not a role, exactly as a substring is not a hazard.
1184//
1185// Policy lives in DATA (rule 11): knowledge/status/organ_kind.conf, rows
1186// <name><TAB-or-SPACE>one-shot|oneshot|daemon|oracle|lib
1187// Returns 1 = one-shot (promotable), 2 = daemon/oracle (deploy lane), 0 = undeclared.
1188// Undeclared falls through to the historical name heuristics, so nothing regresses
1189// and the heuristic becomes the DEFAULT rather than the law.
1190//
1191// ★PERMANENT ROOT FIX 2026-07-31 (debt 1785453784, which /api/promote's own 400 text described but
1192// nobody had closed). TWO defects, and fixing only the first would have LOOKED right while still failing:
1193// 1. WRONG PATH. This opened the SINGULAR-less plural "knowledge/organ_kinds.conf", which was renamed to
1194// .RETIRED-seq1754-use-status-organ_kind. open() returned <0 -> return 0 -> EVERY organ fell through
1195// to the name heuristic, so NO declaration anywhere was readable and gate promotion was a coin flip.
1196// Now reads the surviving SSOT knowledge/status/organ_kind.conf. ONE source of truth, not two.
1197// 2. WRONG SEPARATOR. The surviving file is SPACE-separated (`nx_build_admit oneshot`) but this parser
1198// accepted ONLY a TAB (9), so a path-only fix would have found the file, parsed nothing, and still
1199// returned 0 -- a silent no-op that reads as success. Now takes the FIRST tab OR space.
1200// The value test below already tolerates both spellings: it checks only the leading "on", matching
1201// `oneshot` and `one-shot` alike. Verified against the real file before editing, not assumed.
1202func md_organ_kind(nm: *u8) -> i64 {
1203 let fd: i64 = sys_openat_rd("knowledge/status/organ_kind.conf" as *u8)
1204 if fd < 0 { return 0 }
1205 let buf: *u8 = sys_mmap(65536)
1206 let n: i64 = sys_read(fd, buf, 65536)
1207 sys_close(fd)
1208 if n <= 0 { return 0 }
1209 var nl: i64 = 0
1210 while nm[nl] != (0 as u8) { nl = nl + 1 }
1211 var ls: i64 = 0
1212 while ls < n {
1213 var le: i64 = ls
1214 var g: i64 = 1
1215 while g == 1 { if le >= n { g = 0 } else { if buf[le] == (10 as u8) { g = 0 } else { le = le + 1 } } }
1216 if le > ls { if buf[ls] != (35 as u8) {
1217 var sep: i64 = 0 - 1
1218 var q: i64 = ls
1219 while q < le {
1220 if sep < 0 {
1221 if buf[q] == (9 as u8) { sep = q }
1222 else { if buf[q] == (32 as u8) { sep = q } }
1223 }
1224 q = q + 1
1225 }
1226 if sep > ls {
1227 if sep - ls == nl {
1228 var same: i64 = 1
1229 var c: i64 = 0
1230 while c < nl { if buf[ls + c] != nm[c] { same = 0; c = nl } else { c = c + 1 } }
1231 if same == 1 {
1232 let vs: i64 = sep + 1
1233 if vs < le { if buf[vs] == (111 as u8) { if vs + 1 < le { if buf[vs + 1] == (110 as u8) { return 1 } } } }
1234 return 2
1235 }
1236 }
1237 }
1238 } }
1239 ls = le + 1
1240 }
1241 return 0
1242}
1243
1244// Does `w` occur in `nm` starting at a TOKEN BOUNDARY -- the start of the name,
1245// or immediately after '_'? Organ names are underscore-tokenised, so this asks
1246// "is one of the words in this name `w`" instead of "do these letters appear
1247// anywhere".
1248func mdh_len(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8) { n=n+1 } return n }
1249
1250func mdh_tail_eq(nm: *u8, n: i64, suf: *u8) -> i64 {
1251 let sl: i64 = mdh_len(suf)
1252 if sl > n { return 0 }
1253 var i: i64 = 0
1254 while i < sl { if nm[n - sl + i] != suf[i] { return 0 } i = i + 1 }
1255 return 1
1256}
1257
1258func md_tok_at(nm: *u8, w: *u8) -> i64 {
1259 let wl: i64 = mdh_len(w)
1260 let nl: i64 = mdh_len(nm)
1261 var i: i64 = 0
1262 while i + wl <= nl {
1263 var boundary: i64 = 0
1264 if i == 0 { boundary = 1 } else { if nm[i - 1] == (95 as u8) { boundary = 1 } }
1265 if boundary == 1 {
1266 var j: i64 = 0
1267 var m: i64 = 1
1268 while j < wl { if nm[i + j] != w[j] { m = 0; j = wl } else { j = j + 1 } }
1269 if m == 1 { return 1 }
1270 }
1271 i = i + 1
1272 }
1273 return 0
1274}
1275
1276// Does the name end in one of the verifier suffixes the system already treats as
1277// a declaration (_gate/_test/_kat -- the /api/gate_run bound)?
1278func md_name_is_oracle(nm: *u8) -> i64 {
1279 let n: i64 = mdh_len(nm)
1280 if mdh_tail_eq(nm, n, "_gate" as *u8) == 1 { return 1 }
1281 if mdh_tail_eq(nm, n, "_test" as *u8) == 1 { return 1 }
1282 if mdh_tail_eq(nm, n, "_kat" as *u8) == 1 { return 1 }
1283 return 0
1284}
1285
1286// NON-OVERRIDABLE deny: credential oracles and key material. A conf row must
1287// never be able to make these promotable -- otherwise the role registry becomes
1288// a privilege-escalation surface (config that grants authority).
1289//
1290// ★★★★★ ROOT-FIXED 2026-07-31 (debt 1785511766). This used to ask
1291// md_contains -- a RAW SUBSTRING -- which is the exact defect the sibling
1292// md_promote_deny was already fixed for at seq1789 ("THE SUFFIX IS A
1293// DECLARATION; THE SUBSTRING WAS A GUESS", the nx_survey_serve_gate collision),
1294// left unfixed one layer down here in the NON-OVERRIDABLE deny.
1295//
1296// "vault" means CREDENTIAL CUSTODY here, but as a substring it also matches the
1297// entire MEDIA vault family -- nx_mvault, nx_mvault_coll, nx_mvault_walk. One
1298// substring, two unrelated meanings. The result was that gate-proven media-vault
1299// binaries were unshippable by any sanctioned route, and the 2026-07-23 session
1300// resorted to an ssh cp/mv rename to ship them.
1301// ★★★★★**A GUARD THAT CANNOT BE SATISFIED PRODUCES A BYPASS, NOT SAFETY.**
1302//
1303// TWO CHANGES, BOTH STRICTLY SAFE -- this narrows FALSE positives only, and every
1304// real credential organ below still denies (proven by nx_promote_deny_gate):
1305// 1. TOKEN-BOUNDARY, not substring. "vault" still matches nx_vault_gateway (the
1306// word is a token) but no longer matches nx_mvault (the letters are merely
1307// inside one). A glued credential name like nx_secretstore STILL denies,
1308// because the boundary is checked at the START of the token only.
1309// 2. An ORACLE SUFFIX is exempt. Promoting nx_cap_mint_gate installs
1310// nx_cap_mint_gate.elf -- it CANNOT swap nx_cap_mint.elf -- so a verifier can
1311// never be the credential organ it verifies. Safe by construction, and it
1312// reuses the system's own _gate/_test/_kat rule rather than inventing one.
1313func md_promote_deny_hard(nm: *u8) -> i64 {
1314 if md_name_is_oracle(nm) == 1 { return 0 }
1315 if md_tok_at(nm, "mint" as *u8) == 1 { return 1 }
1316 if md_tok_at(nm, "vault" as *u8) == 1 { return 1 }
1317 if md_tok_at(nm, "secret" as *u8) == 1 { return 1 }
1318 if md_tok_at(nm, "keygen" as *u8) == 1 { return 1 }
1319 if md_tok_at(nm, "login" as *u8) == 1 { return 1 }
1320 return 0
1321}
1322
1323func md_promote_deny(nm: *u8) -> i64 {
1324 // u2605CONVERGED 2026-07-30 (seq1754). This used to consult its OWN role conf
1325 // (knowledge/organ_kinds.conf) -- a SECOND classifier for a concept a
1326 // sibling had already implemented properly as nx_organkind
1327 // (ok_kind_of_path over knowledge/status/organ_kind.conf), wired into the
1328 // promote handler ABOVE this function. Two confs and two readers for one
1329 // concept is the sprawl we keep warning about, and I built half of it by
1330 // not checking /code/tools before starting.
1331 //
1332 // The canonical reader now decides FIRST at the API layer; this function is
1333 // only reached for an UNDECLARED name, where it is the legacy name
1334 // heuristic -- so the duplicate lookup is removed and its rows were merged
1335 // into the canonical conf. deny_hard STAYS: credential oracles must be
1336 // refused non-overridably regardless of any declared kind.
1337 if md_promote_deny_hard(nm) == 1 { return 1 }
1338 if md_contains(nm, "serve" as *u8) == 1 { return 1 }
1339 if md_contains(nm, "daemon" as *u8) == 1 { return 1 }
1340 if md_contains(nm, "mint" as *u8) == 1 { return 1 }
1341 if md_contains(nm, "vault" as *u8) == 1 { return 1 }
1342 if md_contains(nm, "secret" as *u8) == 1 { return 1 }
1343 if md_contains(nm, "keygen" as *u8) == 1 { return 1 }
1344 if md_contains(nm, "login" as *u8) == 1 { return 1 }
1345 if md_contains(nm, "router" as *u8) == 1 { return 1 }
1346 if md_contains(nm, "hostctl" as *u8) == 1 { return 1 }
1347 if md_contains(nm, "signaling" as *u8) == 1 { return 1 }
1348 if md_contains(nm, "gateway" as *u8) == 1 { return 1 }
1349 if md_contains(nm, "torrent" as *u8) == 1 { return 1 }
1350 if md_contains(nm, "mgmt" as *u8) == 1 { return 1 }
1351 if md_contains(nm, "_gw" as *u8) == 1 { return 1 }
1352 if md_streq(nm, "sites" as *u8) == 1 { return 1 }
1353 return 0
1354}
1355func md_staged_elf_ok(nm: *u8) -> i64 {
1356 let p: *u8 = sys_mmap(192)
1357 var o: i64 = 0
1358 var i: i64 = 0
1359 while nm[i] != (0 as u8) { p[o] = nm[i]; o = o + 1; i = i + 1 }
1360 let sfx: *u8 = ".sov.elf.new" as *u8
1361 var j: i64 = 0
1362 while sfx[j] != (0 as u8) { p[o] = sfx[j]; o = o + 1; j = j + 1 }
1363 p[o] = 0 as u8
1364 let fd: i64 = sys_openat_rd(p)
1365 if fd < 0 { return 0 }
1366 let hb: *u8 = sys_mmap(8)
1367 let r: i64 = sys_read(fd, hb, 4)
1368 sys_close(fd)
1369 if r != 4 { return 0 }
1370 if hb[0] != (127 as u8) { return 0 }
1371 if hb[1] != (69 as u8) { return 0 }
1372 if hb[2] != (76 as u8) { return 0 }
1373 if hb[3] != (70 as u8) { return 0 }
1374 return 1
1375}
1376func md_promote_organ_ok(nm: *u8) -> i64 {
1377 if md_promote_deny(nm) == 1 { return 0 }
1378 if md_streq(nm, "nx_ecosystem_maturity_rollup" as *u8) == 1 { return 1 }
1379 if md_streq(nm, "nx_ecomat_seed" as *u8) == 1 { return 1 }
1380 if md_streq(nm, "nx_ecomat_beat" as *u8) == 1 { return 1 }
1381 if md_streq(nm, "nx_ecomat_page" as *u8) == 1 { return 1 }
1382 if md_streq(nm, "nx_tool_argecho" as *u8) == 1 { return 1 }
1383 // 07-17 (eat the ssh-once deploy debt): the fork-exec MCP TOOL organ family -- one-shot elfs the
1384 // tools daemon spawns per call. NOT daemons (those stay refused -> /api/deploy) and NOT the
1385 // credential oracles (nx_session_mint / nx_cap_mint stay OFF this list deliberately).
1386 if md_streq(nm, "nx_shelltool" as *u8) == 1 { return 1 }
1387 if md_streq(nm, "nx_common_tasks" as *u8) == 1 { return 1 }
1388 if md_streq(nm, "nx_frontier_board" as *u8) == 1 { return 1 }
1389 if md_streq(nm, "nx_page_verify" as *u8) == 1 { return 1 }
1390 if md_streq(nm, "nx_store_seed" as *u8) == 1 { return 1 }
1391 if md_streq(nm, "nx_workflow" as *u8) == 1 { return 1 }
1392 if md_streq(nm, "nx_memory" as *u8) == 1 { return 1 }
1393 if md_streq(nm, "nx_heal" as *u8) == 1 { return 1 }
1394 if md_streq(nm, "nx_fs" as *u8) == 1 { return 1 }
1395 if md_streq(nm, "nx_fs_write" as *u8) == 1 { return 1 }
1396 if md_streq(nm, "nx_site_publish" as *u8) == 1 { return 1 }
1397 if md_streq(nm, "nx_https_get" as *u8) == 1 { return 1 }
1398 if md_streq(nm, "nx_verify" as *u8) == 1 { return 1 }
1399 // 07-17 (stem-first-byte-fab lane): the compare-publish pipeline organs -> API-promotable, so a
1400 // brand-new compare domain publishes end-to-end over MCP (build -> promote -> regen), zero shell.
1401 if md_streq(nm, "nx_compare_regen" as *u8) == 1 { return 1 }
1402 if md_streq(nm, "nx_swcompare_matrix" as *u8) == 1 { return 1 }
1403 if md_streq(nm, "nx_swcompare_sota" as *u8) == 1 { return 1 }
1404 if md_streq(nm, "nx_swcompare_hub" as *u8) == 1 { return 1 }
1405 if md_streq(nm, "nx_maturity_board" as *u8) == 1 { return 1 }
1406 // (3) staged-artifact rule: owner-staged one-shot builds are promotable (deny above already refused
1407 // every daemon/oracle shape, so this can only ever admit tool-organ names).
1408 if md_staged_elf_ok(nm) == 1 { return 1 }
1409 return 0
1410}
1411
1412// ---- seq1484: PROMOTION PROVENANCE -- a promote may not walk a binary BACKWARDS ------------------
1413// THE BLEED THIS STOPS (measured 2026-07-30): the mgmt API was reverted THREE times and the compiler
1414// TWICE in a single session, each time by promoting a binary built elsewhere from a stale tree. Every
1415// existing control passed it: the ELF is valid, the size is plausible, and promote_toolchain's canary
1416// COMPILES AND RUNS it GREEN -- because a stale-but-working binary does all of that perfectly.
1417// ★LIVENESS IS NOT CURRENCY. "It works" cannot distinguish the newest build from last week's.
1418//
1419// The invariant that CAN is the same one already protecting sources (nx_symdrop) and tree pushes
1420// (nx_treepack REFUSED-WOULD-DROP-SYMBOLS): a normal promotion installs content this target has NEVER
1421// held; a revert installs content it ALREADY HELD. So keep an append-only per-target content-hash
1422// history. Staged == newest -> a harmless re-promote, allowed. Staged never seen -> a real advance,
1423// allowed and recorded. Staged matches an EARLIER generation -> THE BINARY WOULD GO BACKWARDS, refused
1424// and named. No build-time provenance, no clock, no size heuristic, nothing to spoof by touching a file.
1425//
1426// ESCAPE HATCH BY DESIGN, NOT BY FLAG: going backwards deliberately is what /api/rollback is FOR, and it
1427// does not route through here. A `force` parameter would just be the hole re-opened under a nicer name.
1428const MD_PROV_HIST: *u8 = "knowledge/promote_history.tsv" as *u8
1429const MD_PROV_CAP: i64 = 1048576
1430const MD_PROV_RDCH: i64 = 262144
1431const MD_PROV_FNV_OFF: i64 = 1469598103934665603
1432const MD_PROV_FNV_PRM: i64 = 1099511628211
1433const MD_PROV_TAB: i64 = 9
1434const MD_PROV_NL: i64 = 10
1435
1436// FNV-1a over a whole file, streamed so a large ELF needs no full-size buffer. 0 = unreadable.
1437func md_prov_hash(path: *u8) -> i64 {
1438 let fd: i64 = sys_openat_rd(path)
1439 if fd < 0 { return 0 }
1440 let b: *u8 = sys_mmap(MD_PROV_RDCH)
1441 var h: i64 = MD_PROV_FNV_OFF
1442 var go: i64 = 1
1443 while go == 1 {
1444 let n: i64 = sys_read(fd, b, MD_PROV_RDCH)
1445 if n <= 0 { go = 0 } else {
1446 var i: i64 = 0
1447 while i < n { h = h ^ (b[i] as i64); h = h * MD_PROV_FNV_PRM; i = i + 1 }
1448 }
1449 }
1450 sys_close(fd)
1451 return h
1452}
1453// Walk the history for `name`. out3[0]=generations seen, out3[1]=1 if h is the NEWEST, out3[2]=index of
1454// an EARLIER generation equal to h (-1 if none).
1455func md_prov_probe(name: *u8, h: i64, out3: *i64) -> i64 {
1456 out3[0] = 0; out3[1] = 0; out3[2] = 0 - 1
1457 let buf: *u8 = sys_mmap(MD_PROV_CAP)
1458 let n: i64 = dp_read(MD_PROV_HIST, buf, MD_PROV_CAP - 1)
1459 if n <= 0 { return 0 }
1460 var nl: i64 = 0
1461 while name[nl] != (0 as u8) { nl = nl + 1 }
1462 var gen: i64 = 0
1463 var ls: i64 = 0
1464 var i: i64 = 0
1465 while i <= n {
1466 var eol: i64 = 0
1467 if i == n { eol = 1 } else { if buf[i] == (MD_PROV_NL as u8) { eol = 1 } }
1468 if eol == 1 {
1469 if i > ls {
1470 var tab: i64 = 0 - 1
1471 var t: i64 = ls
1472 while t < i { if buf[t] == (MD_PROV_TAB as u8) { tab = t; t = i } else { t = t + 1 } }
1473 if tab > 0 { if tab - ls == nl {
1474 var m: i64 = 1
1475 var c: i64 = 0
1476 while c < nl { if buf[ls+c] != name[c] { m = 0; c = nl } else { c = c + 1 } }
1477 if m == 1 {
1478 var v: i64 = 0
1479 var neg: i64 = 0
1480 var k: i64 = tab + 1
1481 if k < i { if buf[k] == (45 as u8) { neg = 1; k = k + 1 } }
1482 while k < i { v = v * 10 + ((buf[k] as i64) - 48); k = k + 1 }
1483 if neg == 1 { v = 0 - v }
1484 if v == h { out3[2] = gen; out3[1] = 1 } else { out3[1] = 0 }
1485 gen = gen + 1
1486 }
1487 } }
1488 }
1489 ls = i + 1
1490 }
1491 i = i + 1
1492 }
1493 out3[0] = gen
1494 return gen
1495}
1496func md_prov_record(name: *u8, h: i64) -> i64 {
1497 let line: *u8 = sys_mmap(512)
1498 var o: i64 = 0
1499 var i: i64 = 0
1500 while name[i] != (0 as u8) { line[o] = name[i]; o = o + 1; i = i + 1 }
1501 line[o] = MD_PROV_TAB as u8; o = o + 1
1502 var m: i64 = h
1503 if m < 0 { line[o] = 45 as u8; o = o + 1; m = 0 - m }
1504 let t: *u8 = sys_mmap(32)
1505 var k: i64 = 0
1506 if m == 0 { t[0] = 48 as u8; k = 1 }
1507 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
1508 var z: i64 = k - 1
1509 while z >= 0 { line[o] = t[z]; o = o + 1; z = z - 1 }
1510 line[o] = MD_PROV_NL as u8; o = o + 1
1511 let fd: i64 = sys_openat_append(MD_PROV_HIST, 0x1a4)
1512 if fd < 0 { return 0 - 1 }
1513 sys_write(fd, line, o)
1514 sys_fsync(fd)
1515 sys_close(fd)
1516 return 0
1517}
1518// 1 = this staged content may be promoted; 0 = it would walk `name` BACKWARDS.
1519func md_prov_ok(name: *u8, stagedpath: *u8) -> i64 {
1520 let h: i64 = md_prov_hash(stagedpath)
1521 if h == 0 { return 1 } // unreadable: leave the decision to the existing checks
1522 let p: *i64 = sys_mmap(64) as *i64
1523 md_prov_probe(name, h, p)
1524 if p[0] == 0 { md_prov_record(name, h); return 1 } // first sighting = the baseline
1525 if p[1] == 1 { return 1 } // identical to the newest = harmless re-promote
1526 if p[2] >= 0 { return 0 } // seen EARLIER but not newest = A REVERT
1527 md_prov_record(name, h)
1528 return 1
1529}
1530
1531func md_promote_staged(name: *u8) -> i64 {
1532 let live: *u8 = sys_mmap(160)
1533 let newp: *u8 = sys_mmap(160)
1534 let prevp: *u8 = sys_mmap(160)
1535 var lo: i64 = 0
1536 while name[lo] != (0 as u8) { live[lo] = name[lo]; newp[lo] = name[lo]; prevp[lo] = name[lo]; lo = lo + 1 }
1537 live[lo] = 0 as u8
1538 md_copy_slice_z(newp, name, 0, lo, 160)
1539 md_copy_slice_z(prevp, name, 0, lo, 160)
1540 var no: i64 = lo
1541 let ns: *u8 = ".new" as *u8
1542 var a: i64 = 0
1543 while ns[a] != (0 as u8) { newp[no] = ns[a]; no = no + 1; a = a + 1 }
1544 newp[no] = 0 as u8
1545 // does <name>.new exist?
1546 let nfd: i64 = sys_openat_rd(newp)
1547 if nfd < 0 { return 0 }
1548 sys_close(nfd)
1549 // seq1484 PROVENANCE: refuse a promotion that would walk this target BACKWARDS to content
1550 // it already held. Placed BEFORE any rename, so a refusal leaves live and .prev untouched.
1551 if md_prov_ok(live, newp) == 0 { return 0 - 2 }
1552 var po: i64 = lo
1553 let ps: *u8 = ".prev" as *u8
1554 a = 0
1555 while ps[a] != (0 as u8) { prevp[po] = ps[a]; po = po + 1; a = a + 1 }
1556 prevp[po] = 0 as u8
1557 sys_renameat(live, prevp) // keep the old live as .prev (rollback)
1558 sys_renameat(newp, live) // promote .new -> live
1559 nx_chmod(live, 0x1ed)
1560 return 1
1561}
1562// ---- TOOLCHAIN PROMOTE (never-brick) -- eats seq891/seq903 -------------------------------------
1563// THE GAP THIS CLOSES: the ecosystem could build and deploy every SERVICE over its own API but could
1564// NOT update the COMPILER that builds them, so a PROVEN compiler fix could not be landed API-first
1565// (rule 27). Measured cost on 2026-07-30: nx_fnptr_slot_probe was GREEN on the laptop compiler and RED
1566// on the hub compiler, i.e. obj.fn_field(args) silently emitted no indirect call for every organ in the
1567// tree, and the fix existed but had nowhere to go. A toolchain you cannot update is a toolchain whose
1568// bugs are permanent.
1569//
1570// ⚠THE PATH NOT TAKEN (seq903, and it must stay not-taken): shipping the toolchain through
1571// /api/upload + /api/unpack looks tempting because it touches only this module. nx_treepack writes every
1572// output file 0644 NON-EXECUTABLE and UNLINKS-then-recreates on any open failure, so unpacking over
1573// buildroot/_offc/nx_cc_sovereign.elf would either install a non-executable compiler or destroy the live
1574// one -- EVERY BUILD FOR EVERY SEAT, from a call that looks like a routine source sync.
1575//
1576// ⚠MATCHED-PAIR RULE (seq1315): nx_sov_build_run writes _build/<name>.sov.elf while nx_hostctl
1577// cmd_buildrun reads /tmp/<name>.sov.elf, so those two may only ever be promoted TOGETHER. They are
1578// admitted here because that pair-ship is a legitimate wave -- and admitting them is SAFE precisely
1579// because the caller canary-compiles and auto-rolls-back, so a mismatched pair cannot survive a promote.
1580//
1581// DIALECT NOTE: plain-if (no `else`), no empty string literals, <=6 params. This module is IMPORTED by
1582// nx_mgmt_api, and it must be compiled by TODAY'S hub compiler -- the one that still carries seq533
1583// (imported `else` desyncs the parser), seq907 (an empty literal aliases the next literal) and seq239
1584// (>6 params mishandled). The fix ships in a binary that the defect itself has to be able to build.
1585const MD_TC_MIN_ELF: i64 = 4096 // size floor: refuse a truncated upload or an HTML error page
1586const MD_TC_MODE_EXEC: i64 = 0x1ed // 0755 -- a compiler that is not executable is a dead ecosystem
1587
1588func md_toolchain_target_ok(nm: *u8) -> i64 {
1589 if md_streq(nm, "nx_cc_sovereign.elf" as *u8) == 1 { return 1 }
1590 if md_streq(nm, "nxasm_x86_main.elf" as *u8) == 1 { return 1 }
1591 if md_streq(nm, "nx_sov_build_run.elf" as *u8) == 1 { return 1 }
1592 return 0
1593}
1594
1595func md_tc_live(nm: *u8, buf: *u8) -> i64 {
1596 var o: i64 = md_cmp_cat(buf, 0, "buildroot/_offc/" as *u8)
1597 o = md_cmp_cat(buf, o, nm)
1598 return o
1599}
1600func md_tc_prev(nm: *u8, buf: *u8) -> i64 {
1601 var o: i64 = md_tc_live(nm, buf)
1602 o = md_cmp_cat(buf, o, ".prev" as *u8)
1603 return o
1604}
1605func md_tc_staged(nm: *u8, buf: *u8) -> i64 {
1606 var o: i64 = md_cmp_cat(buf, 0, nm)
1607 o = md_cmp_cat(buf, o, ".new" as *u8)
1608 return o
1609}
1610
1611// ELF magic + size floor. Returns the byte size on success, 0 on refusal. Validating the ARTIFACT (not
1612// an exit code) is the seq363/hostctl lesson: a 0-byte or non-ELF file must never reach the live slot.
1613func md_tc_elf_size(p: *u8) -> i64 {
1614 let fd: i64 = sys_openat_rd(p)
1615 if fd < 0 { return 0 }
1616 let sz: i64 = sys_lseek(fd, 0, 2)
1617 if sz < MD_TC_MIN_ELF { sys_close(fd); return 0 }
1618 sys_lseek(fd, 0, 0)
1619 let hb: *u8 = sys_mmap(8)
1620 var ok: i64 = 0
1621 if sys_read(fd, hb, 4) == 4 {
1622 if hb[0] == (0x7f as u8) {
1623 if hb[1] == (69 as u8) {
1624 if hb[2] == (76 as u8) {
1625 if hb[3] == (70 as u8) { ok = 1 }
1626 }
1627 }
1628 }
1629 }
1630 sys_close(fd)
1631 if ok == 0 { return 0 }
1632 return sz
1633}
1634
1635// Install staged <nm>.new -> buildroot/_offc/<nm>, banking the outgoing binary as .prev FIRST.
1636// Returns the installed size, or 0 if nothing was touched. Ordering is deliberate: validate BEFORE
1637// renaming anything, so a refused upload leaves the live compiler completely untouched.
1638func md_tc_install(nm: *u8) -> i64 {
1639 let stagedp: *u8 = sys_mmap(256)
1640 let livep: *u8 = sys_mmap(256)
1641 let prevp: *u8 = sys_mmap(256)
1642 md_tc_staged(nm, stagedp)
1643 md_tc_live(nm, livep)
1644 md_tc_prev(nm, prevp)
1645 let sz: i64 = md_tc_elf_size(stagedp)
1646 if sz == 0 { return 0 }
1647 // seq1484: the canary proves the incoming toolchain WORKS, which a stale-but-working one
1648 // also does. Provenance is what proves it is not last week s build. Checked before any rename.
1649 if md_prov_ok(livep, stagedp) == 0 { return 0 - 2 }
1650 sys_renameat(livep, prevp) // bank the outgoing compiler (rollback source)
1651 if sys_renameat(stagedp, livep) != 0 {
1652 sys_renameat(prevp, livep) // stage-rename failed: put the old one straight back
1653 nx_chmod(livep, MD_TC_MODE_EXEC)
1654 return 0
1655 }
1656 nx_chmod(livep, MD_TC_MODE_EXEC)
1657 return sz
1658}
1659
1660// Restore buildroot/_offc/<nm>.prev -> live. This is the rollback half of never-brick and it is called
1661// on CANARY FAILURE, so the ecosystem can never be left with a compiler that cannot compile.
1662func md_tc_rollback(nm: *u8) -> i64 {
1663 let livep: *u8 = sys_mmap(256)
1664 let prevp: *u8 = sys_mmap(256)
1665 md_tc_live(nm, livep)
1666 md_tc_prev(nm, prevp)
1667 if md_tc_elf_size(prevp) == 0 { return 0 }
1668 if sys_renameat(prevp, livep) != 0 { return 0 }
1669 nx_chmod(livep, MD_TC_MODE_EXEC)
1670 return 1
1671}
1672
1673// Our own pid. getpid = syscall 39 on x86-64.
1674func md_self_pid() -> i64 { return __syscall(172, 0, 0, 0, 0, 0, 0) } // rv64 getpid=172. Was raw x86 39, which IS an RV64 KEY (umount2) the backend translated to ioctl(16) -> -ENOTTY, so the mgmt API's own pid was -25 (debt idx 2277)
1675// Fork a detached child that waits, then SIGTERMs the given pid. Used so a self-restart can FINISH WRITING
1676// ITS RESPONSE before the process goes away: the reply reaches the caller, then the guard respawns the
1677// already-promoted binary. SIGTERM (not KILL) so a daemon that later grows a drain handler gets to use it.
1678func md_delayed_kill(pid: i64, delay_ms: i64) -> i64 {
1679 let p: i64 = sys_fork()
1680 if p == 0 {
1681 nx_setsid()
1682 sys_sleep_ms(delay_ms)
1683 nx_kill(pid, 15)
1684 sys_exit(0)
1685 }
1686 return p
1687}
1688func md_kill_by_name(needle: *u8) -> i64 {
1689 var self_hit: i64 = 0
1690 let nn: i64 = md_len(needle)
1691 let fd: i64 = sys_openat_rd("/proc" as *u8)
1692 if fd < 0 { return 0 }
1693 let buf: *u8 = sys_mmap(65536)
1694 let path: *u8 = sys_mmap(256)
1695 let clbuf: *u8 = sys_mmap(8192)
1696 var killed: i64 = 0
1697 var run: i64 = 1
1698 while run == 1 {
1699 let n: i64 = sys_getdents64(fd, buf, 65536)
1700 if n <= 0 { run = 0 } else {
1701 var off: i64 = 0
1702 while off < n {
1703 let rec: *u8 = ((buf as i64 + off) as *u8)
1704 let reclen: i64 = dirent_reclen(rec)
1705 if reclen <= 0 { off = n } else {
1706 let name: *u8 = dirent_name(rec)
1707 if name[0] >= (48 as u8) { if name[0] <= (57 as u8) {
1708 var p: i64 = 0
1709 let pre: *u8 = "/proc/" as *u8
1710 var a: i64 = 0
1711 while pre[a] != (0 as u8) { path[p] = pre[a]; p = p + 1; a = a + 1 }
1712 a = 0
1713 while name[a] != (0 as u8) { path[p] = name[a]; p = p + 1; a = a + 1 }
1714 let suf: *u8 = "/cmdline" as *u8
1715 a = 0
1716 while suf[a] != (0 as u8) { path[p] = suf[a]; p = p + 1; a = a + 1 }
1717 path[p] = 0 as u8
1718 let cfd: i64 = sys_openat_rd(path)
1719 if cfd >= 0 {
1720 let cln: i64 = sys_read(cfd, clbuf, 8192)
1721 sys_close(cfd)
1722 if cln > 0 { if md_pk_contains(clbuf, cln, needle, nn) == 1 {
1723 // ---- R5 SEQUENCING: NEVER SIGKILL OURSELVES MID-RESPONSE ----------------
1724 // TWO defects, one root. (1) mgmt restarting/deploying ITSELF matches its own
1725 // cmdline here and SIGKILLs the process that is writing the reply -- that IS
1726 // the FETCH-FAIL every /api/deploy returns (~12x in one session), and it is why
1727 // the seq1563 deploy lease strands (we die before reaching our own release).
1728 // (2) NEW with SO_REUSEPORT: a hot restart runs old and new under the SAME
1729 // cmdline, so a name-matched kill would murder the freshly-spawned instance too
1730 // -- adopting REUSEPORT without this turns a handoff into an outage.
1731 // So: skip our own pid here, remember it, and schedule a DELAYED self-exit
1732 // after the loop. The reply is written first, THEN we go; the supervise guard
1733 // respawns the already-promoted binary. FETCH-FAIL becomes a real JSON body.
1734 let vpid: i64 = md_pk_atoi(name)
1735 if vpid == md_self_pid() { self_hit = 1 } else {
1736 nx_kill(vpid, 9)
1737 killed = killed + 1
1738 }
1739 } }
1740 }
1741 } }
1742 off = off + reclen
1743 }
1744 }
1745 }
1746 }
1747 sys_close(fd)
1748 // We matched OURSELVES: schedule the exit for AFTER the response is on the wire. 1500ms is the
1749 // reply-write window, not a guess at compile time -- the caller returns immediately after this.
1750 // Counted in `killed` so the JSON stays honest about what is going away.
1751 if self_hit == 1 { md_delayed_kill(md_self_pid(), 1500); killed = killed + 1 }
1752 return killed
1753}
1754
1755// ---- secondary adapters: validate / exec / probe ----------------------------------------------------
1756func md_validate_artifact(path: *u8, kind: i64) -> i64 { return dep_validate(path, kind) }
1757
1758// drive the proven allowlisted nx_aw_hostctl with one sub -> its exit code (the supervisor/deploy exec port).
1759func md_exec_hostctl(sub: *u8) -> i64 {
1760 // Run the ON-NAS supervisor CLI directly. The mgmt daemon runs under the (root) supervisor, so it can drive the
1761 // surgical restart subs. The prior "_offc/nx_aw_hostctl.elf" is the LAPTOP->NAS bridge and is ABSENT on the NAS,
1762 // so /api/restart + /api/deploy were gate-proven but never live-executable. Absolute path (mgmt cwd=nishihost).
1763 let helf: *u8 = "/volume1/homes/elderwesto/nishihost/nx_hostctl" as *u8
1764 let args: *i64 = sys_mmap(16) as *i64
1765 args[0] = sub as i64
1766 return dep_run(helf, args, 1)
1767}
1768
1769// P2 off-LAN parity: run one allowlisted hostctl sub and CAPTURE its stdout to outpath (for /api/hostctl -> the
1770// phone gets torstat/routerctl/status output). Same on-NAS nx_hostctl the deploy path uses; single argv element
1771// (execve, no shell) so no injection; the allowlist below fail-closes to a curated safe read/action set.
1772func md_exec_hostctl_capture(sub: *u8, outpath: *u8) -> i64 {
1773 let helf: *u8 = "/volume1/homes/elderwesto/nishihost/nx_hostctl" as *u8
1774 let args: *i64 = sys_mmap(16) as *i64
1775 args[0] = sub as i64
1776 return dep_run_capture(helf, args, 1, outpath)
1777}
1778// 2-arg variant (e.g. `buildrun <target>`): run the on-NAS nx_hostctl <sub> <arg>, capture stdout -> outpath.
1779func md_exec_hostctl_capture2(sub: *u8, arg: *u8, outpath: *u8) -> i64 {
1780 let helf: *u8 = "/volume1/homes/elderwesto/nishihost/nx_hostctl" as *u8
1781 let args: *i64 = sys_mmap(16) as *i64
1782 args[0] = sub as i64
1783 args[1] = arg as i64
1784 return dep_run_capture(helf, args, 2, outpath)
1785}
1786// ---- BUILD ADMISSION (seq708/768/1390) --------------------------------------------------------
1787// THE INCIDENT THIS PREVENTS: /api/build is the one heavyweight mgmt op -- it forks the sovereign nx_cc
1788// toolchain to compile a source tree -- and it did so with NO memory admission. Under a build-heavy
1789// session the host runs out of memory and the supervisor OOM-reaps nx_mgmt_api, taking the WHOLE deploy
1790// path down for every seat. Confirmed twice (seq698/708) and REPRODUCED LIVE 2026-07-30 (seq1390):
1791// both transports died mid-session (503 / status=0, TLS fine so the EDGE was healthy and the BACKEND
1792// was gone) and self-recovered only when the guard respawned it.
1793//
1794// ⚠seq768 recorded this fix as "WRITTEN + BUILT + VERIFIED, staged awaiting one rename" and was marked
1795// EATEN -- but on 2026-07-30 md_exec_build_admit / ma_emit_503 / the ma_do_build call site were found in
1796// NEITHER the laptop SSOT NOR the NAS buildroot (grep: 0 matches across 6972 files). The work never
1797// reached a source tree, so every build-heavy session kept re-rolling the outage. Rebuilt here, in the
1798// SSOT, where a rebuild cannot lose it. ★LAW: a debt is not eaten until its fix is IN A SOURCE TREE --
1799// "built and staged" is not landed, and a binary nobody can rebuild is a rumour.
1800//
1801// FAIL-OPEN BY DESIGN (rule 26 / F881 ratchet stance): nx_build_admit exits 0 GRANT / 3 DENY (below the
1802// memory floor) / 4 QUEUE (load ceiling) / 2 usage / 5 unreadable-proc. We block ONLY on exit 3, the
1803// definitive memory wedge that actually causes the incident. Load-queueing and unreadable /proc both
1804// fall through to GRANT so admission control can never soft-brick the ecosystem's build path -- a
1805// refused-when-it-should-have-built is a worse failure here than an occasional reap.
1806// Floor 1024 MB; load ceiling deliberately huge so MEMORY is the sole gate (the measured cause).
1807func md_exec_build_admit() -> i64 {
1808 let belf: *u8 = "/volume1/homes/elderwesto/nishihost/nx_build_admit.elf" as *u8
1809 let bf: i64 = sys_openat_rd(belf)
1810 if bf < 0 { return 0 } // detector absent -> GRANT (never block on a missing guard)
1811 sys_close(bf)
1812 let args: *i64 = sys_mmap(32) as *i64
1813 args[0] = "check" as *u8 as i64
1814 args[1] = "1024" as *u8 as i64
1815 args[2] = "100000" as *u8 as i64
1816 return dep_run_capture(belf, args, 3, "/tmp/nx_build_admit.out" as *u8)
1817}
1818
1819// ---- GATE-DRY RATCHET RUNNER (2026-07-31, debts 1785529506 / 1785530277) -----------------------
1820// L009 -- gate organs that hand-roll their verdict instead of inheriting nx_gate_verdict -- is not
1821// merely large, it is GROWING: two warden scans hours apart on 2026-07-31 read 2035/2167 then
1822// 2041/2182. A migration campaign that only removes old breaches LOSES to a tree that adds new ones,
1823// so D001 cannot be closed by migrating alone. The 2026 practice for exactly this shape is a RATCHET
1824// (Notion bans an INCREASE in violation count and requires a deliberate re-bank).
1825//
1826// u26a0AND THE PREDECESSOR THIS WAS SUPPOSED TO COPY DOES NOT EXIST. nx_magicratchet is asserted "wired
1827// into /api/build" in FOUR comments in nx_law_warden.nx, but grep finds ZERO call sites in this source
1828// AND ZERO in the deployed mgmt binary, and a two-build experiment (clean -> BUILT, +3 literals >=1024
1829// -> BUILT, not refused) proves it never fires. So this is written fresh, not modelled on prose.
1830//
1831// FAIL-OPEN, the same stance as build admission and the pre-deploy gate: a missing or unreadable
1832// detector returns -1 and the caller proceeds. A guard that cannot be read must never wedge the build
1833// path for every seat.
1834func md_exec_gatedry(srcpath: *u8) -> i64 {
1835 let gelf: *u8 = "/volume1/homes/elderwesto/nishihost/nx_gatedry.elf" as *u8
1836 let gf: i64 = sys_openat_rd(gelf)
1837 if gf < 0 { return 0 - 1 }
1838 sys_close(gf)
1839 let args: *i64 = sys_mmap(16) as *i64
1840 args[0] = srcpath as i64
1841 return dep_run_capture(gelf, args, 1, "/tmp/nx_gatedry.out" as *u8)
1842}
1843
1844// Does this organ name end in the terminal token `_gate`? TERMINAL, not substring -- the seq1789
1845// lesson banked in nx_organkind_gate T12: `nx_survey_serve_gate` ENDS with _gate, `nx_gate_bite`
1846// merely CONTAINS it, and treating containment as the test misclassifies the second.
1847func md_name_is_gate(nm: *u8) -> i64 {
1848 var n: i64 = 0
1849 while nm[n] != (0 as u8) { n = n + 1 }
1850 if n < 5 { return 0 }
1851 if nm[n-5] != (95 as u8) { return 0 }
1852 if nm[n-4] != (103 as u8) { return 0 }
1853 if nm[n-3] != (97 as u8) { return 0 }
1854 if nm[n-2] != (116 as u8) { return 0 }
1855 if nm[n-1] != (101 as u8) { return 0 }
1856 return 1
1857}
1858
1859// Is this organ ALREADY DEPLOYED? That is the GRANDFATHER TEST and it is what makes this a ratchet
1860// rather than a wall: 2041 existing gates hand-roll their verdicts, and refusing all of them would
1861// stop the ecosystem dead. Only a gate with NO deployed artefact -- i.e. a NEW one -- is held to the
1862// base class. Existing breaches are migrated by their owner lanes, never blocked here.
1863// ---- THE BANKED BASELINE: HOW THE RATCHET TELLS NEW FROM OLD (2026-07-31, debt 1785558585) ----
1864// MY FIRST ATTEMPT USED "has no deployed .elf" AS THE NEWNESS TEST AND THAT WAS WRONG. The ledger
1865// measures 2877 gate sources against 175 binaries -- ~94pc of gates were NEVER COMPILED -- so
1866// long-existing gates read as NEW and their rebuilds were REFUSED. A ratchet that cannot tell new
1867// from old is a WALL, and a wall at a shared chokepoint stops every seat. Withdrawn within minutes.
1868//
1869// THE CORRECT TEST IS A RECORD, which is what a ratchet actually is. Notion's ESLint ratcheting keeps
1870// a CHECKED-IN file of known violations and requires approval only when the count INCREASES against
1871// it. knowledge/status/gatedry_baseline.out is that record: the enumerated gate corpus at the moment
1872// the ratchet landed (2189 entries). A gate NOT in the record is NEW and is held to the base class;
1873// everything in the record is grandfathered and migrated by its owner lane.
1874//
1875// FAIL-OPEN BY CONSTRUCTION: an absent or unreadable baseline returns 1 (== "known", allow). If the
1876// record cannot be read we CANNOT distinguish new from old, and the only safe answer is to permit --
1877// otherwise a missing file silently rebuilds the exact wall this replaced.
1878func md_gate_in_baseline(nm: *u8) -> i64 {
1879 let bp: *u8 = "knowledge/status/gatedry_baseline.out" as *u8
1880 let ln: *i64 = sys_mmap(16) as *i64
1881 ln[0] = 0
1882 let buf: *u8 = sys_read_file(bp, ln)
1883 if buf as i64 == 0 { return 1 }
1884 let n: i64 = ln[0]
1885 if n <= 0 { return 1 }
1886 let pat: *u8 = sys_mmap(256)
1887 var o: i64 = 0
1888 pat[o] = (47 as u8)
1889 o = o + 1
1890 var bi: i64 = 0
1891 while nm[bi] != (0 as u8) { pat[o] = nm[bi]; o = o + 1; bi = bi + 1 }
1892 pat[o] = (46 as u8)
1893 o = o + 1
1894 pat[o] = (110 as u8)
1895 o = o + 1
1896 pat[o] = (120 as u8)
1897 o = o + 1
1898 let pn: i64 = o
1899 var k: i64 = 0
1900 while k + pn <= n {
1901 var j: i64 = 0
1902 var hit: i64 = 1
1903 while j < pn { if buf[k+j] != pat[j] { hit = 0; j = pn } else { j = j + 1 } }
1904 if hit == 1 { return 1 }
1905 k = k + 1
1906 }
1907 return 0
1908}
1909
1910func md_organ_deployed(nm: *u8) -> i64 {
1911 let p: *u8 = sys_mmap(256)
1912 let pre: *u8 = "/volume1/homes/elderwesto/nishihost/"
1913 var o: i64 = 0
1914 var i: i64 = 0
1915 while pre[i] != (0 as u8) { p[o] = pre[i]; o = o + 1; i = i + 1 }
1916 i = 0
1917 while nm[i] != (0 as u8) { p[o] = nm[i]; o = o + 1; i = i + 1 }
1918 let suf: *u8 = ".elf"
1919 i = 0
1920 while suf[i] != (0 as u8) { p[o] = suf[i]; o = o + 1; i = i + 1 }
1921 p[o] = 0 as u8
1922 let fd: i64 = sys_openat_rd(p)
1923 if fd < 0 { return 0 }
1924 sys_close(fd)
1925 return 1
1926}
1927
1928// ---- PRE-DEPLOY SAFETY GATE RUNNER (2026-07-30) -------------------------------------------------
1929// nx_deploy_ready computes deploy_safe/blockers/DEPLOY-BLOCKED and publishes it -- and NOTHING AT THE
1930// DEPLOY CHOKEPOINT EVER CONSULTED IT. It is referenced by ecomat seeding, tooldiff and the cron beat,
1931// but nx_mgmt_api never called it, so the one act the gate exists to guard ran unguarded. MEASURED:
1932// the gate returned verdict DEPLOY-BLOCKED (blockers=1) while two of my own deploys succeeded minutes
1933// apart. A gate that is computed, published and unreachable from the act it guards IS the baseline.
1934//
1935// WHY WE PARSE JSON AND NOT THE EXIT CODE: nx_deploy_ready calls sys_exit(0) UNCONDITIONALLY -- even
1936// when the verdict is DEPLOY-BLOCKED -- so its exit status carries no verdict at all and no caller
1937// checking $? could ever act on it. Fixing that is a published-contract change (other callers may
1938// treat nonzero as failure), so it is filed separately rather than changed underneath them here.
1939//
1940// FAIL-OPEN, same stance as build admission: a missing or unreadable gate returns -1 and the caller
1941// proceeds. A guard that cannot be read must never wedge the deploy path for every seat.
1942func md_exec_deploy_ready() -> i64 {
1943 let delf: *u8 = "/volume1/homes/elderwesto/nishihost/nx_deploy_ready.elf" as *u8
1944 let df: i64 = sys_openat_rd(delf)
1945 if df < 0 { return 0 - 1 }
1946 sys_close(df)
1947 let args: *i64 = sys_mmap(16) as *i64
1948 args[0] = "check" as *u8 as i64
1949 // Return the gate's EXIT CODE (0 safe / 3 DEPLOY-BLOCKED) so the caller can surface it. Until today this
1950 // code was constant 0 and therefore meaningless; reporting it live is what proves the new contract landed.
1951 let drc0: i64 = dep_run_capture(delf, args, 1, "/tmp/nx_ma_deploy_ready.out" as *u8); if drc0 >= 0 { return drc0 }
1952 return 0
1953}
1954
1955// mint a ROOT tools-capability token via the on-NAS nx_cap_mint oracle (CLI: <keyfile> <allow-csv> <exp> <nonce>
1956// -> token on stdout, nonzero exit on refusal). The HMAC keyfile is read BY THE ORACLE on-NAS and never crosses
1957// the API. Absolute paths (same stance as the hostctl/treepack exec ports; mgmt cwd=nishihost but explicit wins).
1958// stdout captured -> outpath; caller treats nonzero exit OR empty capture as mint-failed (fail-closed).
1959func md_exec_capmint(allow: *u8, expstr: *u8, noncestr: *u8, outpath: *u8) -> i64 {
1960 let helf: *u8 = "/volume1/homes/elderwesto/nishihost/nx_cap_mint.elf" as *u8
1961 let args: *i64 = sys_mmap(40) as *i64
1962 args[0] = "/volume1/homes/elderwesto/nishihost/tools_cap_secret.key" as *u8 as i64
1963 args[1] = allow as i64
1964 args[2] = expstr as i64
1965 args[3] = noncestr as i64
1966 return dep_run_capture(helf, args, 4, outpath)
1967}
1968// ---- /api/gate_run + /api/proc_kill support (seq1349/1383). RE-APPLIED after a 4th backdate (seq1445).
1969// gate_run bound: name must end gate/test/kat, resolves ONLY a promoted top-level nishihost/<n>.elf, so a pure
1970// verifier is all this route can ever reach -- never a daemon, promoter or deployer.
1971// ⚠ seq1443: dep_run_capture_bounded DUPLICATES tr_run_capture_to (nx_tool_run.nx, seq1412) which is gate-proven
1972// (nx_tool_run_timeout_gate T5 = no leak after a kill) and uses a WATCHDOG FORK because a poll design is not
1973// buildable without sys_fcntl. ADOPT IT next; kept here only so the live verbs stop vanishing from source.
1974// ---- R1 (seq1506): LEASE-GATE THE BUILD PATH -------------------------------------------------------------
1975// OPERATOR 2026-07-30: "why cant we clearly state when we are switching out or updating and coordinate like
1976// road construction". This is the flagger. Concurrent builds of the SAME target are how a session ships a
1977// regression from a mid-churn snapshot -- it happened twice today (21->19 routes lost, then again).
1978// ADOPTION, NOT INVENTION (seq1410's law, 4th instance today): nx_lease ALREADY EXISTS, is gate-proven, and
1979// had ZERO callers in the build path. We reuse it as a SUBPROCESS via its exit-code contract rather than
1980// importing it -- verified live: acquire=0 prints LS-ACQUIRED, BUSY=3 prints "LS-BUSY <name> holder=<who>",
1981// release=0. Exit codes are the contract, so no import coupling and no second implementation.
1982// TTL is the reason this can never deadlock the ecosystem: a session that dies mid-build cannot hold the
1983// lane closed -- the lease expires on its own. A lock without a TTL would be a worse defect than the race.
1984// ⚠ lease NAME grammar is [a-zA-Z0-9_-] ONLY: "build:X" is REFUSED, so the name is built as "build-<target>".
1985func md_lease_run(verb: *u8, name: *u8, owner: *u8, ttl: *u8, nargs: i64, outpath: *u8) -> i64 {
1986 let elf: *u8 = "/volume1/homes/elderwesto/nishihost/nx_lease.elf" as *u8
1987 let args: *i64 = sys_mmap(48) as *i64
1988 args[0] = verb as i64
1989 args[1] = name as i64
1990 args[2] = owner as i64
1991 args[3] = ttl as i64
1992 return dep_run_capture(elf, args, nargs, outpath)
1993}
1994// Build the lease name "build-<target>" into buf. Target is already [A-Za-z0-9_]-sanitized by the caller.
1995func md_lease_name(target: *u8, buf: *u8) -> i64 {
1996 var o: i64 = 0
1997 let p: *u8 = "build-" as *u8
1998 while p[o] != (0 as u8) { buf[o] = p[o]; o = o + 1 }
1999 var i: i64 = 0
2000 while target[i] != (0 as u8) { buf[o] = target[i]; o = o + 1; i = i + 1 }
2001 buf[o] = 0 as u8
2002 return o
2003}
2004// Build "<prefix><src>" into buf, COPYING ONLY lease-legal chars [a-zA-Z0-9_-] from src.
2005// ⚠ nx_lease REFUSES any other byte, and the things we most want to lock are named with dots
2006// ("nx_tools_api_serve.elf") -- so a naive concat produces LS-REFUSED bad name and the guard silently
2007// never engages. A guard that cannot be named is a guard that does not exist; filter, do not assume.
2008func md_lease_name_pfx(prefix: *u8, src: *u8, buf: *u8) -> i64 {
2009 var o: i64 = 0
2010 while prefix[o] != (0 as u8) { buf[o] = prefix[o]; o = o + 1 }
2011 var i: i64 = 0
2012 while src[i] != (0 as u8) {
2013 let c: i64 = src[i] as i64
2014 var ok: i64 = 0
2015 if c >= 48 { if c <= 57 { ok = 1 } }
2016 if c >= 65 { if c <= 90 { ok = 1 } }
2017 if c >= 97 { if c <= 122 { ok = 1 } }
2018 if c == 95 { ok = 1 }
2019 if c == 45 { ok = 1 }
2020 if ok == 1 { buf[o] = src[i]; o = o + 1 }
2021 i = i + 1
2022 }
2023 buf[o] = 0 as u8
2024 return o
2025}
2026func md_gate_name_ok(nm: *u8) -> i64 {
2027 var n: i64 = 0
2028 while nm[n] != (0 as u8) { n = n + 1 }
2029 if n >= 4 { if nm[n-4] == (103 as u8) { if nm[n-3] == (97 as u8) { if nm[n-2] == (116 as u8) { if nm[n-1] == (101 as u8) { return 1 } } } } }
2030 if n >= 4 { if nm[n-4] == (116 as u8) { if nm[n-3] == (101 as u8) { if nm[n-2] == (115 as u8) { if nm[n-1] == (116 as u8) { return 1 } } } } }
2031 if n >= 3 { if nm[n-3] == (107 as u8) { if nm[n-2] == (97 as u8) { if nm[n-1] == (116 as u8) { return 1 } } } }
2032 return 0
2033}
2034// seq1443 ADOPTION: this now delegates to tr_run_capture_to (runtime/nx_tool_run.nx, seq1412) instead of my
2035// own dep_run_capture_bounded, which was a DUPLICATE of it -- a live instance of the ecosystem's own law that
2036// THE BOTTLENECK IS NOT BUILDING PRIMITIVES, IT IS ADOPTING THEM.
2037// The adopted primitive is STRICTLY better and its header says why mine could not work: bounding the drain
2038// needs O_NONBLOCK on the read end and THERE IS NO sys_fcntl in nx_syscalls, so a poll design is NOT
2039// BUILDABLE. It uses a WATCHDOG FORK -- the watchdog SIGKILLs the worker, the dying worker drops the last
2040// write end, and the parent's blocking read gets its EOF naturally, needing no new syscall. It also closes
2041// wfd BEFORE forking the watchdog (order is load-bearing: fork first and the watchdog inherits the write end,
2042// so the pipe never EOFs -- the exact hang the bound exists to remove, reintroduced by the fix).
2043// It ships nx_tool_run_timeout_gate whose T5 is "second timeout identical (no leak after a kill)" = the
2044// no-leak tooth seq1425 was missing. Capturing to a BUFFER also deletes the /tmp/nx_ma_gaterun.out temp file.
2045// Returns the child's exit code, or TR_ERR_TIMEOUT when the deadline fired.
2046func md_exec_gate_capture(elfpath: *u8, out: *u8, cap: i64, outlen: *i64, deadline_ms: i64) -> i64 {
2047 let argv: *i64 = sys_mmap(32) as *i64
2048 argv[0] = elfpath as i64
2049 argv[1] = 0
2050 return tr_run_capture_to(elfpath, argv, out, cap, outlen, deadline_ms)
2051}
2052func md_exec_gate_capture_OLD(elfpath: *u8, outpath: *u8, deadline_ms: i64) -> i64 {
2053 let args: *i64 = sys_mmap(16) as *i64
2054 return dep_run_capture_bounded(elfpath, args, 0, outpath, deadline_ms)
2055}
2056// proc_kill bound: >=6 chars AND must contain .elf (our own organs only, never a system process) AND must not
2057// reach the supervisor (killing the guard stops every respawn). Killing a guard-supervised daemon = a restart.
2058func md_str_contains(hay: *u8, pat: *u8) -> i64 {
2059 let hn: i64 = md_len(hay)
2060 let pn: i64 = md_len(pat)
2061 if pn == 0 { return 0 }
2062 if pn > hn { return 0 }
2063 var i: i64 = 0
2064 while i + pn <= hn {
2065 var k: i64 = 0
2066 var hit: i64 = 1
2067 while k < pn { if hay[i+k] != pat[k] { hit = 0; k = pn } else { k = k + 1 } }
2068 if hit == 1 { return 1 }
2069 i = i + 1
2070 }
2071 return 0
2072}
2073func md_proc_kill_needle_ok(nm: *u8) -> i64 {
2074 if md_len(nm) < 6 { return 0 }
2075 if md_str_contains(nm, ".elf" as *u8) == 0 { return 0 }
2076 if md_str_contains(nm, "supervise" as *u8) == 1 { return 0 }
2077 if md_str_contains(nm, "nx_hostctl" as *u8) == 1 { return 0 }
2078 return 1
2079}
2080func md_cstr_eq(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 }
2081// FAIL-CLOSED allowlist for /api/hostctl. Curated to fast (<~15s) read + safe-maintenance subs so a synchronous
2082// API call returns promptly. DEPLOY/selfswap/rollback are DELIBERATELY excluded (they promote/re-exec -> use the
2083// guarded /api/deploy). Long-running (nettap 60s, portmap timeouts) excluded until an async job path exists.
2084func md_hostctl_action_ok(nm: *u8) -> i64 {
2085 if md_cstr_eq(nm, "status" as *u8) == 1 { return 1 } // supervisor snapshot
2086 if md_cstr_eq(nm, "torstat" as *u8) == 1 { return 1 } // per-torrent seedeval + metadata diag
2087 if md_cstr_eq(nm, "routerctl" as *u8) == 1 { return 1 } // GL.iNet dashboard (model/wan/forwards/clients)
2088 if md_cstr_eq(nm, "receipts" as *u8) == 1 { return 1 } // op-receipts ledger (read-only)
2089 if md_cstr_eq(nm, "kicktorrent" as *u8) == 1 { return 1 } // restart the torrent daemon
2090 if md_cstr_eq(nm, "kickseed" as *u8) == 1 { return 1 } // restart the :6881 seeder
2091 if md_cstr_eq(nm, "kickworkers" as *u8) == 1 { return 1 } // restart stale download workers
2092 if md_cstr_eq(nm, "kickseedann" as *u8) == 1 { return 1 } // restart the DHT/LSD announcer
2093 if md_cstr_eq(nm, "trackerrefresh" as *u8) == 1 { return 1 } // refresh the tracker list (detached)
2094 if md_cstr_eq(nm, "galxpipeline" as *u8) == 1 { return 1 } // analysis-on-ingest: thumbnails + NXVI (detached, idempotent)
2095 if md_cstr_eq(nm, "durindexrun" as *u8) == 1 { return 1 } // duration-index batch (detached, idempotent)
2096 if md_cstr_eq(nm, "searchpagerank" as *u8) == 1 { return 1 } // search: PageRank build on the live web shard (detached, idempotent, additive pr:)
2097 if md_cstr_eq(nm, "searchcompact" as *u8) == 1 { return 1 } // search: web-shard compaction (detached, idempotent, verifies-before-swap)
2098 if md_cstr_eq(nm, "durindexstat" as *u8) == 1 { return 1 } // duration-index coverage (read-only)
2099 return 0
2100}
2101
2102// REAL-HTTP health: GET url -> require 200 + non-empty body. 1 healthy / 0 not (the false-green killer).
2103// LOCAL TCP liveness: connect to 127.0.0.1:port -> 1 if something is listening (service up), 0 if refused.
2104// Dependency-free (no external fetcher, no CA store, no edge round-trip) = the robust health signal for a restart.
2105func md_tcp_alive(port: i64) -> i64 {
2106 let fd: i64 = sys_socket(AF_INET, SOCK_STREAM, 0); if fd < 0 { return 0 }
2107 let sa: *u8 = sys_mmap(16)
2108 sa[0]=2 as u8; sa[1]=0 as u8; sa[2]=((port>>8)&0xff) as u8; sa[3]=(port&0xff) as u8
2109 sa[4]=127 as u8; sa[5]=0 as u8; sa[6]=0 as u8; sa[7]=1 as u8
2110 var z: i64=8; while z<16 { sa[z]=0 as u8; z=z+1 }
2111 let r: i64 = nx_connect_bounded(fd, sa, 16, NX_CONN_DEFAULT_MS); sys_close(fd)
2112 if r == 0 { return 1 }
2113 return 0
2114}
2115func md_health_probe(url: *u8) -> i64 {
2116 // "port:<N>" -> LOCAL TCP-connect health (no nx_research_fetch/CA/edge dependency -- the robust default for
2117 // restart-targets: the earlier /torrent HTTP probe needed an on-NAS fetcher+CA that isn't at the mgmt cwd,
2118 // so it always failed -> conservative rollback). Otherwise the HTTP-fetch probe below. Both retry 10x3s.
2119 if url[0]==(112 as u8) { if url[1]==(111 as u8) { if url[2]==(114 as u8) { if url[3]==(116 as u8) { if url[4]==(58 as u8) {
2120 var pt: i64=0; var pi: i64=5; while url[pi]!=(0 as u8) { if url[pi]>=(48 as u8) { if url[pi]<=(57 as u8) { pt=pt*10+((url[pi] as i64)-48) } } pi=pi+1 }
2121 var at: i64=0
2122 while at < 10 { if md_tcp_alive(pt)==1 { return 1 } at=at+1; if at<10 { sys_sleep_ms(3000) } }
2123 return 0
2124 } } } } }
2125 let pargs: *i64 = sys_mmap(16) as *i64
2126 pargs[0] = url as i64
2127 let pbuf: *u8 = sys_mmap(16384)
2128 // 10 tries x 3s = up to 30s: GENEROUS, because /api/deploy now runs this in a DETACHED watchdog (not on the
2129 // request path) -> it no longer races the edge-proxy read window, so it can wait out a slow guard-respawn
2130 // (~10-15s) and confirm 200+body -> GREEN, instead of a premature conservative rollback.
2131 var attempt: i64 = 0
2132 while attempt < 10 {
2133 dep_run_capture("_offc/nx_research_fetch.elf" as *u8, pargs, 1, "/tmp/nx_ma_deploy_health.out" as *u8)
2134 let pn: i64 = dp_read("/tmp/nx_ma_deploy_health.out" as *u8, pbuf, 16380)
2135 let st: i64 = hh_after(pbuf, pn, "status=" as *u8)
2136 let bbn: i64 = hh_after(pbuf, pn, "body_bytes=" as *u8)
2137 if st == 200 { if bbn > 0 { return 1 } }
2138 attempt = attempt + 1
2139 if attempt < 10 { sys_sleep_ms(3000) }
2140 }
2141 return 0
2142}