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}