code wiki / (root) / nx_swarm_endpoint_lib.nx

nx_swarm_endpoint_lib.nx source

↩ module page · 245 lines · 8952 B

1// nx_swarm_endpoint_lib.nx -- THE one act every swarm caller performs: resolve a ROLE to an ADDRESS. 2// 3// WHY (a live outage, measured 2026-07-30): the laptop GPU endpoint was a hardcoded string literal in 4// FIVE files -- nx_mesh_lb:42, nx_swarm_coord:66, nx_worker_dispatch:67, nx_hostctl:268, 5// nx_mgmt_api:456. DHCP moved the box from .193 to .192 and all five went stale in the same instant. 6// nx_swarm_coord:66 even carried the comment "worker_dispatch SSOT" while COPYING the value -- a 7// claimed single source of truth that was really a fifth duplicate. Meanwhile sd-server sat HEALTHY 8// on the 5080 and all four swarm gates read GREEN, because a gate that measures the PLAN does not 9// measure the REACHABILITY. 10// 11// LAW APPLIED: a fix that must be remembered at N sites has to bind to the ONE act all N perform. 12// That act is this function. Callers ask for a role; nobody spells an address again. 13// 14// LAYER: lives in runtime/ (the PRIMITIVE layer) precisely so BOTH runtime/ organs and 15// _hdl_build/ organs can import it -- the import edge is directed and primitives may not depend on 16// tools (seq1450). Library only: NO main(), so any organ can import it without a duplicate entry. 17// 18// FAIL-CLOSED: unknown role or unreadable table returns 0, never a fabricated address. A wrong 19// endpoint fails SILENTLY at dispatch; a missing one fails LOUDLY at resolve. Prefer loud. 20// 21// The table is re-read on every call (~1KB), so an address edit takes effect with no restart. 22// license_tier: ORIGINAL 23import "nx_syscalls.nx" 24const K_MAGIC_65536: i64 = 65536 25const K_MAGIC_65535: i64 = 65535 26 27func se_len(s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { i = i + 1 } return i } 28 29func se_put(o: *u8, at: i64, s: *u8) -> i64 { 30 var i: i64 = 0 31 var a: i64 = at 32 while s[i] != (0 as u8) { o[a] = s[i]; a = a + 1; i = i + 1 } 33 return a 34} 35 36func se_putn(o: *u8, at: i64, buf: *u8, off: i64, n: i64) -> i64 { 37 var i: i64 = 0 38 var a: i64 = at 39 while i < n { o[a] = buf[off + i]; a = a + 1; i = i + 1 } 40 return a 41} 42 43func se_putd(o: *u8, at: i64, v: i64) -> i64 { 44 if v == 0 { o[at] = (48 as u8); return at + 1 } 45 let tmp: *u8 = sys_mmap(32) 46 var x: i64 = v 47 var k: i64 = 0 48 while x > 0 { tmp[k] = ((48 + (x - ((x / 10) * 10))) as u8); x = x / 10; k = k + 1 } 49 var a: i64 = at 50 var j: i64 = k - 1 51 while j >= 0 { o[a] = tmp[j]; a = a + 1; j = j - 1 } 52 return a 53} 54 55func se_ceq(a: *u8, b: *u8) -> i64 { 56 var i: i64 = 0 57 var ok: i64 = 1 58 var go: i64 = 1 59 while go == 1 { 60 if a[i] != b[i] { ok = 0; go = 0 } 61 else { if a[i] == (0 as u8) { go = 0 } else { i = i + 1 } } 62 } 63 return ok 64} 65 66func se_seq(buf: *u8, off: i64, n: i64, s: *u8) -> i64 { 67 if se_len(s) != n { return 0 } 68 var i: i64 = 0 69 var ok: i64 = 1 70 while i < n { if buf[off + i] != s[i] { ok = 0; i = n } else { i = i + 1 } } 71 return ok 72} 73 74func se_has(buf: *u8, off: i64, n: i64, needle: *u8) -> i64 { 75 let m: i64 = se_len(needle) 76 if m == 0 { return 0 } 77 if m > n { return 0 } 78 var i: i64 = 0 79 var hit: i64 = 0 80 while i <= n - m { 81 var j: i64 = 0 82 var same: i64 = 1 83 while j < m { if buf[off + i + j] != needle[j] { same = 0; j = m } else { j = j + 1 } } 84 if same == 1 { hit = 1; i = n - m + 1 } else { i = i + 1 } 85 } 86 return hit 87} 88 89func se_field(buf: *u8, s: i64, e: i64, idx: i64, box: *i64) -> i64 { 90 var p: i64 = s 91 var f: i64 = 0 92 while f < idx { 93 var go: i64 = 1 94 while go == 1 { 95 if p >= e { go = 0 } 96 else { if buf[p] == (9 as u8) { p = p + 1; go = 0 } else { p = p + 1 } } 97 } 98 f = f + 1 99 } 100 if p >= e { box[0] = 0; box[1] = 0; return 0 } 101 var q: i64 = p 102 var g2: i64 = 1 103 while g2 == 1 { 104 if q >= e { g2 = 0 } 105 else { if buf[q] == (9 as u8) { g2 = 0 } else { q = q + 1 } } 106 } 107 box[0] = p 108 box[1] = q - p 109 return 1 110} 111 112func se_slurp(path: *u8, buf: *u8, cap: i64) -> i64 { 113 let fd: i64 = sys_openat_rd(path) 114 if fd < 0 { return 0 - 1 } 115 var t: i64 = 0 116 var r: i64 = 1 117 while r > 0 { 118 r = sys_read(fd, (((buf as i64) + t) as *u8), cap - t) 119 if r > 0 { t = t + r } 120 } 121 sys_close(fd) 122 return t 123} 124 125// ctx layout (plain i64 slots -- never struct fields, per the house build rules): 126// ctx[0]=buf ptr ctx[1]=byte count ctx[2]=rows ptr ctx[3]=row count (0-1 = unreadable table) 127// rows stride 8: role_off,role_len,node_off,node_len,addr_off,addr_len,desc_off,desc_len 128func se_ctx() -> *i64 { 129 let ctx: *i64 = sys_mmap(64) as *i64 130 let buf: *u8 = sys_mmap(K_MAGIC_65536) 131 // CWD-PROOF (2026-08-04): the gen daemon runs with CWD=/volume1/ai/gen (its batchjob-*.json are 132 // CWD-relative there), where the relative read misses and every consumer silently fell back to 133 // stale argv endpoints -- the exact outage this lib exists to end, reintroduced by a CWD. A 134 // consumer's working directory must not decide whether the SSOT resolves: try relative (local 135 // buildroot/gate runs), then the estate-canonical absolute path. Fail-closed only if BOTH miss. 136 var n: i64 = se_slurp("knowledge/swarm_nodes.conf" as *u8, buf, K_MAGIC_65535) 137 if n <= 0 { n = se_slurp("/volume1/homes/elderwesto/nishihost/knowledge/swarm_nodes.conf" as *u8, buf, K_MAGIC_65535) } 138 ctx[0] = buf as i64 139 ctx[1] = n 140 ctx[2] = 0 141 ctx[3] = 0 - 1 142 if n <= 0 { return ctx } 143 let rows: *i64 = sys_mmap(64 * 8 * 8) as *i64 144 let box: *i64 = sys_mmap(32) as *i64 145 var nr: i64 = 0 146 var p: i64 = 0 147 while p < n { 148 var q: i64 = p 149 var g: i64 = 1 150 while g == 1 { 151 if q >= n { g = 0 } 152 else { if buf[q] == (10 as u8) { g = 0 } else { q = q + 1 } } 153 } 154 if q > p + 1 { 155 if buf[p] == (82 as u8) { 156 if buf[p + 1] == (9 as u8) { 157 if nr < 64 { 158 se_field(buf, p, q, 1, box); rows[nr * 8 + 0] = box[0]; rows[nr * 8 + 1] = box[1] 159 se_field(buf, p, q, 2, box); rows[nr * 8 + 2] = box[0]; rows[nr * 8 + 3] = box[1] 160 se_field(buf, p, q, 3, box); rows[nr * 8 + 4] = box[0]; rows[nr * 8 + 5] = box[1] 161 se_field(buf, p, q, 4, box); rows[nr * 8 + 6] = box[0]; rows[nr * 8 + 7] = box[1] 162 nr = nr + 1 163 } 164 } 165 } 166 } 167 p = q + 1 168 } 169 ctx[2] = rows as i64 170 ctx[3] = nr 171 return ctx 172} 173 174func se_find(ctx: *i64, role: *u8) -> i64 { 175 if ctx[3] < 0 { return 0 - 1 } 176 let buf: *u8 = ctx[0] as *u8 177 let rows: *i64 = ctx[2] as *i64 178 let nr: i64 = ctx[3] 179 var i: i64 = 0 180 var hit: i64 = 0 - 1 181 while i < nr { 182 if se_seq(buf, rows[i * 8 + 0], rows[i * 8 + 1], role) == 1 { hit = i; i = nr } 183 else { i = i + 1 } 184 } 185 return hit 186} 187 188// role -> "ip:port" (fresh NUL-terminated buffer), or 0 as *u8 when unknown. NEVER fabricates. 189func se_addr(role: *u8) -> *u8 { 190 let ctx: *i64 = se_ctx() 191 let idx: i64 = se_find(ctx, role) 192 if idx < 0 { return 0 as *u8 } 193 let buf: *u8 = ctx[0] as *u8 194 let rows: *i64 = ctx[2] as *i64 195 let out: *u8 = sys_mmap(256) 196 var o: i64 = se_putn(out, 0, buf, rows[idx * 8 + 4], rows[idx * 8 + 5]) 197 out[o] = 0 as u8 198 return out 199} 200 201// role -> just the HOST (address with the :port stripped), or 0 as *u8. 202// Exists because nx_mesh_lb renders "name @ ip" with no port -- a consumer that needs a narrower 203// slice of the truth must still take it FROM the truth, never re-spell it. 204func se_host(role: *u8) -> *u8 { 205 let a: *u8 = se_addr(role) 206 if (a as i64) == 0 { return 0 as *u8 } 207 let out: *u8 = sys_mmap(256) 208 var i: i64 = 0 209 var go: i64 = 1 210 while go == 1 { 211 if a[i] == (0 as u8) { go = 0 } 212 else { if a[i] == (58 as u8) { go = 0 } else { out[i] = a[i]; i = i + 1 } } 213 } 214 out[i] = 0 as u8 215 return out 216} 217 218// role -> the short human label, or 0 as *u8. 219func se_desc(role: *u8) -> *u8 { 220 let ctx: *i64 = se_ctx() 221 let idx: i64 = se_find(ctx, role) 222 if idx < 0 { return 0 as *u8 } 223 let buf: *u8 = ctx[0] as *u8 224 let rows: *i64 = ctx[2] as *i64 225 let out: *u8 = sys_mmap(256) 226 var o: i64 = se_putn(out, 0, buf, rows[idx * 8 + 6], rows[idx * 8 + 7]) 227 out[o] = 0 as u8 228 return out 229} 230 231// role -> "ip:port (short label)" = the display form the five old literals spelled by hand. 232func se_addr_desc(role: *u8) -> *u8 { 233 let ctx: *i64 = se_ctx() 234 let idx: i64 = se_find(ctx, role) 235 if idx < 0 { return 0 as *u8 } 236 let buf: *u8 = ctx[0] as *u8 237 let rows: *i64 = ctx[2] as *i64 238 let out: *u8 = sys_mmap(512) 239 var o: i64 = se_putn(out, 0, buf, rows[idx * 8 + 4], rows[idx * 8 + 5]) 240 o = se_put(out, o, " (" as *u8) 241 o = se_putn(out, o, buf, rows[idx * 8 + 6], rows[idx * 8 + 7]) 242 o = se_put(out, o, ")" as *u8) 243 out[o] = 0 as u8 244 return out 245}