code wiki / _hdl_build / nx_ready_census.nx

nx_ready_census.nx source

↩ module page · 108 lines · 5976 B

1// nx_ready_census.nx -- LIVENESS-vs-READINESS CENSUS across the whole supervised fleet (seq1314 oracle). 2// WHY: on 2026-07-29 nx_tools_api_serve was PROCESS-ALIVE with its LISTEN SOCKET GONE; every liveness-only 3// guard read it as healthy and the agent-facing MCP surface stayed dark for an hour. seq1299 fixed the 4// detector and wired it into ONE guard. This organ answers the fleet-wide question that fix raised: 5// "which OTHER services are currently claiming UP while nothing is listening?" 6// METHOD: mgmt_snap.json is the supervisor's OWN per-poll claim (SVC <name> <port> <state> ...). We take 7// each claim and CONNECT to the port -- connect-only, never writing a byte, so this can never disturb a 8// working daemon (the retired /healthz serving-probe crash-looped redirect.elf by writing to it). 9// A DISAGREEMENT (claim=UP, socket=REFUSED) is the exact 07-29 wound, caught by measurement. 10// ★It is ALSO the empirical port oracle for seq1314: the supervisor's guard CALL-SITE COMMENTS are stale 11// (they say the docportal admin daemon is :8456; it is really :18456 -- wiring a guard from the comment 12// would kill a HEALTHY daemon every poll). mgmt_snap.json agrees with the live listen table; comments do not. 13// nx_ready_census [snapfile] exit 0 = all claims honest · 3 = disagreement · 2 = snapshot unreadable 14// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0 15import "nx_health_probe.nx" 16import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc) 17import "nx_syscalls.nx" 18 19// sized: mgmt_snap.json is one line per supervised service; 64KiB is ~100x the observed size 20const RC_CAP: i64 = 65536 21// sized: service basenames in the snapshot are well under this 22const RC_NAMECAP: i64 = 128 23 24func rc_w(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 25// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer 26// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the 27// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls). 28// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign. 29func rc_n(v: i64) -> i64 { nxi_out(v); return 0 } 30 31func main(argc: i64, argv: *i64) -> i64 { 32 var path: *u8 = "mgmt_snap.json" as *u8 33 if argc > 1 { path = argv[1] as *u8 } 34 let fd: i64 = sys_openat_rd(path) 35 if fd < 0 { rc_w("{\"organ\":\"nx_ready_census\",\"refused\":\"snapshot unreadable\"}\n" as *u8); sys_exit(2); return 2 } 36 let buf: *u8 = sys_mmap(RC_CAP) 37 let n: i64 = sys_read(fd, buf, RC_CAP - 1) 38 sys_close(fd) 39 if n <= 0 { rc_w("{\"organ\":\"nx_ready_census\",\"refused\":\"snapshot empty\"}\n" as *u8); sys_exit(2); return 2 } 40 buf[n] = 0 as u8 41 var claimed: i64 = 0 42 var probed: i64 = 0 43 var listening: i64 = 0 44 var dead: i64 = 0 45 var unprobeable: i64 = 0 46 let nm: *u8 = sys_mmap(RC_NAMECAP) 47 rc_w("{\"organ\":\"nx_ready_census\",\"disagreements\":[" as *u8) 48 var first: i64 = 1 49 var i: i64 = 0 50 while i < n { 51 var le: i64 = i 52 var sc: i64 = 1 53 while sc == 1 { if le >= n { sc = 0 } else { if buf[le] == (10 as u8) { sc = 0 } else { le = le + 1 } } } 54 var issvc: i64 = 0 55 if le - i > 4 { if buf[i] == (83 as u8) { if buf[i+1] == (86 as u8) { if buf[i+2] == (67 as u8) { if buf[i+3] == (32 as u8) { issvc = 1 } } } } } 56 if issvc == 1 { 57 var p: i64 = i + 4 58 var nn: i64 = 0 59 while p < le { if buf[p] == (32 as u8) { p = le } else { if nn < RC_NAMECAP - 1 { nm[nn] = buf[p]; nn = nn + 1 } p = p + 1 } } 60 nm[nn] = 0 as u8 61 // re-scan for the port + state fields (field 2 and 3 after SVC) 62 var q: i64 = i + 4 63 var f: i64 = 0 64 var port: i64 = 0 65 var up: i64 = 0 66 while q < le { 67 if buf[q] == (32 as u8) { 68 f = f + 1 69 if f == 1 { 70 var r: i64 = q + 1 71 while r < le { let c: i64 = buf[r] as i64; if c < 48 { r = le } else { if c > 57 { r = le } else { port = port * 10 + (c - 48); r = r + 1 } } } 72 } 73 if f == 2 { 74 if q + 2 < le { if buf[q+1] == (85 as u8) { if buf[q+2] == (80 as u8) { up = 1 } } } 75 q = le 76 } 77 } 78 if q < le { q = q + 1 } 79 } 80 if up == 1 { 81 claimed = claimed + 1 82 if port <= 0 { unprobeable = unprobeable + 1 } else { 83 probed = probed + 1 84 if hp_listening(port) == 1 { listening = listening + 1 } else { 85 // CONFIRM before accusing -- same discipline as the supervisor's own check 86 if hp_socket_dead(port) == 1 { 87 dead = dead + 1 88 if first == 0 { rc_w("," as *u8) } 89 first = 0 90 rc_w("{\"service\":\"" as *u8); sys_write(1, nm, nn) 91 rc_w("\",\"port\":" as *u8); rc_n(port) 92 rc_w(",\"claim\":\"UP\",\"socket\":\"REFUSED\"}" as *u8) 93 } else { listening = listening + 1 } 94 } 95 } 96 } 97 } 98 i = le + 1 99 } 100 rc_w("],\"claimed_up\":" as *u8); rc_n(claimed) 101 rc_w(",\"probed\":" as *u8); rc_n(probed) 102 rc_w(",\"listening\":" as *u8); rc_n(listening) 103 rc_w(",\"alive_but_socket_dead\":" as *u8); rc_n(dead) 104 rc_w(",\"unprobeable_port0\":" as *u8); rc_n(unprobeable) 105 if dead > 0 { rc_w(",\"verdict\":\"DISAGREEMENT -- a service claims UP while nothing listens (the 2026-07-29 class)\"}\n" as *u8); sys_exit(3); return 3 } 106 rc_w(",\"verdict\":\"HONEST -- every UP claim has a live listener\"}\n" as *u8) 107 return 0 108}