code wiki / (root) / nx_daemon_budget_gate.nx

nx_daemon_budget_gate.nx source

↩ module page · 131 lines · 6471 B

1// nx_daemon_budget_gate.nx -- REGRESSION PROOF for the self-retiring-daemon class (debt seq882, 2026-07-25). 2// 3// THE DEFECT IT LOCKS OUT: 44 sovereign daemons run `while served < budget { accept... } sys_exit(0)`. A 4// supervised daemon launched with a SMALL finite budget therefore RETIRES ITSELF -- and nx_hostctl's guard 5// (nx_hostctl.nx:798) reads the repeated exits as a crash-loop and BACKS OFF, so the surface goes genuinely 6// DOWN while `status` still prints UP. Measured live: the gallery gateway, the wiki/hub/torrent/gen gateways, 7// the main OPAQUE login daemon AND the mgmt API itself were all launched with budget=5000. That is the root 8// cause of the operator-reported "gallery wont let me login" AND of the chronic mgmt/MCP transport flake. 9// nx_docportal_search_serve.nx:303 documents this same failure being diagnosed on 2026-07-03 -- fixed there 10// and never swept. This gate exists so it can never silently return. 11// 12// WHY ASSERT ON BYTES, NOT ON INTENT: an author-optimism check ("we fixed it") is not evidence. Each tooth 13// greps the actual launch-command constant. T8 greps the LIVE DEPLOYED BINARY, not just source -- this session 14// proved that class of lie twice (a carve-out banked in memory as "landed in source" was NOT in source, and a 15// deployed gallery thumbnailer predated its own fix by three weeks). Source-GREEN + binary-RED = NOT SHIPPED. 16// T9 is the MUTATION tooth: it fails if the exact bad literal ever reappears, so a revert cannot pass quietly. 17// 18// Run from the nishihost CWD (the tools daemon fork-execs it there). license_tier: ORIGINAL No hw writes (Rule 26). 19import "nx_syscalls.nx" 20 21const DBG_CAP: i64 = 1048576 22const DBG_MIN_BUDGET: i64 = 1000000 23 24func dbg_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 25func dbg_puts(s: *u8) -> i64 { sys_write(1, s, dbg_slen(s)); return 0 } 26func dbg_putn(v: i64) -> i64 { 27 let t: *u8 = sys_mmap(32) 28 var m: i64 = v 29 var k: i64 = 0 30 if m == 0 { t[0] = 48 as u8; k = 1 } 31 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 32 let o: *u8 = sys_mmap(32) 33 var w: i64 = 0 34 var q: i64 = k - 1 35 while q >= 0 { o[w] = t[q]; w = w + 1; q = q - 1 } 36 sys_write(1, o, w) 37 return 0 38} 39 40// bounded whole-file read into a caller buffer; returns bytes read (0 = unreadable/empty) 41func dbg_slurp(path: *u8, buf: *u8, cap: i64) -> i64 { 42 let fd: i64 = sys_openat_rd(path) 43 if fd < 0 { return 0 } 44 var total: i64 = 0 45 var n: i64 = sys_read(fd, buf, cap) 46 while n > 0 { 47 total = total + n 48 if total >= cap { n = 0 } else { n = sys_read(fd, (buf as i64 + total) as *u8, cap - total) } 49 } 50 sys_close(fd) 51 return total 52} 53 54func dbg_contains(hay: *u8, hn: i64, needle: *u8) -> i64 { 55 let nn: i64 = dbg_slen(needle) 56 if nn == 0 { return 1 } 57 var i: i64 = 0 58 while i + nn <= hn { 59 var k: i64 = 0 60 var ok: i64 = 1 61 while k < nn { if hay[i+k] != needle[k] { ok = 0; k = nn } else { k = k + 1 } } 62 if ok == 1 { return 1 } 63 i = i + 1 64 } 65 return 0 66} 67 68// PRESENT tooth: the launch constant must carry an unbounded budget. 69func dbg_tooth(buf: *u8, n: i64, needle: *u8, label: *u8, pass: *i64) -> i64 { 70 let hit: i64 = dbg_contains(buf, n, needle) 71 if hit == 1 { pass[0] = pass[0] + 1; dbg_puts("PASS " as *u8) } else { dbg_puts("FAIL " as *u8) } 72 dbg_puts(label) 73 dbg_puts("\n" as *u8) 74 return hit 75} 76 77// MUTATION tooth: the exact regression literal must be ABSENT. 78func dbg_tooth_absent(buf: *u8, n: i64, needle: *u8, label: *u8, pass: *i64) -> i64 { 79 let hit: i64 = dbg_contains(buf, n, needle) 80 if hit == 0 { pass[0] = pass[0] + 1; dbg_puts("PASS " as *u8) } else { dbg_puts("FAIL " as *u8) } 81 dbg_puts(label) 82 dbg_puts("\n" as *u8) 83 return hit 84} 85 86func main(argc: i64, argv: *i64) -> i64 { 87 let pass: *i64 = sys_mmap(16) as *i64 88 pass[0] = 0 89 let total: i64 = 10 90 91 dbg_puts("nx_daemon_budget_gate -- no supervised daemon may retire itself (min budget " as *u8) 92 dbg_putn(DBG_MIN_BUDGET) 93 dbg_puts(")\n" as *u8) 94 95 let src: *u8 = sys_mmap(DBG_CAP) 96 let sn: i64 = dbg_slurp("buildroot/runtime/_hdl_build/nx_hostctl.nx" as *u8, src, DBG_CAP) 97 if sn == 0 { 98 dbg_puts("FAIL cannot read nx_hostctl.nx source (run from the nishihost CWD)\n" as *u8) 99 dbg_puts("nx_daemon_budget_gate T=10 PASS=0 verdict=RED\n" as *u8) 100 return 1 101 } 102 103 dbg_tooth(src, sn, "galx_gw_store 1000000000 18090" as *u8, "T1 gallery gateway :18190 unbounded" as *u8, pass) 104 dbg_tooth(src, sn, "mgmt_snap.json 1000000000 >" as *u8, "T2 mgmt API :18098 unbounded (the control plane)" as *u8, pass) 105 dbg_tooth(src, sn, "opaque_store.log 1000000000 19456 2 1 >/tmp/login.log" as *u8, "T3 OPAQUE login :9091 unbounded" as *u8, pass) 106 dbg_tooth(src, sn, "nx_wiki_gw.elf 18791 keys store 1000000000" as *u8, "T4 wiki gateway :18791 unbounded" as *u8, pass) 107 dbg_tooth(src, sn, "opaque_store.log 1000000000 65536 3 4 >/tmp/hub_gw.log" as *u8, "T5 hub gateway :18792 unbounded" as *u8, pass) 108 dbg_tooth(src, sn, "opaque_store.log 1000000000 65536 3 4 >/tmp/torrentgw.log" as *u8, "T6 torrent gateway :18793 unbounded" as *u8, pass) 109 dbg_tooth(src, sn, "opaque_store.log 1000000000 18795 0 19456 2 1 86400 >/tmp/gen_gw.log" as *u8, "T7 gen gateway :18794 unbounded" as *u8, pass) 110 111 // T8 -- SOURCE-vs-DEPLOYED DRIFT. A fix that lives only in source is an unverified intention. 112 let live: *u8 = sys_mmap(DBG_CAP) 113 let ln: i64 = dbg_slurp("nx_hostctl" as *u8, live, DBG_CAP) 114 if ln == 0 { 115 dbg_puts("FAIL T8 cannot read the LIVE nx_hostctl binary\n" as *u8) 116 } else { 117 dbg_tooth(live, ln, "galx_gw_store 1000000000 18090" as *u8, "T8 LIVE DEPLOYED binary carries the fix (not just source)" as *u8, pass) 118 } 119 120 // T9/T10 -- MUTATION teeth: the exact regression literals must never reappear. 121 dbg_tooth_absent(src, sn, "galx_gw_store 5000 18090" as *u8, "T9 mutation: gallery budget=5000 is ABSENT from source" as *u8, pass) 122 dbg_tooth_absent(src, sn, "mgmt_snap.json 5000 >" as *u8, "T10 mutation: mgmt API budget=5000 is ABSENT from source" as *u8, pass) 123 124 dbg_puts("nx_daemon_budget_gate T=" as *u8) 125 dbg_putn(total) 126 dbg_puts(" PASS=" as *u8) 127 dbg_putn(pass[0]) 128 if pass[0] == total { dbg_puts(" verdict=GREEN\n" as *u8) } else { dbg_puts(" verdict=RED\n" as *u8) } 129 if pass[0] == total { return 0 } 130 return 1 131}