code wiki / (root) / nx_organkind.nx

nx_organkind.nx source

↩ module page · 207 lines · 10429 B

1// nx_organkind.nx -- WHAT KIND OF THING IS THIS ORGAN? The classifier seq1492 says both ship verbs need. 2// 3// THE DEFECT IT FIXES: `/api/promote target=nx_torrent_get` -> 400 'daemon/oracle names are never 4// promotable (use /api/deploy)'; `/api/deploy target=nx_torrent_get` -> 400 'unknown target'. Promote 5// calls it a DAEMON, deploy has never heard of it, so a gate-proven binary (nx_poolgov_gate 21/21, 6// deleting a hardcoded MAXP=32) cannot ship by ANY sanctioned route. Both verdicts come from matching 7// NAMES -- a `torrent`/daemon-ish substring -- rather than from what the organ IS. 8// LAW: TWO VERBS THAT DISAGREE ABOUT AN ARTEFACT'S KIND LEAVE IT UNSHIPPABLE -- the classifier, not the 9// allowlist, is the defect. 10// 11// WHY A DECLARED KIND AND NOT A BETTER HEURISTIC: a smarter name rule is still a guess, and the cost of 12// guessing is asymmetric -- misfiling a DAEMON as a one-shot lets promote swap a binary underneath a 13// running process with no health check and no rollback (a rule-26 hazard), while misfiling a one-shot as 14// a daemon merely makes it unshippable. So the kind must be DECLARED DATA (rule 11/17), and anything 15// undeclared must be REFUSED BY BOTH VERBS rather than fall through to a name guess. 16// 17// THE ASYMMETRY IS THE WHOLE POLICY: 18// deploy = for things with a HEALTH SURFACE (a port to probe, so never-brick rollback is meaningful). 19// promote = for one-shots/oracles: atomic rename + .prev backup, no probe needed because nothing is 20// holding the old binary open -- its 'health' is that the NEXT invocation succeeds. 21// A one-shot handed to deploy would have a port-based health check run against a target with no port, 22// making the never-brick verdict meaningless-or-wrong BY CONSTRUCTION. 23// 24// Lives in runtime/ (PRIMITIVE layer, seq1450) so BOTH mgmt handlers can reach it; imports nothing above. 25// license_tier: ORIGINAL Pure + read-only. No hw writes (Rule 26). 26import "nx_syscalls.nx" 27 28const OK_UNKNOWN: i64 = 0 29const OK_DAEMON: i64 = 1 30const OK_ONESHOT: i64 = 2 31const OK_ORACLE: i64 = 3 32const OK_LIB: i64 = 4 33const OK_NL: i64 = 10 34const OK_SP: i64 = 32 35const OK_TAB: i64 = 9 36 37func ok_is_sep(c: i64) -> i64 { 38 if c == OK_SP { return 1 } 39 if c == OK_TAB { return 1 } 40 return 0 41} 42 43func ok_kind_code(buf: *u8, s: i64, e: i64) -> i64 { 44 let n: i64 = e - s 45 if n == 6 { if buf[s] == (100 as u8) { return OK_DAEMON } } 46 if n == 7 { if buf[s] == (111 as u8) { if buf[s+1] == (110 as u8) { return OK_ONESHOT } } } 47 if n == 6 { if buf[s] == (111 as u8) { return OK_ORACLE } } 48 if n == 3 { if buf[s] == (108 as u8) { return OK_LIB } } 49 return OK_UNKNOWN 50} 51 52// Look up <name> in a declared-kind table: one `<name> <kind>` row per line. 53// FAIL-CLOSED: an absent name is OK_UNKNOWN, never a guessed default -- that refusal is the point. 54// Match is WHOLE-FIELD (name must be followed by a separator), so `nx_torrent` can never match 55// `nx_torrent_get` -- the substring lie that produced this defect in the first place. 56func ok_kind_of(buf: *u8, n: i64, name: *u8) -> i64 { 57 var nl: i64 = 0 58 while name[nl] != (0 as u8) { nl = nl + 1 } 59 if nl == 0 { return OK_UNKNOWN } 60 var i: i64 = 0 61 while i < n { 62 var bol: i64 = 0 63 if i == 0 { bol = 1 } 64 if i > 0 { if buf[i-1] == (OK_NL as u8) { bol = 1 } } 65 if bol == 1 { 66 var j: i64 = 0 67 var m: i64 = 1 68 while j < nl { 69 if i + j >= n { m = 0; j = nl } else { 70 if buf[i + j] != name[j] { m = 0; j = nl } else { j = j + 1 } 71 } 72 } 73 if m == 1 { 74 var after: i64 = i + nl 75 var whole: i64 = 0 76 if after < n { if ok_is_sep(buf[after] as i64) == 1 { whole = 1 } } 77 if whole == 1 { 78 // skip the separator run after the name (clean sentinel-free idiom: a flag, not an 79 // out-of-range index -- the k=n+1 / k-1 trick I first wrote collapsed to k=n and made 80 // EVERY lookup return UNKNOWN; the gate caught it at 2/11 before it reached mgmt) 81 var k: i64 = after 82 var r1: i64 = 1 83 while r1 == 1 { 84 if k >= n { r1 = 0 } else { 85 if ok_is_sep(buf[k] as i64) == 1 { k = k + 1 } else { r1 = 0 } 86 } 87 } 88 // the kind token ends at the first separator or newline 89 var kend: i64 = k 90 var r2: i64 = 1 91 while r2 == 1 { 92 if kend >= n { r2 = 0 } else { 93 if ok_is_sep(buf[kend] as i64) == 1 { r2 = 0 } else { 94 if buf[kend] == (OK_NL as u8) { r2 = 0 } else { kend = kend + 1 } 95 } 96 } 97 } 98 return ok_kind_code(buf, k, kend) 99 } 100 } 101 } 102 i = i + 1 103 } 104 return OK_UNKNOWN 105} 106 107const OK_CONFBUF: i64 = 65536 108const OK_CONF_PATH_LEN: i64 = 256 109 110// Self-contained lookup: open the declared-kind table and classify <name>. Returns OK_UNKNOWN when the 111// file is absent or the name is undeclared -- callers then fall back to their legacy policy, so adopting 112// this is ADDITIVE and inert until the conf exists. 113// The file read lives HERE, not in the caller, so a consumer's adoption is ONE LINE. That is deliberate: 114// seq1410 measured that primitives fail to get adopted, and the cost of adoption is the main reason -- 115// a primitive that makes its consumer do the plumbing is a primitive that stays dark. 116func ok_kind_of_path(path: *u8, name: *u8) -> i64 { 117 let fd: i64 = sys_openat_rd(path) 118 if fd < 0 { return OK_UNKNOWN } 119 let b: *u8 = sys_mmap(OK_CONFBUF) 120 var tot: i64 = 0 121 var r: i64 = sys_read(fd, b, OK_CONFBUF - 1) 122 while r > 0 { 123 tot = tot + r 124 if tot >= OK_CONFBUF - 1 { r = 0 } else { r = sys_read(fd, ((b as i64) + tot) as *u8, OK_CONFBUF - 1 - tot) } 125 } 126 sys_close(fd) 127 if tot <= 0 { sys_munmap(b, OK_CONFBUF); return OK_UNKNOWN } 128 let k: i64 = ok_kind_of(b, tot, name) 129 sys_munmap(b, OK_CONFBUF) 130 return k 131} 132 133// deploy is ONLY for things with a health surface to probe. 134func ok_may_deploy(kind: i64) -> i64 { 135 if kind == OK_DAEMON { return 1 } 136 return 0 137} 138 139// promote is for one-shots and oracles: atomic rename + .prev, nothing holds the old binary open. 140// A LIB is neither -- it is compiled INTO consumers, so shipping one means rebuilding them. 141func ok_may_promote(kind: i64) -> i64 { 142 if kind == OK_ONESHOT { return 1 } 143 if kind == OK_ORACLE { return 1 } 144 return 0 145} 146 147// The honest end state: an UNDECLARED organ is refused by BOTH verbs, loudly, instead of being 148// name-guessed into the wrong one. Returns 1 when the kind is declared and at least one verb accepts it. 149func ok_is_shippable(kind: i64) -> i64 { 150 if ok_may_deploy(kind) == 1 { return 1 } 151 if ok_may_promote(kind) == 1 { return 1 } 152 return 0 153} 154 155// ---- seq1789: A TERMINAL SUFFIX IS A DECLARATION, A SUBSTRING IS A GUESS -------------------------- 156// THE DEFECT THIS FIXES (measured 2026-07-30): /api/promote refused nx_survey_serve_gate because the 157// legacy shape heuristic md_promote_deny asks `does the name CONTAIN "serve"`. It does -- inside the 158// word `survey_serve` -- so an ORACLE that runs to completion was classified as a long-lived DAEMON and 159// became unshippable by every sanctioned route. Its gate then read exit=127 (binary absent) and that one 160// RED was the only thing holding an otherwise 35/35-grounded, 3/4-green domain off MEASURED-HONEST. 161// LAW: A SUBSTRING CANNOT TELL A ROLE FROM A WORD THAT MERELY APPEARS IN ONE. Position is meaning. 162// 163// WHY THIS IS NOT THE NAME-GUESSING THIS FILE EXISTS TO FORBID. The policy above refuses inference 164// because the cost is ASYMMETRIC: misfiling a DAEMON as a one-shot lets promote swap a binary under a 165// live process with no probe and no rollback (rule 26), while the reverse merely blocks a build. That 166// same asymmetry is what makes a TERMINAL suffix safe where a substring is not: 167// - it is a CONVENTION ENFORCED AT CREATION, not an inference about runtime behaviour; 168// - it is ONE-DIRECTIONAL -- it can only ever yield OK_ORACLE, the SAFE side of the asymmetry, so this 169// rule is INCAPABLE BY CONSTRUCTION of producing the dangerous misfile; 170// - the claim is MECHANICALLY CHECKED, not asserted: nx_organkind_gate proves that no name carrying 171// one of these suffixes can classify as a daemon, and that a DECLARED daemon still wins if one ever 172// did. A guarantee that is only promised is the failure mode this ecosystem keeps paying for. 173// A DECLARED kind always wins -- see ok_kind_or_suffix. This is strictly a floor under UNDECLARED names, 174// which previously fell through to the substring lie. 175func ok_ends_with(name: *u8, sfx: *u8) -> i64 { 176 var nl: i64 = 0 177 while name[nl] != (0 as u8) { nl = nl + 1 } 178 var sl: i64 = 0 179 while sfx[sl] != (0 as u8) { sl = sl + 1 } 180 if sl == 0 { return 0 } 181 if nl < sl { return 0 } 182 var i: i64 = 0 183 var m: i64 = 1 184 while i < sl { 185 if name[nl - sl + i] != sfx[i] { m = 0; i = sl } else { i = i + 1 } 186 } 187 return m 188} 189 190// The oracle-shaped suffixes. Kept in ONE function so the rule and its proof cannot drift apart: the 191// gate exercises this same entry point, so a suffix added here without a daemon-free proof fails there. 192func ok_suffix_kind(name: *u8) -> i64 { 193 if ok_ends_with(name, "_gate" as *u8) == 1 { return OK_ORACLE } 194 if ok_ends_with(name, "_test" as *u8) == 1 { return OK_ORACLE } 195 if ok_ends_with(name, "_kat" as *u8) == 1 { return OK_ORACLE } 196 return OK_UNKNOWN 197} 198 199// THE PRECEDENCE, IN ONE PLACE so no consumer re-implements it (and re-implements it differently, which 200// is how this concern grew two confs and two readers already): a DECLARED kind always wins; only an 201// UNDECLARED name falls back to the terminal-suffix rule; anything else stays UNKNOWN and is refused by 202// both verbs exactly as before. Passing the declared kind IN rather than re-reading the conf keeps this 203// pure and lets the caller pay for the file read once. 204func ok_kind_or_suffix(declared: i64, name: *u8) -> i64 { 205 if declared != OK_UNKNOWN { return declared } 206 return ok_suffix_kind(name) 207}