code wiki / (root) / nx_tools_canary_gate.nx

nx_tools_canary_gate.nx source

↩ module page · 134 lines · 6519 B

1// nx_tools_canary_gate.nx -- CANARY for the tools daemon (debt seq1373). license_tier: ORIGINAL 2// WHY THIS EXISTS: nx_tools_api_serve.elf serves EVERY sovereign MCP tool, so it is the ONE service with no 3// sovereign recovery path -- if a promoted binary crash-loops, the session loses nx_mgmt and therefore cannot 4// call /api/rollback, because the rollback path runs THROUGH the thing being replaced. Promoting it on the 5// strength of a compile is restart-and-hope. This gate makes the proof mechanical instead (rule 26). 6// IT RUNS, IT DOES NOT MERELY COMPILE: it starts the STAGED binary on a throwaway port and then drives that 7// SAME binary's own plain-HTTP probe client against it over a real socket. A successful bind is NOT evidence; 8// a real GET /api/tools round-trip that returns the registry body is. 9// T1 is the NON-VACUITY tooth: the identical probe against a port where nothing listens MUST fail. Without it 10// a probe that silently returns 0 would report GREEN for a dead binary -- proving your 0 is the whole point. 11import "nx_syscalls.nx" 12import "nx_gate.nx" 13 14const TC_STAGED: *u8 = "/volume1/homes/elderwesto/nishihost/nx_tools_api_serve.elf.new" as *u8 15const TC_PORT: *u8 = "18991" as *u8 16const TC_DEADPORT: *u8 = "18992" as *u8 17const TC_SRVLOG: *u8 = "/tmp/nx_tools_canary_srv.out" as *u8 18const TC_PROBELOG: *u8 = "/tmp/nx_tools_canary_probe.out" as *u8 19 20func tc_slen(s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { i = i + 1 } return i } 21func tc_has(buf: *u8, n: i64, pat: *u8) -> i64 { 22 let pl: i64 = tc_slen(pat) 23 if pl == 0 { return 0 } 24 var i: i64 = 0 25 while i + pl <= n { 26 var k: i64 = 0 27 var hit: i64 = 1 28 while k < pl { if buf[i+k] != pat[k] { hit = 0; k = pl } else { k = k + 1 } } 29 if hit == 1 { return 1 } 30 i = i + 1 31 } 32 return 0 33} 34// fork+exec the staged binary in the background (NO wait) -> returns the child pid so we can reap it. 35func tc_spawn(elf: *u8, a1: *u8, a2: *u8, logf: *u8) -> i64 { 36 let pid: i64 = sys_fork() 37 if pid == 0 { 38 let fd: i64 = sys_openat_wr(logf, 420) 39 if fd >= 0 { sys_dup3(fd, 1, 0); sys_dup3(fd, 2, 0) } 40 let argv: *i64 = sys_mmap(64) as *i64 41 let envp: *i64 = sys_mmap(16) as *i64 42 envp[0] = 0 43 argv[0] = elf as i64 44 argv[1] = a1 as i64 45 argv[2] = a2 as i64 46 argv[3] = 0 47 sys_execve(elf, argv, envp) 48 sys_exit(127) 49 } 50 return pid 51} 52// fork+exec the binary's OWN probe client and WAIT -> child exit code (127 = execve failed, -1 = signalled). 53func tc_probe(elf: *u8, port: *u8, path: *u8, logf: *u8) -> i64 { 54 let pid: i64 = sys_fork() 55 if pid == 0 { 56 let fd: i64 = sys_openat_wr(logf, 420) 57 if fd >= 0 { sys_dup3(fd, 1, 0); sys_dup3(fd, 2, 0) } 58 let argv: *i64 = sys_mmap(64) as *i64 59 let envp: *i64 = sys_mmap(16) as *i64 60 envp[0] = 0 61 argv[0] = elf as i64 62 argv[1] = "probe" as *u8 as i64 63 argv[2] = port as i64 64 argv[3] = path as i64 65 argv[4] = 0 66 sys_execve(elf, argv, envp) 67 sys_exit(127) 68 } 69 let st: *i64 = sys_mmap(16) as *i64 70 sys_wait4(pid, st, 0) 71 if (st[0] % 128) != 0 { return 0 - 1 } 72 return (st[0] >> 8) & 0xff 73} 74// BOUNDED reap (seq1383). The first version did a raw kill and then an UNBOUNDED sys_wait4, ignoring the kill 75// return value -- so if the signal never landed, this blocked forever, which hung the gate, which hung 76// ma_do_gate_run's own wait4, which took the ENTIRE mgmt daemon down. A verifier that can hang its caller is 77// more dangerous than the unverified promotion it exists to prevent. 78// NOW: WNOHANG (flag 1) polling with a hard bounded attempt count, SIGKILL re-sent each round, and an honest 79// return (1 = reaped, 0 = gave up). This function can no longer block, whatever the child does. 80func tc_kill(pid: i64) -> i64 { 81 let st: *i64 = sys_mmap(16) as *i64 82 var tries: i64 = 0 83 while tries < 40 { 84 __syscall(129, pid, 9, 0, 0, 0, 0) // rv64 kill=129; raw x86 62 is an RV64 KEY translated to lseek(8) -- the kill never happened (debt idx 2277) 85 let r: i64 = sys_wait4(pid, st, 1) 86 if r == pid { return 1 } 87 if r < 0 { return 1 } 88 sys_sleep_ms(50) 89 tries = tries + 1 90 } 91 return 0 92} 93 94func main() -> i64 { 95 gw("=== nx_tools_canary_gate: the STAGED tools daemon must actually SERVE before it may be promoted ===\n" as *u8) 96 var pass: i64 = 0 97 var tot: i64 = 0 98 let szp: *i64 = sys_mmap(16) as *i64 99 100 // T1 NON-VACUITY: the same probe against a port where NOTHING listens must FAIL. If this passes, every 101 // other result in this gate is meaningless, so it runs FIRST and its failure is fatal to the verdict. 102 tot = tot + 1 103 let deadrc: i64 = tc_probe(TC_STAGED, TC_DEADPORT, "/api/tools" as *u8, TC_PROBELOG) 104 var t1: i64 = 0 105 if deadrc != 0 { t1 = 1 } 106 if t1 == 1 { pass = pass + 1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) } 107 gw("T1 NON-VACUITY: probing a dead port FAILS (a probe that always succeeds would prove nothing)\n" as *u8) 108 109 // T2: the STAGED binary starts and serves a real GET /api/tools round-trip on a throwaway port. 110 let srv: i64 = tc_spawn(TC_STAGED, "serve" as *u8, TC_PORT, TC_SRVLOG) 111 sys_sleep_ms(1500) 112 let rc: i64 = tc_probe(TC_STAGED, TC_PORT, "/api/tools" as *u8, TC_PROBELOG) 113 tot = tot + 1 114 var t2: i64 = 0 115 if rc == 0 { t2 = 1 } 116 if t2 == 1 { pass = pass + 1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) } 117 gw("T2 the STAGED binary binds and answers a real socket round-trip (exit 0)\n" as *u8) 118 119 // T3: the response is the REAL registry body, not merely a 200 shell -- bytes, never a status line. 120 let pbuf: *u8 = sys_read_file(TC_PROBELOG, szp) 121 var pn: i64 = 0 122 if (pbuf as i64) != 0 { pn = szp[0] } 123 tot = tot + 1 124 var t3: i64 = 0 125 if pn > 0 { if tc_has(pbuf, pn, "nx_tools_api" as *u8) == 1 { t3 = 1 } } 126 if t3 == 1 { pass = pass + 1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) } 127 gw("T3 the body carries the real tool registry (nx_tools_api present), not just a 200\n" as *u8) 128 129 tc_kill(srv) 130 131 gw("\n=== nx_tools_canary_gate " as *u8); gn(pass); gw("/" as *u8); gn(tot); gw(" ===\n" as *u8) 132 if pass == tot { gw("TOOLS-CANARY GREEN -- the staged tools daemon SERVES; promotion is safe\n" as *u8); sys_exit(0); return 0 } 133 gw("TOOLS-CANARY RED -- DO NOT PROMOTE the staged tools daemon\n" as *u8); sys_exit(1); return 1 134}