code wiki / _hdl_build / nx_tenancy_gate.nx

nx_tenancy_gate.nx source

↩ module page · 830 lines · 40469 B

1// nx_tenancy_gate.nx -- SOVEREIGN tenancy-orthogonality gate ORGAN (ports nx_tenancy_orthogonality_gate.sh 2// + nx_tenancy_gate_alert.sh out of bash -- operator law: sovereign all the way up and down, no shell loops). 3// Enforces the three-plane model (alpha=private dogfood | product=own domain | client=isolated, billable): 4// orthogonality is DATA-isolation (realms/keys/stores), never port-exclusivity. Reads the LIVE SSOTs each 5// cycle, checks INV-1..9, classifies findings against the acknowledged debt baseline (GREEN with known debt 6// baselined; RED the instant NEW sprawl appears), serves the full report on 127.0.0.1:18094 (seqlock shared 7// page + forked server -- the proven /status pattern; NO tsv, NO files as the interface), and appends NEW 8// violations to tenancy_alerts.log. Invariants: 9// INV-1 every served host (proxy_routes+sites) declares a plane in tenancy_planes.conf 10// INV-3 every RUNNING nx_mgmt_api sits on a DECLARED shards.conf mgmt port (no orphan store-sharer) 11// INV-4 client shards use their OWN keys+store (never the shared control opaque_keys/opaque_store) 12// INV-5 a confirmed product is not a bare path on an alpha domain (products graduate to own domains) 13// INV-6 every property_registry domain routed to :8443 has a cert row in certs/sni_certs.manifest 14// INV-7 every manifest cert+key file exists and is non-empty 15// INV-8 every sites.conf vhost has a domain_lifecycle.reg row (the monitored-domain list) 16// INV-9 LIVE-EDGE EVIDENCE via the sovereign HTTPS client (fork+exec nx_https_get_cli.elf, loopback 17// endpoint, SNI from the URL): stage!=production MUST serve the coming-soon marker (else = an 18// unapproved go-live LEAK); stage=production must NOT (else = launch STUCK). Fetch-failure is its 19// own finding (fetchfail), never mistaken for a leak -- an improvement over the bash gate, which 20// could read an empty failed fetch as "no marker" = false leak. Skipped while the edge is down 21// (selfheal owns that alarm). 22// BOUNDED-VSZ discipline: every buffer is allocated ONCE before the loop; the hot loop performs ZERO mmap. 23// license_tier: ORIGINAL module: nishi-core.ops.tenancy_gate 24import "nx_syscalls.nx" 25import "nx_health_probe.nx" // hp_probe_tls / HP_SERVING (edge-up check), hp_sockaddr (server) 26 27const TG_HOME: *u8 = "/volume1/homes/elderwesto/nishihost" as *u8 28const TG_PLANES: *u8 = "/volume1/homes/elderwesto/nishihost/tenancy_planes.conf" as *u8 29const TG_ROUTES: *u8 = "/volume1/homes/elderwesto/nishihost/proxy_routes.conf" as *u8 30const TG_SHARDS: *u8 = "/volume1/homes/elderwesto/nishihost/shards.conf" as *u8 31const TG_SITES: *u8 = "/volume1/homes/elderwesto/nishihost/sites.conf" as *u8 32const TG_PRODS: *u8 = "/volume1/homes/elderwesto/nishihost/tenancy_products.list" as *u8 33const TG_PROPREG: *u8 = "/volume1/homes/elderwesto/nishihost/property_registry.conf" as *u8 34const TG_MANIF: *u8 = "/volume1/homes/elderwesto/nishihost/certs/sni_certs.manifest" as *u8 35const TG_LREG: *u8 = "/volume1/homes/elderwesto/nishihost/domain_lifecycle.reg" as *u8 36const TG_BASE: *u8 = "/volume1/homes/elderwesto/nishihost/tenancy_debt_baseline.txt" as *u8 37const TG_ALERTS: *u8 = "/volume1/homes/elderwesto/nishihost/tenancy_alerts.log" as *u8 38const TG_CLI: *u8 = "./nx_https_get_cli.elf" as *u8 39const TG_FETCH: *u8 = "/volume1/homes/elderwesto/nishihost/_scratch/tg_fetch.out" as *u8 40const TG_PORT: i64 = 18094 41const TG_CYCLE_SEC: i64 = 120 42const TG_SHSZ: i64 = 16384 43const TG_FBUF: i64 = 65536 44const TG_FETCHCAP: i64 = 131072 45const TG_FETCH_TMO_MS: i64 = 20000 46 47func tg_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 48func tg_p(s: *u8) -> i64 { sys_write(1, s, tg_slen(s)); return 0 } 49func tg_decw(v: i64, out: *u8) -> i64 { 50 var o: i64 = 0 51 var m: i64 = v 52 if m < 0 { out[0] = 45 as u8; o = 1; m = 0 - m } 53 if m == 0 { out[o] = 48 as u8; return o + 1 } 54 var nd: i64 = 1 55 var mm: i64 = m 56 while mm >= 10 { nd = nd + 1; mm = mm / 10 } 57 var i: i64 = nd - 1 58 while m > 0 { out[o + i] = (48 + (m % 10)) as u8; m = m / 10; i = i - 1 } 59 return o + nd 60} 61func tg_sappend(dst: *u8, w: i64, s: *u8) -> i64 { 62 var i: i64 = 0 63 var o: i64 = w 64 while s[i] != (0 as u8) { dst[o] = s[i]; o = o + 1; i = i + 1 } 65 return o 66} 67func tg_streq(a: *u8, b: *u8) -> i64 { 68 var i: i64 = 0 69 while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 } 70 if b[i] != (0 as u8) { return 0 } 71 return 1 72} 73func tg_atoi(s: *u8) -> i64 { 74 var v: i64 = 0; var i: i64 = 0; var go: i64 = 1 75 while go == 1 { let c: i64 = s[i] as i64; if c < 48 { go = 0 } else { if c > 57 { go = 0 } else { v = v * 10 + (c - 48); i = i + 1 } } } 76 return v 77} 78// substring search: needle (NUL-term) in hay[0..hlen) 79func tg_contains(hay: *u8, hlen: i64, needle: *u8) -> i64 { 80 let nlen: i64 = tg_slen(needle) 81 if nlen == 0 { return 1 } 82 if hlen < nlen { return 0 } 83 var s: i64 = 0 84 let last: i64 = hlen - nlen 85 while s <= last { 86 var m: i64 = 0 87 var eq: i64 = 1 88 while m < nlen { if hay[s + m] != needle[m] { eq = 0; m = nlen } m = m + 1 } 89 if eq == 1 { return 1 } 90 s = s + 1 91 } 92 return 0 93} 94func tg_next_line(buf: *u8, pos: i64, end: i64) -> i64 { 95 var p: i64 = pos 96 var go: i64 = 1 97 while go == 1 { if p >= end { go = 0 } else { if (buf[p] as i64) == 10 { p = p + 1; go = 0 } else { p = p + 1 } } } 98 return p 99} 100func tg_line_end(buf: *u8, pos: i64, end: i64) -> i64 { 101 var p: i64 = pos 102 while p < end { if (buf[p] as i64) == 10 { return p } p = p + 1 } 103 return end 104} 105// whitespace-separated field #idx (0-based) of buf[ls..le) -> out (NUL-term). Space AND tab are 106// separators, runs collapse (covers both the ws-cols and the TAB-cols SSOT formats). Returns len. 107func tg_wsfield(buf: *u8, ls: i64, le: i64, idx: i64, out: *u8, cap: i64) -> i64 { 108 var p: i64 = ls 109 var fi: i64 = 0 110 var go: i64 = 1 111 while go == 1 { 112 // skip separator run 113 var sk: i64 = 1 114 while sk == 1 { if p >= le { sk = 0 } else { let c: i64 = buf[p] as i64; if c == 32 { p = p + 1 } else { if c == 9 { p = p + 1 } else { if c == 13 { p = p + 1 } else { sk = 0 } } } } } 115 if p >= le { out[0] = 0 as u8; return 0 } 116 // token [p..q) 117 var q: i64 = p 118 var tk: i64 = 1 119 while tk == 1 { if q >= le { tk = 0 } else { let c2: i64 = buf[q] as i64; if c2 == 32 { tk = 0 } else { if c2 == 9 { tk = 0 } else { if c2 == 13 { tk = 0 } else { q = q + 1 } } } } } 120 if fi == idx { 121 var o: i64 = 0 122 var k: i64 = p 123 while k < q { if o < cap - 1 { out[o] = buf[k]; o = o + 1 } k = k + 1 } 124 out[o] = 0 as u8 125 return o 126 } 127 fi = fi + 1 128 p = q 129 if p >= le { go = 0 } 130 } 131 out[0] = 0 as u8 132 return 0 133} 134// TAB-only field #idx of buf[ls..le) -> out. shards.conf is strict TSV whose fields may CONTAIN 135// spaces (the library realm is literally `Nishi Wiki`), so the whitespace splitter would shift its 136// columns -- TSV rows must use THIS. Trailing CR stripped. Returns len. 137func tg_tabfield(buf: *u8, ls: i64, le: i64, idx: i64, out: *u8, cap: i64) -> i64 { 138 var p: i64 = ls 139 var fi: i64 = 0 140 while fi < idx { 141 var sk: i64 = 1 142 while sk == 1 { 143 if p >= le { out[0] = 0 as u8; return 0 } 144 if (buf[p] as i64) == 9 { sk = 0 } 145 p = p + 1 146 } 147 fi = fi + 1 148 } 149 var o: i64 = 0 150 while p < le { 151 let c: i64 = buf[p] as i64 152 if c == 9 { p = le } 153 else { if c == 13 { p = p + 1 } 154 else { 155 if o < cap - 1 { out[o] = buf[p]; o = o + 1 } 156 p = p + 1 157 } } 158 } 159 out[o] = 0 as u8 160 return o 161} 162// bounded whole-file read into a caller buffer (NEVER sys_read_file -- the 4 GiB-cap leak class) 163func tg_read(path: *u8, buf: *u8, cap: i64) -> i64 { 164 let fd: i64 = sys_openat_rd(path) 165 if fd < 0 { return 0 } 166 let n: i64 = sys_read(fd, buf, cap - 1) 167 sys_close(fd) 168 if n < 0 { return 0 } 169 return n 170} 171// packed NUL-separated list ops: entries appended as <bytes>NUL; list[llen]=extra NUL terminator kept by caller 172func tg_list_add(list: *u8, llen: i64, s: *u8) -> i64 { 173 var o: i64 = llen 174 var i: i64 = 0 175 while s[i] != (0 as u8) { list[o] = s[i]; o = o + 1; i = i + 1 } 176 list[o] = 0 as u8 177 o = o + 1 178 list[o] = 0 as u8 179 return o 180} 181func tg_in_list(list: *u8, s: *u8) -> i64 { 182 var p: i64 = 0 183 var go: i64 = 1 184 while go == 1 { 185 if list[p] == (0 as u8) { return 0 } 186 if tg_streq((((list as i64) + p)) as *u8, s) == 1 { return 1 } 187 while list[p] != (0 as u8) { p = p + 1 } 188 p = p + 1 189 } 190 return 0 191} 192// non-empty file check (INV-7): open + read 1 byte 193func tg_file_nonempty(path: *u8, tmp: *u8) -> i64 { 194 let fd: i64 = sys_openat_rd(path) 195 if fd < 0 { return 0 } 196 let n: i64 = sys_read(fd, tmp, 1) 197 sys_close(fd) 198 if n > 0 { return 1 } 199 return 0 200} 201// append a candidate line "KEY\tMSG\n" (deduped by KEY) into cand; returns new w 202func tg_cand(cand: *u8, w: i64, key: *u8, msg: *u8) -> i64 { 203 if tg_contains(cand, w, key) == 1 { return w } 204 if w > 14000 { return w } 205 var o: i64 = tg_sappend(cand, w, key) 206 cand[o] = 9 as u8 207 o = o + 1 208 o = tg_sappend(cand, o, msg) 209 cand[o] = 10 as u8 210 return o + 1 211} 212 213// ---- INV-9 evidence fetch: fork+exec the sovereign HTTPS client, stdout -> TG_FETCH, bounded wait. 214// Child builds url/argv in ITS OWN memory post-fork (the proven ds_launch lesson). Returns 0 ok, -1 fail. 215func tg_fetch(dom: *u8) -> i64 { 216 let pid: i64 = sys_fork() 217 if pid == 0 { 218 sys_chdir(TG_HOME) // CLI loads data/mozilla_certdata.txt from CWD 219 let ofd: i64 = sys_openat_wr(TG_FETCH, 420) 220 if ofd < 0 { sys_exit(125) } 221 sys_dup3(ofd, 1, 0) 222 sys_dup3(ofd, 2, 0) // CLI handshake diagnostics go to stderr -- capture them too, or they 223 // leak into the ORGAN's stdout file and grow it unbounded per cycle 224 let url: *u8 = sys_mmap(512) 225 var w: i64 = 0 226 w = tg_sappend(url, w, "https://" as *u8) 227 w = tg_sappend(url, w, dom) 228 w = tg_sappend(url, w, "/" as *u8) 229 url[w] = 0 as u8 230 let argv: *i64 = sys_mmap(8 * 8) as *i64 231 argv[0] = (TG_CLI) as i64 232 argv[1] = url as i64 233 argv[2] = ("127.0.0.1:8443" as *u8) as i64 234 argv[3] = 0 235 let envp: *i64 = sys_mmap(8 * 4) as *i64 236 envp[0] = ("PATH=/usr/bin:/bin:/usr/local/bin" as *u8) as i64 237 envp[1] = ("HOME=/volume1/homes/elderwesto" as *u8) as i64 238 envp[2] = 0 239 sys_execve_clean(TG_CLI, argv, envp) 240 sys_exit(127) 241 } 242 if pid < 0 { return 0 - 1 } 243 // bounded wait: WNOHANG poll, then kill on timeout (a hung fetch must never stall the gate cycle) 244 let st: *i64 = sys_mmap(8) as *i64 245 var waited: i64 = 0 246 var done: i64 = 0 247 while done == 0 { 248 let r: i64 = sys_wait4(pid, st, 1) 249 if r == pid { done = 1 } 250 else { 251 if waited >= TG_FETCH_TMO_MS { nx_kill(pid, 9); sys_wait4(pid, st, 0); done = 1; sys_munmap(st as *u8, 8); return 0 - 1 } 252 sys_sleep_ms(200) 253 waited = waited + 200 254 } 255 } 256 sys_munmap(st as *u8, 8) 257 return 0 258} 259 260// ---- report server child on 127.0.0.1:TG_PORT (seqlock shared page; exact Content-Length = CL-LAW) ---- 261func tg_server(shm: *u8) -> i64 { 262 let shq: *i64 = shm as *i64 263 let lfd: i64 = sys_socket(AF_INET, SOCK_STREAM, 0) 264 if lfd < 0 { sys_exit(3) } 265 let one: *i64 = sys_mmap(8) as *i64 266 one[0] = 1 267 sys_setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, one as *u8, 4) 268 let sa: *u8 = sys_mmap(16) 269 hp_sockaddr(sa, TG_PORT, 127, 0, 0, 1) 270 if sys_bind(lfd, sa, 16) < 0 { sys_exit(4) } 271 sys_listen(lfd, 16) 272 let req: *u8 = sys_mmap(2048) 273 let body: *u8 = sys_mmap(TG_SHSZ) 274 let out: *u8 = sys_mmap(TG_SHSZ + 256) 275 var run: i64 = 1 276 while run == 1 { 277 let afd: i64 = sys_accept(lfd) 278 if afd >= 0 { 279 sys_set_socket_timeout(afd, 3) 280 sys_read(afd, req, 2047) 281 var blen: i64 = 0 282 var got: i64 = 0 283 var tries: i64 = 0 284 while got == 0 { 285 let s1: i64 = shq[0] 286 if (s1 % 2) == 0 { 287 var bl: i64 = shq[1] 288 if bl > TG_SHSZ - 16 { bl = TG_SHSZ - 16 } 289 if bl < 0 { bl = 0 } 290 var ci: i64 = 0 291 while ci < bl { body[ci] = shm[16 + ci]; ci = ci + 1 } 292 if shq[0] == s1 { blen = bl; got = 1 } 293 } 294 tries = tries + 1 295 if tries > 10000 { got = 1 } 296 } 297 var w: i64 = 0 298 w = tg_sappend(out, w, "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nConnection: close\r\nContent-Length: " as *u8) 299 w = w + tg_decw(blen, (((out as i64) + w)) as *u8) 300 out[w] = 13 as u8; w = w + 1 301 out[w] = 10 as u8; w = w + 1 302 out[w] = 13 as u8; w = w + 1 303 out[w] = 10 as u8; w = w + 1 304 var bi: i64 = 0 305 while bi < blen { out[w] = body[bi]; w = w + 1; bi = bi + 1 } 306 sys_write(afd, out, w) 307 sys_close(afd) 308 } 309 } 310 return 0 311} 312 313func main() -> i64 { 314 tg_p("nx_tenancy_gate: LIVE -- sovereign INV-1..9 orthogonality gate (report :18094, alerts append; no bash, no tsv)\n" as *u8) 315 let shm: *u8 = sys_mmap_shared(TG_SHSZ) 316 let shq: *i64 = shm as *i64 317 shq[0] = 0 318 shq[1] = 0 319 let sspid: i64 = sys_fork() 320 if sspid == 0 { tg_server(shm); sys_exit(0) } 321 // ---- hoisted buffers (the hot loop performs ZERO mmap) ---- 322 let planes: *u8 = sys_mmap(TG_FBUF) 323 let routes: *u8 = sys_mmap(TG_FBUF) 324 let shards: *u8 = sys_mmap(TG_FBUF) 325 let sites: *u8 = sys_mmap(TG_FBUF) 326 let prods: *u8 = sys_mmap(8192) 327 let preg: *u8 = sys_mmap(TG_FBUF) 328 let manif: *u8 = sys_mmap(TG_FBUF) 329 let lreg: *u8 = sys_mmap(16384) 330 let base: *u8 = sys_mmap(TG_FBUF) 331 let alpha: *u8 = sys_mmap(4096) 332 let client: *u8 = sys_mmap(4096) 333 let cand: *u8 = sys_mmap(TG_SHSZ) 334 let rep: *u8 = sys_mmap(TG_SHSZ) 335 let fetchb: *u8 = sys_mmap(TG_FETCHCAP) 336 let marker: *u8 = sys_mmap(64) 337 let f0: *u8 = sys_mmap(512) 338 let f1: *u8 = sys_mmap(512) 339 let f2: *u8 = sys_mmap(512) 340 let f3: *u8 = sys_mmap(512) 341 let keyb: *u8 = sys_mmap(600) 342 let msgb: *u8 = sys_mmap(900) 343 let dbuf: *u8 = sys_mmap(65536) 344 let cpath: *u8 = sys_mmap(64) 345 let cbuf: *u8 = sys_mmap(4096) 346 let rst: *i64 = sys_mmap(8) as *i64 347 let prevc: *u8 = sys_mmap(TG_SHSZ) 348 var prevw: i64 = 0 - 1 // -1 = no previous cycle (first RED always alerts) 349 // coming-soon marker built without a quote-in-literal: badge + '"' + >Coming soon< 350 var mw: i64 = 0 351 mw = tg_sappend(marker, mw, "badge" as *u8) 352 marker[mw] = 34 as u8 353 mw = mw + 1 354 mw = tg_sappend(marker, mw, ">Coming soon<" as *u8) 355 marker[mw] = 0 as u8 356 var cyc: i64 = 0 357 var forever: i64 = 1 358 while forever == 1 { 359 // reap (the fetch children are waited synchronously; this catches the server child if it dies) 360 var reap: i64 = 1 361 while reap == 1 { if sys_wait4(0 - 1, rst, 1) > 0 { reap = 1 } else { reap = 0 } } 362 cyc = cyc + 1 363 let pl: i64 = tg_read(TG_PLANES, planes, TG_FBUF) 364 let rl: i64 = tg_read(TG_ROUTES, routes, TG_FBUF) 365 let sl: i64 = tg_read(TG_SHARDS, shards, TG_FBUF) 366 let il: i64 = tg_read(TG_SITES, sites, TG_FBUF) 367 let dl: i64 = tg_read(TG_PRODS, prods, 8192) 368 let gl: i64 = tg_read(TG_PROPREG, preg, TG_FBUF) 369 let ml: i64 = tg_read(TG_MANIF, manif, TG_FBUF) 370 let ll: i64 = tg_read(TG_LREG, lreg, 16384) 371 let bl: i64 = tg_read(TG_BASE, base, TG_FBUF) 372 var cw: i64 = 0 373 // ---- plane lists ---- 374 alpha[0] = 0 as u8 375 client[0] = 0 as u8 376 var al: i64 = 0 377 var cl: i64 = 0 378 var pos: i64 = 0 379 while pos < pl { 380 if (planes[pos] as i64) == 35 { pos = tg_next_line(planes, pos, pl) } 381 else { if (planes[pos] as i64) == 10 { pos = pos + 1 } 382 else { 383 let le: i64 = tg_line_end(planes, pos, pl) 384 tg_wsfield(planes, pos, le, 0, f0, 512) 385 tg_wsfield(planes, pos, le, 1, f1, 512) 386 if tg_streq(f1, "alpha" as *u8) == 1 { al = tg_list_add(alpha, al, f0) } 387 if tg_streq(f1, "client" as *u8) == 1 { cl = tg_list_add(client, cl, f0) } 388 pos = tg_next_line(planes, pos, pl) 389 } } 390 } 391 // ---- INV-1: served hosts (routes col0 + sites col0, minus "*") must have a plane row ---- 392 var src: i64 = 0 393 while src < 2 { 394 var sbp: *u8 = routes 395 var sbl: i64 = rl 396 if src == 1 { sbp = sites; sbl = il } 397 pos = 0 398 while pos < sbl { 399 if (sbp[pos] as i64) == 35 { pos = tg_next_line(sbp, pos, sbl) } 400 else { if (sbp[pos] as i64) == 10 { pos = pos + 1 } 401 else { 402 let le2: i64 = tg_line_end(sbp, pos, sbl) 403 let hn: i64 = tg_wsfield(sbp, pos, le2, 0, f0, 512) 404 if hn > 0 { if tg_streq(f0, "*" as *u8) == 0 { 405 // planes col0 lookup 406 var found: i64 = 0 407 var pp: i64 = 0 408 while pp < pl { 409 if (planes[pp] as i64) != 35 { if (planes[pp] as i64) != 10 { 410 let ple: i64 = tg_line_end(planes, pp, pl) 411 tg_wsfield(planes, pp, ple, 0, f1, 512) 412 if tg_streq(f0, f1) == 1 { found = 1; pp = pl } 413 } } 414 if found == 0 { pp = tg_next_line(planes, pp, pl) } 415 } 416 if found == 0 { 417 var kw: i64 = 0 418 kw = tg_sappend(keyb, kw, "INV1|" as *u8) 419 kw = tg_sappend(keyb, kw, f0) 420 keyb[kw] = 0 as u8 421 var mw2: i64 = 0 422 mw2 = tg_sappend(msgb, mw2, "served host " as *u8) 423 mw2 = tg_sappend(msgb, mw2, f0) 424 mw2 = tg_sappend(msgb, mw2, " has NO declared plane" as *u8) 425 msgb[mw2] = 0 as u8 426 cw = tg_cand(cand, cw, keyb, msgb) 427 } 428 } } 429 pos = tg_next_line(sbp, pos, sbl) 430 } } 431 } 432 src = src + 1 433 } 434 // ---- INV-3: every running nx_mgmt_api must sit on a declared mgmt shard port ---- 435 let dfd: i64 = sys_openat_rd("/proc\x00" as *u8) 436 if dfd >= 0 { 437 var reading: i64 = 1 438 while reading == 1 { 439 let dn: i64 = sys_getdents64(dfd, dbuf, 65536) 440 if dn <= 0 { reading = 0 } 441 else { 442 var off: i64 = 0 443 while off < dn { 444 let recp: *u8 = (((dbuf as i64) + off)) as *u8 445 let reclen: i64 = dirent_reclen(recp) 446 let nm: *u8 = dirent_name(recp) 447 if nm[0] >= (48 as u8) { if nm[0] <= (57 as u8) { 448 var pi: i64 = 0 449 let pre: *u8 = "/proc/" as *u8 450 while pre[pi] != (0 as u8) { cpath[pi] = pre[pi]; pi = pi + 1 } 451 var qi: i64 = 0 452 while nm[qi] != (0 as u8) { cpath[pi] = nm[qi]; pi = pi + 1; qi = qi + 1 } 453 let suf: *u8 = "/cmdline" as *u8 454 var si: i64 = 0 455 while suf[si] != (0 as u8) { cpath[pi] = suf[si]; pi = pi + 1; si = si + 1 } 456 cpath[pi] = 0 as u8 457 let cfd: i64 = sys_openat_rd(cpath) 458 if cfd >= 0 { 459 let cn: i64 = sys_read(cfd, cbuf, 4095) 460 sys_close(cfd) 461 if cn > 0 { 462 var ci2: i64 = 0 463 while ci2 < cn { if (cbuf[ci2] as i64) == 0 { cbuf[ci2] = 32 as u8 } ci2 = ci2 + 1 } 464 cbuf[cn] = 0 as u8 465 if tg_contains(cbuf, cn, "nx_mgmt_api" as *u8) == 1 { 466 // port = first digit-run after the binary token 467 var dpos: i64 = 0 468 var port: i64 = 0 469 var scan2: i64 = 1 470 while scan2 == 1 { 471 if dpos >= cn { scan2 = 0 } 472 else { 473 let ch: i64 = cbuf[dpos] as i64 474 if ch >= 48 { if ch <= 57 { port = tg_atoi((((cbuf as i64) + dpos)) as *u8); scan2 = 0 } } 475 dpos = dpos + 1 476 } 477 } 478 if port > 0 { 479 // declared? shards rows col2=="mgmt" col3==port 480 var dec: i64 = 0 481 var sp: i64 = 0 482 while sp < sl { 483 if (shards[sp] as i64) != 35 { if (shards[sp] as i64) != 10 { 484 let sle: i64 = tg_line_end(shards, sp, sl) 485 tg_tabfield(shards, sp, sle, 2, f1, 512) 486 tg_tabfield(shards, sp, sle, 3, f2, 512) 487 if tg_streq(f1, "mgmt" as *u8) == 1 { if tg_atoi(f2) == port { dec = 1; sp = sl } } 488 } } 489 if dec == 0 { sp = tg_next_line(shards, sp, sl) } 490 } 491 if dec == 0 { 492 var kw3: i64 = 0 493 kw3 = tg_sappend(keyb, kw3, "INV3|mgmt_api:" as *u8) 494 kw3 = kw3 + tg_decw(port, (((keyb as i64) + kw3)) as *u8) 495 keyb[kw3] = 0 as u8 496 var mw3: i64 = 0 497 mw3 = tg_sappend(msgb, mw3, "running nx_mgmt_api on an UNDECLARED shard port (orphan store-sharer) :" as *u8) 498 mw3 = mw3 + tg_decw(port, (((msgb as i64) + mw3)) as *u8) 499 msgb[mw3] = 0 as u8 500 cw = tg_cand(cand, cw, keyb, msgb) 501 } 502 } 503 } 504 } 505 } 506 } } 507 off = off + reclen 508 } 509 } 510 } 511 sys_close(dfd) 512 } 513 // ---- INV-4: client shards must not use the shared control opaque_keys/opaque_store ---- 514 pos = 0 515 while pos < sl { 516 if (shards[pos] as i64) == 35 { pos = tg_next_line(shards, pos, sl) } 517 else { if (shards[pos] as i64) == 10 { pos = pos + 1 } 518 else { 519 let le4: i64 = tg_line_end(shards, pos, sl) 520 tg_tabfield(shards, pos, le4, 0, f0, 512) 521 tg_tabfield(shards, pos, le4, 1, f1, 512) 522 tg_tabfield(shards, pos, le4, 5, f2, 512) 523 tg_tabfield(shards, pos, le4, 6, f3, 512) 524 if tg_in_list(client, f1) == 1 { 525 var bad: i64 = 0 526 if tg_contains(f2, tg_slen(f2), "opaque_keys" as *u8) == 1 { bad = 1 } 527 if tg_contains(f3, tg_slen(f3), "opaque_store" as *u8) == 1 { bad = 1 } 528 if bad == 1 { 529 var kw4: i64 = 0 530 kw4 = tg_sappend(keyb, kw4, "INV4|" as *u8) 531 kw4 = tg_sappend(keyb, kw4, f0) 532 keyb[kw4] = 0 as u8 533 var mw4: i64 = 0 534 mw4 = tg_sappend(msgb, mw4, "client shard " as *u8) 535 mw4 = tg_sappend(msgb, mw4, f0) 536 mw4 = tg_sappend(msgb, mw4, " uses the SHARED control store -- must be isolated" as *u8) 537 msgb[mw4] = 0 as u8 538 cw = tg_cand(cand, cw, keyb, msgb) 539 } 540 } 541 pos = tg_next_line(shards, pos, sl) 542 } } 543 } 544 // ---- INV-5: a confirmed product must not be a bare path on an alpha domain ---- 545 pos = 0 546 while pos < dl { 547 if (prods[pos] as i64) == 35 { pos = tg_next_line(prods, pos, dl) } 548 else { if (prods[pos] as i64) == 10 { pos = pos + 1 } 549 else { 550 let le5: i64 = tg_line_end(prods, pos, dl) 551 let pn: i64 = tg_wsfield(prods, pos, le5, 0, f0, 512) 552 if pn > 0 { 553 // route rows: col0 in alpha && col1 == "/"+prod 554 f1[0] = 47 as u8 555 var pj: i64 = 0 556 while f0[pj] != (0 as u8) { f1[pj + 1] = f0[pj]; pj = pj + 1 } 557 f1[pj + 1] = 0 as u8 558 var rp: i64 = 0 559 while rp < rl { 560 if (routes[rp] as i64) != 35 { if (routes[rp] as i64) != 10 { 561 let rle: i64 = tg_line_end(routes, rp, rl) 562 tg_wsfield(routes, rp, rle, 0, f2, 512) 563 tg_wsfield(routes, rp, rle, 1, f3, 512) 564 if tg_in_list(alpha, f2) == 1 { if tg_streq(f3, f1) == 1 { 565 var kw5: i64 = 0 566 kw5 = tg_sappend(keyb, kw5, "INV5|" as *u8) 567 kw5 = tg_sappend(keyb, kw5, f2) 568 kw5 = tg_sappend(keyb, kw5, f1) 569 keyb[kw5] = 0 as u8 570 var mw5: i64 = 0 571 mw5 = tg_sappend(msgb, mw5, "product " as *u8) 572 mw5 = tg_sappend(msgb, mw5, f1) 573 mw5 = tg_sappend(msgb, mw5, " is a bare path on alpha domain " as *u8) 574 mw5 = tg_sappend(msgb, mw5, f2) 575 mw5 = tg_sappend(msgb, mw5, " -- graduate to own domain" as *u8) 576 msgb[mw5] = 0 as u8 577 cw = tg_cand(cand, cw, keyb, msgb) 578 } } 579 } } 580 rp = tg_next_line(routes, rp, rl) 581 } 582 } 583 pos = tg_next_line(prods, pos, dl) 584 } } 585 } 586 // ---- INV-6: every :8443-routed property_registry domain has a cert-manifest row ---- 587 pos = 0 588 while pos < gl { 589 if (preg[pos] as i64) == 35 { pos = tg_next_line(preg, pos, gl) } 590 else { if (preg[pos] as i64) == 10 { pos = pos + 1 } 591 else { 592 let le6: i64 = tg_line_end(preg, pos, gl) 593 tg_wsfield(preg, pos, le6, 0, f0, 512) 594 tg_wsfield(preg, pos, le6, 1, f1, 512) 595 if tg_streq(f1, "8443" as *u8) == 1 { 596 var mfound: i64 = 0 597 var mp: i64 = 0 598 while mp < ml { 599 if (manif[mp] as i64) != 35 { if (manif[mp] as i64) != 10 { 600 let mle: i64 = tg_line_end(manif, mp, ml) 601 tg_wsfield(manif, mp, mle, 0, f2, 512) 602 if tg_streq(f0, f2) == 1 { mfound = 1; mp = ml } 603 } } 604 if mfound == 0 { mp = tg_next_line(manif, mp, ml) } 605 } 606 if mfound == 0 { 607 var kw6: i64 = 0 608 kw6 = tg_sappend(keyb, kw6, "INV6|" as *u8) 609 kw6 = tg_sappend(keyb, kw6, f0) 610 keyb[kw6] = 0 as u8 611 var mw6: i64 = 0 612 mw6 = tg_sappend(msgb, mw6, "routed to :8443 but NO cert-manifest row (edge serves default cert): " as *u8) 613 mw6 = tg_sappend(msgb, mw6, f0) 614 msgb[mw6] = 0 as u8 615 cw = tg_cand(cand, cw, keyb, msgb) 616 } 617 } 618 pos = tg_next_line(preg, pos, gl) 619 } } 620 } 621 // ---- INV-7: manifest cert+key files exist and are non-empty ---- 622 pos = 0 623 while pos < ml { 624 if (manif[pos] as i64) == 35 { pos = tg_next_line(manif, pos, ml) } 625 else { if (manif[pos] as i64) == 10 { pos = pos + 1 } 626 else { 627 let le7: i64 = tg_line_end(manif, pos, ml) 628 let dn7: i64 = tg_wsfield(manif, pos, le7, 0, f0, 512) 629 let cn7: i64 = tg_wsfield(manif, pos, le7, 1, f1, 512) 630 let kn7: i64 = tg_wsfield(manif, pos, le7, 2, f2, 512) 631 if dn7 > 0 { if cn7 > 0 { 632 if tg_file_nonempty(f1, f3) == 0 { 633 var kw7: i64 = 0 634 kw7 = tg_sappend(keyb, kw7, "INV7|" as *u8) 635 kw7 = tg_sappend(keyb, kw7, f0) 636 keyb[kw7] = 0 as u8 637 var mw7: i64 = 0 638 mw7 = tg_sappend(msgb, mw7, "manifest cert file MISSING/empty for " as *u8) 639 mw7 = tg_sappend(msgb, mw7, f0) 640 msgb[mw7] = 0 as u8 641 cw = tg_cand(cand, cw, keyb, msgb) 642 } 643 if kn7 > 0 { if tg_file_nonempty(f2, f3) == 0 { 644 var kw7b: i64 = 0 645 kw7b = tg_sappend(keyb, kw7b, "INV7|" as *u8) 646 kw7b = tg_sappend(keyb, kw7b, f0) 647 kw7b = tg_sappend(keyb, kw7b, "-key" as *u8) 648 keyb[kw7b] = 0 as u8 649 var mw7b: i64 = 0 650 mw7b = tg_sappend(msgb, mw7b, "manifest key file MISSING/empty for " as *u8) 651 mw7b = tg_sappend(msgb, mw7b, f0) 652 msgb[mw7b] = 0 as u8 653 cw = tg_cand(cand, cw, keyb, msgb) 654 } } 655 } } 656 pos = tg_next_line(manif, pos, ml) 657 } } 658 } 659 // ---- INV-8: every served vhost (sites col0) has a lifecycle row ---- 660 pos = 0 661 while pos < il { 662 if (sites[pos] as i64) == 35 { pos = tg_next_line(sites, pos, il) } 663 else { if (sites[pos] as i64) == 10 { pos = pos + 1 } 664 else { 665 let le8: i64 = tg_line_end(sites, pos, il) 666 let hn8: i64 = tg_wsfield(sites, pos, le8, 0, f0, 512) 667 if hn8 > 0 { if tg_streq(f0, "*" as *u8) == 0 { 668 var lfound: i64 = 0 669 var lp: i64 = 0 670 while lp < ll { 671 if (lreg[lp] as i64) != 35 { if (lreg[lp] as i64) != 10 { 672 let lle: i64 = tg_line_end(lreg, lp, ll) 673 tg_wsfield(lreg, lp, lle, 0, f1, 512) 674 if tg_streq(f0, f1) == 1 { lfound = 1; lp = ll } 675 } } 676 if lfound == 0 { lp = tg_next_line(lreg, lp, ll) } 677 } 678 if lfound == 0 { 679 var kw8: i64 = 0 680 kw8 = tg_sappend(keyb, kw8, "INV8|" as *u8) 681 kw8 = tg_sappend(keyb, kw8, f0) 682 keyb[kw8] = 0 as u8 683 var mw8: i64 = 0 684 mw8 = tg_sappend(msgb, mw8, "served vhost has NO lifecycle row (stage unknown): " as *u8) 685 mw8 = tg_sappend(msgb, mw8, f0) 686 msgb[mw8] = 0 as u8 687 cw = tg_cand(cand, cw, keyb, msgb) 688 } 689 } } 690 pos = tg_next_line(sites, pos, il) 691 } } 692 } 693 // ---- INV-9: live-edge stage evidence (skip when the edge is down; selfheal owns that alarm) ---- 694 var edge_up: i64 = 0 695 if hp_probe_tls(8443, 3) == HP_SERVING { edge_up = 1 } 696 if edge_up == 1 { 697 pos = 0 698 while pos < ll { 699 if (lreg[pos] as i64) == 35 { pos = tg_next_line(lreg, pos, ll) } 700 else { if (lreg[pos] as i64) == 10 { pos = pos + 1 } 701 else { 702 let le9: i64 = tg_line_end(lreg, pos, ll) 703 let dn9: i64 = tg_wsfield(lreg, pos, le9, 0, f0, 512) 704 tg_wsfield(lreg, pos, le9, 1, f1, 512) 705 if dn9 > 0 { 706 let frc: i64 = tg_fetch(f0) 707 var flen: i64 = 0 708 if frc == 0 { flen = tg_read(TG_FETCH, fetchb, TG_FETCHCAP) } 709 if flen <= 0 { 710 var kw9f: i64 = 0 711 kw9f = tg_sappend(keyb, kw9f, "INV9|" as *u8) 712 kw9f = tg_sappend(keyb, kw9f, f0) 713 kw9f = tg_sappend(keyb, kw9f, "-fetchfail" as *u8) 714 keyb[kw9f] = 0 as u8 715 var mw9f: i64 = 0 716 mw9f = tg_sappend(msgb, mw9f, "stage evidence fetch FAILED for " as *u8) 717 mw9f = tg_sappend(msgb, mw9f, f0) 718 mw9f = tg_sappend(msgb, mw9f, " (cannot verify stage; NOT treated as a leak)" as *u8) 719 msgb[mw9f] = 0 as u8 720 cw = tg_cand(cand, cw, keyb, msgb) 721 } 722 else { 723 let has_mark: i64 = tg_contains(fetchb, flen, marker) 724 let is_prod: i64 = tg_streq(f1, "production" as *u8) 725 if is_prod == 1 { if has_mark == 1 { 726 var kw9s: i64 = 0 727 kw9s = tg_sappend(keyb, kw9s, "INV9|" as *u8) 728 kw9s = tg_sappend(keyb, kw9s, f0) 729 kw9s = tg_sappend(keyb, kw9s, "-stuck" as *u8) 730 keyb[kw9s] = 0 as u8 731 var mw9s: i64 = 0 732 mw9s = tg_sappend(msgb, mw9s, "production domain still SERVES coming-soon (launch did not take): " as *u8) 733 mw9s = tg_sappend(msgb, mw9s, f0) 734 msgb[mw9s] = 0 as u8 735 cw = tg_cand(cand, cw, keyb, msgb) 736 } } 737 if is_prod == 0 { if has_mark == 0 { 738 var kw9l: i64 = 0 739 kw9l = tg_sappend(keyb, kw9l, "INV9|" as *u8) 740 kw9l = tg_sappend(keyb, kw9l, f0) 741 kw9l = tg_sappend(keyb, kw9l, "-leak" as *u8) 742 keyb[kw9l] = 0 as u8 743 var mw9l: i64 = 0 744 mw9l = tg_sappend(msgb, mw9l, "stage!=production domain serves NON-coming-soon content publicly (unapproved go-live leak): " as *u8) 745 mw9l = tg_sappend(msgb, mw9l, f0) 746 msgb[mw9l] = 0 as u8 747 cw = tg_cand(cand, cw, keyb, msgb) 748 } } 749 } 750 } 751 pos = tg_next_line(lreg, pos, ll) 752 } } 753 } 754 } 755 // ---- classify vs baseline + build the report ---- 756 var new_n: i64 = 0 757 var debt_n: i64 = 0 758 var rw: i64 = 0 759 rw = tg_sappend(rep, rw, "nx_tenancy_gate cycle=" as *u8) 760 rw = rw + tg_decw(cyc, (((rep as i64) + rw)) as *u8) 761 rw = tg_sappend(rep, rw, " edge=" as *u8) 762 if edge_up == 1 { rw = tg_sappend(rep, rw, "SERVING" as *u8) } else { rw = tg_sappend(rep, rw, "DOWN(inv9-skipped)" as *u8) } 763 rep[rw] = 10 as u8 764 rw = rw + 1 765 var body_start: i64 = rw 766 pos = 0 767 while pos < cw { 768 let cle: i64 = tg_line_end(cand, pos, cw) 769 // key = up to TAB 770 var kp: i64 = pos 771 var ko: i64 = 0 772 while kp < cle { if (cand[kp] as i64) == 9 { kp = cle } else { keyb[ko] = cand[kp]; ko = ko + 1; kp = kp + 1 } } 773 keyb[ko] = 0 as u8 774 let known: i64 = tg_contains(base, bl, keyb) 775 if rw < TG_SHSZ - 1024 { 776 if known == 1 { rw = tg_sappend(rep, rw, "[debt] " as *u8); debt_n = debt_n + 1 } 777 else { rw = tg_sappend(rep, rw, "[NEW ] " as *u8); new_n = new_n + 1 } 778 var bp: i64 = pos 779 while bp < cle { rep[rw] = cand[bp]; rw = rw + 1; bp = bp + 1 } 780 rep[rw] = 10 as u8 781 rw = rw + 1 782 } 783 pos = tg_next_line(cand, pos, cw) 784 } 785 // verdict line FIRST would be nicer, but the header carries counts: append summary+verdict at end 786 rw = tg_sappend(rep, rw, "summary: debt=" as *u8) 787 rw = rw + tg_decw(debt_n, (((rep as i64) + rw)) as *u8) 788 rw = tg_sappend(rep, rw, " new=" as *u8) 789 rw = rw + tg_decw(new_n, (((rep as i64) + rw)) as *u8) 790 rw = tg_sappend(rep, rw, " VERDICT=" as *u8) 791 if new_n == 0 { rw = tg_sappend(rep, rw, "GREEN" as *u8) } else { rw = tg_sappend(rep, rw, "RED" as *u8) } 792 rep[rw] = 10 as u8 793 rw = rw + 1 794 // seqlock publish 795 shq[0] = shq[0] + 1 796 var pj2: i64 = 0 797 while pj2 < rw { shm[16 + pj2] = rep[pj2]; pj2 = pj2 + 1 } 798 shq[1] = rw 799 shq[0] = shq[0] + 1 800 // alert append on NEW (same file the bash alert used; append-only). DEDUPED: a persistent 801 // unchanged RED alerts ONCE, not every cycle (the bash version re-appended every 60s = 1440 802 // lines/day of the same regression). Any CHANGE in the finding set while new>0 re-alerts. 803 var same_as_prev: i64 = 0 804 if cw == prevw { 805 var mi2: i64 = 0 806 same_as_prev = 1 807 while mi2 < cw { if cand[mi2] != prevc[mi2] { same_as_prev = 0; mi2 = cw } mi2 = mi2 + 1 } 808 } 809 var pc: i64 = 0 810 while pc < cw { prevc[pc] = cand[pc]; pc = pc + 1 } 811 prevw = cw 812 if new_n > 0 { if same_as_prev == 0 { 813 let afd: i64 = sys_openat_append(TG_ALERTS, 420) 814 if afd >= 0 { 815 var aw: i64 = rw 816 aw = tg_sappend(rep, aw, "ts=" as *u8) 817 aw = aw + tg_decw(sys_now_realtime_sec(), (((rep as i64) + aw)) as *u8) 818 aw = tg_sappend(rep, aw, " TENANCY-REGRESSION new=" as *u8) 819 aw = aw + tg_decw(new_n, (((rep as i64) + aw)) as *u8) 820 aw = tg_sappend(rep, aw, " (report: 127.0.0.1:18094)" as *u8) 821 rep[aw] = 10 as u8 822 aw = aw + 1 823 sys_write(afd, (((rep as i64) + rw)) as *u8, aw - rw) 824 sys_close(afd) 825 } 826 } } 827 sys_sleep_ms(TG_CYCLE_SEC * 1000) 828 } 829 return 0 830}