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}