code wiki / (root) / nx_http_probe.nx

nx_http_probe.nx source

↩ module page · 103 lines · 5531 B

1// nx_http_probe.nx -- CLI over nx_http_probe_lib. Sovereign bounded loopback HTTP probe and dead-man 2// heartbeat stamper; retires the `curl | grep -q && date > log` cron one-liner that was the estate's 3// only compensating control over a fail-open authentication path and whose own failure was unobservable. 4// 5// THIS FILE IS DELIBERATELY THIN. All logic lives in nx_http_probe_lib.nx so the gate can drive the SAME 6// code the CLI drives -- a gate that re-implements its subject proves nothing about the subject. 7// 8// USAGE 9// nx_http_probe <path> [expect-substring] [port] 10// legacy positional form, byte-compatible with the pre-2026-08-20 organ (exit 0 ok / 1 usage / 11// 2 connect-failed / 3 pattern-absent are unchanged). No heartbeat, no explicit budget. 12// nx_http_probe watch <port> <path> <expect-substring> <budget-sec> <stamp-path> 13// the supervised form: asserts a 2xx status AND the substring, and ONLY THEN writes 14// `ts=<epoch>` to <stamp-path> for nx_cron_watch to read. Every failure is NAMED on stdout and 15// on stderr and leaves the stamp untouched, so the watch goes STALE instead of reading healthy. 16// Pass "" as <expect-substring> to assert liveness and status only. 17// 18// A path beginning with '/' can never be the literal verb `watch`, so the two grammars cannot collide. 19// 20// EXIT CODES (each is a diagnosis; hp_reason() names them in the output) 21// 0 ok 1 usage 2 connect-failed 3 pattern-absent 4 status-not-2xx 5 stamp-write-failed 22// 6 recv-timeout 7 send-failed 8 body-truncated-pattern-unproven 9 path-too-long 23// 10 socket-failed 11 empty-response 12 not-http 24// 25// expect_exit: 0 license_tier: ORIGINAL No hw writes (Rule 26). 26import "nx_http_probe_lib.nx" 27 28const HPC_LEGACY_ARGC_PATH: i64 = 2 29const HPC_LEGACY_ARGC_EXPECT: i64 = 3 30const HPC_LEGACY_ARGC_PORT: i64 = 4 31const HPC_WATCH_ARGC: i64 = 7 32const HPC_PORT_DIGITS: i64 = 12 33const HPC_VERBOSE: i64 = 1 34 35func hpc_usage() -> i64 { 36 gw("usage: nx_http_probe <path> [expect-substring] [port]\n" as *u8) 37 gw(" nx_http_probe watch <port> <path> <expect-substring> <budget-sec> <stamp-path>\n" as *u8) 38 gw(" loopback 127.0.0.1 ONLY, by construction -- a health endpoint answers for the process that\n" as *u8) 39 gw(" served it, so the public edge is a different binary and a different question.\n" as *u8) 40 gw(" the stamp is written ONLY on a fully clean run; every failure is named on stdout AND stderr.\n" as *u8) 41 return HP_EXIT_USAGE 42} 43 44// A NON-NUMERIC PORT USED TO atoi TO 0 AND THEN PROBE PORT 0, WHICH ANSWERS connect-failed -- a verdict 45// INDISTINGUISHABLE FROM A DEAD SERVICE. The commonest way to reach it is calling this tool host-first 46// (<host> <port> <path>), a grammar it does NOT have: the host lands in <path>, the path lands in <port>, 47// and the reply then confidently indicts a perfectly healthy daemon. That cost a seat five wrong 48// hypotheses on 2026-09-03 against a service that had been serving for 53 minutes. A probe whose argv it 49// could not parse must REFUSE, never publish a verdict about a subject it never reached. 50func hpc_bad_port(s: *u8) -> i64 { 51 gw("NX-HTTP-PROBE port argument is not numeric: " as *u8) 52 gw(s) 53 gw("\n this tool takes <path> [expect-substring] [port] and has NO host argument -- it probes\n" as *u8) 54 gw(" loopback 127.0.0.1 by construction, so a host-first call puts your path where the port belongs.\n" as *u8) 55 gw(" refusing rather than probing port 0, whose connect-failed reads exactly like a dead service.\n" as *u8) 56 gw("NX-HTTP-PROBE verdict=REFUSED reason=port-not-numeric exit=1\n" as *u8) 57 return HP_EXIT_USAGE 58} 59 60func main(argc: i64, argv: *i64) -> i64 { 61 if argc < HPC_LEGACY_ARGC_PATH { 62 let u: i64 = hpc_usage() 63 sys_exit(u) 64 return u 65 } 66 let a1: *u8 = argv[1] as *u8 67 let empty: *u8 = "" as *u8 68 69 if hp_streq(a1, "watch" as *u8) == 1 { 70 if argc < HPC_WATCH_ARGC { 71 let u2: i64 = hpc_usage() 72 sys_exit(u2) 73 return u2 74 } 75 let wport: i64 = hp_atoi(argv[2] as *u8, 0, HPC_PORT_DIGITS) 76 let wpath: *u8 = argv[3] as *u8 77 let wexp: *u8 = argv[4] as *u8 78 let wbud: i64 = hp_atoi(argv[5] as *u8, 0, HPC_PORT_DIGITS) 79 let wstamp: *u8 = argv[6] as *u8 80 let rc: i64 = hp_run(wport, wpath, wexp, wbud, wstamp, HPC_VERBOSE) 81 sys_exit(rc) 82 return rc 83 } 84 85 // ---- legacy positional form ------------------------------------------------------------------- 86 var port: i64 = HP_LEGACY_DEFAULT_PORT 87 if argc >= HPC_LEGACY_ARGC_PORT { 88 let pa: *u8 = argv[3] as *u8 89 let p0: i64 = pa[0] as i64 90 if p0 < HP_ASCII_ZERO { let b: i64 = hpc_bad_port(pa); sys_exit(b); return b } 91 if p0 > HP_ASCII_NINE { let b2: i64 = hpc_bad_port(pa); sys_exit(b2); return b2 } 92 port = hp_atoi(pa, 0, HPC_PORT_DIGITS) 93 if port <= 0 { let b3: i64 = hpc_bad_port(pa); sys_exit(b3); return b3 } 94 } 95 var expect: *u8 = empty 96 if argc >= HPC_LEGACY_ARGC_EXPECT { expect = argv[2] as *u8 } 97 // The legacy form had no budget argument at all and inherited nx_connect's built-in bound; expressing 98 // that same bound here in seconds keeps the old behaviour instead of inventing a new number for it. 99 let legacy_budget: i64 = NX_CONN_DEFAULT_MS / HP_MS_PER_SEC 100 let rc2: i64 = hp_run(port, a1, expect, legacy_budget, empty, HPC_VERBOSE) 101 sys_exit(rc2) 102 return rc2 103}