code wiki / (root) / nx_artifact_root.nx

nx_artifact_root.nx source

↩ module page · 106 lines · 4776 B

1// nx_artifact_root.nx -- ARTIFACT ROOT RESOLUTION, shared. One resolver, one roots table, for every organ 2// that has to turn a by-convention path into a file that actually exists on THIS host. 3// 4// WHY THIS IS A SHARED LIB AND NOT A LOCAL HELPER: two independent lanes paid for the same defect within a 5// day of each other, which is the signal that it is a CLASS, not a bug. 6// * evidence lane (2026-07-30): a `.gates` row names `_offc/nx_X_gate.elf` and a matrix row names 7// `runtime/nx_X.nx`. BOTH resolve on the laptop and NEITHER on the NAS -- /api/promote installs binaries 8// FLAT at the serving root, and sources live under `buildroot/`. MEASURED: 9 of 9 real warden axes read 9// UNGROUNDED on the NAS while every symbol was present in the tree the whole time. 10// * seq1339 (sibling): the maturity rollup reads evidence logs at one root while gates emit 1164 of them 11// at another, leaving ~85% of real evidence invisible and whole lanes reading UNMEASURED. 12// Same shape both times: an organ decided "not here" when the honest answer was "look somewhere else". 13// 14// ★THE LAW THIS ENCODES: a path convention that differs per host is a DATA fact, not a code constant. 15// The roots live in a conf (rule 11/17) so a new deployment layout is a config edit, not a recompile of 16// every consumer. 17// 18// RESOLUTION ORDER, and why all three steps are needed: 19// 1. the path EXACTLY as written -- so nothing that works today changes behaviour 20// 2. each root + the FULL relative path -- sources keep their directory part (`buildroot/` + `runtime/x.nx`) 21// 3. each root + the BASENAME -- binaries lose theirs (`` + `x.elf`, because promote installs flat) 22// First hit wins. A miss returns 0 and leaves `out` equal to the input, so the caller's error message stays 23// truthful about what it was actually looking for. 24// license_tier: ORIGINAL 25import "nx_syscalls.nx" 26 27const AR_CONF_MAX: i64 = 8192 28 29static ar_buf: *u8 30static ar_n: i64 31static ar_loaded: i64 32 33func ar_read(path: *u8, buf: *u8, cap: i64) -> i64 { 34 let fd: i64 = sys_openat_rd(path) 35 if fd < 0 { return 0 - 1 } 36 var tot: i64 = 0 37 var go: i64 = 1 38 while go == 1 { 39 let n: i64 = sys_read(fd, ((buf as i64)+tot) as *u8, cap - tot) 40 if n <= 0 { go = 0 } else { tot = tot + n; if tot >= cap { go = 0 } } 41 } 42 sys_close(fd) 43 return tot 44} 45 46func ar_exists(p: *u8) -> i64 { 47 let fd: i64 = sys_openat_rd(p) 48 if fd >= 0 { sys_close(fd); return 1 } 49 return 0 50} 51 52// Resolve against an EXPLICIT roots conf. Taking the conf path as a parameter is deliberate: a resolver 53// that can only ever read one hardcoded file cannot be tested without editing production config, and an 54// untestable resolver is exactly the kind of component that silently rots into the defect above. 55func ar_resolve_with(inp: *u8, out: *u8, confpath: *u8) -> i64 { 56 var n: i64 = 0 57 while inp[n] != (0 as u8) { n = n + 1 } 58 var i: i64 = 0 59 while i <= n { out[i] = inp[i]; i = i + 1 } 60 if ar_exists(inp) == 1 { return 1 } 61 let cbuf: *u8 = sys_mmap(AR_CONF_MAX) 62 let cn: i64 = ar_read(confpath, cbuf, AR_CONF_MAX - 1) 63 if cn <= 0 { return 0 } 64 var base: i64 = 0 65 var b: i64 = 0 66 while b < n { if inp[b] == (47 as u8) { base = b + 1 } b = b + 1 } 67 var ls: i64 = 0 68 var p: i64 = 0 69 while p <= cn { 70 var eol: i64 = 0 71 if p == cn { eol = 1 } else { if cbuf[p] == (10 as u8) { eol = 1 } } 72 if eol == 1 { 73 if p > ls { if cbuf[ls] != (35 as u8) { if p - ls >= 5 { 74 var isroot: i64 = 1 75 let rk: *u8 = "root=" 76 var q: i64 = 0 77 while q < 5 { if cbuf[ls+q] != rk[q] { isroot = 0; q = 5 } else { q = q + 1 } } 78 if isroot == 1 { 79 var o: i64 = 0 80 var k: i64 = ls + 5 81 while k < p { out[o] = cbuf[k]; o = o + 1; k = k + 1 } 82 let rootlen: i64 = o 83 var c: i64 = 0 84 while c < n { out[o] = inp[c]; o = o + 1; c = c + 1 } 85 out[o] = 0 as u8 86 if ar_exists(out) == 1 { return 1 } 87 o = rootlen 88 c = base 89 while c < n { out[o] = inp[c]; o = o + 1; c = c + 1 } 90 out[o] = 0 as u8 91 if ar_exists(out) == 1 { return 1 } 92 } 93 } } } 94 ls = p + 1 95 } 96 p = p + 1 97 } 98 var j: i64 = 0 99 while j <= n { out[j] = inp[j]; j = j + 1 } 100 return 0 101} 102 103// The ecosystem default. Consumers call this; the roots table is one file for the whole host. 104func ar_resolve(inp: *u8, out: *u8) -> i64 { 105 return ar_resolve_with(inp, out, "knowledge/evidence_roots.conf" as *u8) 106}