code wiki / _hdl_build / nx_mgmt_data_toolchain_session_20260910.nx
nx_mgmt_data_toolchain_session_20260910.nx source
↩ module page · 2851 lines · 155834 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)
15import "_hdl_build/nx_staging_guard.nx" // sg_classify -- REFUSE a promote that walks the target backwards (staging hygiene)
16
17// Owned deployment target record for guarded installation. Eight columns are the
18// registry schema, not a maximum record/path size. Legacy resolver callers remain
19// unchanged until their output contracts are migrated.
20import "nx_file_install_session_20260910.nx"
21const MD_TARGET_COLUMNS: i64=8
22struct NxDeployTargetRecord {
23 bytes: *u8,
24 allocation: i64,
25 name: *u8,
26 kind_text: *u8,
27 staged: *u8,
28 deploy: *u8,
29 health: *u8,
30 rollback: *u8,
31 live: *u8,
32 process: *u8,
33 stage: *u8,
34 code: i64,
35 line: i64,
36 conflicting_line: i64,
37}
38func md_target_init(out: *NxDeployTargetRecord) -> i64 {
39 let raw: *u8=out as *u8;var i: i64=0
40 while i<__size_of(NxDeployTargetRecord) { raw[i]=0 as u8;i=i+1 }
41 out.stage="target-input";out.code=FIO_EINVAL;return 0
42}
43func md_target_close(out: *NxDeployTargetRecord) -> i64 {
44 var rc: i64=0
45 if (out.bytes as i64)!=0 { rc=sys_munmap(out.bytes,out.allocation) }
46 md_target_init(out);return rc
47}
48func md_target_space(c: u8) -> i64 {
49 return (c==(32 as u8) || c==(9 as u8) || c==(13 as u8)) as i64
50}
51func md_target_field(out: *NxDeployTargetRecord,index: i64,value: *u8) -> i64 {
52 if index==0 { out.name=value }
53 if index==1 { out.kind_text=value }
54 if index==2 { out.staged=value }
55 if index==3 { out.deploy=value }
56 if index==4 { out.health=value }
57 if index==5 { out.rollback=value }
58 if index==6 { out.live=value }
59 if index==7 { out.process=value }
60 return 0
61}
62// Reads the complete caller-owned snapshot before selecting a unique name.
63// Missing/malformed/ambiguous records never fall back to compiled-in defaults.
64// Output owns a length-derived row copy; it never aliases caller snapshot bytes.
65// Initialize/close the result between uses; do not reinitialize a live result.
66func md_target_decode(snapshot: *u8,n: i64,name: *u8,name_length: i64,out: *NxDeployTargetRecord) -> i64 {
67 md_target_init(out)
68 if (snapshot as i64)==0 || n<0 || (name as i64)==0 || name_length<=0 { return out.code }
69 var i: i64=0
70 while i<name_length {
71 if (name[i] as i64)<=32 || name[i]==(127 as u8) { return out.code };i=i+1
72 }
73 var selected: i64=0-1;var selected_end: i64=0
74 var cursor: i64=0;var line: i64=1
75 while cursor<n {
76 let end: i64=md_eol(snapshot,n,cursor)
77 i=cursor
78 while i<end {
79 let ch: i64=snapshot[i] as i64
80 if ch==0 || ch==127 || (ch<32 && md_target_space(snapshot[i])==0) {
81 out.stage="registry-control-byte";out.code=FIO_EBADMSG;out.line=line;return out.code
82 }
83 i=i+1
84 }
85 var start: i64=cursor
86 while start<end && md_target_space(snapshot[start])==1 { start=start+1 }
87 if start<end && snapshot[start]!=(35 as u8) {
88 var token_end: i64=start
89 while token_end<end && md_target_space(snapshot[token_end])==0 { token_end=token_end+1 }
90 if md_slice_eq(snapshot,start,token_end-start,name,0,name_length)==1 {
91 if selected>=0 {
92 out.stage="duplicate-target";out.code=FIO_EEXIST;out.conflicting_line=line;return out.code
93 }
94 selected=start;selected_end=end;out.line=line
95 }
96 }
97 cursor=end+1;line=line+1
98 }
99 if selected<0 { out.stage="target-absent";out.code=FI_ENOENT;return out.code }
100 let length: i64=selected_end-selected
101 if length==0x7fffffffffffffff { out.stage="row-size-overflow";out.code=FIO_EINVAL;return out.code }
102 let copy: *u8=sys_mmap(length+1)
103 if (copy as i64)<0 { out.stage="row-allocation";out.code=copy as i64;return out.code }
104 fi_copy(copy,snapshot+selected,length);copy[length]=0 as u8
105 var fields: i64=0;i=0
106 while i<length {
107 while i<length && md_target_space(copy[i])==1 { copy[i]=0 as u8;i=i+1 }
108 if i<length {
109 if fields==MD_TARGET_COLUMNS { break }
110 md_target_field(out,fields,copy+i);fields=fields+1
111 while i<length && md_target_space(copy[i])==0 { i=i+1 }
112 }
113 }
114 var rc: i64=0;var stage: *u8="target-resolved"
115 if fields!=MD_TARGET_COLUMNS || i<length { rc=FIO_EBADMSG;stage="target-columns" }
116 if rc==0 && fi_path_valid(out.live)==0 { rc=FIO_EBADMSG;stage="target-live-path" }
117 if rc==0 { if md_streq(out.kind_text,"toolchain-v2")==1 { if md_streq(out.deploy,"-")!=1 || md_streq(out.health,"-")!=1 || md_streq(out.rollback,"-")!=1 { rc=FIO_EBADMSG;stage="versioned-target-actions" } } }
118 if rc!=0 {
119 let original_line: i64=out.line
120 sys_munmap(copy,length+1);md_target_init(out);out.line=original_line
121 } else { out.bytes=copy;out.allocation=length+1 }
122 out.stage=stage;out.code=rc;return rc
123}
124
125func md_target_read(path: *u8,snapshot_allowance: i64,name: *u8,name_length: i64,out: *NxDeployTargetRecord) -> i64 {
126 md_target_init(out)
127 if snapshot_allowance<=0 || (path as i64)==0 { return out.code }
128 let region: *NxFileReadRegion=sys_mmap(__size_of(NxFileReadRegion)) as *NxFileReadRegion
129 if (region as i64)<0 { out.stage="reader-allocation";out.code=region as i64;return out.code }
130 fio_region_init(region);var rc: i64=fio_region_open(path,region)
131 var stage: *u8=region.stage
132 let n: i64=region.total;var bytes: *u8=0 as *u8
133 if rc==0 && n>snapshot_allowance { rc=FIO_EINVAL;stage="snapshot-admission" }
134 if rc==0 && n==0 { rc=FI_ENOENT;stage="target-absent" }
135 if rc==0 {
136 bytes=sys_mmap(n)
137 if (bytes as i64)<0 { rc=bytes as i64;bytes=0 as *u8;stage="snapshot-allocation" }
138 }
139 if rc==0 {
140 let got: i64=fio_region_next(region,bytes,n)
141 if got!=n { rc=got;if rc>=0 { rc=FIO_EIO };stage=region.stage }
142 }
143 fio_region_close(region)
144 if rc==0 && region.code!=0 { rc=region.code;stage=region.stage }
145 if rc==0 { rc=md_target_decode(bytes,n,name,name_length,out) }
146 else { out.code=rc;out.stage=stage }
147 if (bytes as i64)!=0 { sys_munmap(bytes,n) }
148 sys_munmap(region as *u8,__size_of(NxFileReadRegion))
149 return rc
150}
151
152// One data-plane operation: resolve the requested registered target and apply the
153// caller-approved immutable intent. Authentication and approval belong to the
154// management boundary; merely supplying a digest does not confer authority.
155struct NxRegisteredInstallResult {
156 target: NxDeployTargetRecord,
157 install: NxFileInstallResult,
158 stage: *u8,
159 code: i64,
160}
161func md_registered_install_init(out: *NxRegisteredInstallResult) -> i64 {
162 md_target_init(&out.target)
163 out.install.stage="not-started";out.install.code=0;out.install.already_published=0
164 out.install.io.stage="not-started";out.install.io.code=0
165 out.install.io.written=0;out.install.io.close_code=0
166 fio_replace_init(&out.install.replacement)
167 out.stage="approval-input";out.code=FIO_EINVAL;return 0
168}
169func md_registered_install_close(out: *NxRegisteredInstallResult) -> i64 {
170 let rc: i64=md_target_close(&out.target)
171 md_registered_install_init(out);return rc
172}
173// Owns the resolved row until close, so reporting never relies on a later
174// registry reread. A failed resolver cannot dispatch installation or restart.
175// The caller accounts separately for snapshot, intent, scratch and result memory.
176func md_install_registered(registry: *u8,snapshot_allowance: i64,target: *u8,target_length: i64,intent: *u8,intent_allowance: i64,approved_intent_digest: *u8,scratch: *u8,scratch_bytes: i64,out: *NxRegisteredInstallResult) -> i64 {
177 md_registered_install_init(out)
178 if (approved_intent_digest as i64)==0 || fi_path_valid(intent)==0 || intent_allowance<=0 || (scratch as i64)==0 || scratch_bytes<=0 { return out.code }
179 out.stage="target-resolution"
180 out.code=md_target_read(registry,snapshot_allowance,target,target_length,&out.target)
181 if out.code!=0 { return out.code }
182 out.stage="artifact-installation"
183 out.code=fi_install_bound(intent,intent_allowance,scratch,scratch_bytes,out.target.live,approved_intent_digest,&out.install)
184 if out.code==0 { out.stage="artifact-installed-serving-unverified" }
185 return out.code
186}
187
188// ---- parse primitives over a buffer (slices, no null terminators) -----------------------------------
189func md_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
190
191// ASCII '2' -- the generation suffix on <live>.prev2. NAMED so the rotation carries no bare literal.
192const MD_CH_DIGIT_TWO: i64 = 50
193// THE ONE ROLLBACK-CHAIN ROTATION (2026-08-21). Rotate <prev> one generation deeper to <prev>2 so that a
194// SECOND promote of the same target cannot destroy the pre-change binary. Returns 1 if a generation was
195// rotated, 0 if there was nothing to rotate. Fail-safe: any failure leaves both slots exactly as found.
196//
197// MEASURED THIS SESSION: two promotes of ONE target left live and .prev BOTH holding new builds and the
198// original GONE -- and an older-generation binary CANNOT be rebuilt. The slot holds "the PREVIOUS
199// PROMOTE", never "the last known good", so the SECOND promote is the one that leaves you with no way
200// back. The only defence was the operator remembering to copy the artifact aside first, and
201// *A SAFETY PROPERTY THAT DEPENDS ON SOMEONE REMEMBERING IS AN ADOPTION GAP WITH EXTRA STEPS.
202//
203// *WHY A FUNCTION AND NOT N INLINE EDITS: this estate implements the live -> .prev bank FIVE TIMES
204// (nx_mgmt_data x3, nx_mgmt_api x1, nx_hostctl x1), found by TRACING the call chain rather than by
205// assuming -- the first two candidates I inspected were a staged-slot bank and a static-content promote,
206// neither of which is this. EVERY COPY OF A PATTERN IS A PLACE A FIX CAN FAIL TO ARRIVE, which
207// nx_hostctl.nx says in those words about its own 27 deploy twins. All FOUR copies inside this daemon
208// now call THIS. nx_hostctl is a SEPARATE BINARY and carries its own twin -- NAMED here so the next
209// reader inherits the fact instead of re-deriving it.
210// Bounded deliberately: exactly one extra file per target, no directory, no new import.
211func md_rotate_prev(prevp: *u8) -> i64 {
212 let pf: i64 = sys_openat_rd(prevp)
213 if pf < 0 { return 0 }
214 sys_close(pf)
215 let p2: *u8 = sys_mmap(256)
216 var i: i64 = 0
217 while prevp[i] != (0 as u8) { p2[i] = prevp[i]; i = i + 1 }
218 p2[i] = MD_CH_DIGIT_TWO as u8
219 p2[i + 1] = 0 as u8
220 if sys_renameat(prevp, p2) != 0 { return 0 }
221 return 1
222}
223
224func md_cat_slice(d: *u8, o: i64, src: *u8, off: i64, len: i64) -> i64 {
225 var i: i64 = 0
226 while i < len { d[o] = src[off + i]; o = o + 1; i = i + 1 }
227 return o
228}
229
230func md_tok_eq(src: *u8, off: i64, len: i64, s: *u8) -> i64 {
231 let sl: i64 = md_len(s)
232 if sl != len { return 0 }
233 var i: i64 = 0
234 while i < len { if (src[off + i] as i64) != (s[i] as i64) { return 0 } i = i + 1 }
235 return 1
236}
237
238func md_slice_atoi(src: *u8, off: i64, len: i64) -> i64 {
239 var v: i64 = 0
240 var i: i64 = 0
241 while i < len {
242 let c: i64 = src[off + i] as i64
243 if c >= 48 { if c <= 57 { v = v * 10 + (c - 48) } }
244 i = i + 1
245 }
246 return v
247}
248
249// index of '\n' at-or-after start, or n.
250func md_eol(snap: *u8, n: i64, start: i64) -> i64 {
251 var i: i64 = start
252 var f: i64 = 0
253 while f == 0 {
254 if i >= n { f = 1 } else { if (snap[i] as i64) == 10 { f = 1 } else { i = i + 1 } }
255 }
256 return i
257}
258
259// split snap[ls..le) on spaces into up-to-maxf (offs,lens) absolute slices. returns field count.
260func md_split(snap: *u8, ls: i64, le: i64, offs: *i64, lens: *i64, maxf: i64) -> i64 {
261 var nf: i64 = 0
262 var i: i64 = ls
263 while i < le {
264 var sk: i64 = 1
265 while sk == 1 { if i >= le { sk = 0 } else { if (snap[i] as i64) == 32 { i = i + 1 } else { sk = 0 } } }
266 if i < le {
267 let st: i64 = i
268 var sc: i64 = 1
269 while sc == 1 { if i >= le { sc = 0 } else { if (snap[i] as i64) == 32 { sc = 0 } else { i = i + 1 } } }
270 if nf < maxf { offs[nf] = st; lens[nf] = i - st; nf = nf + 1 }
271 }
272 }
273 return nf
274}
275
276func md_slice_eq(a: *u8, ao: i64, al: i64, b: *u8, bo: i64, bl: i64) -> i64 {
277 if al != bl { return 0 }
278 var i: i64 = 0
279 while i < al { if (a[ao + i] as i64) != (b[bo + i] as i64) { return 0 } i = i + 1 }
280 return 1
281}
282
283func md_copy_slice_z(dst: *u8, src: *u8, off: i64, len: i64, cap: i64) -> i64 {
284 var n: i64 = len
285 if n > cap - 1 { n = cap - 1 }
286 var i: i64 = 0
287 while i < n { dst[i] = src[off + i]; i = i + 1 }
288 dst[n] = 0 as u8
289 return n
290}
291
292// ---- file / config adapters -------------------------------------------------------------------------
293func md_read_file(path: *u8, szbox: *i64) -> *u8 {
294 szbox[0] = 0
295 return sys_read_file(path, szbox)
296}
297
298// ---- ADNET BILLING (debt 1785513943): the outside-world half of the invoice route ----------------
299// Lives HERE, in the DATA ring, not in nx_mgmt_api: that ring owns transport only (see the api header).
300// The money math stays in nx_adnet_bill and the join in nx_adnet_invoice -- this function is purely the
301// file access those two are deliberately free of.
302// FAIL-CLOSED: an unreadable INVENTORY or RATE CARD returns 0 (the route answers 503) rather than an
303// empty invoice -- "no rows" and "could not read the rows" must never look alike to a biller. An absent
304// JOURNAL is different and legitimate: it means zero events, so it degrades to an empty count.
305const MD_ADNET_INV: *u8 = "/volume1/homes/elderwesto/nishihost/sites/nishifamily/synth/adnet_inventory.txt" as *u8
306const MD_ADNET_RATES: *u8 = "/volume1/homes/elderwesto/nishihost/knowledge/status/adnet_rates.conf" as *u8
307const MD_ADNET_SERVED: *u8 = "/volume1/homes/elderwesto/nishihost/adnet_impressions.log" as *u8
308const MD_ADNET_CLICKS: *u8 = "/volume1/homes/elderwesto/nishihost/adnet_clicks.log" as *u8
309const MD_ADNET_VIEW: *u8 = "/volume1/homes/elderwesto/nishihost/knowledge/status/adnet_viewable.log" as *u8
310
311// ---- ADNET CREATIVE INTAKE (debt 1785512202): the outside-world half of the upload route --------
312// Validation and naming live in nx_adnet_creative (pure, gated 13/13); this is only the file write.
313// CONTENT-ADDRESSED, so the write is IDEMPOTENT by construction (rule 10): re-uploading identical bytes
314// lands on the identical path. No overwrite hazard, no version skew, and the URL doubles as a cache key.
315// Returns the ACR_* verdict; urlout receives the first-party url ONLY on ACR_OK.
316const MD_ADNET_SYNTH: *u8 = "/volume1/homes/elderwesto/nishihost/sites/nishifamily/synth/" as *u8
317
318func md_adnet_creative_store(b: *u8, n: i64, urlout: *u8, urlcap: i64) -> i64 {
319 urlout[0] = 0 as u8
320 let v: i64 = acr_validate(b, n)
321 if v != ACR_OK { return v }
322 let nm: *u8 = sys_mmap(64)
323 if acr_name(b, n, nm, 64) == 0 { return ACR_NOT_PNG }
324 let path: *u8 = sys_mmap(512)
325 var o: i64 = 0
326 var i: i64 = 0
327 while MD_ADNET_SYNTH[i] != (0 as u8) { path[o] = MD_ADNET_SYNTH[i]; o = o + 1; i = i + 1 }
328 i = 0
329 while nm[i] != (0 as u8) { path[o] = nm[i]; o = o + 1; i = i + 1 }
330 path[o] = 0 as u8
331 let fd: i64 = sys_openat_wr(path, 420)
332 if fd < 0 { return 0 - 1 }
333 let w: i64 = sys_write(fd, b, n)
334 sys_close(fd)
335 if w != n { return 0 - 1 }
336 if acr_url(b, n, urlout, urlcap) == 0 { return 0 - 1 }
337 return ACR_OK
338}
339
340func md_adnet_invoice_report(out: *u8, cap: i64) -> i64 {
341 let bx: *i64 = sys_mmap(16) as *i64
342 let inv: *u8 = md_read_file(MD_ADNET_INV, bx)
343 if (inv as i64) == 0 { return 0 }
344 let iln: i64 = bx[0]
345 let bx2: *i64 = sys_mmap(16) as *i64
346 let rates: *u8 = md_read_file(MD_ADNET_RATES, bx2)
347 if (rates as i64) == 0 { return 0 }
348 let rln: i64 = bx2[0]
349 let bx3: *i64 = sys_mmap(16) as *i64
350 var served: *u8 = md_read_file(MD_ADNET_SERVED, bx3)
351 var sln: i64 = bx3[0]
352 if (served as i64) == 0 { served = "" as *u8; sln = 0 }
353 let bx4: *i64 = sys_mmap(16) as *i64
354 var view: *u8 = md_read_file(MD_ADNET_VIEW, bx4)
355 var vln: i64 = bx4[0]
356 if (view as i64) == 0 { view = "" as *u8; vln = 0 }
357 let bx5: *i64 = sys_mmap(16) as *i64
358 var clk: *u8 = md_read_file(MD_ADNET_CLICKS, bx5)
359 var cln: i64 = bx5[0]
360 if (clk as i64) == 0 { clk = "" as *u8; cln = 0 }
361 return ainv_report(inv, iln, rates, rln, served, sln, view, vln, clk, cln, out, cap)
362}
363
364// resolve a deploy target NAME (slice nm[off..off+len)) against the allowlist file -> kind + src/sub/url
365// copied null-terminated into caller buffers. 1 = resolved, 0 = unknown (fail-closed). '#' = comment line.
366// COMPILED-IN fallback for the deploy plane's OWN bootstrap targets, so a freshly-deployed mgmt API can deploy
367// mgmtapi/hostctl/torrentstack off-LAN WITHOUT first getting an updated deploy_targets.conf onto the NAS (the
368// file isn't upload-able off-LAN). The file (md_resolve_target) still WINS when present -> it stays the
369// extensible SSOT; this only covers the plane's self-knowledge. Each: kind, src(staged .new), sub(promote), url(health), rb(rollback).
370func md_builtin_target(nm: *u8, off: i64, len: i64, kindb: *i64, srcbuf: *u8, subbuf: *u8, urlbuf: *u8, rbbuf: *u8) -> i64 {
371 if md_slice_eq(nm, off, len, "mgmtapi" as *u8, 0, 7) == 1 {
372 // health = LOCAL TCP-connect to the mgmt API's own port :18098 (robust). The old "https://.../api/" HTTP
373 // probe ran nx_research_fetch from the NAS -> nishifamily.com, which hits DSM's loopback nginx (coin-flip)
374 // -> flaky false-rollback. The new mgmt respawns on :18098 within the 30s retry window -> port-connect greens.
375 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 }
376 if md_slice_eq(nm, off, len, "hostctl" as *u8, 0, 7) == 1 {
377 // health = LOCAL TCP-connect to sites.elf :8443 (robust, like torrentstack). The old "https://.../api/"
378 // HTTP-fetch probe needed nx_research_fetch+CA from the mgmt cwd + hit the :443 DSM-nginx coin-flip + raced
379 // the self-swap -> it ALWAYS false-rolled-back (why no hostctl deploy landed since 07-09). sites.elf stays
380 // up across a self-swap (only the supervisor re-execs), so the port-connect greens reliably.
381 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 }
382 if md_slice_eq(nm, off, len, "torrentstack" as *u8, 0, 12) == 1 {
383 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 }
384 // ethical CLEAN-SERVE daemon (:8102, /clean) -- first-class builtin so it deploys purely over the API (no NAS
385 // deploy_targets.conf write). hostctl cleanservedeploy promotes the .new + guard respawns; health = TCP :8102.
386 if md_slice_eq(nm, off, len, "cleanserve" as *u8, 0, 10) == 1 {
387 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 }
388 // DOCPORTAL admin daemon (:18456, /search + /doc + /api) -- first-class builtin so the SEARCH daemon deploys
389 // purely over the API (no more manual .sov.elf.new swap). hostctl docportaldeploy promotes the .sov.elf.new the
390 // build stages + guard respawns; health = local TCP :18456; rollback = docportalrollback (.prev -> live).
391 if md_slice_eq(nm, off, len, "docportal" as *u8, 0, 9) == 1 {
392 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 }
393 return 0
394}
395func md_resolve_target(cfgpath: *u8, nm: *u8, off: i64, len: i64, kindb: *i64, srcbuf: *u8, subbuf: *u8, urlbuf: *u8, rbbuf: *u8) -> i64 {
396 let szp: *i64 = sys_mmap(16) as *i64
397 var buf: *u8 = md_read_file(cfgpath, szp)
398 // primary path (knowledge/hosting/, the root-owned data plane) ABSENT -> fall back to the operator-writable
399 // bootstrap conf in the daemon cwd (nishihost/deploy_targets.conf). knowledge/ is root-owned (the root mgmt
400 // daemon created it), so the elderwesto bootstrap that REGISTERS deploy targets can only write the cwd -- this
401 // fallback is what lets a new target (e.g. relate) be registered WITHOUT root. Primary still WINS when present.
402 if (buf as i64) == 0 { buf = md_read_file("deploy_targets.conf" as *u8, szp) }
403 // both configs ABSENT -> still honor the compiled-in bootstrap targets, else the whole deploy plane is dead
404 // off-LAN when the NAS lacks the files (the live-400 that caught this).
405 if (buf as i64) == 0 { return md_builtin_target(nm, off, len, kindb, srcbuf, subbuf, urlbuf, rbbuf) }
406 let n: i64 = szp[0]
407 let offs: *i64 = sys_mmap(64) as *i64
408 let lens: *i64 = sys_mmap(64) as *i64
409 var cur: i64 = 0
410 while cur < n {
411 let le: i64 = md_eol(buf, n, cur)
412 var isc: i64 = 0
413 if le > cur { if (buf[cur] as i64) == 35 { isc = 1 } }
414 if isc == 0 {
415 let nf: i64 = md_split(buf, cur, le, offs, lens, 8)
416 if nf >= 5 {
417 if md_slice_eq(buf, offs[0], lens[0], nm, off, len) == 1 {
418 // Versioned-only rows cannot fall through to legacy hostctl action execution.
419 if md_slice_eq(buf,offs[1],lens[1],"toolchain-v2",0,12)==1 { return 2 }
420 kindb[0] = md_slice_atoi(buf, offs[1], lens[1])
421 md_copy_slice_z(srcbuf, buf, offs[2], lens[2], 512)
422 md_copy_slice_z(subbuf, buf, offs[3], lens[3], 64)
423 md_copy_slice_z(urlbuf, buf, offs[4], lens[4], 256)
424 // OPTIONAL 6th field = per-target rollback sub (generalized deploy: a torrent target must
425 // roll back the TORRENT binary, not sites.elf). Absent (5-field legacy rows) -> "rollback".
426 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) }
427 return 1
428 }
429 }
430 }
431 cur = le + 1
432 }
433 // not in the file -> try the compiled-in bootstrap targets (off-LAN self-enable). Fail-closed if neither.
434 return md_builtin_target(nm, off, len, kindb, srcbuf, subbuf, urlbuf, rbbuf)
435}
436
437// FAIL-CLOSED allowlist of artifact names that /api/upload may STAGE (write <name>.upload -> <name>.new). These
438// are the deployable binaries the on-NAS supervisor promotes from *.new (HC_*_NEW in nx_hostctl). 1 = allowed,
439// 0 = refused (unknown target -> 400, NOTHING written). Names checked as a slice (nm[off..off+len)) so the caller
440// can hand a query-string slice without copying. Data lives HERE (the DATA ring), not buried in the transport layer.
441// NOTE the deliberate absence of directory separators in every entry -- an upload target is a BARE basename, so a
442// caller can never traverse ('/' or '..' would fail every md_slice_eq below), which keeps the staging write pinned
443// to the mgmt daemon's cwd by construction (defense-in-depth over the allowlist itself).
444func md_upload_target_ok(nm: *u8, off: i64, len: i64) -> i64 {
445 if md_slice_eq(nm, off, len, "nx_mgmt_api.elf" as *u8, 0, 15) == 1 { return 1 }
446 if md_slice_eq(nm, off, len, "sites.elf" as *u8, 0, 9) == 1 { return 1 }
447 if md_slice_eq(nm, off, len, "nx_gallery_serve.elf" as *u8, 0, 20) == 1 { return 1 }
448 if md_slice_eq(nm, off, len, "nx_gallery_gateway.elf" as *u8, 0, 22) == 1 { return 1 }
449 if md_slice_eq(nm, off, len, "nx_docportal_admin_daemon.elf" as *u8, 0, 29) == 1 { return 1 }
450 if md_slice_eq(nm, off, len, "nx_hostctl" as *u8, 0, 10) == 1 { return 1 }
451 if md_slice_eq(nm, off, len, "nx_wiki_gw.elf" as *u8, 0, 14) == 1 { return 1 }
452 if md_slice_eq(nm, off, len, "nx_hub_gw.elf" as *u8, 0, 13) == 1 { return 1 }
453 if md_slice_eq(nm, off, len, "nx_torrent_gw.elf" as *u8, 0, 17) == 1 { return 1 }
454 // P1 off-LAN parity: the torrent STACK binaries (deployed cross-dir into /volume1/ai/torrent/ by the
455 // torrentdeploy hostctl sub). Staged as <name>.new in nishihost cwd like every other target.
456 if md_slice_eq(nm, off, len, "nx_torrent_daemon.sov.elf" as *u8, 0, 25) == 1 { return 1 }
457 if md_slice_eq(nm, off, len, "nx_torrent_seedeval.elf" as *u8, 0, 23) == 1 { return 1 }
458 // build-over-API: the tree-pack primitive elf + the source-tree blob (unpacked by /api/unpack via nx_treepack).
459 if md_slice_eq(nm, off, len, "nx_treepack.elf" as *u8, 0, 15) == 1 { return 1 }
460 if md_slice_eq(nm, off, len, "buildsrc.pack" as *u8, 0, 13) == 1 { return 1 }
461 if md_slice_eq(nm, off, len, "buildknow.pack" as *u8, 0, 14) == 1 { return 1 }
462 // /api/compare server-side regen: the hub generator elf (exec'd by md_cmp_regen; updatable over the API).
463 if md_slice_eq(nm, off, len, "nx_swcompare_hub.elf" as *u8, 0, 20) == 1 { return 1 }
464 // /api/compare/publish staging slot: page bytes arrive chunked here, then publish pins them by sha256.
465 if md_slice_eq(nm, off, len, "compare.page" as *u8, 0, 12) == 1 { return 1 }
466 // the Relationship OS daemon (binds loopback :8027; conf row `relate` promotes it once hostctl ships relatedeploy).
467 if md_slice_eq(nm, off, len, "nx_relate_daemon.elf" as *u8, 0, 20) == 1 { return 1 }
468 // the site-visuals editor (loopback :18466; cut over API-pure via /api/restart service=siteedit which
469 // promotes the staged .new -> the FULL editor deploy loop is upload+restart, zero ssh).
470 if md_slice_eq(nm, off, len, "nx_siteedit_daemon.elf" as *u8, 0, 22) == 1 { return 1 }
471 // seq1433 HALF-WIRED DEPLOY LOOP FIXED: md_direct_restart_ok mapped service=toolsapi -> nx_tools_api_serve.elf
472 // and hc_restart_ok/hc_guard_tapi allowed+respawned it, but there was NO upload row -- so the staging slot
473 // nx_tools_api_serve.elf.new could never be written over the API and /api/restart toolsapi could only ever
474 // re-promote a STALE artifact. A restart verb without a staging slot is not a deploy loop. This is the daemon
475 // that gates EVERY agent capability = the one binary the ecosystem could not update API-first (cf. galxgw seq1049).
476 if md_slice_eq(nm, off, len, "nx_tools_api_serve.elf" as *u8, 0, 22) == 1 { return 1 }
477 // the Nishi Pulse survey/insights daemon (:8031, cron-reconciled; /api/restart service=survey promotes
478 // the staged .new -> the survey deploy loop is upload+restart, zero ssh/scp).
479 if md_slice_eq(nm, off, len, "nx_survey_daemon.elf" as *u8, 0, 20) == 1 { return 1 }
480 // the ETHICAL CLEAN-SERVE daemon (:8102, /clean -- neutralize attacks + PRESERVE safe ads + safety receipt;
481 // SSRF-guarded public fetch proxy). Deploy loop = /api/upload + /api/deploy target=cleanserve (hostctl supervise).
482 if md_slice_eq(nm, off, len, "nx_clean_serve_daemon.elf" as *u8, 0, 25) == 1 { return 1 }
483 // the Nishi Office daemon (:8030, cron-reconciled) + its client JS. Deploy loop = /api/upload + /api/restart
484 // service=office (daemon: promote .new + kill -> nx_office_reconcile respawns) / officejs (JS: promote only,
485 // the daemon reads office_app.js per-request). Zero ssh -- matches the survey pattern.
486 if md_slice_eq(nm, off, len, "nx_office_daemon.elf" as *u8, 0, 20) == 1 { return 1 }
487 if md_slice_eq(nm, off, len, "office_app.js" as *u8, 0, 13) == 1 { return 1 }
488 // THE BUILD TOOLCHAIN ITSELF (seq891/903). Staged as <name>.new in nishihost cwd like every other
489 // target, then promoted into buildroot/_offc by /api/promote_toolchain -- which validates the ELF,
490 // banks .prev, chmod +x, CANARY-COMPILES and auto-rolls-back. Uploading merely STAGES; it can never
491 // touch the live compiler, so these rows are safe on their own. Closes the gap where the ecosystem
492 // could deploy every service over its own API but not the compiler that builds them.
493 if md_slice_eq(nm, off, len, "nx_cc_sovereign.elf" as *u8, 0, 19) == 1 { return 1 }
494 if md_slice_eq(nm, off, len, "nxasm_x86_main.elf" as *u8, 0, 18) == 1 { return 1 }
495 if md_slice_eq(nm, off, len, "nx_sov_build_run.elf" as *u8, 0, 20) == 1 { return 1 }
496 return 0
497}
498
499// ---- /api/unpack: resolve a fail-closed unpack destination (dest-key -> staged .pack + abs NAS dir) ----------
500// NEVER-BRICK (#26): only allowlisted dest keys resolve; an unknown key -> 400, nothing written. Each key maps to
501// the STAGED pack (<key>.pack.new, from /api/upload) + a FIXED abs dir under nishihost (nx_treepack writes only
502// under it). Extend by adding a row. `buildsrc` = the runtime source tree for build-over-API.
503func md_unpack_resolve(nm: *u8, off: i64, len: i64, packbuf: *u8, destbuf: *u8) -> i64 {
504 if md_slice_eq(nm, off, len, "buildsrc" as *u8, 0, 8) == 1 {
505 md_copy_slice_z(packbuf, "buildsrc.pack.new" as *u8, 0, 17, 128)
506 md_copy_slice_z(destbuf, "/volume1/homes/elderwesto/nishihost/buildroot/runtime" as *u8, 0, 53, 256)
507 return 1
508 }
509 // `buildknow` = buildroot/knowledge DATA ring (2026-08-05, debt 1785937893): compare .q/.axes corpus banks
510 // and other knowledge data the buildroot-CWD generators (gapmap frontier) read; same staged-pack discipline.
511 if md_slice_eq(nm, off, len, "buildknow" as *u8, 0, 9) == 1 {
512 md_copy_slice_z(packbuf, "buildknow.pack.new" as *u8, 0, 18, 128)
513 md_copy_slice_z(destbuf, "/volume1/homes/elderwesto/nishihost/buildroot/knowledge" as *u8, 0, 55, 256)
514 return 1
515 }
516 return 0
517}
518// fork+exec the on-NAS nx_treepack (unpack mode) with (packpath, destpath); capture stdout -> outpath; exit code.
519func md_exec_treepack(packpath: *u8, destpath: *u8, outpath: *u8) -> i64 {
520 let helf: *u8 = "/volume1/homes/elderwesto/nishihost/nx_treepack.elf" as *u8
521 let args: *i64 = sys_mmap(32) as *i64
522 args[0] = "unpack" as *u8 as i64
523 args[1] = packpath as i64
524 args[2] = destpath as i64
525 return dep_run_capture(helf, args, 3, outpath)
526}
527
528// ---- CONTENT-PUBLISH namespace (publish-from-anywhere for STATIC site files, 2026-07-06) ------------------
529// A content target is `sites/nishifamily/synth/<basename>`: FIXED directory prefix (extend = add a prefix row
530// here, data-ring) + strict basename charset [a-z0-9_.-] (first char alphanumeric, no ".." run, bounded) +
531// extension in {.html, .png, .stl}. '/' is impossible inside the basename by charset, and the prefix is fixed,
532// so path traversal is impossible BY CONSTRUCTION. Services (.elf) stay on md_upload_target_ok + /api/deploy
533// (health-checked promotion); this namespace is static files promoted by /api/promote_content (atomic
534// .prev-backed swap -- no health-check needed, and it can never touch a binary or leave the site down).
535func md_content_ext_ok(nm: *u8, off: i64, len: i64) -> i64 {
536 if len > 5 {
537 var m: i64 = 1
538 let e1: *u8 = ".html" as *u8
539 var i: i64 = 0
540 while i < 5 { if (nm[off + len - 5 + i] as i64) != (e1[i] as i64) { m = 0; i = 5 } else { i = i + 1 } }
541 if m == 1 { return 1 }
542 }
543 if len > 4 {
544 var m2: i64 = 1
545 let e2: *u8 = ".png" as *u8
546 var j: i64 = 0
547 while j < 4 { if (nm[off + len - 4 + j] as i64) != (e2[j] as i64) { m2 = 0; j = 4 } else { j = j + 1 } }
548 if m2 == 1 { return 1 }
549 var m3: i64 = 1
550 let e3: *u8 = ".stl" as *u8
551 var k: i64 = 0
552 while k < 4 { if (nm[off + len - 4 + k] as i64) != (e3[k] as i64) { m3 = 0; k = 4 } else { k = k + 1 } }
553 if m3 == 1 { return 1 }
554 // whole-site static set (site-factory publish): the self-emitted sitemap.xml + robots.txt
555 var m4: i64 = 1
556 let e4: *u8 = ".xml" as *u8
557 var k4: i64 = 0
558 while k4 < 4 { if (nm[off + len - 4 + k4] as i64) != (e4[k4] as i64) { m4 = 0; k4 = 4 } else { k4 = k4 + 1 } }
559 if m4 == 1 { return 1 }
560 var m5: i64 = 1
561 let e5: *u8 = ".txt" as *u8
562 var k5: i64 = 0
563 while k5 < 4 { if (nm[off + len - 4 + k5] as i64) != (e5[k5] as i64) { m5 = 0; k5 = 4 } else { k5 = k5 + 1 } }
564 if m5 == 1 { return 1 }
565 }
566 // the sovereign video-client set (2026-07-11): app.v2.js + nx_video_client.wasm ride /api/upload ->
567 // /api/promote_content like every other static file (retires the nx_aw_send ssh push). Same trust rank
568 // as .html (which can carry <script> anyway); binaries (.elf) stay OUT of this namespace by construction.
569 if len > 3 {
570 var m6: i64 = 1
571 let e6: *u8 = ".js" as *u8
572 var k6: i64 = 0
573 while k6 < 3 { if (nm[off + len - 3 + k6] as i64) != (e6[k6] as i64) { m6 = 0; k6 = 3 } else { k6 = k6 + 1 } }
574 if m6 == 1 { return 1 }
575 }
576 if len > 5 {
577 var m7: i64 = 1
578 let e7: *u8 = ".wasm" as *u8
579 var k7: i64 = 0
580 while k7 < 5 { if (nm[off + len - 5 + k7] as i64) != (e7[k7] as i64) { m7 = 0; k7 = 5 } else { k7 = k7 + 1 } }
581 if m7 == 1 { return 1 }
582 }
583 // the EVIDENCE workstream (2026-07-16): every published evidence run carries api.json machine detail
584 // beside its index.html (dashboards speak plain english; machines get JSON). Same trust rank as .txt.
585 if len > 5 {
586 var m8: i64 = 1
587 let e8: *u8 = ".json" as *u8
588 var k8: i64 = 0
589 while k8 < 5 { if (nm[off + len - 5 + k8] as i64) != (e8[k8] as i64) { m8 = 0; k8 = 5 } else { k8 = k8 + 1 } }
590 if m8 == 1 { return 1 }
591 }
592 return 0
593}
594// prefix TABLE (the data ring this namespace was designed to grow by): returns the matched prefix length,
595// or -1. Each row is a FIXED site subdirectory; extend = add a row.
596func md_content_pfx(nm: *u8, off: i64, len: i64) -> i64 {
597 let p1: *u8 = "sites/nishifamily/synth/" as *u8
598 let l1: i64 = 24
599 if len > l1 {
600 var i: i64 = 0
601 var m: i64 = 1
602 while i < l1 { if (nm[off + i] as i64) != (p1[i] as i64) { m = 0; i = l1 } else { i = i + 1 } }
603 if m == 1 { return l1 }
604 }
605 let p2: *u8 = "sites/nishifamily/swgpu/" as *u8
606 let l2: i64 = 24
607 if len > l2 {
608 var i2: i64 = 0
609 var m2: i64 = 1
610 while i2 < l2 { if (nm[off + i2] as i64) != (p2[i2] as i64) { m2 = 0; i2 = l2 } else { i2 = i2 + 1 } }
611 if m2 == 1 { return l2 }
612 }
613 // the SITE-FACTORY showcase (generated archetype gallery) -- publishes via upload+promote_content
614 let p3: *u8 = "sites/nishifamily/factory/" as *u8
615 let l3: i64 = 26
616 if len > l3 {
617 var i3: i64 = 0
618 var m3: i64 = 1
619 while i3 < l3 { if (nm[off + i3] as i64) != (p3[i3] as i64) { m3 = 0; i3 = l3 } else { i3 = i3 + 1 } }
620 if m3 == 1 { return l3 }
621 }
622 // the SOVEREIGN-INFINIGEN showcases (/world, /gsplat) -- retires the flaky ssh-cat push (2026-07-09):
623 // publish = /api/upload (chunked+staged) -> /api/promote_content (atomic .prev-backed swap)
624 let p4: *u8 = "sites/nishifamily/world/" as *u8
625 let l4: i64 = 24
626 if len > l4 {
627 var i4: i64 = 0
628 var m4: i64 = 1
629 while i4 < l4 { if (nm[off + i4] as i64) != (p4[i4] as i64) { m4 = 0; i4 = l4 } else { i4 = i4 + 1 } }
630 if m4 == 1 { return l4 }
631 }
632 let p5: *u8 = "sites/nishifamily/gsplat/" as *u8
633 let l5: i64 = 25
634 if len > l5 {
635 var i5: i64 = 0
636 var m5: i64 = 1
637 while i5 < l5 { if (nm[off + i5] as i64) != (p5[i5] as i64) { m5 = 0; i5 = l5 } else { i5 = i5 + 1 } }
638 if m5 == 1 { return l5 }
639 }
640 // WHOLESALE-emitted multi-page sites (site-factory R-SITESHAPE): subdir paths allowed under this
641 // prefix via the guarded '/' rule in md_content_target_ok (never doubled, ".." runs still refused).
642 let p6: *u8 = "sites/nishifamily/wholesale/" as *u8
643 let l6: i64 = 28
644 if len > l6 {
645 var i6: i64 = 0
646 var m6: i64 = 1
647 while i6 < l6 { if (nm[off + i6] as i64) != (p6[i6] as i64) { m6 = 0; i6 = l6 } else { i6 = i6 + 1 } }
648 if m6 == 1 { return l6 }
649 }
650 // the public generate-UI over nx_gen (R10 of the Infinigen ladder)
651 let p7: *u8 = "sites/nishifamily/generate/" as *u8
652 let l7: i64 = 27
653 if len > l7 {
654 var i7: i64 = 0
655 var m7: i64 = 1
656 while i7 < l7 { if (nm[off + i7] as i64) != (p7[i7] as i64) { m7 = 0; i7 = l7 } else { i7 = i7 + 1 } }
657 if m7 == 1 { return l7 }
658 }
659 // the sovereign VIDEO CODEC client set (2026-07-11): index.html + app.v2.js + nx_video_client.wasm +
660 // ver.txt. Retires the last ssh (nx_aw_send) in the codec ship loop -- deploy8XX becomes /api/upload ->
661 // /api/promote_content, and the ship gate verifies the :8443 sovereign edge.
662 let p8: *u8 = "sites/nishifamily/video/" as *u8
663 let l8: i64 = 24
664 if len > l8 {
665 var i8: i64 = 0
666 var m8: i64 = 1
667 while i8 < l8 { if (nm[off + i8] as i64) != (p8[i8] as i64) { m8 = 0; i8 = l8 } else { i8 = i8 + 1 } }
668 if m8 == 1 { return l8 }
669 }
670 // the EVIDENCE workstream namespace (2026-07-16, operator: "publish evidence consistent workstream"):
671 // /evidence/<run>/ = nx_evidence_pack output (index.html + api.json + screenshots/recordings),
672 // published via the proven upload->promote_content lane (nx_content_ship ship.manifest). Subdir runs
673 // ride the same guarded '/' rule as wholesale/.
674 let p9: *u8 = "sites/nishifamily/evidence/" as *u8
675 let l9: i64 = 27
676 if len > l9 {
677 var i9: i64 = 0
678 var m9: i64 = 1
679 while i9 < l9 { if (nm[off + i9] as i64) != (p9[i9] as i64) { m9 = 0; i9 = l9 } else { i9 = i9 + 1 } }
680 if m9 == 1 { return l9 }
681 }
682 // the EXPERIENTIAL census page (2026-07-16): EMITTED by nx_s21_census (sync-by-construction) and
683 // republished through this lane on every census re-run -- the page can never drift from disk truth.
684 let p10: *u8 = "sites/nishifamily/experiential/" as *u8
685 let l10: i64 = 31
686 if len > l10 {
687 var i10: i64 = 0
688 var m10: i64 = 1
689 while i10 < l10 { if (nm[off + i10] as i64) != (p10[i10] as i64) { m10 = 0; i10 = l10 } else { i10 = i10 + 1 } }
690 if m10 == 1 { return l10 }
691 }
692 // the COMPARE hub artifacts (2026-08-05, debt 1785937233): /compare api.json + index.html + openapi.json
693 // are laptop-generated (registry is laptop-owned by design) and ship through the proven
694 // upload -> promote_content lane; per-domain spoke pages stay NAS-regen-owned (nx_compare_regen).
695 // This row closes the hub-vs-spoke drift class: the hub gets a DOOR instead of a frozen snapshot.
696 let p11: *u8 = "sites/nishifamily/compare/" as *u8
697 let l11: i64 = 26
698 if len > l11 {
699 var i11: i64 = 0
700 var m11: i64 = 1
701 while i11 < l11 { if (nm[off + i11] as i64) != (p11[i11] as i64) { m11 = 0; i11 = l11 } else { i11 = i11 + 1 } }
702 if m11 == 1 { return l11 }
703 }
704 return 0 - 1
705}
706func md_content_target_ok(nm: *u8, off: i64, len: i64) -> i64 {
707 let pl: i64 = md_content_pfx(nm, off, len)
708 if pl < 0 { return 0 }
709 if len <= pl + 4 { return 0 } // needs prefix + at least an "a.png"-sized basename
710 if len > pl + 64 { return 0 } // bounded basename
711 let c0: i64 = nm[off + pl] as i64 // first basename char: alphanumeric only (blocks ".x" "-x" "..")
712 var ok0: i64 = 0
713 if c0 >= 97 { if c0 <= 122 { ok0 = 1 } }
714 if c0 >= 48 { if c0 <= 57 { ok0 = 1 } }
715 if ok0 == 0 { return 0 }
716 var j: i64 = pl
717 var prevdot: i64 = 0
718 var prevslash: i64 = 0
719 while j < len {
720 let c: i64 = nm[off + j] as i64
721 var okc: i64 = 0
722 if c >= 97 { if c <= 122 { okc = 1 } }
723 if c >= 48 { if c <= 57 { okc = 1 } }
724 if c == 95 { okc = 1 }
725 if c == 45 { okc = 1 }
726 // subdir separator for multi-page sites: never doubled, never after a dot (with the ".."-run
727 // refusal below and the pinned prefix, traversal stays impossible by construction).
728 if c == 47 {
729 if prevslash == 1 { return 0 }
730 if prevdot == 1 { return 0 }
731 okc = 1
732 prevslash = 1
733 } else { prevslash = 0 }
734 if c == 46 {
735 if prevdot == 1 { return 0 } // ".." run -> refuse
736 okc = 1
737 prevdot = 1
738 } else { prevdot = 0 }
739 if okc == 0 { return 0 }
740 j = j + 1
741 }
742 return md_content_ext_ok(nm, off, len)
743}
744
745// ---- COMPARE namespace (Nishi Compare registry SSOT + server-side hub regen, 2026-07-09) --------------------
746// The CONCURRENT-WORK coordination plane for /compare: many sessions publish comparisons, so the shared registry
747// + hub are mutated through THIS one serialized daemon instead of racing raw file writes. The unit of mutation is
748// the COMPARISON RECORD keyed by its /compare/<domain> href segment: different-domain upserts are commutative
749// (merge, no clobber possible); same-domain upserts replace, with the previous line preserved in registry.hist
750// (additive-only). After a mutation the hub index.html + api.json are regenerated SERVER-SIDE from the SSOT by the
751// on-NAS nx_swcompare_hub.elf, so the published surface can never reflect a session's stale partial registry.
752// Installs are sanity-gated + .prev-backed atomic renames (never-brick: a failed regen leaves live files untouched).
753
754// extract the /compare/<domain> merge key from a registry line (field 3 of title|kind|href|radar|stat).
755// Returns domain length copied into domb (NUL-terminated), or 0 if the line/href is malformed. Charset [a-z0-9_-].
756func md_cmp_domain_of(src: *u8, off: i64, len: i64, domb: *u8, cap: i64) -> i64 {
757 var p: i64 = 0
758 var f: i64 = 0
759 while p < len {
760 if (src[off + p] as i64) == 124 { f = f + 1; if f == 2 { p = p + 1; break } }
761 p = p + 1
762 }
763 if f != 2 { return 0 }
764 let pfx: *u8 = "/compare/" as *u8
765 var k: i64 = 0
766 while k < 9 {
767 if p + k >= len { return 0 }
768 if (src[off + p + k] as i64) != (pfx[k] as i64) { return 0 }
769 k = k + 1
770 }
771 var q: i64 = p + 9
772 var o: i64 = 0
773 while q < len {
774 let c: i64 = src[off + q] as i64
775 if c == 124 { break }
776 if c == 47 { break }
777 var okc: i64 = 0
778 if c >= 97 { if c <= 122 { okc = 1 } }
779 if c >= 48 { if c <= 57 { okc = 1 } }
780 if c == 95 { okc = 1 }
781 if c == 45 { okc = 1 }
782 if okc == 0 { return 0 }
783 if o < cap - 1 { domb[o] = src[off + q]; o = o + 1 }
784 q = q + 1
785 }
786 domb[o] = 0 as u8
787 if o < 1 { return 0 }
788 return o
789}
790
791func 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 }
792func md_cmp_wn(fd: i64, v: i64) -> i64 {
793 var m: i64 = v
794 if m < 0 { md_cmp_ws(fd, "-" as *u8); m = 0 - m }
795 let t: *u8 = sys_mmap(24)
796 var k: i64 = 0
797 if m == 0 { t[0] = 48 as u8; k = 1 }
798 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
799 let o2: *u8 = sys_mmap(24)
800 var i: i64 = 0
801 while i < k { o2[i] = t[k - 1 - i]; i = i + 1 }
802 sys_write(fd, o2, k)
803 return 0
804}
805
806// merge ONE registry line into the SSOT by domain key: replace the matching entry (old line -> .hist) or append.
807// Atomic (write registry.tmp -> rename); additive history appended AFTER the rename lands. Returns the new entry
808// count, or -1 on write failure (registry untouched -- the tmp+rename never half-writes the live file).
809func md_cmp_upsert(line: *u8, ln: i64, domb: *u8, domn: i64, replacedb: *i64) -> i64 {
810 let szp: *i64 = sys_mmap(16) as *i64
811 let old: *u8 = md_read_file("knowledge/compare/registry" as *u8, szp)
812 let on: i64 = szp[0]
813 let nb: *u8 = sys_mmap(262144)
814 let d2: *u8 = sys_mmap(128)
815 let oldline: *u8 = sys_mmap(4096)
816 var oldn: i64 = 0
817 var o: i64 = 0
818 var entries: i64 = 0
819 var replaced: i64 = 0
820 if (old as i64) != 0 {
821 var i: i64 = 0
822 while i < on {
823 let le: i64 = md_eol(old, on, i)
824 var wrote: i64 = 0
825 if le > i {
826 if (old[i] as i64) != 35 {
827 let dl2: i64 = md_cmp_domain_of(old, i, le - i, d2, 120)
828 if dl2 > 0 {
829 entries = entries + 1
830 if md_slice_eq(d2, 0, dl2, domb, 0, domn) == 1 {
831 replaced = 1
832 wrote = 1
833 oldn = 0
834 var c: i64 = 0
835 while c < (le - i) { if c < 4090 { oldline[c] = old[i + c]; oldn = c + 1 } c = c + 1 }
836 var w2: i64 = 0
837 while w2 < ln { nb[o] = line[w2]; o = o + 1; w2 = w2 + 1 }
838 nb[o] = 10 as u8
839 o = o + 1
840 }
841 }
842 }
843 }
844 if wrote == 0 {
845 var c2: i64 = i
846 while c2 < le { nb[o] = old[c2]; o = o + 1; c2 = c2 + 1 }
847 nb[o] = 10 as u8
848 o = o + 1
849 }
850 i = le + 1
851 }
852 }
853 if replaced == 0 {
854 var w3: i64 = 0
855 while w3 < ln { nb[o] = line[w3]; o = o + 1; w3 = w3 + 1 }
856 nb[o] = 10 as u8
857 o = o + 1
858 entries = entries + 1
859 }
860 let fd: i64 = sys_openat_wr("knowledge/compare/registry.tmp" as *u8, 0x1a4)
861 if fd < 0 { replacedb[0] = replaced; return 0 - 1 }
862 sys_write(fd, nb, o)
863 sys_close(fd)
864 if sys_renameat("knowledge/compare/registry.tmp" as *u8, "knowledge/compare/registry" as *u8) != 0 {
865 replacedb[0] = replaced
866 return 0 - 1
867 }
868 let hf: i64 = sys_openat_append("knowledge/compare/registry.hist" as *u8, 0x1a4)
869 if hf >= 0 {
870 md_cmp_ws(hf, "ts=" as *u8)
871 md_cmp_wn(hf, sys_now_realtime_sec())
872 md_cmp_ws(hf, " op=upsert domain=" as *u8)
873 var hd: i64 = 0
874 while hd < domn { sys_write(hf, ((domb as i64) + hd) as *u8, 1); hd = hd + 1 }
875 md_cmp_ws(hf, " replaced=" as *u8)
876 md_cmp_wn(hf, replaced)
877 md_cmp_ws(hf, "\n" as *u8)
878 if replaced == 1 { if oldn > 0 {
879 md_cmp_ws(hf, " prev: " as *u8)
880 sys_write(hf, oldline, oldn)
881 md_cmp_ws(hf, "\n" as *u8)
882 } }
883 sys_close(hf)
884 }
885 replacedb[0] = replaced
886 return entries
887}
888
889// install a generator-captured output file as a live docroot file: sanity (size + first byte) -> write tmp ->
890// back up live -> rename tmp over live; on failure the previous live file is restored (mirror of promote_content).
891func md_cmp_install(srcp: *u8, tmpp: *u8, prevp: *u8, livep: *u8, firstc: i64) -> i64 {
892 let szp: *i64 = sys_mmap(16) as *i64
893 let b: *u8 = md_read_file(srcp, szp)
894 let n: i64 = szp[0]
895 if (b as i64) == 0 { return 0 }
896 if n < 200 { return 0 }
897 if (b[0] as i64) != firstc { return 0 }
898 let fd: i64 = sys_openat_wr(tmpp, 0x1a4)
899 if fd < 0 { return 0 }
900 sys_write(fd, b, n)
901 sys_close(fd)
902 var had: i64 = 0
903 let pf: i64 = sys_openat_rd(livep)
904 if pf >= 0 { sys_close(pf); had = 1 }
905 if had == 1 { md_rotate_prev(prevp); if sys_renameat(livep, prevp) != 0 { return 0 } }
906 if sys_renameat(tmpp, livep) != 0 {
907 if had == 1 { sys_renameat(prevp, livep) }
908 return 0
909 }
910 return 1
911}
912
913// regenerate the /compare hub (index.html + api.json) from the registry SSOT via the on-NAS hub generator elf.
914// Fail-safe: generator output must pass sanity before install; a missing elf / bad output leaves live files alone.
915func md_cmp_regen() -> i64 {
916 let helf: *u8 = "/volume1/homes/elderwesto/nishihost/nx_swcompare_hub.elf" as *u8
917 // Capture the generator output to an ELDERWESTO-OWNED scratch dir (knowledge/compare/, CWD=nishihost), NOT
918 // world-writable /tmp. WHY (2026-07-14 root cause): a root-era mgmt run left /tmp/nx_ma_cmp_*.out root-owned;
919 // after the root->elderwesto guard migration this daemon could no longer OVERWRITE them -> dep_run_capture's
920 // sys_openat_wr failed (EACCES), it ran the generator WITHOUT redirect, and md_cmp_install re-installed the
921 // STALE file every regen (silent "OK", frozen hub). A path this daemon owns always truncates fresh -> correct
922 // install, or an empty capture that fails md_cmp_install's sanity gate -> honest REGEN-FAILED (never stale).
923 let a1: *i64 = sys_mmap(16) as *i64
924 a1[0] = "html" as *u8 as i64
925 dep_run_capture(helf, a1, 1, "knowledge/compare/.regen_html.out" as *u8)
926 let a2: *i64 = sys_mmap(16) as *i64
927 a2[0] = "json" as *u8 as i64
928 dep_run_capture(helf, a2, 1, "knowledge/compare/.regen_json.out" as *u8)
929 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)
930 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)
931 if ok1 == 1 { if ok2 == 1 { return 1 } }
932 return 0
933}
934
935// validate a bare compare DOMAIN atom: charset [a-z0-9_-], first char alphanumeric, len 1..60 -> copy NUL-terminated.
936// Path segments are built ONLY from this validated atom + fixed literals, so traversal is impossible by construction.
937func md_cmp_dom_ok(src: *u8, off: i64, len: i64, domb: *u8) -> i64 {
938 if len < 1 { return 0 }
939 if len > 60 { return 0 }
940 let c0: i64 = src[off] as i64
941 var ok0: i64 = 0
942 if c0 >= 97 { if c0 <= 122 { ok0 = 1 } }
943 if c0 >= 48 { if c0 <= 57 { ok0 = 1 } }
944 if ok0 == 0 { return 0 }
945 var i: i64 = 0
946 while i < len {
947 let c: i64 = src[off + i] as i64
948 var okc: i64 = 0
949 if c >= 97 { if c <= 122 { okc = 1 } }
950 if c >= 48 { if c <= 57 { okc = 1 } }
951 if c == 95 { okc = 1 }
952 if c == 45 { okc = 1 }
953 if okc == 0 { return 0 }
954 domb[i] = src[off + i]
955 i = i + 1
956 }
957 domb[len] = 0 as u8
958 return 1
959}
960func 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 }
961
962// publish the STAGED compare.page.new as the live artifact for (domain, kind). kind: 1=page 2=frontier 3=bench 4=api.
963// Server derives the FIXED docroot path from the validated domain atom + a kind enum (no caller-supplied paths at
964// all). Dirs are created as needed; install is sanity-gated + .prev-backed (md_cmp_install). kind=api ALSO refreshes
965// the hub data-link marker knowledge/compare/<domain>-api.json (tmp+rename). Returns 1 ok / 0 fail (live untouched).
966func md_cmp_publish(domb: *u8, kind: i64) -> i64 {
967 let base: *u8 = sys_mmap(512)
968 var o: i64 = md_cmp_cat(base, 0, "sites/nishifamily/compare/" as *u8)
969 o = md_cmp_cat(base, o, domb)
970 sys_mkdir(base, 0x1ed)
971 if kind == 2 { o = md_cmp_cat(base, o, "/frontier" as *u8); sys_mkdir(base, 0x1ed) }
972 if kind == 3 { o = md_cmp_cat(base, o, "/bench" as *u8); sys_mkdir(base, 0x1ed) }
973 let live: *u8 = sys_mmap(512)
974 var lo: i64 = md_cmp_cat(live, 0, base)
975 var fc: i64 = 60
976 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) }
977 let tmpp: *u8 = sys_mmap(512)
978 var to: i64 = md_cmp_cat(tmpp, 0, live)
979 to = md_cmp_cat(tmpp, to, ".tmp2" as *u8)
980 let prevp: *u8 = sys_mmap(512)
981 var po: i64 = md_cmp_cat(prevp, 0, live)
982 po = md_cmp_cat(prevp, po, ".prev" as *u8)
983 let oki: i64 = md_cmp_install("compare.page.new" as *u8, tmpp, prevp, live, fc)
984 if oki != 1 { return 0 }
985 if kind == 4 {
986 let mk: *u8 = sys_mmap(512)
987 var mo: i64 = md_cmp_cat(mk, 0, "knowledge/compare/" as *u8)
988 mo = md_cmp_cat(mk, mo, domb)
989 mo = md_cmp_cat(mk, mo, "-api.json" as *u8)
990 let mt: *u8 = sys_mmap(512)
991 var mto: i64 = md_cmp_cat(mt, 0, mk)
992 mto = md_cmp_cat(mt, mto, ".tmp" as *u8)
993 let szp: *i64 = sys_mmap(16) as *i64
994 let b: *u8 = md_read_file("compare.page.new" as *u8, szp)
995 if (b as i64) != 0 { if szp[0] > 0 {
996 let fd: i64 = sys_openat_wr(mt, 0x1a4)
997 if fd >= 0 { sys_write(fd, b, szp[0]); sys_close(fd); sys_renameat(mt, mk) }
998 } }
999 }
1000 return 1
1001}
1002
1003// map an allowlisted service name -> the proven nx_aw_hostctl surgical-restart sub (fail-closed: unknown -> 0).
1004// ---- RESTART ALLOWLISTS: ONE OWNER PER LANE, TABLE-DRIVEN (2026-08-28) ------------------------------
1005// WHY THIS SHAPE. These two allowlists were if-chains and the /api/restart error message was a THIRD
1006// hand-written copy of both. That is the duplicate-ruler defect, and THIS FILE ALREADY RECORDS IT
1007// HAPPENING -- seq1433: "this list DRIFTED from the two allowlists it describes -- office/officejs/
1008// toolsapi were wired into md_direct_restart_ok but never named here, so a caller asking for a service
1009// that IS supported was told it was unknown." That drift was repaired by hand, which fixes the instance
1010// and leaves the shape. ★WHEN TWO THINGS MUST AGREE, MAKE DISAGREEMENT IMPOSSIBLE BY CONSTRUCTION --
1011// DO NOT COORDINATE BY DISCIPLINE. The rows below are now the ONLY declaration of a restart target: the
1012// resolvers read them and the published message is GENERATED from them (md_tbl_names), so the message
1013// can never again name a route that does not resolve, nor omit one that does. Adding a route is ONE
1014// data row, and nx_restart_routes_gate proves both directions.
1015//
1016// GRAMMAR: <svc>|<target>;<svc>|<target>;... Separators are plain printable ASCII with NO escape
1017// sequence -- a bare tab or newline inside an nx literal is a banked lexer hazard, and '|' is already
1018// the separator the published message uses, so md_tbl_names emits it verbatim.
1019// MATCHING IS EXACT-LENGTH, INHERITED NOT RE-IMPLEMENTED: md_slice_eq refuses on a length mismatch, so
1020// "site" still cannot match "siteedit" and "office" cannot match "officejs" -- the property the if-chain
1021// got from its hand-counted literal lengths, now DERIVED from the row's own field width. That deletes
1022// the hand-counted-length class outright (a literal and a number beside it that drift in silence).
1023const MD_TBL_FIELD: i64 = 124 // '|' -- field separator, and the message's own display separator
1024const MD_TBL_ROW: i64 = 59 // ';' -- row terminator
1025// The resolvers write into a caller-allocated buffer. ONE const owns that size and BOTH the callers in
1026// nx_mgmt_api and the copies here read it, so the allocation and the bound cannot disagree.
1027const MD_RESTART_BUFCAP: i64 = 64
1028//
1029// LANE 1 -- hostctl-sub restarts: the mgmt daemon runs `nx_hostctl <sub>`; hostctl owns the kill+respawn.
1030const MD_RESTART_SUB_TBL: *u8 = "reader|kickreader;torrent|kicktorrent;torrentgw|kicktorrentgw;docportal|kickdocportal;"
1031//
1032// LANE 2 -- DIRECT restarts (no hostctl sub): svc -> the exact process cmdline needle. The mgmt daemon
1033// (root, itself guard-supervised) kills by name; a supervisor respawns the on-disk binary. Promote a
1034// staged .new first and this IS the full API-pure deploy loop. WHY EACH ROW EXISTS, kept with the row it
1035// explains so a reader can still grep the svc name and find its history:
1036// siteedit -- the andelinwest site editor.
1037// sites -- sites.elf = THE EDGE. Restart (kill -> guard respawns) RE-READS proxy_routes.conf, so
1038// this doubles as the API-pure route reload AND an edge redeploy if sites.elf.new is
1039// staged. The needle "sites.elf" is unique to the edge (nx_sites_daemon_v2 and
1040// nx_sites_reconciled do not contain it).
1041// survey -- Nishi Pulse: kill -> the nx_survey_reconcile cron respawns <=60s (not hostctl-guarded;
1042// the reconcile row IS its supervisor -- crash+reboot proven 2026-07-10).
1043// office -- kill -> nx_office_reconcile cron respawns <=60s (SO_REUSEADDR = fast rebind).
1044// officejs -- the client JS: promote office_app.js.new -> live. kill-by-name matches NO process (it
1045// is a file, not a daemon) -> harmless; the daemon reads it fresh on the next request.
1046// toolsapi -- the R0 agent-facing tools daemon :18096 (hostctl guard-supervised): respawn <=15s.
1047// Makes the tools/MCP plane API-pure-deployable.
1048// seed -- BitTorrent SEEDER :6881. ★NOTE THE SPLIT, IT IS DELIBERATE: md_promote_staged would
1049// target nishihost/nx_torrent_seed.elf.new, but the LIVE seeder lives at
1050// /volume1/ai/torrent/ and is promoted there by cmd_torrentdeploy. No such .new exists at
1051// the nishihost root, so the promote half cleanly NO-OPS and only the kill does the work.
1052// Promoting to the nishihost path instead would place a binary NOTHING EVER RUNS while
1053// reporting a successful restart.
1054// clock -- nx_clock_tickless.elf (guard-supervised). Its main loop only exits at 120 windows x
1055// 1800s = 60 HOURS per life, so without this route a promoted clock binary sat on disk
1056// while the running process kept executing the previous code for up to 60 hours -- every
1057// clock fix adopted by TIMEOUT rather than by deploy. ★PROMOTED IS A CLAIM ABOUT THE
1058// DISK; ADOPTED IS A CLAIM ABOUT A PROCESS. Safe: the same respawn the life budget
1059// already performs, just on demand; schedule state persists to knowledge/store/
1060// clocksched- at every window end and clk_edf_migrate re-arms stale deadlines.
1061// wiki -- nx_wiki_gw.elf :18791 (guard-supervised). Added 2026-08-17 when it was the
1062// worst-committed process on the box (~0.7 MB/beat past 3.0 GB, swap 757->787 permil):
1063// the one daemon driving the host toward the swap cliff was ALSO the one /api/restart
1064// could not bounce, so the only remediation was a hard host action.
1065// email -- nx_email_portal_daemon.elf :18465. Added 2026-08-28 for EXACTLY the reason the wiki row
1066// was, one daemon later: measured worst_committed_kb=2,783,996 (2.78 GB) with swap at
1067// 943-951 permil against a 850 RED bar, nx_resmon verdict=RED sev=2 -- and no sanctioned
1068// way to recycle it. SUPERVISOR VERIFIED BEFORE WIRING, not assumed: daemons.reg row
1069// `email_portal_daemon ... | 18465 | revive | http` is armed to REVIVE, and this daemon is
1070// absent from nx_hostctl's supervise roster, so there is exactly ONE supervisor and this
1071// route cannot create the dueling-supervisor amplifier this estate has hit three times.
1072// Kill -> revive is the same respawn that supervisor already performs on a crash.
1073// ⚠HONEST RESIDUAL: unlike the wiki, a mail portal holds a maildir; the kill is a normal
1074// process death, so anything the daemon had not yet fsynced is lost exactly as it would
1075// be on any crash. Recycle it when the box is quiet, not mid-delivery.
1076const MD_RESTART_DIRECT_TBL: *u8 = "siteedit|nx_siteedit_daemon.elf;sites|sites.elf;survey|nx_survey_daemon.elf;office|nx_office_daemon.elf;officejs|office_app.js;toolsapi|nx_tools_api_serve.elf;seed|nx_torrent_seed.elf;clock|nx_clock_tickless.elf;wiki|nx_wiki_gw.elf;email|nx_email_portal_daemon.elf;"
1077
1078// Scan ONE table row starting at `i`: barbox gets the '|' offset (-1 if the row has none), endbox gets
1079// the row terminator (the ';' or the NUL). ONE scanner, TWO consumers (lookup + names), so a grammar
1080// change cannot land in one and be missed by the other. The cursor is never clobbered to exit -- `end`
1081// is a separate flag-cum-result, the banked remedy for the loop-exit-sentinel class.
1082func md_tbl_row(tbl: *u8, i: i64, barbox: *i64, endbox: *i64) -> i64 {
1083 var p: i64 = i
1084 var bar: i64 = 0 - 1
1085 var end: i64 = 0 - 1
1086 while end < 0 {
1087 let c: i64 = tbl[p] as i64
1088 if c == 0 { end = p } else {
1089 if c == MD_TBL_ROW { end = p } else {
1090 if c == MD_TBL_FIELD { if bar < 0 { bar = p } }
1091 p = p + 1
1092 }
1093 }
1094 }
1095 barbox[0] = bar
1096 endbox[0] = end
1097 return end
1098}
1099
1100// A row is USABLE only with a non-empty name AND a non-empty target. A malformed row is SKIPPED, never
1101// half-read: without this, a row like "x|;" would resolve to an EMPTY needle and md_kill_by_name("")
1102// is a kill request with no subject. The guard is wrong in the direction of doing nothing.
1103func md_tbl_row_ok(i: i64, bar: i64, end: i64) -> i64 {
1104 if bar <= i { return 0 }
1105 if end <= bar + 1 { return 0 }
1106 return 1
1107}
1108
1109// Walk a table; on an EXACT-LENGTH svc match copy that row's target into outbuf and return 1, else 0.
1110// Composes md_slice_eq and md_copy_slice_z -- the same two primitives the if-chains used, so matching
1111// and copying behaviour is inherited rather than rewritten.
1112func md_tbl_lookup(tbl: *u8, svc: *u8, off: i64, len: i64, outbuf: *u8, cap: i64) -> i64 {
1113 let barbox: *i64 = sys_mmap(16) as *i64
1114 let endbox: *i64 = sys_mmap(16) as *i64
1115 var i: i64 = 0
1116 var done: i64 = 0
1117 var hit: i64 = 0
1118 while done == 0 {
1119 if tbl[i] == (0 as u8) { done = 1 } else {
1120 md_tbl_row(tbl, i, barbox, endbox)
1121 let bar: i64 = barbox[0]
1122 let end: i64 = endbox[0]
1123 if md_tbl_row_ok(i, bar, end) == 1 {
1124 if md_slice_eq(svc, off, len, tbl, i, bar - i) == 1 {
1125 md_copy_slice_z(outbuf, tbl, bar + 1, end - bar - 1, cap)
1126 hit = 1
1127 done = 1
1128 }
1129 }
1130 if done == 0 { if tbl[end] == (0 as u8) { done = 1 } else { i = end + 1 } }
1131 }
1132 }
1133 return hit
1134}
1135
1136// Append every declared svc name from a table as "a|b|c" (no trailing separator), returning the new
1137// offset. THE PUBLISHED ERROR MESSAGE IS BUILT FROM THIS, so it is a projection of the resolver's own
1138// data and cannot drift from it.
1139func md_tbl_names(tbl: *u8, dst: *u8, o: i64) -> i64 {
1140 let barbox: *i64 = sys_mmap(16) as *i64
1141 let endbox: *i64 = sys_mmap(16) as *i64
1142 var out: i64 = o
1143 var i: i64 = 0
1144 var first: i64 = 1
1145 var done: i64 = 0
1146 while done == 0 {
1147 if tbl[i] == (0 as u8) { done = 1 } else {
1148 md_tbl_row(tbl, i, barbox, endbox)
1149 let bar: i64 = barbox[0]
1150 let end: i64 = endbox[0]
1151 if md_tbl_row_ok(i, bar, end) == 1 {
1152 if first == 0 { dst[out] = MD_TBL_FIELD as u8; out = out + 1 }
1153 var k: i64 = i
1154 while k < bar { dst[out] = tbl[k]; out = out + 1; k = k + 1 }
1155 first = 0
1156 }
1157 if tbl[end] == (0 as u8) { done = 1 } else { i = end + 1 }
1158 }
1159 }
1160 return out
1161}
1162
1163func md_restart_sub(svc: *u8, off: i64, len: i64, subbuf: *u8) -> i64 {
1164 return md_tbl_lookup(MD_RESTART_SUB_TBL, svc, off, len, subbuf, MD_RESTART_BUFCAP)
1165}
1166
1167// DIRECT-restart allowlist (no hostctl sub needed): svc name -> the exact process cmdline needle. The mgmt
1168// daemon (root, itself guard-supervised) kills by name; the hostctl supervise guard respawns the on-disk
1169// binary <=15s -- so promote-a-staged-.new + /api/restart = the full API-pure editor deploy loop.
1170// THE RESOLVER IS NOW A PROJECTION OF THE TABLE ABOVE. It was an if-chain of hand-counted literal
1171// lengths; every row's reasoning is preserved verbatim in the MD_RESTART_DIRECT_TBL comment block, where
1172// it stays greppable by svc name. Behaviour is EQUIVALENT, not merely similar, and that equivalence is
1173// proven exhaustively (all 9 legacy names -> their exact legacy needles, plus prefix/suffix/case/
1174// cross-table negatives) by nx_restart_routes_gate rather than asserted here.
1175func md_direct_restart_ok(svc: *u8, off: i64, len: i64, namebuf: *u8) -> i64 {
1176 return md_tbl_lookup(MD_RESTART_DIRECT_TBL, svc, off, len, namebuf, MD_RESTART_BUFCAP)
1177}
1178// -- /api/route: validate + append a proxy_routes.conf row (data ring). Format: "<host> <prefix> <port> <mode>".
1179func md_catn(d: *u8, o: i64, v: i64) -> i64 {
1180 if v == 0 { d[o] = 48 as u8; return o + 1 }
1181 var m: i64 = v
1182 var oo: i64 = o
1183 if m < 0 { d[oo] = 45 as u8; oo = oo + 1; m = 0 - m }
1184 var nd: i64 = 1
1185 var t: i64 = m
1186 while t >= 10 { nd = nd + 1; t = t / 10 }
1187 var i: i64 = nd - 1
1188 while i >= 0 { d[oo + i] = (48 + (m % 10)) as u8; m = m / 10; i = i - 1 }
1189 return oo + nd
1190}
1191// fail-closed validation of a route (null-terminated host/prefix/mode + numeric port).
1192func md_route_valid(host: *u8, prefix: *u8, port: i64, mode: *u8) -> i64 {
1193 let hl: i64 = md_len(host)
1194 if hl < 3 { return 0 }
1195 if hl > 64 { return 0 }
1196 if (host[0] as i64) == 46 { return 0 }
1197 if (host[0] as i64) == 45 { return 0 }
1198 var hasdot: i64 = 0
1199 var i: i64 = 0
1200 while i < hl {
1201 let c: i64 = host[i] as i64
1202 var ok: i64 = 0
1203 if c >= 97 { if c <= 122 { ok = 1 } }
1204 if c >= 48 { if c <= 57 { ok = 1 } }
1205 if c == 45 { ok = 1 }
1206 if c == 46 { ok = 1; hasdot = 1; if i > 0 { if (host[i - 1] as i64) == 46 { return 0 } } }
1207 if ok == 0 { return 0 }
1208 i = i + 1
1209 }
1210 if hasdot == 0 { return 0 }
1211 let pl: i64 = md_len(prefix)
1212 if pl < 2 { return 0 }
1213 if pl > 48 { return 0 }
1214 if (prefix[0] as i64) != 47 { return 0 }
1215 i = 1
1216 while i < pl {
1217 let c: i64 = prefix[i] as i64
1218 var ok: i64 = 0
1219 if c >= 97 { if c <= 122 { ok = 1 } }
1220 if c >= 48 { if c <= 57 { ok = 1 } }
1221 if c == 95 { ok = 1 }
1222 if c == 45 { ok = 1 }
1223 if c == 47 { ok = 1; if (prefix[i - 1] as i64) == 47 { return 0 } }
1224 if c == 46 { ok = 1; if (prefix[i - 1] as i64) == 46 { return 0 } }
1225 if ok == 0 { return 0 }
1226 i = i + 1
1227 }
1228 if port < 1024 { return 0 }
1229 if port > 65535 { return 0 }
1230 var mok: i64 = 0
1231 if md_cstr_eq(mode, "buffered" as *u8) == 1 { mok = 1 }
1232 if md_cstr_eq(mode, "stream" as *u8) == 1 { mok = 1 }
1233 if md_cstr_eq(mode, "gated" as *u8) == 1 { mok = 1 }
1234 if mok == 0 { return 0 }
1235 return 1
1236}
1237// atomically upsert the route row into knowledge/hosting/proxy_routes.conf: preserve every OTHER line, replace
1238// any existing "<host> <prefix> ..." row, append the new one; tmp+rename. Returns 1 ok / 0 write-fail.
1239func md_route_append(confp: *u8, host: *u8, prefix: *u8, port: i64, mode: *u8) -> i64 {
1240 let szp: *i64 = sys_mmap(16) as *i64
1241 szp[0] = 0
1242 let old: *u8 = md_read_file(confp, szp)
1243 let oldn: i64 = szp[0]
1244 // NEVER-BRICK: refuse to write when the existing table is unreadable/empty. A transient read failure
1245 // (fd exhaustion etc.) with old==0 would otherwise REPLACE the populated edge table with one row ->
1246 // every proxied surface incl. /api itself lost = self-lockout. The live table always has rows; a
1247 // genuinely fresh bootstrap is an ssh-once operation, not this API's job. Fail-closed.
1248 if (old as i64) == 0 { return 0 }
1249 if oldn == 0 { return 0 }
1250 // build the dedup match key: "<host> <prefix> "
1251 let mk: *u8 = sys_mmap(160)
1252 var ko: i64 = 0
1253 var a: i64 = 0
1254 while host[a] != (0 as u8) { mk[ko] = host[a]; ko = ko + 1; a = a + 1 }
1255 mk[ko] = 32 as u8; ko = ko + 1
1256 a = 0
1257 while prefix[a] != (0 as u8) { mk[ko] = prefix[a]; ko = ko + 1; a = a + 1 }
1258 mk[ko] = 32 as u8; ko = ko + 1
1259 mk[ko] = 0 as u8
1260 let mkl: i64 = ko
1261 let out: *u8 = sys_mmap(262144)
1262 var o: i64 = 0
1263 if (old as i64) != 0 {
1264 var ls: i64 = 0
1265 while ls < oldn {
1266 var le: i64 = ls
1267 var sc: i64 = 1
1268 while sc == 1 { if le >= oldn { sc = 0 } else { if (old[le] as i64) == 10 { sc = 0 } else { le = le + 1 } } }
1269 // does this line start with the match key?
1270 var m: i64 = 1
1271 if ls + mkl > le + 1 { m = 0 }
1272 if m == 1 {
1273 var j: i64 = 0
1274 while j < mkl { if (old[ls + j] as i64) != (mk[j] as i64) { m = 0; j = mkl } else { j = j + 1 } }
1275 }
1276 if m == 0 {
1277 var k: i64 = ls
1278 while k <= le { if k < oldn { out[o] = old[k]; o = o + 1 } k = k + 1 }
1279 }
1280 ls = le + 1
1281 }
1282 }
1283 // append the new row (ensure a trailing newline precedes if the file didn't end in one)
1284 if o > 0 { if (out[o - 1] as i64) != 10 { out[o] = 10 as u8; o = o + 1 } }
1285 a = 0
1286 while host[a] != (0 as u8) { out[o] = host[a]; o = o + 1; a = a + 1 }
1287 out[o] = 32 as u8; o = o + 1
1288 a = 0
1289 while prefix[a] != (0 as u8) { out[o] = prefix[a]; o = o + 1; a = a + 1 }
1290 out[o] = 32 as u8; o = o + 1
1291 o = md_catn(out, o, port)
1292 out[o] = 32 as u8; o = o + 1
1293 a = 0
1294 while mode[a] != (0 as u8) { out[o] = mode[a]; o = o + 1; a = a + 1 }
1295 out[o] = 10 as u8; o = o + 1
1296 // NEVER-BRICK: bank the pre-edit table as confp+".prev" FIRST (recovery: cp .prev back), then
1297 // atomic write: tmp = confp + ".tmp", rename over confp. Same idiom as binary deploys.
1298 let prevp: *u8 = sys_mmap(512)
1299 var pj: i64 = 0
1300 while confp[pj] != (0 as u8) { prevp[pj] = confp[pj]; pj = pj + 1 }
1301 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
1302 let pfd: i64 = sys_openat_wr(prevp, 0x1a4)
1303 if pfd >= 0 { sys_write(pfd, old, oldn); sys_close(pfd) }
1304 let tmpp: *u8 = sys_mmap(512)
1305 var tj: i64 = 0
1306 while confp[tj] != (0 as u8) { tmpp[tj] = confp[tj]; tj = tj + 1 }
1307 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
1308 let fd: i64 = sys_openat_wr(tmpp, 0x1a4)
1309 if fd < 0 { return 0 }
1310 sys_write(fd, out, o)
1311 sys_close(fd)
1312 sys_renameat(tmpp, confp)
1313 return 1
1314}
1315// kill every process whose /proc/<pid>/cmdline CONTAINS needle (full-cmdline match -- the 15-char comm
1316// truncation trap). Returns processes signalled. Mirrors the proven hostctl proc_kill_by_name.
1317func md_pk_contains(hay: *u8, hn: i64, needle: *u8, nl: i64) -> i64 {
1318 if nl == 0 { return 0 }
1319 var i: i64 = 0
1320 while i + nl <= hn {
1321 var j: i64 = 0
1322 var ok: i64 = 1
1323 while j < nl { if (hay[i + j] as i64) != (needle[j] as i64) { ok = 0; j = nl } else { j = j + 1 } }
1324 if ok == 1 { return 1 }
1325 i = i + 1
1326 }
1327 return 0
1328}
1329func md_pk_atoi(s: *u8) -> i64 {
1330 var v: i64 = 0
1331 var i: i64 = 0
1332 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 }
1333 return v
1334}
1335// promote a staged <cwd>/<name>.new -> live <name> (.prev kept), chmod +x. Returns 1 if a .new existed
1336// and was promoted, else 0 (restart still valid -- just reloads the same on-disk binary). cwd = nishihost.
1337// IDEMPOTENCY for /api/tools/register: is <nm> already the first TAB-field of a line in tool_allowlist.conf?
1338// Reads the (small) allowlist raw; matches a line that starts with "<nm>\t". Fail-open to 0 (absent) so a
1339// missing/unreadable allowlist doesn't block a first registration.
1340func md_allow_has_name(nm: *u8) -> i64 {
1341 let fd: i64 = sys_openat_rd("tool_allowlist.conf" as *u8)
1342 if fd < 0 { return 0 }
1343 let cap: i64 = 1 << 18
1344 let buf: *u8 = sys_mmap(cap)
1345 let n: i64 = sys_read(fd, buf, cap - 1)
1346 sys_close(fd)
1347 if n <= 0 { return 0 }
1348 buf[n] = 0 as u8
1349 let nl: i64 = md_len(nm)
1350 var i: i64 = 0
1351 while i + nl < n {
1352 var ls: i64 = 0
1353 if i == 0 { ls = 1 } else { if buf[i-1] == (10 as u8) { ls = 1 } }
1354 if ls == 1 {
1355 var m: i64 = 1
1356 var j: i64 = 0
1357 while j < nl { if buf[i+j] != nm[j] { m = 0; j = nl } else { j = j + 1 } }
1358 if m == 1 { if buf[i+nl] == (9 as u8) { return 1 } }
1359 }
1360 i = i + 1
1361 }
1362 return 0
1363}
1364
1365// seq1281 (RESTORED AGAIN 2026-07-30 -- 3rd backdate, see seq1439/nx_srcguard): read the existing pinned-
1366// args column (4th TAB field .. EOL) of tool <nm>'s allowlist row into dst. Returns copied length; 0 = no
1367// row / no args / unreadable. Lets register-update PRESERVE pinned args when args= is omitted -- an omitted
1368// field must never silently widen a pinned oracle into caller-controlled argv.
1369func md_allow_get_args(nm: *u8, dst: *u8, cap: i64) -> i64 {
1370 let fd: i64 = sys_openat_rd("tool_allowlist.conf" as *u8)
1371 if fd < 0 { return 0 }
1372 let bcap: i64 = 1 << 18
1373 let buf: *u8 = sys_mmap(bcap)
1374 let n: i64 = sys_read(fd, buf, bcap - 1)
1375 sys_close(fd)
1376 if n <= 0 { return 0 }
1377 buf[n] = 0 as u8
1378 let nl: i64 = md_len(nm)
1379 var i: i64 = 0
1380 while i + nl < n {
1381 var ls: i64 = 0
1382 if i == 0 { ls = 1 } else { if buf[i-1] == (10 as u8) { ls = 1 } }
1383 if ls == 1 {
1384 var m: i64 = 1
1385 var j: i64 = 0
1386 while j < nl { if buf[i+j] != nm[j] { m = 0; j = nl } else { j = j + 1 } }
1387 if m == 1 { if buf[i+nl] == (9 as u8) {
1388 var p: i64 = i + nl + 1
1389 var tabs: i64 = 0
1390 var argst: i64 = 0
1391 while p < n {
1392 let c: i64 = buf[p] as i64
1393 if c == 10 { p = n } else {
1394 if c == 9 { tabs = tabs + 1; if tabs == 2 { argst = p + 1; p = n } }
1395 if p < n { p = p + 1 }
1396 }
1397 }
1398 if argst == 0 { return 0 }
1399 var o: i64 = 0
1400 var q: i64 = argst
1401 while q < n {
1402 if buf[q] == (10 as u8) { q = n } else {
1403 if o < cap - 1 { dst[o] = buf[q]; o = o + 1 }
1404 q = q + 1
1405 }
1406 }
1407 dst[o] = 0 as u8
1408 return o
1409 } }
1410 }
1411 i = i + 1
1412 }
1413 return 0
1414}
1415
1416// atomically REPLACE the tool_allowlist.conf row for tool <nm> -- the register-update verb's mutation
1417// (eats the ssh-once row-repoint class: evidence_checkin/mvault/clock repoints). Preserves every OTHER
1418// line byte-exact, drops the existing "<nm>\t..." row(s), appends the replacement
1419// "<nm>\t<elfp>\tGREEN[\t<args>]" row; banks .prev FIRST then tmp+rename (md_route_append idiom).
1420// UPDATE CAN NEVER CREATE: refuses (0) when no row matches. NEVER-BRICK: refuses when the allowlist is
1421// unreadable/empty so a transient read failure cannot truncate the live tool table. 1 ok / 0 refused.
1422func md_allow_update_row(nm: *u8, elfp: *u8, argp: *u8, argn: i64) -> i64 {
1423 let szp: *i64 = sys_mmap(16) as *i64
1424 szp[0] = 0
1425 let old: *u8 = md_read_file("tool_allowlist.conf" as *u8, szp)
1426 let oldn: i64 = szp[0]
1427 if (old as i64) == 0 { return 0 }
1428 if oldn == 0 { return 0 }
1429 let nl: i64 = md_len(nm)
1430 let out: *u8 = sys_mmap(262144)
1431 var o: i64 = 0
1432 var found: i64 = 0
1433 var ls: i64 = 0
1434 while ls < oldn {
1435 var le: i64 = ls
1436 var sc: i64 = 1
1437 while sc == 1 { if le >= oldn { sc = 0 } else { if (old[le] as i64) == 10 { sc = 0 } else { le = le + 1 } } }
1438 var m: i64 = 0
1439 if ls + nl < le {
1440 if (old[ls + nl] as i64) == 9 {
1441 m = 1
1442 var j: i64 = 0
1443 while j < nl { if old[ls + j] != nm[j] { m = 0; j = nl } else { j = j + 1 } }
1444 }
1445 }
1446 if m == 1 { found = 1 } else {
1447 var k: i64 = ls
1448 while k <= le { if k < oldn { out[o] = old[k]; o = o + 1 } k = k + 1 }
1449 }
1450 ls = le + 1
1451 }
1452 if found == 0 { return 0 }
1453 if o > 0 { if (out[o - 1] as i64) != 10 { out[o] = 10 as u8; o = o + 1 } }
1454 var a: i64 = 0
1455 while nm[a] != (0 as u8) { out[o] = nm[a]; o = o + 1; a = a + 1 }
1456 out[o] = 9 as u8; o = o + 1
1457 a = 0
1458 while elfp[a] != (0 as u8) { out[o] = elfp[a]; o = o + 1; a = a + 1 }
1459 out[o] = 9 as u8; o = o + 1
1460 out[o] = 71 as u8; o = o + 1
1461 out[o] = 82 as u8; o = o + 1
1462 out[o] = 69 as u8; o = o + 1
1463 out[o] = 69 as u8; o = o + 1
1464 out[o] = 78 as u8; o = o + 1
1465 if argn > 0 {
1466 out[o] = 9 as u8; o = o + 1
1467 a = 0
1468 while a < argn { out[o] = argp[a]; o = o + 1; a = a + 1 }
1469 }
1470 out[o] = 10 as u8; o = o + 1
1471 let pfd: i64 = sys_openat_wr("tool_allowlist.conf.prev" as *u8, 0x1a4)
1472 if pfd >= 0 { sys_write(pfd, old, oldn); sys_close(pfd) }
1473 let fd: i64 = sys_openat_wr("tool_allowlist.conf.nxtmp" as *u8, 0x1a4)
1474 if fd < 0 { return 0 }
1475 sys_write(fd, out, o)
1476 sys_close(fd)
1477 sys_renameat("tool_allowlist.conf.nxtmp" as *u8, "tool_allowlist.conf" as *u8)
1478 return 1
1479}
1480
1481func md_streq(a: *u8, b: *u8) -> i64 {
1482 var i: i64 = 0
1483 while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 }
1484 if b[i] != (0 as u8) { return 0 }
1485 return 1
1486}
1487
1488// FAIL-CLOSED promote policy for POST /api/promote -- STRUCTURAL, still by-construction (F-210b eaten 07-18):
1489// (1) compiled-in DENY first: daemons + credential oracles (family substrings, so new members inherit the
1490// refusal). Daemons carry live connections -> the health-checked auto-rollback /api/deploy, NEVER a rename.
1491// The deny can never be overridden by any data plane or later rule.
1492// (2) the enumerated one-shot allows (back-compat fast path).
1493// (3) else STAGED-ARTIFACT rule: a name whose <name>.sov.elf.new exists as a real ELF is promotable. Staging is
1494// only reachable via the owner-gated /api/build//api/upload, so this widens promote to OWNER-only actions,
1495// never to a write cap -- the same stance as nx_fs_write denying the tool allowlist, without the
1496// add-a-name-recompile-mgmt treadmill for every new one-shot organ.
1497func md_contains(a: *u8, sub: *u8) -> i64 {
1498 var i: i64 = 0
1499 while a[i] != (0 as u8) {
1500 var j: i64 = 0
1501 var mism: i64 = 0
1502 var run: i64 = 1
1503 while run == 1 {
1504 if sub[j] == (0 as u8) { run = 0 } else {
1505 if a[i+j] == (0 as u8) { mism = 1; run = 0 } else {
1506 if a[i+j] != sub[j] { mism = 1; run = 0 } else { j = j + 1 }
1507 }
1508 }
1509 }
1510 if mism == 0 { return 1 }
1511 i = i + 1
1512 }
1513 return 0
1514}
1515// ---------- organ ROLE, declared (seq1492) ----------
1516//
1517// u2605THE DEFECT THIS REPLACES: promote classified by NAME SUBSTRING, so a
1518// one-shot worker called nx_torrent_get was refused as a "daemon" while
1519// /api/deploy refused it as an unknown target. **Two verbs disagreeing about an
1520// artefact's KIND leave it unshippable** -- and the only remaining way to update
1521// it was the raw scp/ssh path that seq1439 identified as the WORK-DESTROYER. A
1522// substring is not a role, exactly as a substring is not a hazard.
1523//
1524// Policy lives in DATA (rule 11): knowledge/status/organ_kind.conf, rows
1525// <name><TAB-or-SPACE>one-shot|oneshot|daemon|oracle|lib
1526// Returns 1 = one-shot (promotable), 2 = daemon/oracle (deploy lane), 0 = undeclared.
1527// Undeclared falls through to the historical name heuristics, so nothing regresses
1528// and the heuristic becomes the DEFAULT rather than the law.
1529//
1530// ★PERMANENT ROOT FIX 2026-07-31 (debt 1785453784, which /api/promote's own 400 text described but
1531// nobody had closed). TWO defects, and fixing only the first would have LOOKED right while still failing:
1532// 1. WRONG PATH. This opened the SINGULAR-less plural "knowledge/organ_kinds.conf", which was renamed to
1533// .RETIRED-seq1754-use-status-organ_kind. open() returned <0 -> return 0 -> EVERY organ fell through
1534// to the name heuristic, so NO declaration anywhere was readable and gate promotion was a coin flip.
1535// Now reads the surviving SSOT knowledge/status/organ_kind.conf. ONE source of truth, not two.
1536// 2. WRONG SEPARATOR. The surviving file is SPACE-separated (`nx_build_admit oneshot`) but this parser
1537// accepted ONLY a TAB (9), so a path-only fix would have found the file, parsed nothing, and still
1538// returned 0 -- a silent no-op that reads as success. Now takes the FIRST tab OR space.
1539// The value test below already tolerates both spellings: it checks only the leading "on", matching
1540// `oneshot` and `one-shot` alike. Verified against the real file before editing, not assumed.
1541func md_organ_kind(nm: *u8) -> i64 {
1542 let fd: i64 = sys_openat_rd("knowledge/status/organ_kind.conf" as *u8)
1543 if fd < 0 { return 0 }
1544 let buf: *u8 = sys_mmap(65536)
1545 let n: i64 = sys_read(fd, buf, 65536)
1546 sys_close(fd)
1547 if n <= 0 { return 0 }
1548 var nl: i64 = 0
1549 while nm[nl] != (0 as u8) { nl = nl + 1 }
1550 var ls: i64 = 0
1551 while ls < n {
1552 var le: i64 = ls
1553 var g: i64 = 1
1554 while g == 1 { if le >= n { g = 0 } else { if buf[le] == (10 as u8) { g = 0 } else { le = le + 1 } } }
1555 if le > ls { if buf[ls] != (35 as u8) {
1556 var sep: i64 = 0 - 1
1557 var q: i64 = ls
1558 while q < le {
1559 if sep < 0 {
1560 if buf[q] == (9 as u8) { sep = q }
1561 else { if buf[q] == (32 as u8) { sep = q } }
1562 }
1563 q = q + 1
1564 }
1565 if sep > ls {
1566 if sep - ls == nl {
1567 var same: i64 = 1
1568 var c: i64 = 0
1569 while c < nl { if buf[ls + c] != nm[c] { same = 0; c = nl } else { c = c + 1 } }
1570 if same == 1 {
1571 let vs: i64 = sep + 1
1572 if vs < le { if buf[vs] == (111 as u8) { if vs + 1 < le { if buf[vs + 1] == (110 as u8) { return 1 } } } }
1573 return 2
1574 }
1575 }
1576 }
1577 } }
1578 ls = le + 1
1579 }
1580 return 0
1581}
1582
1583// Does `w` occur in `nm` starting at a TOKEN BOUNDARY -- the start of the name,
1584// or immediately after '_'? Organ names are underscore-tokenised, so this asks
1585// "is one of the words in this name `w`" instead of "do these letters appear
1586// anywhere".
1587func mdh_len(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8) { n=n+1 } return n }
1588
1589func mdh_tail_eq(nm: *u8, n: i64, suf: *u8) -> i64 {
1590 let sl: i64 = mdh_len(suf)
1591 if sl > n { return 0 }
1592 var i: i64 = 0
1593 while i < sl { if nm[n - sl + i] != suf[i] { return 0 } i = i + 1 }
1594 return 1
1595}
1596
1597func md_tok_at(nm: *u8, w: *u8) -> i64 {
1598 let wl: i64 = mdh_len(w)
1599 let nl: i64 = mdh_len(nm)
1600 var i: i64 = 0
1601 while i + wl <= nl {
1602 var boundary: i64 = 0
1603 if i == 0 { boundary = 1 } else { if nm[i - 1] == (95 as u8) { boundary = 1 } }
1604 if boundary == 1 {
1605 var j: i64 = 0
1606 var m: i64 = 1
1607 while j < wl { if nm[i + j] != w[j] { m = 0; j = wl } else { j = j + 1 } }
1608 if m == 1 { return 1 }
1609 }
1610 i = i + 1
1611 }
1612 return 0
1613}
1614
1615// Does the name end in one of the verifier suffixes the system already treats as
1616// a declaration (_gate/_test/_kat -- the /api/gate_run bound)?
1617func md_name_is_oracle(nm: *u8) -> i64 {
1618 let n: i64 = mdh_len(nm)
1619 if mdh_tail_eq(nm, n, "_gate" as *u8) == 1 { return 1 }
1620 if mdh_tail_eq(nm, n, "_test" as *u8) == 1 { return 1 }
1621 if mdh_tail_eq(nm, n, "_kat" as *u8) == 1 { return 1 }
1622 return 0
1623}
1624
1625// NON-OVERRIDABLE deny: credential oracles and key material. A conf row must
1626// never be able to make these promotable -- otherwise the role registry becomes
1627// a privilege-escalation surface (config that grants authority).
1628//
1629// ★★★★★ ROOT-FIXED 2026-07-31 (debt 1785511766). This used to ask
1630// md_contains -- a RAW SUBSTRING -- which is the exact defect the sibling
1631// md_promote_deny was already fixed for at seq1789 ("THE SUFFIX IS A
1632// DECLARATION; THE SUBSTRING WAS A GUESS", the nx_survey_serve_gate collision),
1633// left unfixed one layer down here in the NON-OVERRIDABLE deny.
1634//
1635// "vault" means CREDENTIAL CUSTODY here, but as a substring it also matches the
1636// entire MEDIA vault family -- nx_mvault, nx_mvault_coll, nx_mvault_walk. One
1637// substring, two unrelated meanings. The result was that gate-proven media-vault
1638// binaries were unshippable by any sanctioned route, and the 2026-07-23 session
1639// resorted to an ssh cp/mv rename to ship them.
1640// ★★★★★**A GUARD THAT CANNOT BE SATISFIED PRODUCES A BYPASS, NOT SAFETY.**
1641//
1642// TWO CHANGES, BOTH STRICTLY SAFE -- this narrows FALSE positives only, and every
1643// real credential organ below still denies (proven by nx_promote_deny_gate):
1644// 1. TOKEN-BOUNDARY, not substring. "vault" still matches nx_vault_gateway (the
1645// word is a token) but no longer matches nx_mvault (the letters are merely
1646// inside one). A glued credential name like nx_secretstore STILL denies,
1647// because the boundary is checked at the START of the token only.
1648// 2. An ORACLE SUFFIX is exempt. Promoting nx_cap_mint_gate installs
1649// nx_cap_mint_gate.elf -- it CANNOT swap nx_cap_mint.elf -- so a verifier can
1650// never be the credential organ it verifies. Safe by construction, and it
1651// reuses the system's own _gate/_test/_kat rule rather than inventing one.
1652func md_promote_deny_hard(nm: *u8) -> i64 {
1653 if md_name_is_oracle(nm) == 1 { return 0 }
1654 if md_tok_at(nm, "mint" as *u8) == 1 { return 1 }
1655 if md_tok_at(nm, "vault" as *u8) == 1 { return 1 }
1656 if md_tok_at(nm, "secret" as *u8) == 1 { return 1 }
1657 if md_tok_at(nm, "keygen" as *u8) == 1 { return 1 }
1658 if md_tok_at(nm, "login" as *u8) == 1 { return 1 }
1659 return 0
1660}
1661
1662func md_promote_deny(nm: *u8) -> i64 {
1663 // u2605CONVERGED 2026-07-30 (seq1754). This used to consult its OWN role conf
1664 // (knowledge/organ_kinds.conf) -- a SECOND classifier for a concept a
1665 // sibling had already implemented properly as nx_organkind
1666 // (ok_kind_of_path over knowledge/status/organ_kind.conf), wired into the
1667 // promote handler ABOVE this function. Two confs and two readers for one
1668 // concept is the sprawl we keep warning about, and I built half of it by
1669 // not checking /code/tools before starting.
1670 //
1671 // The canonical reader now decides FIRST at the API layer; this function is
1672 // only reached for an UNDECLARED name, where it is the legacy name
1673 // heuristic -- so the duplicate lookup is removed and its rows were merged
1674 // into the canonical conf. deny_hard STAYS: credential oracles must be
1675 // refused non-overridably regardless of any declared kind.
1676 if md_promote_deny_hard(nm) == 1 { return 1 }
1677 if md_contains(nm, "serve" as *u8) == 1 { return 1 }
1678 if md_contains(nm, "daemon" as *u8) == 1 { return 1 }
1679 if md_contains(nm, "mint" as *u8) == 1 { return 1 }
1680 if md_contains(nm, "vault" as *u8) == 1 { return 1 }
1681 if md_contains(nm, "secret" as *u8) == 1 { return 1 }
1682 if md_contains(nm, "keygen" as *u8) == 1 { return 1 }
1683 if md_contains(nm, "login" as *u8) == 1 { return 1 }
1684 if md_contains(nm, "router" as *u8) == 1 { return 1 }
1685 if md_contains(nm, "hostctl" as *u8) == 1 { return 1 }
1686 if md_contains(nm, "signaling" as *u8) == 1 { return 1 }
1687 if md_contains(nm, "gateway" as *u8) == 1 { return 1 }
1688 if md_contains(nm, "torrent" as *u8) == 1 { return 1 }
1689 if md_contains(nm, "mgmt" as *u8) == 1 { return 1 }
1690 if md_contains(nm, "_gw" as *u8) == 1 { return 1 }
1691 if md_streq(nm, "sites" as *u8) == 1 { return 1 }
1692 return 0
1693}
1694func md_staged_elf_ok(nm: *u8) -> i64 {
1695 let p: *u8 = sys_mmap(192)
1696 var o: i64 = 0
1697 var i: i64 = 0
1698 while nm[i] != (0 as u8) { p[o] = nm[i]; o = o + 1; i = i + 1 }
1699 let sfx: *u8 = ".sov.elf.new" as *u8
1700 var j: i64 = 0
1701 while sfx[j] != (0 as u8) { p[o] = sfx[j]; o = o + 1; j = j + 1 }
1702 p[o] = 0 as u8
1703 let fd: i64 = sys_openat_rd(p)
1704 if fd < 0 { return 0 }
1705 let hb: *u8 = sys_mmap(8)
1706 let r: i64 = sys_read(fd, hb, 4)
1707 sys_close(fd)
1708 if r != 4 { return 0 }
1709 if hb[0] != (127 as u8) { return 0 }
1710 if hb[1] != (69 as u8) { return 0 }
1711 if hb[2] != (76 as u8) { return 0 }
1712 if hb[3] != (70 as u8) { return 0 }
1713 return 1
1714}
1715func md_promote_organ_ok(nm: *u8) -> i64 {
1716 if md_promote_deny(nm) == 1 { return 0 }
1717 if md_streq(nm, "nx_ecosystem_maturity_rollup" as *u8) == 1 { return 1 }
1718 if md_streq(nm, "nx_ecomat_seed" as *u8) == 1 { return 1 }
1719 if md_streq(nm, "nx_ecomat_beat" as *u8) == 1 { return 1 }
1720 if md_streq(nm, "nx_ecomat_page" as *u8) == 1 { return 1 }
1721 if md_streq(nm, "nx_tool_argecho" as *u8) == 1 { return 1 }
1722 // 07-17 (eat the ssh-once deploy debt): the fork-exec MCP TOOL organ family -- one-shot elfs the
1723 // tools daemon spawns per call. NOT daemons (those stay refused -> /api/deploy) and NOT the
1724 // credential oracles (nx_session_mint / nx_cap_mint stay OFF this list deliberately).
1725 if md_streq(nm, "nx_shelltool" as *u8) == 1 { return 1 }
1726 if md_streq(nm, "nx_common_tasks" as *u8) == 1 { return 1 }
1727 if md_streq(nm, "nx_frontier_board" as *u8) == 1 { return 1 }
1728 if md_streq(nm, "nx_page_verify" as *u8) == 1 { return 1 }
1729 if md_streq(nm, "nx_store_seed" as *u8) == 1 { return 1 }
1730 if md_streq(nm, "nx_workflow" as *u8) == 1 { return 1 }
1731 if md_streq(nm, "nx_memory" as *u8) == 1 { return 1 }
1732 if md_streq(nm, "nx_heal" as *u8) == 1 { return 1 }
1733 if md_streq(nm, "nx_fs" as *u8) == 1 { return 1 }
1734 if md_streq(nm, "nx_fs_write" as *u8) == 1 { return 1 }
1735 if md_streq(nm, "nx_site_publish" as *u8) == 1 { return 1 }
1736 if md_streq(nm, "nx_https_get" as *u8) == 1 { return 1 }
1737 if md_streq(nm, "nx_verify" as *u8) == 1 { return 1 }
1738 // 07-17 (stem-first-byte-fab lane): the compare-publish pipeline organs -> API-promotable, so a
1739 // brand-new compare domain publishes end-to-end over MCP (build -> promote -> regen), zero shell.
1740 if md_streq(nm, "nx_compare_regen" as *u8) == 1 { return 1 }
1741 if md_streq(nm, "nx_swcompare_matrix" as *u8) == 1 { return 1 }
1742 if md_streq(nm, "nx_swcompare_sota" as *u8) == 1 { return 1 }
1743 if md_streq(nm, "nx_swcompare_hub" as *u8) == 1 { return 1 }
1744 if md_streq(nm, "nx_maturity_board" as *u8) == 1 { return 1 }
1745 // (3) staged-artifact rule: owner-staged one-shot builds are promotable (deny above already refused
1746 // every daemon/oracle shape, so this can only ever admit tool-organ names).
1747 if md_staged_elf_ok(nm) == 1 { return 1 }
1748 return 0
1749}
1750
1751// ---- seq1484: PROMOTION PROVENANCE -- a promote may not walk a binary BACKWARDS ------------------
1752// THE BLEED THIS STOPS (measured 2026-07-30): the mgmt API was reverted THREE times and the compiler
1753// TWICE in a single session, each time by promoting a binary built elsewhere from a stale tree. Every
1754// existing control passed it: the ELF is valid, the size is plausible, and promote_toolchain's canary
1755// COMPILES AND RUNS it GREEN -- because a stale-but-working binary does all of that perfectly.
1756// ★LIVENESS IS NOT CURRENCY. "It works" cannot distinguish the newest build from last week's.
1757//
1758// The invariant that CAN is the same one already protecting sources (nx_symdrop) and tree pushes
1759// (nx_treepack REFUSED-WOULD-DROP-SYMBOLS): a normal promotion installs content this target has NEVER
1760// held; a revert installs content it ALREADY HELD. So keep an append-only per-target content-hash
1761// history. Staged == newest -> a harmless re-promote, allowed. Staged never seen -> a real advance,
1762// allowed and recorded. Staged matches an EARLIER generation -> THE BINARY WOULD GO BACKWARDS, refused
1763// and named. No build-time provenance, no clock, no size heuristic, nothing to spoof by touching a file.
1764//
1765// ESCAPE HATCH BY DESIGN, NOT BY FLAG: going backwards deliberately is what /api/rollback is FOR, and it
1766// does not route through here. A `force` parameter would just be the hole re-opened under a nicer name.
1767const MD_PROV_HIST: *u8 = "knowledge/promote_history.tsv" as *u8
1768const MD_PROV_CAP: i64 = 1048576
1769const MD_PROV_RDCH: i64 = 262144
1770const MD_PROV_FNV_OFF: i64 = 1469598103934665603
1771const MD_PROV_FNV_PRM: i64 = 1099511628211
1772const MD_PROV_TAB: i64 = 9
1773const MD_PROV_NL: i64 = 10
1774
1775// FNV-1a over a whole file, streamed so a large ELF needs no full-size buffer. 0 = unreadable.
1776func md_prov_hash(path: *u8) -> i64 {
1777 let fd: i64 = sys_openat_rd(path)
1778 if fd < 0 { return 0 }
1779 let b: *u8 = sys_mmap(MD_PROV_RDCH)
1780 var h: i64 = MD_PROV_FNV_OFF
1781 var go: i64 = 1
1782 while go == 1 {
1783 let n: i64 = sys_read(fd, b, MD_PROV_RDCH)
1784 if n <= 0 { go = 0 } else {
1785 var i: i64 = 0
1786 while i < n { h = h ^ (b[i] as i64); h = h * MD_PROV_FNV_PRM; i = i + 1 }
1787 }
1788 }
1789 sys_close(fd)
1790 return h
1791}
1792// Walk the history for `name`. out3[0]=generations seen, out3[1]=1 if h is the NEWEST, out3[2]=index of
1793// an EARLIER generation equal to h (-1 if none).
1794func md_prov_probe(name: *u8, h: i64, out3: *i64) -> i64 {
1795 out3[0] = 0; out3[1] = 0; out3[2] = 0 - 1
1796 let buf: *u8 = sys_mmap(MD_PROV_CAP)
1797 let n: i64 = dp_read(MD_PROV_HIST, buf, MD_PROV_CAP - 1)
1798 if n <= 0 { return 0 }
1799 var nl: i64 = 0
1800 while name[nl] != (0 as u8) { nl = nl + 1 }
1801 var gen: i64 = 0
1802 var ls: i64 = 0
1803 var i: i64 = 0
1804 while i <= n {
1805 var eol: i64 = 0
1806 if i == n { eol = 1 } else { if buf[i] == (MD_PROV_NL as u8) { eol = 1 } }
1807 if eol == 1 {
1808 if i > ls {
1809 var tab: i64 = 0 - 1
1810 var t: i64 = ls
1811 while t < i { if buf[t] == (MD_PROV_TAB as u8) { tab = t; t = i } else { t = t + 1 } }
1812 if tab > 0 { if tab - ls == nl {
1813 var m: i64 = 1
1814 var c: i64 = 0
1815 while c < nl { if buf[ls+c] != name[c] { m = 0; c = nl } else { c = c + 1 } }
1816 if m == 1 {
1817 var v: i64 = 0
1818 var neg: i64 = 0
1819 var k: i64 = tab + 1
1820 if k < i { if buf[k] == (45 as u8) { neg = 1; k = k + 1 } }
1821 while k < i { v = v * 10 + ((buf[k] as i64) - 48); k = k + 1 }
1822 if neg == 1 { v = 0 - v }
1823 if v == h { out3[2] = gen; out3[1] = 1 } else { out3[1] = 0 }
1824 gen = gen + 1
1825 }
1826 } }
1827 }
1828 ls = i + 1
1829 }
1830 i = i + 1
1831 }
1832 out3[0] = gen
1833 return gen
1834}
1835func md_prov_record(name: *u8, h: i64) -> i64 {
1836 let line: *u8 = sys_mmap(512)
1837 var o: i64 = 0
1838 var i: i64 = 0
1839 while name[i] != (0 as u8) { line[o] = name[i]; o = o + 1; i = i + 1 }
1840 line[o] = MD_PROV_TAB as u8; o = o + 1
1841 var m: i64 = h
1842 if m < 0 { line[o] = 45 as u8; o = o + 1; m = 0 - m }
1843 let t: *u8 = sys_mmap(32)
1844 var k: i64 = 0
1845 if m == 0 { t[0] = 48 as u8; k = 1 }
1846 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
1847 var z: i64 = k - 1
1848 while z >= 0 { line[o] = t[z]; o = o + 1; z = z - 1 }
1849 line[o] = MD_PROV_NL as u8; o = o + 1
1850 let fd: i64 = sys_openat_append(MD_PROV_HIST, 0x1a4)
1851 if fd < 0 { return 0 - 1 }
1852 sys_write(fd, line, o)
1853 sys_fsync(fd)
1854 sys_close(fd)
1855 return 0
1856}
1857// 1 = this staged content may be promoted; 0 = it would walk `name` BACKWARDS.
1858func md_prov_ok(name: *u8, stagedpath: *u8) -> i64 {
1859 let h: i64 = md_prov_hash(stagedpath)
1860 if h == 0 { return 1 } // unreadable: leave the decision to the existing checks
1861 let p: *i64 = sys_mmap(64) as *i64
1862 md_prov_probe(name, h, p)
1863 if p[0] == 0 { md_prov_record(name, h); return 1 } // first sighting = the baseline
1864 if p[1] == 1 { return 1 } // identical to the newest = harmless re-promote
1865 if p[2] >= 0 { return 0 } // seen EARLIER but not newest = A REVERT
1866 md_prov_record(name, h)
1867 return 1
1868}
1869
1870// STAGING HYGIENE (2026-08-06, debt 1785531571). md_prov_ok above refuses only content this target
1871// ALREADY HELD, so a staged artifact that was never itself promoted is a FIRST SIGHTING: md_prov_ok
1872// records it as the baseline and returns ALLOW -- even when it PREDATES the live binary it replaces.
1873// MEASURED on this deploy root the same day (nx_staghyg scan): 584 staged artifacts, 88 with a live
1874// counterpart, 31 of those OLDER than the binary they would replace, and 7 that would DROP live
1875// capability tokens DESPITE BEING LARGER -- so neither mtime nor size alone catches the class.
1876// allow_loss=1 is the deliberate operator override (/api/promote allow_capability_loss=yes); 0 is
1877// fail-closed. Placed BEFORE any rename, so a refusal leaves live, .prev AND the staged file untouched.
1878// POST-PROMOTE MIRROR REFRESH (2026-08-14, debt 1786758132). Forks nx_offc_install <bare> refresh.
1879// ★SAFE TO CALL AUTOMATICALLY BECAUSE THE `refresh` VERB CREATES NOTHING: it installs the promoted
1880// binary only where an _offc mirror ALREADY EXISTS (proven both directions -- nx_resmon REFRESHED,
1881// nx_memvel_gate SKIP). An unconditional install here would quietly WIDEN what runners can fork.
1882func md_exec_offc_refresh(bare: *u8) -> i64 {
1883 let oelf: *u8 = "/volume1/homes/elderwesto/nishihost/nx_offc_install.elf" as *u8
1884 let args: *i64 = sys_mmap(16) as *i64
1885 args[0] = bare as i64
1886 args[1] = "refresh" as *u8 as i64
1887 return dep_run(oelf, args, 2)
1888}
1889
1890func md_promote_staged_ex(name: *u8, allow_loss: i64) -> i64 {
1891 let live: *u8 = sys_mmap(160)
1892 let newp: *u8 = sys_mmap(160)
1893 let prevp: *u8 = sys_mmap(160)
1894 var lo: i64 = 0
1895 while name[lo] != (0 as u8) { live[lo] = name[lo]; newp[lo] = name[lo]; prevp[lo] = name[lo]; lo = lo + 1 }
1896 live[lo] = 0 as u8
1897 md_copy_slice_z(newp, name, 0, lo, 160)
1898 md_copy_slice_z(prevp, name, 0, lo, 160)
1899 var no: i64 = lo
1900 let ns: *u8 = ".new" as *u8
1901 var a: i64 = 0
1902 while ns[a] != (0 as u8) { newp[no] = ns[a]; no = no + 1; a = a + 1 }
1903 newp[no] = 0 as u8
1904 // does <name>.new exist?
1905 let nfd: i64 = sys_openat_rd(newp)
1906 if nfd < 0 { return 0 }
1907 sys_close(nfd)
1908 // seq1484 PROVENANCE: refuse a promotion that would walk this target BACKWARDS to content
1909 // it already held. Placed BEFORE any rename, so a refusal leaves live and .prev untouched.
1910 if md_prov_ok(live, newp) == 0 { return 0 - 2 }
1911 if allow_loss == 0 {
1912 let sgcfg: *i64 = sys_mmap(8 * SG_C_SLOTS) as *i64
1913 sg_conf_load(sgcfg)
1914 let sgout: *i64 = sys_mmap(8 * SG_OUT_SLOTS) as *i64
1915 let sgcls: i64 = sg_classify(live, newp, sys_now_realtime_sec(), sgcfg, sgout)
1916 if sg_allows(sgcls) == 0 { return 0 - 3 }
1917 }
1918 var po: i64 = lo
1919 let ps: *u8 = ".prev" as *u8
1920 a = 0
1921 while ps[a] != (0 as u8) { prevp[po] = ps[a]; po = po + 1; a = a + 1 }
1922 prevp[po] = 0 as u8
1923 md_rotate_prev(prevp) // rotate .prev -> .prev2 FIRST: a 2nd promote must not destroy the original
1924 sys_renameat(live, prevp) // keep the old live as .prev (rollback)
1925 sys_renameat(newp, live) // promote .new -> live
1926 nx_chmod(live, 0x1ed)
1927 // ---- MAINTAIN THE _offc MIRROR (2026-08-14, debt 1786758132) --------------------------------
1928 // THE INVARIANT: _offc/<n>.elf must equal the promoted root binary. nx_job_run and organ-to-organ
1929 // forks resolve _offc/ while promote writes the ROOT, so WITHOUT THIS a promote reports success
1930 // while every caller keeps executing the OLD artifact, with no signal anywhere.
1931 // MEASURED THE DAY THIS LANDED: 20 stale _offc artifacts including the assembler, the build runner
1932 // and nx_mgmt_api itself -- and a stale copy of a CORRECT security scanner read as a VACUOUS
1933 // detector convincingly enough that a sev-8 was filed against the wrong thing.
1934 // ★HOOKED HERE, NOT IN ma_do_promote, BECAUSE THIS IS THE ONE RENAME: /api/restart and /api/deploy
1935 // reach this function WITHOUT passing through the promote handler, so hooking the handler would have
1936 // fixed one path of four.
1937 // ★THE RETURN VALUE IS DELIBERATELY IGNORED AND THE CALL IS LAST: the rename above has ALREADY
1938 // succeeded, so a mirror failure must never un-promote or fail a promotion that worked. Worst case
1939 // the mirror stays stale and the offccensus beat reports it -- which is exactly the pre-existing
1940 // state, so this can only improve on it.
1941 if lo > 4 {
1942 let bare: *u8 = sys_mmap(160)
1943 md_copy_slice_z(bare, name, 0, lo - 4, 160)
1944 md_exec_offc_refresh(bare)
1945 }
1946 return 1
1947}
1948
1949// BACK-COMPAT WRAPPER: every existing call site keeps its exact signature and gets the guard by default
1950// (fail-closed). Only a caller that DELIBERATELY passes the override reaches _ex with allow_loss=1 --
1951// so wiring the guard cannot be forgotten at a call site, which is the built-but-not-wired class this
1952// ecosystem keeps rediscovering. Returns: 1 promoted, 0 nothing staged, -2 provenance revert, -3 staging hygiene.
1953func md_promote_staged(name: *u8) -> i64 { return md_promote_staged_ex(name, 0) }
1954// ---- TOOLCHAIN PROMOTE (never-brick) -- eats seq891/seq903 -------------------------------------
1955// THE GAP THIS CLOSES: the ecosystem could build and deploy every SERVICE over its own API but could
1956// NOT update the COMPILER that builds them, so a PROVEN compiler fix could not be landed API-first
1957// (rule 27). Measured cost on 2026-07-30: nx_fnptr_slot_probe was GREEN on the laptop compiler and RED
1958// on the hub compiler, i.e. obj.fn_field(args) silently emitted no indirect call for every organ in the
1959// tree, and the fix existed but had nowhere to go. A toolchain you cannot update is a toolchain whose
1960// bugs are permanent.
1961//
1962// ⚠THE PATH NOT TAKEN (seq903, and it must stay not-taken): shipping the toolchain through
1963// /api/upload + /api/unpack looks tempting because it touches only this module. nx_treepack writes every
1964// output file 0644 NON-EXECUTABLE and UNLINKS-then-recreates on any open failure, so unpacking over
1965// buildroot/_offc/nx_cc_sovereign.elf would either install a non-executable compiler or destroy the live
1966// one -- EVERY BUILD FOR EVERY SEAT, from a call that looks like a routine source sync.
1967//
1968// ⚠MATCHED-PAIR RULE (seq1315): nx_sov_build_run writes _build/<name>.sov.elf while nx_hostctl
1969// cmd_buildrun reads /tmp/<name>.sov.elf, so those two may only ever be promoted TOGETHER. They are
1970// admitted here because that pair-ship is a legitimate wave -- and admitting them is SAFE precisely
1971// because the caller canary-compiles and auto-rolls-back, so a mismatched pair cannot survive a promote.
1972//
1973// DIALECT NOTE: plain-if (no `else`), no empty string literals, <=6 params. This module is IMPORTED by
1974// nx_mgmt_api, and it must be compiled by TODAY'S hub compiler -- the one that still carries seq533
1975// (imported `else` desyncs the parser), seq907 (an empty literal aliases the next literal) and seq239
1976// (>6 params mishandled). The fix ships in a binary that the defect itself has to be able to build.
1977const MD_TC_MIN_ELF: i64 = 4096 // size floor: refuse a truncated upload or an HTML error page
1978const MD_TC_MODE_EXEC: i64 = 0x1ed // 0755 -- a compiler that is not executable is a dead ecosystem
1979
1980func md_toolchain_target_ok(nm: *u8) -> i64 {
1981 if md_streq(nm, "nx_cc_sovereign.elf" as *u8) == 1 { return 1 }
1982 if md_streq(nm, "nxasm_x86_main.elf" as *u8) == 1 { return 1 }
1983 if md_streq(nm, "nx_sov_build_run.elf" as *u8) == 1 { return 1 }
1984 return 0
1985}
1986
1987func md_tc_live(nm: *u8, buf: *u8) -> i64 {
1988 var o: i64 = md_cmp_cat(buf, 0, "buildroot/_offc/" as *u8)
1989 o = md_cmp_cat(buf, o, nm)
1990 return o
1991}
1992func md_tc_prev(nm: *u8, buf: *u8) -> i64 {
1993 var o: i64 = md_tc_live(nm, buf)
1994 o = md_cmp_cat(buf, o, ".prev" as *u8)
1995 return o
1996}
1997func md_tc_staged(nm: *u8, buf: *u8) -> i64 {
1998 var o: i64 = md_cmp_cat(buf, 0, nm)
1999 o = md_cmp_cat(buf, o, ".new" as *u8)
2000 return o
2001}
2002
2003// ELF magic + size floor. Returns the byte size on success, 0 on refusal. Validating the ARTIFACT (not
2004// an exit code) is the seq363/hostctl lesson: a 0-byte or non-ELF file must never reach the live slot.
2005func md_tc_elf_size(p: *u8) -> i64 {
2006 let fd: i64 = sys_openat_rd(p)
2007 if fd < 0 { return 0 }
2008 let sz: i64 = sys_lseek(fd, 0, 2)
2009 if sz < MD_TC_MIN_ELF { sys_close(fd); return 0 }
2010 sys_lseek(fd, 0, 0)
2011 let hb: *u8 = sys_mmap(8)
2012 var ok: i64 = 0
2013 if sys_read(fd, hb, 4) == 4 {
2014 if hb[0] == (0x7f as u8) {
2015 if hb[1] == (69 as u8) {
2016 if hb[2] == (76 as u8) {
2017 if hb[3] == (70 as u8) { ok = 1 }
2018 }
2019 }
2020 }
2021 }
2022 sys_close(fd)
2023 if ok == 0 { return 0 }
2024 return sz
2025}
2026
2027// Install staged <nm>.new -> buildroot/_offc/<nm>, banking the outgoing binary as .prev FIRST.
2028// Returns the installed size, or 0 if nothing was touched. Ordering is deliberate: validate BEFORE
2029// renaming anything, so a refused upload leaves the live compiler completely untouched.
2030const MD_TC_ERR_BACKUP: i64 = 0 - 3
2031func md_tc_install(nm: *u8) -> i64 {
2032 let stagedp: *u8 = sys_mmap(256)
2033 let livep: *u8 = sys_mmap(256)
2034 let prevp: *u8 = sys_mmap(256)
2035 md_tc_staged(nm, stagedp)
2036 md_tc_live(nm, livep)
2037 md_tc_prev(nm, prevp)
2038 let sz: i64 = md_tc_elf_size(stagedp)
2039 if sz == 0 { return 0 }
2040 // seq1484: the canary proves the incoming toolchain WORKS, which a stale-but-working one
2041 // also does. Provenance is what proves it is not last week s build. Checked before any rename.
2042 if md_prov_ok(livep, stagedp) == 0 { return 0 - 2 }
2043 // A failed bank must never consume the candidate or overwrite the live compiler.
2044 let previous: i64=sys_openat_rd(prevp)
2045 if previous >= 0 {
2046 sys_close(previous)
2047 if md_rotate_prev(prevp) != 1 { return MD_TC_ERR_BACKUP }
2048 }
2049 if previous < 0 { if previous != (0-2) { return MD_TC_ERR_BACKUP } }
2050 if sys_renameat(livep, prevp) != 0 { return MD_TC_ERR_BACKUP }
2051 if sys_renameat(stagedp, livep) != 0 {
2052 sys_renameat(prevp, livep) // stage-rename failed: put the old one straight back
2053 nx_chmod(livep, MD_TC_MODE_EXEC)
2054 return 0
2055 }
2056 nx_chmod(livep, MD_TC_MODE_EXEC)
2057 return sz
2058}
2059
2060
2061// Restore buildroot/_offc/<nm>.prev -> live. This is the rollback half of never-brick and it is called
2062// on CANARY FAILURE, so the ecosystem can never be left with a compiler that cannot compile.
2063func md_tc_rollback(nm: *u8) -> i64 {
2064 let livep: *u8 = sys_mmap(256)
2065 let prevp: *u8 = sys_mmap(256)
2066 md_tc_live(nm, livep)
2067 md_tc_prev(nm, prevp)
2068 if md_tc_elf_size(prevp) == 0 { return 0 }
2069 if sys_renameat(prevp, livep) != 0 { return 0 }
2070 nx_chmod(livep, MD_TC_MODE_EXEC)
2071 return 1
2072}
2073
2074// Our own pid. getpid = syscall 39 on x86-64.
2075func 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)
2076// Fork a detached child that waits, then SIGTERMs the given pid. Used so a self-restart can FINISH WRITING
2077// ITS RESPONSE before the process goes away: the reply reaches the caller, then the guard respawns the
2078// already-promoted binary. SIGTERM (not KILL) so a daemon that later grows a drain handler gets to use it.
2079func md_delayed_kill(pid: i64, delay_ms: i64) -> i64 {
2080 let p: i64 = sys_fork()
2081 if p == 0 {
2082 nx_setsid()
2083 sys_sleep_ms(delay_ms)
2084 nx_kill(pid, 15)
2085 sys_exit(0)
2086 }
2087 return p
2088}
2089func md_kill_by_name(needle: *u8) -> i64 {
2090 var self_hit: i64 = 0
2091 let nn: i64 = md_len(needle)
2092 let fd: i64 = sys_openat_rd("/proc" as *u8)
2093 if fd < 0 { return 0 }
2094 let buf: *u8 = sys_mmap(65536)
2095 let path: *u8 = sys_mmap(256)
2096 let clbuf: *u8 = sys_mmap(8192)
2097 var killed: i64 = 0
2098 var run: i64 = 1
2099 while run == 1 {
2100 let n: i64 = sys_getdents64(fd, buf, 65536)
2101 if n <= 0 { run = 0 } else {
2102 var off: i64 = 0
2103 while off < n {
2104 let rec: *u8 = ((buf as i64 + off) as *u8)
2105 let reclen: i64 = dirent_reclen(rec)
2106 if reclen <= 0 { off = n } else {
2107 let name: *u8 = dirent_name(rec)
2108 if name[0] >= (48 as u8) { if name[0] <= (57 as u8) {
2109 var p: i64 = 0
2110 let pre: *u8 = "/proc/" as *u8
2111 var a: i64 = 0
2112 while pre[a] != (0 as u8) { path[p] = pre[a]; p = p + 1; a = a + 1 }
2113 a = 0
2114 while name[a] != (0 as u8) { path[p] = name[a]; p = p + 1; a = a + 1 }
2115 let suf: *u8 = "/cmdline" as *u8
2116 a = 0
2117 while suf[a] != (0 as u8) { path[p] = suf[a]; p = p + 1; a = a + 1 }
2118 path[p] = 0 as u8
2119 let cfd: i64 = sys_openat_rd(path)
2120 if cfd >= 0 {
2121 let cln: i64 = sys_read(cfd, clbuf, 8192)
2122 sys_close(cfd)
2123 if cln > 0 { if md_pk_contains(clbuf, cln, needle, nn) == 1 {
2124 // ---- R5 SEQUENCING: NEVER SIGKILL OURSELVES MID-RESPONSE ----------------
2125 // TWO defects, one root. (1) mgmt restarting/deploying ITSELF matches its own
2126 // cmdline here and SIGKILLs the process that is writing the reply -- that IS
2127 // the FETCH-FAIL every /api/deploy returns (~12x in one session), and it is why
2128 // the seq1563 deploy lease strands (we die before reaching our own release).
2129 // (2) NEW with SO_REUSEPORT: a hot restart runs old and new under the SAME
2130 // cmdline, so a name-matched kill would murder the freshly-spawned instance too
2131 // -- adopting REUSEPORT without this turns a handoff into an outage.
2132 // So: skip our own pid here, remember it, and schedule a DELAYED self-exit
2133 // after the loop. The reply is written first, THEN we go; the supervise guard
2134 // respawns the already-promoted binary. FETCH-FAIL becomes a real JSON body.
2135 let vpid: i64 = md_pk_atoi(name)
2136 if vpid == md_self_pid() { self_hit = 1 } else {
2137 nx_kill(vpid, 9)
2138 killed = killed + 1
2139 }
2140 } }
2141 }
2142 } }
2143 off = off + reclen
2144 }
2145 }
2146 }
2147 }
2148 sys_close(fd)
2149 // We matched OURSELVES: schedule the exit for AFTER the response is on the wire. 1500ms is the
2150 // reply-write window, not a guess at compile time -- the caller returns immediately after this.
2151 // Counted in `killed` so the JSON stays honest about what is going away.
2152 if self_hit == 1 { md_delayed_kill(md_self_pid(), 1500); killed = killed + 1 }
2153 return killed
2154}
2155
2156// ---- secondary adapters: validate / exec / probe ----------------------------------------------------
2157func md_validate_artifact(path: *u8, kind: i64) -> i64 { return dep_validate(path, kind) }
2158
2159// drive the proven allowlisted nx_aw_hostctl with one sub -> its exit code (the supervisor/deploy exec port).
2160func md_exec_hostctl(sub: *u8) -> i64 {
2161 // Run the ON-NAS supervisor CLI directly. The mgmt daemon runs under the (root) supervisor, so it can drive the
2162 // surgical restart subs. The prior "_offc/nx_aw_hostctl.elf" is the LAPTOP->NAS bridge and is ABSENT on the NAS,
2163 // so /api/restart + /api/deploy were gate-proven but never live-executable. Absolute path (mgmt cwd=nishihost).
2164 let helf: *u8 = "/volume1/homes/elderwesto/nishihost/nx_hostctl" as *u8
2165 let args: *i64 = sys_mmap(16) as *i64
2166 args[0] = sub as i64
2167 return dep_run(helf, args, 1)
2168}
2169
2170// P2 off-LAN parity: run one allowlisted hostctl sub and CAPTURE its stdout to outpath (for /api/hostctl -> the
2171// phone gets torstat/routerctl/status output). Same on-NAS nx_hostctl the deploy path uses; single argv element
2172// (execve, no shell) so no injection; the allowlist below fail-closes to a curated safe read/action set.
2173func md_exec_hostctl_capture(sub: *u8, outpath: *u8) -> i64 {
2174 let helf: *u8 = "/volume1/homes/elderwesto/nishihost/nx_hostctl" as *u8
2175 let args: *i64 = sys_mmap(16) as *i64
2176 args[0] = sub as i64
2177 return dep_run_capture(helf, args, 1, outpath)
2178}
2179// 2-arg variant (e.g. `buildrun <target>`): run the on-NAS nx_hostctl <sub> <arg>, capture stdout -> outpath.
2180func md_exec_hostctl_capture2(sub: *u8, arg: *u8, outpath: *u8) -> i64 {
2181 let helf: *u8 = "/volume1/homes/elderwesto/nishihost/nx_hostctl" as *u8
2182 let args: *i64 = sys_mmap(16) as *i64
2183 args[0] = sub as i64
2184 args[1] = arg as i64
2185 return dep_run_capture(helf, args, 2, outpath)
2186}
2187// ---- BUILD ADMISSION (seq708/768/1390) --------------------------------------------------------
2188// THE INCIDENT THIS PREVENTS: /api/build is the one heavyweight mgmt op -- it forks the sovereign nx_cc
2189// toolchain to compile a source tree -- and it did so with NO memory admission. Under a build-heavy
2190// session the host runs out of memory and the supervisor OOM-reaps nx_mgmt_api, taking the WHOLE deploy
2191// path down for every seat. Confirmed twice (seq698/708) and REPRODUCED LIVE 2026-07-30 (seq1390):
2192// both transports died mid-session (503 / status=0, TLS fine so the EDGE was healthy and the BACKEND
2193// was gone) and self-recovered only when the guard respawned it.
2194//
2195// ⚠seq768 recorded this fix as "WRITTEN + BUILT + VERIFIED, staged awaiting one rename" and was marked
2196// EATEN -- but on 2026-07-30 md_exec_build_admit / ma_emit_503 / the ma_do_build call site were found in
2197// NEITHER the laptop SSOT NOR the NAS buildroot (grep: 0 matches across 6972 files). The work never
2198// reached a source tree, so every build-heavy session kept re-rolling the outage. Rebuilt here, in the
2199// SSOT, where a rebuild cannot lose it. ★LAW: a debt is not eaten until its fix is IN A SOURCE TREE --
2200// "built and staged" is not landed, and a binary nobody can rebuild is a rumour.
2201//
2202// FAIL-OPEN BY DESIGN (rule 26 / F881 ratchet stance): nx_build_admit exits 0 GRANT / 3 DENY (below the
2203// memory floor) / 4 QUEUE (load ceiling) / 2 usage / 5 unreadable-proc. We block ONLY on exit 3, the
2204// definitive memory wedge that actually causes the incident. Load-queueing and unreadable /proc both
2205// fall through to GRANT so admission control can never soft-brick the ecosystem's build path -- a
2206// refused-when-it-should-have-built is a worse failure here than an occasional reap.
2207// Floor 1024 MB; load ceiling deliberately huge so MEMORY is the sole gate (the measured cause).
2208// THE ADMISSION ENVELOPE LIVES IN ONE FILE (2026-08-18): knowledge/build_admit.conf, read by nx_build_admit
2209// itself on every check (argv > conf > host-derived). This path passes NO thresholds any more -- for one
2210// day it carried them as named consts here, which was still a second copy beside nx_sov_build_run's and
2211// nx_orchestrate's bare `headroom:`; three callers on one conf cannot disagree. A caller that MEANS a
2212// different envelope still says so on argv, and the report line prints envelope_src so it shows.
2213func md_exec_build_admit() -> i64 {
2214 let belf: *u8 = "/volume1/homes/elderwesto/nishihost/nx_build_admit.elf" as *u8
2215 let bf: i64 = sys_openat_rd(belf)
2216 if bf < 0 { return 0 } // detector absent -> GRANT (never block on a missing guard)
2217 sys_close(bf)
2218 let args: *i64 = sys_mmap(32) as *i64
2219 args[0] = "check" as *u8 as i64
2220 return dep_run_capture(belf, args, 1, "/tmp/nx_build_admit.out" as *u8)
2221}
2222
2223// ---- GATE-DRY RATCHET RUNNER (2026-07-31, debts 1785529506 / 1785530277) -----------------------
2224// L009 -- gate organs that hand-roll their verdict instead of inheriting nx_gate_verdict -- is not
2225// merely large, it is GROWING: two warden scans hours apart on 2026-07-31 read 2035/2167 then
2226// 2041/2182. A migration campaign that only removes old breaches LOSES to a tree that adds new ones,
2227// so D001 cannot be closed by migrating alone. The 2026 practice for exactly this shape is a RATCHET
2228// (Notion bans an INCREASE in violation count and requires a deliberate re-bank).
2229//
2230// u26a0AND THE PREDECESSOR THIS WAS SUPPOSED TO COPY DOES NOT EXIST. nx_magicratchet is asserted "wired
2231// into /api/build" in FOUR comments in nx_law_warden.nx, but grep finds ZERO call sites in this source
2232// AND ZERO in the deployed mgmt binary, and a two-build experiment (clean -> BUILT, +3 literals >=1024
2233// -> BUILT, not refused) proves it never fires. So this is written fresh, not modelled on prose.
2234//
2235// FAIL-OPEN, the same stance as build admission and the pre-deploy gate: a missing or unreadable
2236// detector returns -1 and the caller proceeds. A guard that cannot be read must never wedge the build
2237// path for every seat.
2238func md_exec_gatedry(srcpath: *u8) -> i64 {
2239 let gelf: *u8 = "/volume1/homes/elderwesto/nishihost/nx_gatedry.elf" as *u8
2240 let gf: i64 = sys_openat_rd(gelf)
2241 if gf < 0 { return 0 - 1 }
2242 sys_close(gf)
2243 let args: *i64 = sys_mmap(16) as *i64
2244 args[0] = srcpath as i64
2245 return dep_run_capture(gelf, args, 1, "/tmp/nx_gatedry.out" as *u8)
2246}
2247
2248// ---- F881 RULE-11 RATCHET RUNNER (2026-08-15, debt 1785530277) ---------------------------------
2249// The wiring four comments in nx_law_warden.nx asserted as fact, and which the comment above correctly
2250// records as never having existed. Confirmed absent TWICE today before writing this: nx_wiredclaim
2251// reports CLAIMED-UNWIRED call_sites=0, and behaviourally four real /api/build runs of one organ left
2252// its baseline at -1 while a single direct call created one. The chokepoint was exercised; nothing
2253// refused.
2254//
2255// WHY THIS IS SAFE WHERE L009 WAS NOT, and the difference is the whole reason it can land: L009 keyed
2256// "is this gate NEW?" on "has no deployed .elf", which was false for ~94pc of the corpus and turned the
2257// ratchet into a WALL at a shared chokepoint. nx_magicratchet uses the model that comment says a correct
2258// one needs -- a BANKED PER-ORGAN BASELINE, refusing only an INCREASE against the record. First sight of
2259// any organ self-baselines and GRANTS, so the existing corpus passes by construction.
2260//
2261// It was NOT safe to wire before today. Measured and fixed 2026-08-15, both in nx_magicratchet:
2262// - its parser skipped a leading '-', so countfile's -1 (UNMEASURED) read as a COUNT OF 1 and the
2263// fail-open branch could never fire;
2264// - it resolved _hdl_build/<n>.nx unconditionally, blind to every organ under runtime/.
2265// Together those banked baselines from files never opened, arming exactly the false refusal that killed
2266// L009. All four paths are now proven: resolves in either tree, unresolvable -> ALLOW-UNCOUNTED, and
2267// REFUSE still fires naming the offending line.
2268//
2269// FAIL-OPEN, the same stance as build admission and the gate-dry runner: a missing or unreadable
2270// detector returns -1 and the caller proceeds. A guard that cannot be read must never wedge the build
2271// path for every seat.
2272func md_exec_magicratchet(nm: *u8) -> i64 {
2273 let melf: *u8 = "/volume1/homes/elderwesto/nishihost/nx_magicratchet.elf" as *u8
2274 let mf: i64 = sys_openat_rd(melf)
2275 if mf < 0 { return 0 - 1 }
2276 sys_close(mf)
2277 let args: *i64 = sys_mmap(16) as *i64
2278 args[0] = "check" as *u8 as i64
2279 args[1] = nm as i64
2280 return dep_run_capture(melf, args, 2, "/tmp/nx_magicratchet.out" as *u8)
2281}
2282
2283// Does this organ name end in the terminal token `_gate`? TERMINAL, not substring -- the seq1789
2284// lesson banked in nx_organkind_gate T12: `nx_survey_serve_gate` ENDS with _gate, `nx_gate_bite`
2285// merely CONTAINS it, and treating containment as the test misclassifies the second.
2286func md_name_is_gate(nm: *u8) -> i64 {
2287 var n: i64 = 0
2288 while nm[n] != (0 as u8) { n = n + 1 }
2289 if n < 5 { return 0 }
2290 if nm[n-5] != (95 as u8) { return 0 }
2291 if nm[n-4] != (103 as u8) { return 0 }
2292 if nm[n-3] != (97 as u8) { return 0 }
2293 if nm[n-2] != (116 as u8) { return 0 }
2294 if nm[n-1] != (101 as u8) { return 0 }
2295 return 1
2296}
2297
2298// Is this organ ALREADY DEPLOYED? That is the GRANDFATHER TEST and it is what makes this a ratchet
2299// rather than a wall: 2041 existing gates hand-roll their verdicts, and refusing all of them would
2300// stop the ecosystem dead. Only a gate with NO deployed artefact -- i.e. a NEW one -- is held to the
2301// base class. Existing breaches are migrated by their owner lanes, never blocked here.
2302// ---- THE BANKED BASELINE: HOW THE RATCHET TELLS NEW FROM OLD (2026-07-31, debt 1785558585) ----
2303// MY FIRST ATTEMPT USED "has no deployed .elf" AS THE NEWNESS TEST AND THAT WAS WRONG. The ledger
2304// measures 2877 gate sources against 175 binaries -- ~94pc of gates were NEVER COMPILED -- so
2305// long-existing gates read as NEW and their rebuilds were REFUSED. A ratchet that cannot tell new
2306// from old is a WALL, and a wall at a shared chokepoint stops every seat. Withdrawn within minutes.
2307//
2308// THE CORRECT TEST IS A RECORD, which is what a ratchet actually is. Notion's ESLint ratcheting keeps
2309// a CHECKED-IN file of known violations and requires approval only when the count INCREASES against
2310// it. knowledge/status/gatedry_baseline.out is that record: the enumerated gate corpus at the moment
2311// the ratchet landed (2189 entries). A gate NOT in the record is NEW and is held to the base class;
2312// everything in the record is grandfathered and migrated by its owner lane.
2313//
2314// FAIL-OPEN BY CONSTRUCTION: an absent or unreadable baseline returns 1 (== "known", allow). If the
2315// record cannot be read we CANNOT distinguish new from old, and the only safe answer is to permit --
2316// otherwise a missing file silently rebuilds the exact wall this replaced.
2317func md_gate_in_baseline(nm: *u8) -> i64 {
2318 let bp: *u8 = "knowledge/status/gatedry_baseline.out" as *u8
2319 let ln: *i64 = sys_mmap(16) as *i64
2320 ln[0] = 0
2321 let buf: *u8 = sys_read_file(bp, ln)
2322 if buf as i64 == 0 { return 1 }
2323 let n: i64 = ln[0]
2324 if n <= 0 { return 1 }
2325 let pat: *u8 = sys_mmap(256)
2326 var o: i64 = 0
2327 pat[o] = (47 as u8)
2328 o = o + 1
2329 var bi: i64 = 0
2330 while nm[bi] != (0 as u8) { pat[o] = nm[bi]; o = o + 1; bi = bi + 1 }
2331 pat[o] = (46 as u8)
2332 o = o + 1
2333 pat[o] = (110 as u8)
2334 o = o + 1
2335 pat[o] = (120 as u8)
2336 o = o + 1
2337 let pn: i64 = o
2338 var k: i64 = 0
2339 while k + pn <= n {
2340 var j: i64 = 0
2341 var hit: i64 = 1
2342 while j < pn { if buf[k+j] != pat[j] { hit = 0; j = pn } else { j = j + 1 } }
2343 if hit == 1 { return 1 }
2344 k = k + 1
2345 }
2346 return 0
2347}
2348
2349func md_organ_deployed(nm: *u8) -> i64 {
2350 let p: *u8 = sys_mmap(256)
2351 let pre: *u8 = "/volume1/homes/elderwesto/nishihost/"
2352 var o: i64 = 0
2353 var i: i64 = 0
2354 while pre[i] != (0 as u8) { p[o] = pre[i]; o = o + 1; i = i + 1 }
2355 i = 0
2356 while nm[i] != (0 as u8) { p[o] = nm[i]; o = o + 1; i = i + 1 }
2357 let suf: *u8 = ".elf"
2358 i = 0
2359 while suf[i] != (0 as u8) { p[o] = suf[i]; o = o + 1; i = i + 1 }
2360 p[o] = 0 as u8
2361 let fd: i64 = sys_openat_rd(p)
2362 if fd < 0 { return 0 }
2363 sys_close(fd)
2364 return 1
2365}
2366
2367// ---- PRE-DEPLOY SAFETY GATE RUNNER (2026-07-30) -------------------------------------------------
2368// nx_deploy_ready computes deploy_safe/blockers/DEPLOY-BLOCKED and publishes it -- and NOTHING AT THE
2369// DEPLOY CHOKEPOINT EVER CONSULTED IT. It is referenced by ecomat seeding, tooldiff and the cron beat,
2370// but nx_mgmt_api never called it, so the one act the gate exists to guard ran unguarded. MEASURED:
2371// the gate returned verdict DEPLOY-BLOCKED (blockers=1) while two of my own deploys succeeded minutes
2372// apart. A gate that is computed, published and unreachable from the act it guards IS the baseline.
2373//
2374// WHY WE PARSE JSON AND NOT THE EXIT CODE: nx_deploy_ready calls sys_exit(0) UNCONDITIONALLY -- even
2375// when the verdict is DEPLOY-BLOCKED -- so its exit status carries no verdict at all and no caller
2376// checking $? could ever act on it. Fixing that is a published-contract change (other callers may
2377// treat nonzero as failure), so it is filed separately rather than changed underneath them here.
2378//
2379// FAIL-OPEN, same stance as build admission: a missing or unreadable gate returns -1 and the caller
2380// proceeds. A guard that cannot be read must never wedge the deploy path for every seat.
2381func md_exec_deploy_ready(target: *u8) -> i64 {
2382 let delf: *u8 = "/volume1/homes/elderwesto/nishihost/nx_deploy_ready.elf" as *u8
2383 let df: i64 = sys_openat_rd(delf)
2384 if df < 0 { return 0 - 1 }
2385 sys_close(df)
2386 let args: *i64 = sys_mmap(32) as *i64
2387 args[0] = "check" as *u8 as i64
2388 // LOOSE COUPLING (2026-09-02): pass the deploy TARGET so nx_deploy_ready scopes its evidence-honesty block
2389 // to the target's own domain + shared-fate domains (knowledge/registry/deploy_domain.conf), never to an
2390 // unrelated domain's RED. argv[2] is the default manifest because the target rides argv[3]; an EMPTY
2391 // target keeps the old one-arg call, i.e. GLOBAL scope, so no caller changes behaviour by accident.
2392 var nargs: i64 = 1
2393 if target != (0 as *u8) { if target[0] != (0 as u8) {
2394 args[1] = "knowledge/registry/deploy_checks.tsv" as *u8 as i64
2395 args[2] = target as i64
2396 nargs = 3
2397 } }
2398 // Return the gate's EXIT CODE (0 safe / 3 DEPLOY-BLOCKED) so the caller can surface it. Until today this
2399 // code was constant 0 and therefore meaningless; reporting it live is what proves the new contract landed.
2400 let drc0: i64 = dep_run_capture(delf, args, nargs, "/tmp/nx_ma_deploy_ready.out" as *u8); if drc0 >= 0 { return drc0 }
2401 return 0
2402}
2403
2404// mint a ROOT tools-capability token via the on-NAS nx_cap_mint oracle (CLI: <keyfile> <allow-csv> <exp> <nonce>
2405// -> token on stdout, nonzero exit on refusal). The HMAC keyfile is read BY THE ORACLE on-NAS and never crosses
2406// the API. Absolute paths (same stance as the hostctl/treepack exec ports; mgmt cwd=nishihost but explicit wins).
2407// stdout captured -> outpath; caller treats nonzero exit OR empty capture as mint-failed (fail-closed).
2408func md_exec_capmint(allow: *u8, expstr: *u8, noncestr: *u8, outpath: *u8) -> i64 {
2409 let helf: *u8 = "/volume1/homes/elderwesto/nishihost/nx_cap_mint.elf" as *u8
2410 let args: *i64 = sys_mmap(40) as *i64
2411 args[0] = "/volume1/homes/elderwesto/nishihost/tools_cap_secret.key" as *u8 as i64
2412 args[1] = allow as i64
2413 args[2] = expstr as i64
2414 args[3] = noncestr as i64
2415 return dep_run_capture(helf, args, 4, outpath)
2416}
2417// ---- /api/gate_run + /api/proc_kill support (seq1349/1383). RE-APPLIED after a 4th backdate (seq1445).
2418// gate_run bound: name must end gate/test/kat, resolves ONLY a promoted top-level nishihost/<n>.elf, so a pure
2419// verifier is all this route can ever reach -- never a daemon, promoter or deployer.
2420// ⚠ seq1443: dep_run_capture_bounded DUPLICATES tr_run_capture_to (nx_tool_run.nx, seq1412) which is gate-proven
2421// (nx_tool_run_timeout_gate T5 = no leak after a kill) and uses a WATCHDOG FORK because a poll design is not
2422// buildable without sys_fcntl. ADOPT IT next; kept here only so the live verbs stop vanishing from source.
2423// ---- R1 (seq1506): LEASE-GATE THE BUILD PATH -------------------------------------------------------------
2424// OPERATOR 2026-07-30: "why cant we clearly state when we are switching out or updating and coordinate like
2425// road construction". This is the flagger. Concurrent builds of the SAME target are how a session ships a
2426// regression from a mid-churn snapshot -- it happened twice today (21->19 routes lost, then again).
2427// ADOPTION, NOT INVENTION (seq1410's law, 4th instance today): nx_lease ALREADY EXISTS, is gate-proven, and
2428// had ZERO callers in the build path. We reuse it as a SUBPROCESS via its exit-code contract rather than
2429// importing it -- verified live: acquire=0 prints LS-ACQUIRED, BUSY=3 prints "LS-BUSY <name> holder=<who>",
2430// release=0. Exit codes are the contract, so no import coupling and no second implementation.
2431// TTL is the reason this can never deadlock the ecosystem: a session that dies mid-build cannot hold the
2432// lane closed -- the lease expires on its own. A lock without a TTL would be a worse defect than the race.
2433// ⚠ lease NAME grammar is [a-zA-Z0-9_-] ONLY: "build:X" is REFUSED, so the name is built as "build-<target>".
2434func md_lease_run(verb: *u8, name: *u8, owner: *u8, ttl: *u8, nargs: i64, outpath: *u8) -> i64 {
2435 let elf: *u8 = "/volume1/homes/elderwesto/nishihost/nx_lease.elf" as *u8
2436 let args: *i64 = sys_mmap(48) as *i64
2437 args[0] = verb as i64
2438 args[1] = name as i64
2439 args[2] = owner as i64
2440 args[3] = ttl as i64
2441 return dep_run_capture(elf, args, nargs, outpath)
2442}
2443// Build the lease name "build-<target>" into buf. Target is already [A-Za-z0-9_]-sanitized by the caller.
2444func md_lease_name(target: *u8, buf: *u8) -> i64 {
2445 var o: i64 = 0
2446 let p: *u8 = "build-" as *u8
2447 while p[o] != (0 as u8) { buf[o] = p[o]; o = o + 1 }
2448 var i: i64 = 0
2449 while target[i] != (0 as u8) { buf[o] = target[i]; o = o + 1; i = i + 1 }
2450 buf[o] = 0 as u8
2451 return o
2452}
2453// Build "<prefix><src>" into buf, COPYING ONLY lease-legal chars [a-zA-Z0-9_-] from src.
2454// ⚠ nx_lease REFUSES any other byte, and the things we most want to lock are named with dots
2455// ("nx_tools_api_serve.elf") -- so a naive concat produces LS-REFUSED bad name and the guard silently
2456// never engages. A guard that cannot be named is a guard that does not exist; filter, do not assume.
2457func md_lease_name_pfx(prefix: *u8, src: *u8, buf: *u8) -> i64 {
2458 var o: i64 = 0
2459 while prefix[o] != (0 as u8) { buf[o] = prefix[o]; o = o + 1 }
2460 var i: i64 = 0
2461 while src[i] != (0 as u8) {
2462 let c: i64 = src[i] as i64
2463 var ok: i64 = 0
2464 if c >= 48 { if c <= 57 { ok = 1 } }
2465 if c >= 65 { if c <= 90 { ok = 1 } }
2466 if c >= 97 { if c <= 122 { ok = 1 } }
2467 if c == 95 { ok = 1 }
2468 if c == 45 { ok = 1 }
2469 if ok == 1 { buf[o] = src[i]; o = o + 1 }
2470 i = i + 1
2471 }
2472 buf[o] = 0 as u8
2473 return o
2474}
2475func md_gate_name_ok(nm: *u8) -> i64 {
2476 var n: i64 = 0
2477 while nm[n] != (0 as u8) { n = n + 1 }
2478 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 } } } } }
2479 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 } } } } }
2480 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 } } } }
2481 return 0
2482}
2483// seq1443 ADOPTION: this now delegates to tr_run_capture_to (runtime/nx_tool_run.nx, seq1412) instead of my
2484// own dep_run_capture_bounded, which was a DUPLICATE of it -- a live instance of the ecosystem's own law that
2485// THE BOTTLENECK IS NOT BUILDING PRIMITIVES, IT IS ADOPTING THEM.
2486// The adopted primitive is STRICTLY better and its header says why mine could not work: bounding the drain
2487// needs O_NONBLOCK on the read end and THERE IS NO sys_fcntl in nx_syscalls, so a poll design is NOT
2488// BUILDABLE. It uses a WATCHDOG FORK -- the watchdog SIGKILLs the worker, the dying worker drops the last
2489// write end, and the parent's blocking read gets its EOF naturally, needing no new syscall. It also closes
2490// wfd BEFORE forking the watchdog (order is load-bearing: fork first and the watchdog inherits the write end,
2491// so the pipe never EOFs -- the exact hang the bound exists to remove, reintroduced by the fix).
2492// It ships nx_tool_run_timeout_gate whose T5 is "second timeout identical (no leak after a kill)" = the
2493// no-leak tooth seq1425 was missing. Capturing to a BUFFER also deletes the /tmp/nx_ma_gaterun.out temp file.
2494// Returns the child's exit code, or TR_ERR_TIMEOUT when the deadline fired.
2495func md_exec_gate_capture(elfpath: *u8, out: *u8, cap: i64, outlen: *i64, deadline_ms: i64) -> i64 {
2496 let argv: *i64 = sys_mmap(32) as *i64
2497 argv[0] = elfpath as i64
2498 argv[1] = 0
2499 return tr_run_capture_to(elfpath, argv, out, cap, outlen, deadline_ms)
2500}
2501// (md_exec_gate_capture_OLD REMOVED 2026-08-07 by a different seat than the one that landed the seq1443
2502// adoption above. It had ZERO callers -- grep finds only its own definition -- but it still called
2503// dep_run_capture_bounded, whose definition went away when md_exec_gate_capture adopted
2504// tr_run_capture_to. DEAD CODE STILL HAS TO COMPILE: that one unreachable line made /api/build fail for
2505// target=nx_mgmt_api for EVERY seat, i.e. it took down the deploy path itself, and the second reported
2506// error -- a bogus "sd_catn arg 3 is a POINTER" at nx_mgmt_api.nx:1907, where dl is plainly `var dl:
2507// i64` -- was a CASCADE of this same undefined name, not a second defect.
2508// A REFACTOR THAT DELETES A DEFINITION BUT LEAVES ITS DEAD CALLER HAS NOT LANDED, IT HAS HALF-LANDED.)
2509// proc_kill bound: >=6 chars AND must contain .elf (our own organs only, never a system process) AND must not
2510// reach the supervisor (killing the guard stops every respawn). Killing a guard-supervised daemon = a restart.
2511func md_str_contains(hay: *u8, pat: *u8) -> i64 {
2512 let hn: i64 = md_len(hay)
2513 let pn: i64 = md_len(pat)
2514 if pn == 0 { return 0 }
2515 if pn > hn { return 0 }
2516 var i: i64 = 0
2517 while i + pn <= hn {
2518 var k: i64 = 0
2519 var hit: i64 = 1
2520 while k < pn { if hay[i+k] != pat[k] { hit = 0; k = pn } else { k = k + 1 } }
2521 if hit == 1 { return 1 }
2522 i = i + 1
2523 }
2524 return 0
2525}
2526func md_proc_kill_needle_ok(nm: *u8) -> i64 {
2527 if md_len(nm) < 6 { return 0 }
2528 if md_str_contains(nm, ".elf" as *u8) == 0 { return 0 }
2529 if md_str_contains(nm, "supervise" as *u8) == 1 { return 0 }
2530 if md_str_contains(nm, "nx_hostctl" as *u8) == 1 { return 0 }
2531 return 1
2532}
2533func 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 }
2534// FAIL-CLOSED allowlist for /api/hostctl. Curated to fast (<~15s) read + safe-maintenance subs so a synchronous
2535// API call returns promptly. DEPLOY/selfswap/rollback are DELIBERATELY excluded (they promote/re-exec -> use the
2536// guarded /api/deploy). Long-running (nettap 60s, portmap timeouts) excluded until an async job path exists.
2537func md_hostctl_action_ok(nm: *u8) -> i64 {
2538 if md_cstr_eq(nm, "status" as *u8) == 1 { return 1 } // supervisor snapshot
2539 if md_cstr_eq(nm, "torstat" as *u8) == 1 { return 1 } // per-torrent seedeval + metadata diag
2540 if md_cstr_eq(nm, "routerctl" as *u8) == 1 { return 1 } // GL.iNet dashboard (model/wan/forwards/clients)
2541 if md_cstr_eq(nm, "receipts" as *u8) == 1 { return 1 } // op-receipts ledger (read-only)
2542 if md_cstr_eq(nm, "kicktorrent" as *u8) == 1 { return 1 } // restart the torrent daemon
2543 if md_cstr_eq(nm, "kickseed" as *u8) == 1 { return 1 } // restart the :6881 seeder
2544 if md_cstr_eq(nm, "kickworkers" as *u8) == 1 { return 1 } // restart stale download workers
2545 if md_cstr_eq(nm, "kickseedann" as *u8) == 1 { return 1 } // restart the DHT/LSD announcer
2546 if md_cstr_eq(nm, "trackerrefresh" as *u8) == 1 { return 1 } // refresh the tracker list (detached)
2547 if md_cstr_eq(nm, "galxpipeline" as *u8) == 1 { return 1 } // analysis-on-ingest: thumbnails + NXVI (detached, idempotent)
2548 if md_cstr_eq(nm, "durindexrun" as *u8) == 1 { return 1 } // duration-index batch (detached, idempotent)
2549 if md_cstr_eq(nm, "searchpagerank" as *u8) == 1 { return 1 } // search: PageRank build on the live web shard (detached, idempotent, additive pr:)
2550 if md_cstr_eq(nm, "searchcompact" as *u8) == 1 { return 1 } // search: web-shard compaction (detached, idempotent, verifies-before-swap)
2551 if md_cstr_eq(nm, "durindexstat" as *u8) == 1 { return 1 } // duration-index coverage (read-only)
2552 return 0
2553}
2554
2555// REAL-HTTP health: GET url -> require 200 + non-empty body. 1 healthy / 0 not (the false-green killer).
2556// LOCAL TCP liveness: connect to 127.0.0.1:port -> 1 if something is listening (service up), 0 if refused.
2557// Dependency-free (no external fetcher, no CA store, no edge round-trip) = the robust health signal for a restart.
2558func md_tcp_alive(port: i64) -> i64 {
2559 let fd: i64 = sys_socket(AF_INET, SOCK_STREAM, 0); if fd < 0 { return 0 }
2560 let sa: *u8 = sys_mmap(16)
2561 sa[0]=2 as u8; sa[1]=0 as u8; sa[2]=((port>>8)&0xff) as u8; sa[3]=(port&0xff) as u8
2562 sa[4]=127 as u8; sa[5]=0 as u8; sa[6]=0 as u8; sa[7]=1 as u8
2563 var z: i64=8; while z<16 { sa[z]=0 as u8; z=z+1 }
2564 let r: i64 = nx_connect_bounded(fd, sa, 16, NX_CONN_DEFAULT_MS); sys_close(fd)
2565 if r == 0 { return 1 }
2566 return 0
2567}
2568// ---- DEPLOY LISTEN VERIFICATION (debt 1786068492) -----------------------------------------------------
2569// THE DEFECT THIS CLOSES IS NOT "THE CHECK WAS MISSING" -- THE CHECK WAS HERE AND IT GREENED ON THE WRONG
2570// PROCESS. hc_deploy_one promotes, then proc_kill_by_name(SIGKILL), then returns IMMEDIATELY; the deploy
2571// watchdog then called md_tcp_alive() with NO initial delay. SIGKILL is asynchronous -- the kernel releases
2572// the dying process's listening socket some time AFTER the syscall returns -- so a connect issued inside that
2573// window SUCCEEDS AGAINST THE OUTGOING LISTENER and the deploy is recorded DEPLOYED-GREEN having never once
2574// observed the incoming binary. That is precisely why debt 1786068492 records "the auto-rollback health check
2575// did not catch this either": it ran, and it PASSED, on the process being replaced.
2576// AAAAAA A LEVEL-TRIGGERED PROBE CANNOT WITNESS A HANDOFF -- "SOMETHING IS LISTENING" IS TRUE BOTH BEFORE AND
2577// AFTER, SO THE ONE INSTANT THAT DISTINGUISHES A REAL REPLACEMENT FROM A NO-OP IS THE ONLY THING IT NEVER
2578// SAMPLES. The same blindness is why a surviving second instance, or a kill that matched nothing, also greens.
2579//
2580// THIS IS A DETECTOR, NOT A CURE, AND THE DIFFERENCE IS STATED SO NO READER OVER-CLAIMS IT. It does NOT close
2581// the race: with kill-then-respawn there is still an instant at which ZERO processes hold the socket, and only
2582// descriptor handoff removes that (knowledge/compare/trafficsafety.matrix -- nginx, HAProxy and Envoy pass the
2583// fd and never re-bind; HAProxy measured 155 connection failures per million over 180 reloads before moving
2584// it). SO_REUSEPORT is NOT the fix either: it is accept-distribution, and its own documentation states it
2585// CREATES a drop window when the listening set changes. What this buys is that the failure is now NAMED and
2586// LOUD instead of silent and green.
2587// AAAAA AND IT PROVES ONLY THAT THE PORT ANSWERS, NEVER THAT THE RIGHT BINARY ANSWERS -- a promote of an
2588// unadjudicated artifact that listens perfectly is GREEN here and always will be. That is a different axis
2589// (artifact provenance) and it is guarded separately, above, by the routeguard and staging-hygiene classes.
2590//
2591// THREE NAMED OUTCOMES, BECAUSE UNOBSERVED MUST BE ITS OWN BUCKET: folding "I could not witness the handoff"
2592// into either GREEN or ROLLED-BACK is the false-proof-with-an-authoritative-name defect. An axis that cannot
2593// see must ABSTAIN, not acquit.
2594const MD_HP_NEVER_UP: i64 = 0 // the port never accepted inside the up budget -> the incoming process never listened -> ROLL BACK
2595const MD_HP_VERIFIED: i64 = 1 // REFUSED observed, then ACCEPTED -> the socket demonstrably changed hands
2596const MD_HP_UNVERIFIED: i64 = 2 // never observed REFUSED, but the port accepts -> healthy-looking, provenance UNPROVEN
2597
2598// probe budget slots (rule 11: the loops below index these, they never carry a bare number)
2599const MD_PC_UP_TRIES: i64 = 0
2600const MD_PC_UP_TICK_MS: i64 = 1
2601const MD_PC_DOWN_TICK_MS: i64 = 2
2602const MD_PC_SLOTS: i64 = 3
2603
2604// DERIVATION OF EVERY NUMBER HERE, stated because a timeout without a derivation is a magic number wearing a
2605// units suffix:
2606// up_tries x up_tick_ms must exceed the guard-respawn bound hc_deploy_one ITSELF announces to the operator
2607// ("guard respawns the NEW binary ... <=15s"). 10 x 3000 = 30s is 2x that bound and is the value this path
2608// was ALREADY calibrated at -- REUSED, not re-invented, so no deploy that passes today can begin failing
2609// because a budget moved under it.
2610// down_tick_ms is NOT a second budget. The REFUSED edge is a process-teardown transient (SIGKILL -> fd table
2611// release), sub-second on an idle box, and AN EVENT CAN ONLY BE OBSERVED BY SAMPLING FASTER THAN IT LASTS --
2612// so the down phase samples in milliseconds where the up phase samples in seconds. The down WINDOW is
2613// DERIVED as exactly ONE up-tick (md_probe_down_tries), so this file holds ONE calibrated quantity, not two
2614// that can silently drift apart.
2615func md_probe_conf(out: *i64) -> i64 {
2616 out[MD_PC_UP_TRIES] = 10
2617 out[MD_PC_UP_TICK_MS] = 3000
2618 // DEFAULT 0 = DOWN-WATCH OFF, and that default is a SAFETY DECISION, not a tuning choice.
2619 // MEASURED 2026-08-21 on the live control plane: sampling for the REFUSED edge before deciding
2620 // anything extended the watchdog's life by one up-tick (~3s) BEFORE it could write a verdict, and the
2621 // watchdog is KILLED inside that window -- deploy_status stuck at RUNNING, no terminal state, one
2622 // nx_mgmt_api process left, and NO segfault in dmesg (so killed, not crashed). The previous
2623 // level-triggered probe survived only because it wrote within milliseconds.
2624 // AAAAAA A DETECTOR THAT LENGTHENS THE LIFE OF THE PROCESS THAT MUST SURVIVE TO REPORT ITS VERDICT CAN
2625 // DESTROY THE VERDICT IT EXISTS TO PRODUCE -- and here that verdict IS the never-brick auto-rollback,
2626 // so the "improvement" silently disarmed a safety control it was written to strengthen.
2627 // THE EDGE IS STILL CAUGHT FOR FREE with the watch off: if the FIRST up-sample is REFUSED we have
2628 // witnessed the port down, and a later ACCEPT is a genuine handoff. If the first sample already
2629 // ACCEPTS we never saw it drop -- which is precisely UNVERIFIED. Identical timing to the incumbent,
2630 // zero added lifetime, and the silent false-green still becomes a NAMED state.
2631 out[MD_PC_DOWN_TICK_MS] = 0
2632 let szp: *i64 = sys_mmap(16) as *i64
2633 let buf: *u8 = md_read_file("deploy_probe.conf" as *u8, szp)
2634 if (buf as i64) == 0 { return 0 } // ABSENT -> compiled-in defaults. FAIL-OPEN: a probe that cannot read a conf must still probe.
2635 let n: i64 = szp[0]
2636 let offs: *i64 = sys_mmap(64) as *i64
2637 let lens: *i64 = sys_mmap(64) as *i64
2638 var cur: i64 = 0
2639 while cur < n {
2640 let le: i64 = md_eol(buf, n, cur)
2641 var isc: i64 = 0
2642 if le > cur { if (buf[cur] as i64) == 35 { isc = 1 } }
2643 if isc == 0 {
2644 let nf: i64 = md_split(buf, cur, le, offs, lens, 8)
2645 if nf >= 2 {
2646 let v: i64 = md_slice_atoi(buf, offs[1], lens[1])
2647 // v > 0 REFUSES a zero or unparsable value rather than adopting it: a 0 tick spins the box and
2648 // a 0 try-count turns the whole probe into an unconditional pass. Defensive at the boundary.
2649 if v > 0 {
2650 if md_tok_eq(buf, offs[0], lens[0], "up_tries" as *u8) == 1 { out[MD_PC_UP_TRIES] = v }
2651 if md_tok_eq(buf, offs[0], lens[0], "up_tick_ms" as *u8) == 1 { out[MD_PC_UP_TICK_MS] = v }
2652 if md_tok_eq(buf, offs[0], lens[0], "down_tick_ms" as *u8) == 1 { out[MD_PC_DOWN_TICK_MS] = v }
2653 }
2654 }
2655 }
2656 cur = le + 1
2657 }
2658 return 1
2659}
2660
2661// The down window is exactly ONE up-tick, DERIVED -- never a second typed constant that can drift.
2662func md_probe_down_tries(cfg: *i64) -> i64 {
2663 let t: i64 = cfg[MD_PC_DOWN_TICK_MS]
2664 if t <= 0 { return 1 }
2665 var k: i64 = cfg[MD_PC_UP_TICK_MS] / t
2666 if k < 1 { k = 1 }
2667 return k
2668}
2669
2670// "port:<N>" grammar, factored out so the deploy watchdog and the legacy 1/0 probe read the SAME grammar.
2671// TWO READERS OF ONE GRAMMAR IS HOW A WIRE MISMATCH IS BORN.
2672func md_url_is_port(url: *u8) -> i64 {
2673 if url[0] != (112 as u8) { return 0 }
2674 if url[1] != (111 as u8) { return 0 }
2675 if url[2] != (114 as u8) { return 0 }
2676 if url[3] != (116 as u8) { return 0 }
2677 if url[4] != (58 as u8) { return 0 }
2678 return 1
2679}
2680func md_url_port(url: *u8) -> i64 {
2681 var pt: i64 = 0
2682 var pi: i64 = 5
2683 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 }
2684 return pt
2685}
2686
2687// EDGE-TRIGGERED listener verification on 127.0.0.1:<port>. COMPOSES md_tcp_alive -- the one TCP ruler in this
2688// plane -- and deliberately does NOT fork an HTTP prober: the forked-fetcher leg of md_health_probe below is
2689// recorded IN ITS OWN COMMENT as having ALWAYS false-rolled-back because the fetcher is not reliably present at
2690// the mgmt cwd, and re-introducing a fork here would re-introduce exactly that fragility.
2691// SPLIT FOR TESTABILITY: the budget arrives as a PARAMETER, so a gate can drive THIS EXACT CODE PATH at
2692// millisecond timescales instead of the production 30s one. AAAAA A LOGIC THAT CAN ONLY BE EXERCISED AT
2693// PRODUCTION TIMESCALES IS A LOGIC NOBODY WILL TEST -- and an untested probe is how the level-triggered one
2694// survived. The production entry point (md_probe_listen_edge) is immediately below and loads the conf.
2695func md_probe_listen_edge_cfg(port: i64, cfg: *i64) -> i64 {
2696 // PHASE 1 -- watch for the REFUSED edge. Bounded, and NEVER a rollback trigger on its own: some targets
2697 // restart WITHOUT ever dropping the socket (the hostctl self-swap leaves sites.elf :8443 up throughout),
2698 // and refusing those would be a detector that fails in the DESTRUCTIVE direction. Missing the edge only
2699 // DOWNGRADES the verdict; it can never cause a rollback.
2700 var saw_down: i64 = 0
2701 // OPT-IN ONLY (see md_probe_conf): this watch costs watchdog LIFETIME before any verdict is written,
2702 // and that cost once destroyed the verdict itself. OFF by default; a caller that can afford the extra
2703 // latency before deciding enables it by setting down_tick_ms. With it off the first up-sample below
2704 // still supplies the edge for free.
2705 if cfg[MD_PC_DOWN_TICK_MS] > 0 {
2706 let dtries: i64 = md_probe_down_tries(cfg)
2707 var d: i64 = 0
2708 var scanning: i64 = 1
2709 while scanning == 1 {
2710 if md_tcp_alive(port) == 0 { saw_down = 1; scanning = 0 }
2711 else {
2712 d = d + 1
2713 if d >= dtries { scanning = 0 } else { sys_sleep_ms(cfg[MD_PC_DOWN_TICK_MS]) }
2714 }
2715 }
2716 }
2717 // PHASE 2 -- the incoming process must LISTEN inside the up budget. This is the tooth that fires on the
2718 // exact failure debt 1786068492 describes: a new process that starts, cannot bind, and exits.
2719 var at: i64 = 0
2720 while at < cfg[MD_PC_UP_TRIES] {
2721 if md_tcp_alive(port) == 1 {
2722 if saw_down == 1 { return MD_HP_VERIFIED }
2723 return MD_HP_UNVERIFIED
2724 }
2725 saw_down = 1 // a REFUSED seen during the up wait IS the same edge, observed later
2726 at = at + 1
2727 if at < cfg[MD_PC_UP_TRIES] { sys_sleep_ms(cfg[MD_PC_UP_TICK_MS]) }
2728 }
2729 return MD_HP_NEVER_UP
2730}
2731
2732// PRODUCTION ENTRY POINT: load the conf, then run the SAME logic the gate exercises. There is exactly ONE
2733// implementation of the edge check; this wrapper only supplies the budget.
2734func md_probe_listen_edge(port: i64) -> i64 {
2735 let cfg: *i64 = sys_mmap(8 * MD_PC_SLOTS) as *i64
2736 md_probe_conf(cfg)
2737 return md_probe_listen_edge_cfg(port, cfg)
2738}
2739
2740// Named-outcome health for the DEPLOY watchdog. port:<N> -> edge-verified listener check. Anything else -> the
2741// HTTP leg unchanged and still level-triggered ON PURPOSE: an HTTP 200 with a non-empty body already proves an
2742// APPLICATION answered, which is strictly stronger than "something holds the socket", so an edge phase would
2743// add nothing there.
2744func md_deploy_health(url: *u8) -> i64 {
2745 if md_url_is_port(url) == 1 { return md_probe_listen_edge(md_url_port(url)) }
2746 if md_health_probe(url) == 1 { return MD_HP_VERIFIED }
2747 return MD_HP_NEVER_UP
2748}
2749
2750// The status SUFFIX for each outcome. The FIRST token of the status line is deliberately left to the caller and
2751// unchanged (DEPLOYED-GREEN / ROLLED-BACK), so every existing reader of /api/deploy_status keeps working and
2752// this is purely ADDITIVE -- rule 19.
2753func md_hp_suffix(code: i64) -> *u8 {
2754 if code == MD_HP_VERIFIED { return " listen=VERIFIED" as *u8 }
2755 if code == MD_HP_UNVERIFIED { return " listen=UNVERIFIED-may-be-outgoing-process" as *u8 }
2756 return " listen=NEVER-LISTENED" as *u8
2757}
2758
2759func md_health_probe(url: *u8) -> i64 {
2760 // "port:<N>" -> LOCAL TCP-connect health (no nx_research_fetch/CA/edge dependency -- the robust default for
2761 // restart-targets: the earlier /torrent HTTP probe needed an on-NAS fetcher+CA that isn't at the mgmt cwd,
2762 // so it always failed -> conservative rollback). Otherwise the HTTP-fetch probe below. Both retry 10x3s.
2763 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) {
2764 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 }
2765 // DELEGATES to the edge-verified probe so there is exactly ONE listener-verification implementation in
2766 // this plane. The 1/0 contract of THIS function is preserved EXACTLY (up=1, down=0) -- the richer
2767 // three-state answer is reached through md_deploy_health, so no existing 1/0 consumer changes meaning.
2768 if md_probe_listen_edge(pt) == MD_HP_NEVER_UP { return 0 }
2769 return 1
2770 } } } } }
2771 let pargs: *i64 = sys_mmap(16) as *i64
2772 pargs[0] = url as i64
2773 let pbuf: *u8 = sys_mmap(16384)
2774 // 10 tries x 3s = up to 30s: GENEROUS, because /api/deploy now runs this in a DETACHED watchdog (not on the
2775 // request path) -> it no longer races the edge-proxy read window, so it can wait out a slow guard-respawn
2776 // (~10-15s) and confirm 200+body -> GREEN, instead of a premature conservative rollback.
2777 var attempt: i64 = 0
2778 while attempt < 10 {
2779 dep_run_capture("_offc/nx_research_fetch.elf" as *u8, pargs, 1, "/tmp/nx_ma_deploy_health.out" as *u8)
2780 let pn: i64 = dp_read("/tmp/nx_ma_deploy_health.out" as *u8, pbuf, 16380)
2781 let st: i64 = hh_after(pbuf, pn, "status=" as *u8)
2782 let bbn: i64 = hh_after(pbuf, pn, "body_bytes=" as *u8)
2783 if st == 200 { if bbn > 0 { return 1 } }
2784 attempt = attempt + 1
2785 if attempt < 10 { sys_sleep_ms(3000) }
2786 }
2787 return 0
2788}
2789
2790// Private integration of the existing registered-target and file-session owners.
2791// Canary executable/argv/budgets are supplied by the authenticated host policy,
2792// never copied from request form fields. Caller retains the session through its
2793// durable outcome receipt, then closes it on every path.
2794struct NxToolchainSessionResult {
2795 target: NxDeployTargetRecord,
2796 session: NxFileInstallSession,
2797 reverse_admission: NxFileInstallResult,
2798 publication: NxFileInstallResult,
2799 rollback: NxFileInstallResult,
2800 stage: *u8,
2801 code: i64,
2802 canary_attempted: i64,
2803 canary_exit: i64,
2804 canary_bytes: i64,
2805 canary_truncated: i64,
2806 rollback_attempted: i64,
2807}
2808func md_tc_session_init(out:*NxToolchainSessionResult)->i64 {
2809 md_target_init(&out.target);fi_session_init(&out.session)
2810 fi_session_result_init(&out.reverse_admission);fi_session_result_init(&out.publication);fi_session_result_init(&out.rollback)
2811 out.stage="toolchain-input";out.code=FIO_EINVAL
2812 out.canary_attempted=0;out.canary_exit=0;out.canary_bytes=0;out.canary_truncated=0;out.rollback_attempted=0;return 0
2813}
2814func md_tc_session_close(out:*NxToolchainSessionResult)->i64 {
2815 let released:i64=fi_session_close(&out.session)
2816 let closed:i64=md_target_close(&out.target)
2817 if released!=0 { return released };return closed
2818}
2819func md_tc_session_run(registry:*u8,snapshot_budget:i64,target:*u8,forward:*u8,forward_hash:*u8,reverse:*u8,reverse_hash:*u8,intent_budget:i64,canary:*u8,args:*i64,deadline_ms:i64,scratch:*u8,scratch_bytes:i64,capture:*u8,capture_bytes:i64,out:*NxToolchainSessionResult)->i64 {
2820 if (out as i64)==0 { return FIO_EINVAL }
2821 if out.session.held==1 { return FIO_EEXIST }
2822 md_tc_session_close(out);md_tc_session_init(out)
2823 if (target as i64)==0 || md_toolchain_target_ok(target)!=1 || deadline_ms<=0 || (args as i64)==0 || fi_path_valid(canary)==0 || (capture as i64)==0 || capture_bytes<=0 || (scratch as i64)==0 || scratch_bytes<=0 { return out.code }
2824 out.stage="toolchain-target-resolution"
2825 out.code=md_target_read(registry,snapshot_budget,target,fi_len(target),&out.target)
2826 if out.code!=0 { return out.code }
2827 out.stage="toolchain-target-kind"
2828 if md_streq(out.target.kind_text,"toolchain")!=1 && md_streq(out.target.kind_text,"toolchain-v2")!=1 { out.code=FI_EACCES;return out.code }
2829 out.stage="toolchain-session-begin"
2830 out.code=fi_session_begin(forward,intent_budget,out.target.live,forward_hash,&out.session)
2831 if out.code!=0 { return out.code }
2832 out.stage="toolchain-staged-binding"
2833 if md_streq(out.session.plan.source,out.target.staged)!=1 { out.code=FI_EACCES;return out.code }
2834 out.stage="toolchain-reverse-admission"
2835 out.code=fi_session_prepare_reverse(&out.session,reverse,intent_budget,reverse_hash,scratch,scratch_bytes,&out.reverse_admission)
2836 if out.code!=0 { return out.code }
2837 out.stage="toolchain-publish"
2838 out.code=fi_session_publish(&out.session,scratch,scratch_bytes,&out.publication)
2839 if out.code==0 {
2840 out.stage="toolchain-canary";out.canary_attempted=1
2841 out.canary_exit=tr_run_capture_tr(canary,args,capture,capture_bytes,&out.canary_bytes,deadline_ms,&out.canary_truncated)
2842 if out.canary_exit!=0 || out.canary_truncated!=0 { out.code=FIO_EIO }
2843 }
2844 if out.code!=0 && out.publication.replacement.publication.visible==1 {
2845 out.stage="toolchain-rollback";out.rollback_attempted=1
2846 let restored:i64=fi_session_reverse(&out.session,reverse,intent_budget,reverse_hash,scratch,scratch_bytes,&out.rollback)
2847 if restored!=0 { out.code=restored;out.stage="toolchain-rollback-needs-intervention" }
2848 else { out.stage="toolchain-rejected-restored" }
2849 } else { if out.code==0 { out.stage="toolchain-canary-accepted-receipt-pending" } }
2850 return out.code
2851}