code wiki / (root) / nx_http_probe_lib.nx

nx_http_probe_lib.nx

buildroot/runtime/nx_http_probe_lib.nx

20417 B414 linesdepth 6pulls 9 transitivereach 2 importersview sourcekind probetopic http
docsdependenciesstructsconstsfunctions

about

nx_http_probe_lib.nx -- THE BOUNDED LOOPBACK HTTP LIVENESS PROBE + DEAD-MAN HEARTBEAT STAMPER. WHY THIS EXISTS (2026-08-20). The estate's only compensating control over a FAIL-OPEN authentication path was a curl one-liner in cron.reg: curl -s --max-time 8 http://127.0.0.1:18096/api/cap/status | grep -q '"secure":true' \ && date +ts=%s > knowledge/status/capplane.log The cron row FIRED and the command FAILED, and because the row DISCARDS ALL OUTPUT its own failure was unobservable: 7 of 8 conjuncts were proven true and the 8th ("does curl run") was structurally unmeasurable. The cap-plane watch went stale at 10.9x its budget and nobody could say which link broke. **** A PROBE THAT FAILS SILENTLY IS NOT A CONTROL, IT IS A DECORATION. **** So the single disqualifying property for the replacement is a silent failure mode. EVERY exit path here carries a NAMED reason, printed on stdout AND stderr, and the verdict line is LAST so a positional reader (gv_last_line, nx_gate_green) can anchor on it without matching text anywhere else. WHY A LIB AND NOT ONE FILE: the CLI (nx_http_probe.nx) and the gate (nx_http_probe_gate.nx) both need the same logic, and a gate that re-implements its subject proves nothing. One ruler, two callers. LOOPBACK-ONLY BY CONSTRUCTION -- THIS IS A SAFETY PROPERTY, NOT A LIMITATION. The destination address is hard-pinned to 127.0.0.1 and cannot be passed in. cron.reg's own cap-plane comment records why: during the 2026-08-06 outage the PUBLIC url returned insecure_placeholder/secure=false while loopback :18096 returned provisioned/secure=true, because the public :443 answer came from a DIFFERENT BINARY (DSM nginx co-squat, D008) -- and that sent the diagnosis down a false path for an hour. **** A HEALTH ENDPOINT ANSWERS FOR THE PROCESS THAT SERVED IT, NEVER FOR THE SERVICE. **** A sibling seat had to re-derive that this session after taking the convenient answer. Pinning the address makes the mistake unrepresentable rather than merely documented. COMPOSED, NOT RE-IMPLEMENTED. Three proven primitives do the work: nx_connect_bounded (nx_connect.nx) -- SO_RCVTIMEO does NOT bound connect(); this does nx_http_client_build_request(nx_http_client.nx) -- the ONE request-format definition in the estate sys_set_socket_timeout (nx_syscalls.nx) -- SO_RCVTIMEO/SO_SNDTIMEO The only new code is a DEADLINE-AWARE drain. nx_http_client_get's own _drain has no read bound, so a daemon that accepts and then wedges -- WHICH IS EXACTLY THE 2026-08-06 INCIDENT -- parks the probe forever. On a */5 beat that piles up processes, which is a resource bug even when the feature works. nx_http_client_get is otherwise the right organ and is deliberately left byte-untouched: editing a lib with ~100 consumers to add one caller's timeout is a fleet-wide blast radius for a local need. THE BUDGET IS AN ARGUMENT, NOT A CONSTANT. Rule 11: no magic numbers. The caller states the budget and states its derivation at the call site (cron.reg). Worst-case wall time is ~2x budget: the connect leg and the receive leg each get the full budget, deliberately, so that "slow to connect" and "slow to answer" are separately diagnosable instead of sharing one squeezed allowance. Pick budget <= beat/2.

dependencies 4 imports · 2 importers

nx_syscalls.nx nx_connect.nx nx_http_client.nx nx_gate.nx nx_http_probe_lib.nx nx_http_probe.nx nx_http_probe_gate.nx

imports: nx_syscalls.nxnx_connect.nxnx_http_client.nxnx_gate.nx

imported by: nx_http_probe.nxnx_http_probe_gate.nx

structs

none

consts

58const HP_EXIT_OK: i64 = 0
59const HP_EXIT_USAGE: i64 = 1
60const HP_EXIT_CONNECT_FAILED: i64 = 2
61const HP_EXIT_PATTERN_ABSENT: i64 = 3
62const HP_EXIT_STATUS_NOT_2XX: i64 = 4
63const HP_EXIT_STAMP_WRITE_FAILED:i64 = 5
64const HP_EXIT_RECV_TIMEOUT: i64 = 6
65const HP_EXIT_SEND_FAILED: i64 = 7
66const HP_EXIT_BODY_TRUNCATED: i64 = 8
67const HP_EXIT_PATH_TOO_LONG: i64 = 9
68const HP_EXIT_SOCKET_FAILED: i64 = 10
69const HP_EXIT_EMPTY_RESPONSE: i64 = 11
70const HP_EXIT_NOT_HTTP: i64 = 12
74const HP_LEGACY_DEFAULT_PORT: i64 = 8080
78const HP_BODY_CAP: i64 = 4194304
82const HP_REQ_CAP: i64 = 4096
86const HP_PATH_MAX: i64 = 2048
88const HP_ADDR_BYTES: i64 = 16
89const HP_STAMP_CAP: i64 = 64
90const HP_MODE_0644: i64 = 420
91const HP_FD_STDOUT: i64 = 1
92const HP_FD_STDERR: i64 = 2
93const HP_ASCII_ZERO: i64 = 48
94const HP_ASCII_NINE: i64 = 57
95const HP_ASCII_SPACE: i64 = 32
96const HP_ASCII_NL: i64 = 10
97const HP_B10: i64 = 10
98const HP_STATUS_DIGITS: i64 = 3
99const HP_STATUS_2XX_LO: i64 = 200
100const HP_STATUS_2XX_HI: i64 = 300
101const HP_MS_PER_SEC: i64 = 1000
102const HP_FLAG_SLOTS: i64 = 32
104const HP_LOOPBACK_A: i64 = 127
105const HP_LOOPBACK_B: i64 = 0
106const HP_LOOPBACK_C: i64 = 0
107const HP_LOOPBACK_D: i64 = 1
110const HP_FL_CLEAN_EOF: i64 = 0
111const HP_FL_CAP_HIT: i64 = 1
112const HP_FL_DEADLINE: i64 = 2
113const HP_FL_READS: i64 = 3

functions

115func hp_strlen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
117func hp_werr(s: *u8) -> i64 { sys_write(HP_FD_STDERR, s, hp_strlen(s)); return 0 }
called by 1: hp_emit calls 2: sys_writehp_strlen
121func hp_streq(a: *u8, b: *u8) -> i64
called by 2: mainmain
131func hp_nerr(v: i64) -> i64
called by 1: hp_emit calls 3: sys_mmapsys_writesys_munmap
149func hp_atoi(buf: *u8, off: i64, len: i64) -> i64
called by 2: mainmain
163func hp_contains(buf: *u8, n: i64, needle: *u8) -> i64
called by 2: mainhp_run calls 1: hp_strlen
181func hp_status_of(buf: *u8, n: i64) -> i64
called by 2: mainhp_run calls 1: hp_strlen
213func hp_drain(fd: i64, buf: *u8, cap: i64, deadline_sec: i64, fl: *i64) -> i64
called by 1: hp_run calls 2: sys_now_realtime_secsys_read
250func hp_write_stamp(path: *u8, ts: i64) -> i64
282func hp_reason(code: i64) -> *u8
called by 2: mainhp_emit
304func hp_emit(code: i64) -> i64
called by 1: hp_run calls 5: gwhp_reasongnhp_werrhp_nerr
324func hp_run(port: i64, path: *u8, expect: *u8, budget_sec: i64, stamp_path: *u8, verbose: i64) -> i64