nx_http_health_emit.nx source
↩ module page · 258 lines · 9686 B
1// nx_http_health_emit.nx -- /health and /ready endpoint emitter.
2//
3// RESTORED 2026-07-31. This code was NOT unwritten -- it was OVERWRITTEN. A dedupe/reconcile on
4// 2026-07-21 found two files named nx_http_health.nx and treated a NAME COLLISION as a COPY. They are
5// different organs on OPPOSITE sides of HTTP health: one PROBES a remote URL (a CLI with main()), this
6// one EMITS a /health response (a library). The reconcile kept the prober and renamed this to
7// nx_http_health.nx.dupe-reconciled, silently deleting the emit capability and darkening every consumer:
8// nx_http_health_test.nx, nx_audit_server_routed.nx, and wiki/nx_wiki_routes.nx (2 call sites).
9//
10// THE LAW IT PROVES, already in the corpus and violated anyway: a duplicate-basename detector cannot tell
11// a COPY from a COLLISION, and the remedies are OPPOSITE -- retire vs RENAME. Canonical must be chosen by
12// WHO IMPORTS IT, not by directory convention: this file had 3 importers, the prober had zero.
13//
14// Restored under a DISTINCT name rather than by taking the old one back, so the running prober binary is
15// untouched and the collision can never be load-bearing again. Probe and emit are different jobs and now
16// have different names (rule 9).
17//
18// Standard S-class hosting observability:
19// - /health => liveness probe (process is alive + responding)
20// - /ready => readiness probe (process is ready to serve)
21//
22// Per cardinal feedback-no-third-party-trust-native-or-nothing:
23// substrate's own probe handlers; no Prometheus SDK, no /healthz
24// framework, no liveness-probe-via-CRD.
25//
26// Per cardinal feedback-defensive-at-boundaries-trusting-internally:
27// these emitters never read external input; they emit a fixed
28// shape with caller-provided stats (uptime, request count, etc.).
29//
30// nx_capability_claims:
31// needs: [sealed_enum, bounded_buffer]
32// provides: [http_health_emit, http_ready_emit]
33// safety: [no_unchecked_deref, no_floating_point, no_syscall,
34// bounded_iteration, bit_equal_reproducible,
35// target_agnostic]
36// verdict: [sealed_enum_4_state]
37// license: ORIGINAL
38// kind: racing_crew_specialist
39// layer: L3 (algorithm: response shape emitter)
40
41// ---- Sealed enum: health-emit verdict ----------------------------
42
43const NXHL_OK: i64 = 0
44const NXHL_OOM_BUFFER: i64 = 1
45const NXHL_BAD_ARG: i64 = 2
46const NXHL_VERDICT_N: i64 = 3
47
48func nxhl_verdict_is_valid(v: i64) -> i64 {
49 if v < 0 { return 0 }
50 if v >= NXHL_VERDICT_N { return 0 }
51 return 1
52}
53
54func nxhl_verdict_name(v: i64) -> *u8 {
55 if v == NXHL_OK { return "OK" as *u8 }
56 if v == NXHL_OOM_BUFFER { return "OOM_BUFFER" as *u8 }
57 if v == NXHL_BAD_ARG { return "BAD_ARG" as *u8 }
58 return "INVALID" as *u8
59}
60
61// ---- Sealed enum: health status ----------------------------------
62//
63// Mirrors the convention from kubernetes / docker / consul health
64// checks. Substrate emits PASS / WARN / FAIL. Caller picks which
65// based on their own predicates.
66
67const NXHL_STATUS_PASS: i64 = 0
68const NXHL_STATUS_WARN: i64 = 1
69const NXHL_STATUS_FAIL: i64 = 2
70const NXHL_STATUS_N: i64 = 3
71
72func nxhl_status_is_valid(s: i64) -> i64 {
73 if s < 0 { return 0 }
74 if s >= NXHL_STATUS_N { return 0 }
75 return 1
76}
77
78func nxhl_status_name(s: i64) -> *u8 {
79 if s == NXHL_STATUS_PASS { return "pass" as *u8 }
80 if s == NXHL_STATUS_WARN { return "warn" as *u8 }
81 if s == NXHL_STATUS_FAIL { return "fail" as *u8 }
82 return "invalid" as *u8
83}
84
85// HTTP status code for each health status.
86// PASS -> 200 OK
87// WARN -> 200 OK (still healthy enough; consumers can filter)
88// FAIL -> 503 Service Unavailable
89func nxhl_status_http_code(s: i64) -> i64 {
90 if s == NXHL_STATUS_PASS { return 200 }
91 if s == NXHL_STATUS_WARN { return 200 }
92 if s == NXHL_STATUS_FAIL { return 503 }
93 return 500
94}
95
96// ---- Byte emit helpers (sealed-enum verdict) ---------------------
97
98func nxhl_put(out: *u8, off: *i64, cap: i64, b: i64) -> i64 {
99 if *off >= cap { return NXHL_OOM_BUFFER }
100 out[*off] = b as u8
101 *off = *off + 1
102 return NXHL_OK
103}
104
105func nxhl_put_cstr(out: *u8, off: *i64, cap: i64, s: *u8) -> i64 {
106 var i: i64 = 0
107 while s[i] != 0 {
108 let rc: i64 = nxhl_put(out, off, cap, s[i] as i64)
109 if rc != NXHL_OK { return rc }
110 i = i + 1
111 }
112 return NXHL_OK
113}
114
115func nxhl_put_bytes(out: *u8, off: *i64, cap: i64, src: *u8, n: i64) -> i64 {
116 var i: i64 = 0
117 while i < n {
118 let rc: i64 = nxhl_put(out, off, cap, src[i] as i64)
119 if rc != NXHL_OK { return rc }
120 i = i + 1
121 }
122 return NXHL_OK
123}
124
125// ---- i64 -> ASCII decimal (no syscalls; uses caller-provided scratch) ----
126//
127// Caller provides a 24-byte scratch buffer (enough for any i64 in
128// decimal: max 20 digits + sign + NUL). Emits the digits forward
129// into out_buf via nxhl_put.
130
131func nxhl_put_dec(out: *u8, off: *i64, cap: i64,
132 scratch: *u8, v: i64) -> i64 {
133 if v == 0 { return nxhl_put(out, off, cap, 0x30) }
134 var n: i64 = v
135 var sign: i64 = 0
136 if n < 0 { sign = 1; n = 0 - n }
137 var k: i64 = 0
138 while n > 0 {
139 scratch[k] = (0x30 + (n - (n / 10) * 10)) as u8
140 n = n / 10
141 k = k + 1
142 }
143 if sign == 1 {
144 let rcs: i64 = nxhl_put(out, off, cap, 0x2d)
145 if rcs != NXHL_OK { return rcs }
146 }
147 var ri: i64 = k - 1
148 while ri >= 0 {
149 let rc: i64 = nxhl_put(out, off, cap, scratch[ri] as i64)
150 if rc != NXHL_OK { return rc }
151 ri = ri - 1
152 }
153 return NXHL_OK
154}
155
156// ---- /health response emitter -----------------------------------
157//
158// Emits a full HTTP response:
159//
160// HTTP/1.1 <code> <reason>\r\n
161// Content-Type: application/health+json\r\n
162// Content-Length: <N>\r\n
163// \r\n
164// {"status":"pass","uptime_ms":12345,"checks":N}
165//
166// The Content-Type follows draft-inadarei-api-health-check-06
167// ("application/health+json"). Substrate uses this; consumers
168// that don't recognize it MIME-fallback to application/json.
169//
170// Caller supplies:
171// status sealed-enum NXHL_STATUS_*
172// uptime_ms monotonic uptime (caller computes; substrate
173// doesn't read sys_clock here)
174// n_checks number of internal checks aggregated into status
175// scratch 24-byte buffer for decimal conversion
176// out response buffer + cap
177// Returns sealed-enum verdict + writes total bytes to *out_n.
178
179func nx_http_emit_health(
180 out: *u8, cap: i64, out_n: *i64,
181 status: i64, uptime_ms: i64, n_checks: i64,
182 scratch: *u8) -> i64 {
183 if out == (0 as *u8) { return NXHL_BAD_ARG }
184 if out_n == (0 as *i64) { return NXHL_BAD_ARG }
185 if scratch == (0 as *u8) { return NXHL_BAD_ARG }
186 if cap <= 0 { return NXHL_BAD_ARG }
187 if nxhl_status_is_valid(status) != 1 { return NXHL_BAD_ARG }
188
189 // Body is variable-width (uptime_ms + n_checks), but Content-Length must be known BEFORE the headers
190 // are written. So: build the body at a reserved offset, measure it, emit headers at 0, then move the
191 // body down to sit immediately after them.
192 let body_start: i64 = 256
193 if cap < body_start + 128 { return NXHL_OOM_BUFFER }
194
195 var body_off: i64 = body_start
196 let r_body_start: i64 = nxhl_put_cstr(out, &body_off, cap,
197 "{\"status\":\"" as *u8)
198 if r_body_start != NXHL_OK { return r_body_start }
199 let r_status: i64 = nxhl_put_cstr(out, &body_off, cap,
200 nxhl_status_name(status))
201 if r_status != NXHL_OK { return r_status }
202 let r_up_pre: i64 = nxhl_put_cstr(out, &body_off, cap,
203 "\",\"uptime_ms\":" as *u8)
204 if r_up_pre != NXHL_OK { return r_up_pre }
205 let r_up: i64 = nxhl_put_dec(out, &body_off, cap, scratch, uptime_ms)
206 if r_up != NXHL_OK { return r_up }
207 let r_chk_pre: i64 = nxhl_put_cstr(out, &body_off, cap,
208 ",\"checks\":" as *u8)
209 if r_chk_pre != NXHL_OK { return r_chk_pre }
210 let r_chk: i64 = nxhl_put_dec(out, &body_off, cap, scratch, n_checks)
211 if r_chk != NXHL_OK { return r_chk }
212 let r_body_end: i64 = nxhl_put(out, &body_off, cap, 0x7d) // }
213 if r_body_end != NXHL_OK { return r_body_end }
214
215 let body_n: i64 = body_off - body_start
216
217 var h_off: i64 = 0
218 let http_code: i64 = nxhl_status_http_code(status)
219
220 if http_code == 200 {
221 let rh: i64 = nxhl_put_cstr(out, &h_off, body_start,
222 "HTTP/1.1 200 OK\r\nContent-Type: application/health+json\r\nContent-Length: " as *u8)
223 if rh != NXHL_OK { return rh }
224 } else {
225 let rh: i64 = nxhl_put_cstr(out, &h_off, body_start,
226 "HTTP/1.1 503 Service Unavailable\r\nContent-Type: application/health+json\r\nContent-Length: " as *u8)
227 if rh != NXHL_OK { return rh }
228 }
229 let r_clen: i64 = nxhl_put_dec(out, &h_off, body_start, scratch, body_n)
230 if r_clen != NXHL_OK { return r_clen }
231 let r_blank: i64 = nxhl_put_cstr(out, &h_off, body_start,
232 "\r\n\r\n" as *u8)
233 if r_blank != NXHL_OK { return r_blank }
234
235 let move_n: i64 = body_n
236 var mi: i64 = 0
237 while mi < move_n {
238 out[h_off + mi] = out[body_start + mi]
239 mi = mi + 1
240 }
241
242 *out_n = h_off + move_n
243 return NXHL_OK
244}
245
246// ---- /ready response emitter (alias to /health for v1) ----------
247//
248// Future: /ready may differ from /health by requiring stricter
249// preconditions (e.g., "database connection established"). For
250// v1 we share the same emitter.
251
252func nx_http_emit_ready(
253 out: *u8, cap: i64, out_n: *i64,
254 status: i64, uptime_ms: i64, n_checks: i64,
255 scratch: *u8) -> i64 {
256 return nx_http_emit_health(out, cap, out_n,
257 status, uptime_ms, n_checks, scratch)
258}