nx_health_probe_gate.nx source
↩ module page · 83 lines · 4786 B
1// nx_health_probe_gate.nx -- loopback KAT for the serving health probe. Forks two mock servers (one that
2// serves 200, one that ACCEPTS but never responds = hung) + probes an unused port. Proves the probe tells
3// SERVING / HUNG / REFUSED apart -- crucially HUNG, which PID-liveness + a LISTEN check both miss.
4// exit 0 = pass, N = assertion N failed.
5import "nx_health_probe.nx"
6import "nx_assert.nx"
7
8// sized: throwaway loopback ports in the same block as the existing mocks
9const HPG_PORT_BOUND: i64 = 19004
10const HPG_PORT_DEAD: i64 = 19005
11// derived: must outlast two confirm gaps (2 x HP_CONFIRM_GAP_MS) plus connect timeouts, with margin
12const HPG_HOLD_MS: i64 = 4000
13
14// bind+listen `port`, fork a one-shot handler. mode 0 = reply 200, mode 1 = accept then HANG (no reply).
15// mode 2 = bind, then CLOSE the listen socket and stay ALIVE = THE seq1299 WOUND REPRODUCED (the state
16// every liveness-only guard reads as healthy). mode 3 = stay BOUND but never accept = a merely BUSY
17// daemon, which the probe must NOT accuse (the redirect.elf false-positive class).
18func hpg_mock(port: i64, mode: i64) -> i64 {
19 let lfd: i64 = sys_socket(AF_INET, SOCK_STREAM, 0); if lfd < 0 { return 0 - 1 }
20 let opt: *u8 = sys_mmap(4); opt[0]=1 as u8; sys_setsockopt(lfd, 1, 2, opt, 4) // SO_REUSEADDR
21 let addr: *u8 = sys_mmap(16); hp_sockaddr(addr, port, 0, 0, 0, 0) // bind 0.0.0.0:port
22 if sys_bind(lfd, addr, 16) != 0 { sys_close(lfd); return 0 - 1 }
23 sys_listen(lfd, 8)
24 let pid: i64 = sys_fork()
25 if pid == 0 {
26 if mode == 2 { sys_close(lfd); sys_sleep_ms(HPG_HOLD_MS); sys_exit(0) }
27 if mode == 3 { sys_sleep_ms(HPG_HOLD_MS); sys_close(lfd); sys_exit(0) }
28 let cfd: i64 = sys_accept(lfd)
29 if cfd >= 0 {
30 let rb: *u8 = sys_mmap(4096); sys_read(cfd, rb, 4095)
31 if mode == 0 {
32 let resp: *u8 = "HTTP/1.1 200 OK\r\nContent-Length: 2\r\nConnection: close\r\n\r\nok" as *u8
33 var rl: i64 = 0; while resp[rl] != (0 as u8) { rl = rl + 1 }
34 sys_write(cfd, resp, rl)
35 } else { sys_sleep_ms(5000) } // HANG: never reply within the probe's 3s timeout
36 sys_close(cfd)
37 }
38 sys_close(lfd); sys_exit(0)
39 }
40 sys_close(lfd)
41 return pid
42}
43
44func main() -> i64 {
45 let p1: i64 = hpg_mock(19001, 0) // serving mock
46 let p2: i64 = hpg_mock(19002, 1) // hung mock
47 sys_sleep_ms(150) // let the children reach accept()
48
49 let v1: i64 = hp_probe(19001, 3)
50 nx_puts_err("SERVING mock -> "); nx_puts_err(hp_name(v1)); nx_puts_err("\n" as *u8)
51 let v2: i64 = hp_probe(19002, 3) // ~3s: connect ok, recv times out -> HUNG
52 nx_puts_err("HUNG mock -> "); nx_puts_err(hp_name(v2)); nx_puts_err("\n" as *u8)
53 let v3: i64 = hp_probe(19003, 3) // nothing bound -> connect refused
54 nx_puts_err("no-daemon -> "); nx_puts_err(hp_name(v3)); nx_puts_err("\n" as *u8)
55
56 let st: *i64 = sys_mmap(16) as *i64
57 nx_kill(p1, 9); sys_wait4(p1, st, 0); nx_kill(p2, 9); sys_wait4(p2, st, 0)
58
59 if v1 != HP_SERVING { return 1 }
60 if v2 != HP_HUNG { return 2 } // ★ the exceed: a hung daemon is CAUGHT
61 if v3 != HP_REFUSED { return 3 }
62
63 // ---- seq1299: CONNECT-ONLY readiness (the state that took the MCP surface dark) ----
64 let p4: i64 = hpg_mock(HPG_PORT_BOUND, 3) // ALIVE + BOUND, never accepts (a merely BUSY daemon)
65 let p5: i64 = hpg_mock(HPG_PORT_DEAD, 2) // ALIVE + socket CLOSED (THE WOUND)
66 sys_sleep_ms(150)
67 let l4: i64 = hp_listening(HPG_PORT_BOUND)
68 let d4: i64 = hp_socket_dead(HPG_PORT_BOUND)
69 let d5: i64 = hp_socket_dead(HPG_PORT_DEAD)
70 nx_puts_err("bound+busy -> listening="); nx_puti_err(l4); nx_puts_err(" socket_dead="); nx_puti_err(d4); nx_puts_err("\n" as *u8)
71 nx_puts_err("alive+closed -> socket_dead="); nx_puti_err(d5); nx_puts_err("\n" as *u8)
72 nx_kill(p4, 9); sys_wait4(p4, st, 0); nx_kill(p5, 9); sys_wait4(p5, st, 0)
73
74 if l4 != 1 { return 4 } // a BOUND socket must read as listening even when nothing ever accepts
75 if d4 != 0 { return 5 } // ANTI-FALSE-POSITIVE: never accuse a busy-but-bound daemon (redirect.elf class)
76 if d5 != 1 { return 6 } // ★THE WOUND: process ALIVE, listen socket GONE -> CONFIRMED dead
77
78 nx_puts_err("--- vs systemd PID-liveness / nx_hostctl /proc-name-scan ---\n" as *u8)
79 nx_puts_err("HUNG (process up + port bound but not responding): systemd/proc-scan = ALIVE (MISS) ; nishi = HUNG (CAUGHT) -> EXCEEDS\n" as *u8)
80 nx_puts_err("ALIVE + listen socket GONE: PID-liveness = HEALTHY (MISS, the 2026-07-29 MCP outage) ; nishi = CONFIRMED-DEAD (CAUGHT) -> EXCEEDS\n" as *u8)
81 nx_puts_err("nx_health_probe_gate verdict=GREEN pass=6\n" as *u8)
82 return 0
83}