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}