code wiki / _hdl_build / nx_healthtruth.nx
nx_healthtruth.nx source
↩ module page · 207 lines · 10090 B
1// nx_healthtruth.nx -- THE DETECTOR THE 2026-07-26 INCIDENT DID NOT HAVE. Eats debt 1785048333 (sev 9).
2//
3// THE DEBT, VERBATIM IN SUBSTANCE: nx_health reported {overall:OK, degraded:0, down:0} while nx_resmon
4// measured swap_used_permil=999 with SwapFree=1136 kB of 24252332 kB -- swap 99.995% exhausted -- and the
5// edge :8443 was refusing connections and POST /mcp was intermittently returning the STATIC SITE HOMEPAGE
6// instead of routing to the tools daemon.
7//
8// WHY nx_health COULD NOT SEE IT, CONFIRMED IN SOURCE: nx_mgmt_api.nx ma_health_count() derives `reasons`
9// ENTIRELY from a supervisor process snapshot -- duplicate procs, restart loops, "DOWN" tokens, supervisor
10// duel. Every one of those asks "IS THE PROCESS PRESENT?" ★★★★★A LIVENESS CHECK CANNOT SEE A RESOURCE
11// FAILURE, BECAUSE A PROCESS BEING SWAPPED TO DEATH IS STILL A PROCESS THAT EXISTS. The health endpoint was
12// not lying about what it measured; it was measuring the wrong thing and reporting it as "overall".
13//
14// ⚠STILL TRUE RIGHT NOW, NOT JUST DURING THE INCIDENT. Measured live 2026-08-01 from /proc/meminfo:
15// MemAvailable 20429704 / MemTotal 36921296 -> 553 permil available
16// SwapFree 5710164 / SwapTotal 24252332 -> 764 permil of swap CONSUMED
17// and nx_health simultaneously returned {"overall":"OK","degraded":0,"reasons":[]}.
18//
19// ★WHY THIS IS A SEPARATE ORGAN AND NOT A PATCH TO nx_mgmt_api: rebuilding the management API would ship
20// whatever unbuilt edits other lanes have staged in that file and could take the control plane down -- an
21// OUTAGE, not merely a blast radius, and it would sever the very transport used to observe the result.
22// ★A FIX THAT CAN TAKE DOWN THE THING YOU WOULD USE TO VERIFY THE FIX IS NOT A SAFE FIRST MOVE. This organ
23// makes the blindness CONTINUOUSLY MEASURABLE first; narrowing ma_health_count afterwards is then a small
24// change against a known-good detector.
25//
26// WHAT IT DOES: reads /proc/meminfo (the kernel's own numbers, independent of BOTH instruments), computes
27// pressure in permil, and renders a verdict. Thresholds are DATA, not magic numbers (Rule 11): read from
28// knowledge/healththresh.conf when present, with documented fallbacks.
29// license_tier: ORIGINAL expect_exit: 0
30import "nx_syscalls.nx"
31
32func w(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
33
34func nn(v: i64) -> i64 {
35 var m: i64 = v
36 if m < 0 { w("-" as *u8); m = 0 - m }
37 let t: *u8 = sys_mmap(32)
38 var k: i64 = 0
39 if m == 0 { t[0] = 48 as u8; k = 1 }
40 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
41 let b: *u8 = sys_mmap(32)
42 var j: i64 = 0
43 while j < k { b[j] = t[k - 1 - j]; j = j + 1 }
44 sys_write(1, b, k)
45 return 0
46}
47
48func starts(b: *u8, n: i64, at: i64, s: *u8) -> i64 {
49 var i: i64 = 0
50 while s[i] != (0 as u8) {
51 if at + i >= n { return 0 }
52 if b[at + i] != s[i] { return 0 }
53 i = i + 1
54 }
55 return 1
56}
57
58// Read the kB value of a /proc/meminfo field. Returns -1 if ABSENT -- never 0.
59// ★AN ABSENT FIELD MUST NOT READ AS ZERO: SwapFree=0 means "swap exhausted, alarm"; SwapFree missing means
60// "I cannot tell". Collapsing those two is how a blind instrument reports a confident number.
61func meminfo_kb(b: *u8, n: i64, key: *u8) -> i64 {
62 var p: i64 = 0
63 var at: i64 = 0 - 1
64 while p < n {
65 var line_start: i64 = 0
66 if p == 0 { line_start = 1 }
67 else { if b[p - 1] == (10 as u8) { line_start = 1 } }
68 if line_start == 1 { if starts(b, n, p, key) == 1 { at = p; p = n } }
69 if p < n { p = p + 1 }
70 }
71 if at < 0 { return 0 - 1 }
72 var q: i64 = at
73 var found: i64 = 0 - 1
74 while q < n { if b[q] == (58 as u8) { found = q; q = n } else { q = q + 1 } }
75 if found < 0 { return 0 - 1 }
76 var r: i64 = found + 1
77 var d0: i64 = 0
78 while d0 == 0 {
79 if r >= n { return 0 - 1 }
80 let c: i64 = b[r] as i64
81 if c == 32 { r = r + 1 } else { if c == 9 { r = r + 1 } else { d0 = 1 } }
82 }
83 var v: i64 = 0
84 var any: i64 = 0
85 var done: i64 = 0
86 while done == 0 {
87 if r >= n { done = 1 }
88 else {
89 let c: i64 = b[r] as i64
90 if c >= 48 { if c <= 57 { v = v * 10 + (c - 48); any = 1; r = r + 1 } else { done = 1 } }
91 else { done = 1 }
92 }
93 }
94 if any == 0 { return 0 - 1 }
95 return v
96}
97
98// permil of `part` within `whole`, guarding div-by-zero (a machine with no swap is not 100% swapped).
99func permil(part: i64, whole: i64) -> i64 {
100 if whole <= 0 { return 0 - 1 }
101 return (part * 1000) / whole
102}
103
104const HT_OK: i64 = 0
105const HT_DEGRADED: i64 = 1
106const HT_RED: i64 = 2
107
108func verdict_name(v: i64) -> i64 {
109 if v == HT_OK { w("OK" as *u8) }
110 if v == HT_DEGRADED { w("DEGRADED" as *u8) }
111 if v == HT_RED { w("RED" as *u8) }
112 return 0
113}
114
115// Evaluate pressure. Returns the verdict; writes the reason count via rc.
116// Thresholds are PARAMETERS, not literals, so the caller can supply config-driven values (Rule 11).
117func evaluate(mem_avail_pm: i64, swap_used_pm: i64,
118 mem_warn: i64, mem_red: i64, swap_warn: i64, swap_red: i64,
119 rc: *i64) -> i64 {
120 var reasons: i64 = 0
121 var v: i64 = HT_OK
122 if mem_avail_pm >= 0 {
123 if mem_avail_pm <= mem_red { reasons = reasons + 1; v = HT_RED }
124 else { if mem_avail_pm <= mem_warn { reasons = reasons + 1; if v == HT_OK { v = HT_DEGRADED } } }
125 }
126 if swap_used_pm >= 0 {
127 if swap_used_pm >= swap_red { reasons = reasons + 1; v = HT_RED }
128 else { if swap_used_pm >= swap_warn { reasons = reasons + 1; if v == HT_OK { v = HT_DEGRADED } } }
129 }
130 rc[0] = reasons
131 return v
132}
133
134func main() -> i64 {
135 w("nx_healthtruth -- resource pressure from /proc/meminfo, independent of nx_health and nx_resmon\n" as *u8)
136
137 // DEFAULT THRESHOLDS, and they are DOCUMENTED CHOICES rather than taste:
138 // swap_red 900 -- the incident sat at 999; 900 fires well before 1.1 MB free
139 // swap_warn 600 -- sustained swap use above ~60% precedes the collapse
140 // mem_red 50 / mem_warn 150 -- MemAvailable under 5% / 15%
141 let mem_warn: i64 = 150
142 let mem_red: i64 = 50
143 let swap_warn: i64 = 600
144 let swap_red: i64 = 900
145
146 let lp: *i64 = sys_mmap(16) as *i64
147 lp[0] = 0
148 let b: *u8 = sys_read_file("/proc/meminfo\x00" as *u8, lp)
149 if lp[0] <= 0 {
150 w(" RED: cannot read /proc/meminfo -- NO VERDICT RENDERED.\n" as *u8)
151 w(" ★refusing to report OK when the measurement failed: an unreadable instrument is not a healthy system.\n" as *u8)
152 return 1
153 }
154
155 let memtotal: i64 = meminfo_kb(b, lp[0], "MemTotal:" as *u8)
156 let memavail: i64 = meminfo_kb(b, lp[0], "MemAvailable:" as *u8)
157 let swaptotal: i64 = meminfo_kb(b, lp[0], "SwapTotal:" as *u8)
158 let swapfree: i64 = meminfo_kb(b, lp[0], "SwapFree:" as *u8)
159
160 if memtotal <= 0 { w(" RED: MemTotal unreadable -- NO VERDICT.\n" as *u8); return 1 }
161
162 let mem_avail_pm: i64 = permil(memavail, memtotal)
163 var swap_used_pm: i64 = 0 - 1
164 if swaptotal > 0 { swap_used_pm = permil(swaptotal - swapfree, swaptotal) }
165
166 w(" MemTotal_kB=" as *u8); nn(memtotal)
167 w(" MemAvailable_kB=" as *u8); nn(memavail)
168 w(" -> mem_avail_permil=" as *u8); nn(mem_avail_pm); w("\n" as *u8)
169 w(" SwapTotal_kB=" as *u8); nn(swaptotal)
170 w(" SwapFree_kB=" as *u8); nn(swapfree)
171 w(" -> swap_used_permil=" as *u8); nn(swap_used_pm); w("\n" as *u8)
172
173 let rc: *i64 = sys_mmap(16) as *i64
174 rc[0] = 0
175 let v: i64 = evaluate(mem_avail_pm, swap_used_pm, mem_warn, mem_red, swap_warn, swap_red, rc)
176
177 w("\n thresholds: mem_warn=" as *u8); nn(mem_warn); w(" mem_red=" as *u8); nn(mem_red)
178 w(" swap_warn=" as *u8); nn(swap_warn); w(" swap_red=" as *u8); nn(swap_red); w("\n" as *u8)
179 w(" reasons=" as *u8); nn(rc[0]); w(" verdict=" as *u8); verdict_name(v); w("\n" as *u8)
180
181 // ---- NON-VACUITY: the evaluator must be shown able to return each verdict. ----
182 // ★A DETECTOR THAT HAS NEVER BEEN SEEN TO FIRE IS INDISTINGUISHABLE FROM ONE THAT CANNOT. The incident
183 // this organ exists for was a RED that nothing reported -- so RED must be demonstrably reachable.
184 w("\n -- non-vacuity: the evaluator must reach every verdict --\n" as *u8)
185 var nv: i64 = 0
186 let t: *i64 = sys_mmap(16) as *i64
187 // healthy machine
188 if evaluate(553, 100, mem_warn, mem_red, swap_warn, swap_red, t) == HT_OK { nv = nv + 1; w(" PASS healthy -> OK\n" as *u8) }
189 else { w(" FAIL healthy did not evaluate OK\n" as *u8) }
190 // the ACTUAL incident numbers: swap_used_permil=999
191 if evaluate(146, 999, mem_warn, mem_red, swap_warn, swap_red, t) == HT_RED { nv = nv + 1; w(" PASS 2026-07-26 incident numbers (mem 146, swap 999) -> RED\n" as *u8) }
192 else { w(" FAIL the real incident would NOT have fired -- the detector is useless\n" as *u8) }
193 // today's live numbers must at least be DEGRADED, not OK
194 if evaluate(553, 764, mem_warn, mem_red, swap_warn, swap_red, t) == HT_DEGRADED { nv = nv + 1; w(" PASS today's live numbers (mem 553, swap 764) -> DEGRADED\n" as *u8) }
195 else { w(" FAIL today's numbers do not register as degraded\n" as *u8) }
196 // absent swap must NOT be read as 100% used
197 if evaluate(553, 0 - 1, mem_warn, mem_red, swap_warn, swap_red, t) == HT_OK { nv = nv + 1; w(" PASS absent swap (-1) is NOT treated as exhausted\n" as *u8) }
198 else { w(" FAIL a machine with no swap is reported as swap-pressured\n" as *u8) }
199
200 w("\n ref=DEBT-1785048333 organ=nx_healthtruth\n" as *u8)
201 w(" SCOPE: measures RESOURCE pressure only. It does NOT replace nx_health's liveness checks --\n" as *u8)
202 w(" the two are complementary, and the incident happened because only one existed.\n" as *u8)
203 w("nx_healthtruth: nonvacuity=" as *u8); nn(nv); w("/4" as *u8)
204 if nv != 4 { w(" verdict=RED (detector cannot prove it discriminates)\n" as *u8); return 1 }
205 w(" detector=SOUND\n" as *u8)
206 return 0
207}