nx_browser_gate.nx source
↩ module page · 694 lines · 32435 B
1// nx_browser_gate.nx -- THE BROWSER GATE: one command re-proves the
2// Nishi browser claim set, evidence-driven + durable.
3//
4// Data-driven row table (add a row = add a table entry). Each row
5// names a runtime/<module>.nx; the gate builds it through THE SOVEREIGN
6// BUILD LANE (_offc/nx_sov_build_run.elf <mod> --build-only -- nx_cc ->
7// nxasm_x86, no gcc, no binutils), runs the artifact that lane names,
8// and judges via the RAW wait4 status (the sovereign exit-judging law --
9// never $? through a shell).
10//
11// Rows (the browser claim set, 2026-06-10):
12// arg7-tailcall compiler regression gate: >6-arg tail-call drop
13// (the defect that zeroed every TLS Derive-Secret)
14// tls-kdf RFC 8448 Derive-Secret KATs
15// tls-schedule TLS 1.3 key-schedule KATs
16// jpeg-ascii deterministic synthetic-JPEG decode + raster
17// page-demo live HTTP fetch + html_to_text (example.com:80)
18// own-site-live LIVE sovereign HTTPS fetch of nishifamily.com
19// (TLS 1.3 + real Mozilla CA chain validation)
20// render-own paints the FETCHED page's text -> framebuffer
21// (consumes own-site-live's /tmp/nishi_own.html)
22// recv-hs-probe B1 rung: full encrypted-handshake walk vs
23// example.com (per-record/per-message markers,
24// cert-pipeline verdict + chain sig-alg census);
25// exit 0 = handshake to WAIT_CLIENT_FIN
26// p256-keyshare B4 rung: P-256 ECDH (RFC 5903 KAT both
27// directions, boundary rejects) + dual key_share
28// wire format (x25519 + secp256r1 in one CH)
29// derefcast compiler regression gate: `*p as T` precedence
30// (pre-fix: cast bound inside the deref -> byte-
31// load of the pointer cell -> SIGSEGV; fixed in
32// parse_unary 2026-06-10)
33// p256-loopback B4 server side: OUR server completes a P-256-only
34// client (no HRR), both roles derive byte-identical
35// traffic secrets, off-curve share rejected (the
36// nishifamily P-256 gap, healed + loopback-proven)
37// ch-compat B2 rung, two live legs: httpbin.org census
38// (TLS-1.2-only signature classified -- the CH
39// was never the defect; auto-escalates to a full
40// handshake if they enable 1.3) + github.com
41// P-256-only CH walked to verified server
42// Finished (GR=23: live proof of the B4 path)
43// browser-shot (2026-08-27) THE SHIPPED BROWSER ITSELF: forks the
44// nx_browser artifact's headless `shot` verb over a
45// fixture assembled at run time, png-out "-" (full
46// layout, PNG deliberately not written). Subject is
47// SUBSTITUTABLE: argv[1] names the artifact to judge
48// (the ship loop passes the staged build, nx_gate_bite
49// `subject nx_browser` passes the mutant); argless, the
50// PROMOTED ./nx_browser.elf (or ../) -- what is served.
51// Rows 0..11 prove the LIBRARIES the browser is built
52// from; none of them executes nx_browser.elf, so without
53// this row a ship could pass with a browser that cannot
54// lay out a page, and a subject-mode bite reached nothing.
55//
56// Verdict lines stream to stdout AND append to the durable log
57// knowledge/status/browser_gate.log (Archivist durability rule --
58// /tmp dies on reboot, knowledge/ does not).
59//
60// ---- THE SOVEREIGN BUILD LANE (2026-08-27) -------------------------------
61// Until this day bg_build compiled each row with the laptop's native x86
62// compiler and then shelled the .s through /usr/bin/as and /usr/bin/ld.
63// MEASURED on the NAS: that compiler does not exist here at all
64// (nx_catalog: ABSENT), and the moment the sovereign compiler was
65// substituted every row moved build=1 -> build=2 -- the NEXT stage died,
66// because the sovereign host deliberately ships NO binutils ("SOVEREIGN
67// (nx_cc->nxasm_x86, no gcc)" is what this estate's own builder prints).
68// So for its entire recent history this gate published "passed 0/12
69// verdict=RED" about a browser nothing had examined. A GATE THAT CANNOT
70// BUILD ITS SUBJECT MUST ABSTAIN, NOT CONVICT -- and the remedy is not a
71// third compiler path, it is to build the rows THE WAY EVERYTHING ELSE ON
72// THIS HOST IS BUILT: fork the estate's one builder. That buys, for free,
73// its content-addressed build cache (a repeat of this gate is a cache HIT
74// per row, so the roster cost is the rows' own runtime), its per-target
75// flock, its build deadline, its tree-canon admission and its load
76// governor. A refusal by that governor is a fact about the BOX, not the
77// browser, so it is recorded as a PRECONDITION (gv_need -> SKIP), never
78// as a row FAIL. Only "the row's source does not build" and "the row ran
79// and exited nonzero" are failures, because only those are evidence about
80// the claim set.
81//
82// Exit: gv_verdict -- 0 GREEN (every declared row built and exited 0),
83// 1 RED (a row failed), 3 SKIP (a precondition kept some row from running
84// and nothing that ran failed). ORCHESTRATION is pure NishiLang
85// fork/dup3/execve/wait4 -- NO shell, NO .sh.
86//
87// license_tier: ORIGINAL
88
89import "nx_syscalls.nx"
90import "nx_gate_verdict.nx"
91
92const BG_BUILD_ROWS: i64 = 12 // rows built from runtime/<mod>.nx through the sovereign lane
93const BG_SHOT_ROW: i64 = 12 // index of the browser-shot row (the shipped artifact itself)
94const BG_ROWS: i64 = 13 // every declared row: BG_BUILD_ROWS + the shot row
95const BG_SHOT_ARGV_SLOTS: i64 = 5 // artifact, shot, fixture, png-out, NUL
96const BG_PATH_CAP: i64 = 512 // one resolved path
97const BG_LINE_CAP: i64 = 512 // the builder's last output line, carried onto the row that failed to build
98const BG_NUM_CAP: i64 = 28 // decimal scratch
99const BG_ARGV_SLOTS: i64 = 4 // builder argv: elf, module, --build-only, NUL
100const BG_MODE_644: i64 = 420
101const BG_COPY_CHUNK: i64 = 65536
102// wait4 status decoding: exit code in bits 8..15, terminating signal in bits 0..6
103const BG_EXIT_SHIFT: i64 = 8
104const BG_EXIT_MASK: i64 = 255
105const BG_SIG_MASK: i64 = 127
106const BG_EXEC_FAIL: i64 = 127 // what bg_run's child exits with when execve itself fails
107const BG_SPACE: i64 = 32
108const BG_TAB: i64 = 9
109const BG_LF: i64 = 10
110const BG_CR: i64 = 13
111
112// bg_build outcomes. The first four are EVIDENCE about the row and count as a row that RAN;
113// the rest mean the row was NOT RUN and name why, so gv_need can carry the reason to the verdict.
114const BG_B_OK: i64 = 0
115const BG_B_COMPILE_FAIL: i64 = 1 // the row's source does not compile/assemble -- a row FAIL
116const BG_B_SRC_NOT_FOUND: i64 = 4 // runtime/<mod>.nx absent under both roots -- a row FAIL (the claim set names a source that is gone)
117const BG_B_NO_ARTIFACT: i64 = 5 // the builder exited 0 and named no artifact -- fail loud, never run a fossil
118const BG_B_BUILDER_OTHER: i64 = 10 // any other builder exit (usage etc.) -- a row FAIL with the builder's own last line as the reason
119const BG_B_ADMIT_REFUSED: i64 = 6 // nx_build_admit said no: a fact about the BOX -> NOT RUN
120const BG_B_CANON_REFUSED: i64 = 7 // tree-canon divergence: compiling would prove a FORKED copy -> NOT RUN
121const BG_B_BUILD_TIMEOUT: i64 = 8 // the builder's own deadline fired -> NOT RUN
122const BG_B_BUILDER_EXEC: i64 = 9 // the builder could not be exec'd or died by signal -> NOT RUN
123
124// The builder's exit codes, mirrored from nx_sov_build_run.nx (SBR_USAGE .. SBR_BUILD_TIMEOUT).
125// That file carries a main() and cannot be imported; the names are kept identical to its SBR_* consts
126// so a grep for either spelling finds both, and its comment block is the source of truth.
127const BG_SBR_USAGE: i64 = 2
128const BG_SBR_COMPILE_FAIL: i64 = 3
129const BG_SBR_ASM_FAIL: i64 = 4
130const BG_SBR_ADMIT_REFUSED: i64 = 6
131const BG_SBR_CANON_REFUSED: i64 = 7
132const BG_SBR_BUILD_TIMEOUT: i64 = 8
133
134func bg_puts(s: *u8) -> i64 {
135 var n: i64 = 0
136 while s[n] != (0 as u8) { n = n + 1 }
137 sys_write(1, s, n)
138 return 0
139}
140
141func bg_fputs(fd: i64, s: *u8) -> i64 {
142 var n: i64 = 0
143 while s[n] != (0 as u8) { n = n + 1 }
144 sys_write(fd, s, n)
145 return 0
146}
147
148func bg_fputn(fd: i64, v: i64) -> i64 {
149 let bb: *u8 = sys_mmap(BG_NUM_CAP)
150 var m: i64 = v
151 if m < 0 { m = 0 - m; sys_write(fd, "-" as *u8, 1) }
152 let t: *u8 = sys_mmap(BG_NUM_CAP)
153 var k: i64 = 0
154 if m == 0 { t[0] = 48 as u8; k = 1 }
155 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
156 var i: i64 = 0
157 while i < k { bb[i] = t[k - 1 - i]; i = i + 1 }
158 sys_write(fd, bb, k)
159 return 0
160}
161
162func bg_putn(v: i64) -> i64 { bg_fputn(1, v); return 0 }
163
164func bg_cat(dst: *u8, off: i64, s: *u8) -> i64 {
165 var i: i64 = 0
166 while s[i] != (0 as u8) { dst[off + i] = s[i]; i = i + 1 }
167 return off + i
168}
169
170// fork + redirects + execve; parent waits; returns RAW wait status.
171func bg_run(path: *u8, argv: *i64, envp: *i64, redir_out: i64, redir_err: i64) -> i64 {
172 let pid: i64 = sys_fork()
173 if pid == 0 {
174 if redir_out >= 0 { sys_dup3(redir_out, 1, 0) }
175 if redir_err >= 0 { sys_dup3(redir_err, 2, 0) }
176 sys_execve(path, argv, envp)
177 sys_exit(BG_EXEC_FAIL)
178 }
179 let st: *i64 = sys_mmap(16) as *i64
180 sys_wait4(pid, st, 0)
181 return st[0]
182}
183
184func bg_exists(path: *u8) -> i64 {
185 let fd: i64 = sys_openat_rd(path)
186 if fd < 0 { return 0 }
187 sys_close(fd)
188 return 1
189}
190
191// ---- HOST-RESOLVED ROOTS --------------------------------------------------------------------
192// MEASURED 2026-08-27: _offc/, data/ and knowledge/ resolve at the SERVING root (this gate's own log
193// lands there), while runtime/ exists ONLY under buildroot/ -- and /api/gate_run launches a gate with
194// CWD=buildroot while nx_organ_ship and the roster launch it from the serving root. No single CWD
195// satisfies the path set, so every root-relative path is RESOLVED BY PROBING both vantages. Fixing one
196// vantage would be half a fix, and the missing half is the one the other launcher exercises.
197
198// The estate's builder, probed at the runners' twin (_offc/) first, then the serving-root copy, at both
199// vantages. 0 means the caller must SKIP -- a gate that cannot build its rows has no evidence to give.
200func bg_sov_resolve() -> *u8 {
201 let p0: *u8 = "_offc/nx_sov_build_run.elf\x00"
202 if bg_exists(p0) == 1 { return p0 }
203 let p1: *u8 = "../_offc/nx_sov_build_run.elf\x00"
204 if bg_exists(p1) == 1 { return p1 }
205 let p2: *u8 = "nx_sov_build_run.elf\x00"
206 if bg_exists(p2) == 1 { return p2 }
207 let p3: *u8 = "../nx_sov_build_run.elf\x00"
208 if bg_exists(p3) == 1 { return p3 }
209 return 0 as *u8
210}
211
212// Resolve <root>/runtime/<mod>.nx against the roots this host has. 1 = found.
213func bg_src_resolve(mod: *u8, out: *u8) -> i64 {
214 var o: i64 = 0
215 o = bg_cat(out, o, "buildroot/runtime/\x00" as *u8)
216 o = bg_cat(out, o, mod)
217 o = bg_cat(out, o, ".nx\x00" as *u8)
218 out[o] = 0 as u8
219 if bg_exists(out) == 1 { return 1 }
220 o = 0
221 o = bg_cat(out, o, "runtime/\x00" as *u8)
222 o = bg_cat(out, o, mod)
223 o = bg_cat(out, o, ".nx\x00" as *u8)
224 out[o] = 0 as u8
225 if bg_exists(out) == 1 { return 1 }
226 return 0
227}
228
229// Where the builder leaves the artifact. nx_sov_build_run anchors ITS OWN CWD to buildroot/ whatever
230// the caller's is and writes _build/<mod>.sov.elf there, so from the serving root that is
231// buildroot/_build/<mod>.sov.elf and from buildroot/ it is _build/<mod>.sov.elf. Write the path for
232// candidate `which` (0 or 1) into out.
233func bg_artifact_path(mod: *u8, which: i64, out: *u8) -> i64 {
234 var o: i64 = 0
235 if which == 0 { o = bg_cat(out, o, "buildroot/_build/\x00" as *u8) }
236 if which == 1 { o = bg_cat(out, o, "_build/\x00" as *u8) }
237 o = bg_cat(out, o, mod)
238 o = bg_cat(out, o, ".sov.elf\x00" as *u8)
239 out[o] = 0 as u8
240 return o
241}
242
243func bg_artifact_resolve(mod: *u8, out: *u8) -> i64 {
244 bg_artifact_path(mod, 0, out)
245 if bg_exists(out) == 1 { return 1 }
246 bg_artifact_path(mod, 1, out)
247 if bg_exists(out) == 1 { return 1 }
248 return 0
249}
250
251// A stale artifact from an earlier build must never be what a row RUNS: the builder unlinks its own
252// before compiling, and this gate unlinks both candidates too, so the invariant is local to this file
253// -- THE ARTIFACT EXISTS IFF THIS BUILD SUCCEEDED -- and does not depend on which builder ran.
254func bg_artifact_unlink(mod: *u8) -> i64 {
255 let p: *u8 = sys_mmap(BG_PATH_CAP)
256 bg_artifact_path(mod, 0, p)
257 sys_unlinkat(p)
258 bg_artifact_path(mod, 1, p)
259 sys_unlinkat(p)
260 return 0
261}
262
263// Last non-empty line of a file, tabs/CRs spaced, NUL-terminated into out (cap bytes). Returns its
264// length, 0 when the file is absent or empty. THE REASON TRAVELS WITH THE COUNT: a row that failed to
265// build carries the builder's own last line, so a reader does not have to re-run the build to learn why.
266func bg_last_line(path: *u8, out: *u8, cap: i64) -> i64 {
267 out[0] = 0 as u8
268 let lenp: *i64 = sys_mmap(16) as *i64
269 let b: *u8 = sys_read_file(path, lenp)
270 if (b as i64) == 0 { return 0 }
271 let n: i64 = lenp[0]
272 if n <= 0 { return 0 }
273 var e: i64 = n
274 var fend: i64 = 0
275 while fend == 0 {
276 if e <= 0 { fend = 1 } else {
277 if b[e - 1] == (BG_LF as u8) { e = e - 1 } else {
278 if b[e - 1] == (BG_CR as u8) { e = e - 1 } else { fend = 1 }
279 }
280 }
281 }
282 var s: i64 = e
283 var fstart: i64 = 0
284 while fstart == 0 {
285 if s <= 0 { fstart = 1 } else {
286 if b[s - 1] == (BG_LF as u8) { fstart = 1 } else { s = s - 1 }
287 }
288 }
289 var k: i64 = 0
290 while s < e {
291 if k < cap - 1 {
292 var c: u8 = b[s]
293 if c == (BG_TAB as u8) { c = BG_SPACE as u8 }
294 if c == (BG_CR as u8) { c = BG_SPACE as u8 }
295 out[k] = c
296 k = k + 1
297 }
298 s = s + 1
299 }
300 out[k] = 0 as u8
301 sys_free_file(b, n)
302 return k
303}
304
305// Build <mod> through the sovereign lane. On BG_B_OK `art` holds the artifact path to run.
306// The builder's stdout+stderr go to `logpath` (truncated per row) so its last line can be carried.
307func bg_build(mod: *u8, sov: *u8, envp: *i64, logpath: *u8, art: *u8) -> i64 {
308 let src: *u8 = sys_mmap(BG_PATH_CAP)
309 if bg_src_resolve(mod, src) == 0 { return BG_B_SRC_NOT_FOUND }
310 bg_artifact_unlink(mod)
311 let lfd: i64 = sys_openat_wr(logpath, BG_MODE_644)
312 let a: *i64 = sys_mmap(8 * BG_ARGV_SLOTS) as *i64
313 a[0] = sov as i64
314 a[1] = mod as i64
315 a[2] = "--build-only\x00" as *u8 as i64
316 a[3] = 0
317 let raw: i64 = bg_run(sov, a, envp, lfd, lfd)
318 if lfd >= 0 { sys_close(lfd) }
319 let ec: i64 = (raw >> BG_EXIT_SHIFT) & BG_EXIT_MASK
320 let sg: i64 = raw & BG_SIG_MASK
321 if sg != 0 { return BG_B_BUILDER_EXEC }
322 if ec == BG_EXEC_FAIL { return BG_B_BUILDER_EXEC }
323 if ec == BG_SBR_ADMIT_REFUSED { return BG_B_ADMIT_REFUSED }
324 if ec == BG_SBR_CANON_REFUSED { return BG_B_CANON_REFUSED }
325 if ec == BG_SBR_BUILD_TIMEOUT { return BG_B_BUILD_TIMEOUT }
326 if ec == BG_SBR_COMPILE_FAIL { return BG_B_COMPILE_FAIL }
327 if ec == BG_SBR_ASM_FAIL { return BG_B_COMPILE_FAIL }
328 if ec != 0 { return BG_B_BUILDER_OTHER }
329 if bg_artifact_resolve(mod, art) == 0 { return BG_B_NO_ARTIFACT }
330 return BG_B_OK
331}
332
333// Emit one row verdict to stdout + durable log. Fields 1..5 are the shape every reader of
334// browser_gate.log already indexes; verdict gained the word NOT-RUN and `reason=` is ADDITIVE (only
335// printed when non-empty), rule 19.
336func bg_report(logfd: i64, name: *u8, build_rc: i64, exit_code: i64, sig: i64, word: *u8, reason: *u8) -> i64 {
337 var fdi: i64 = 0
338 while fdi < 2 {
339 var fd: i64 = 1
340 if fdi == 1 { fd = logfd }
341 if fd > 0 {
342 bg_fputs(fd, "BROWSER-GATE row=\x00" as *u8)
343 bg_fputs(fd, name)
344 bg_fputs(fd, " build=\x00" as *u8)
345 bg_fputn(fd, build_rc)
346 bg_fputs(fd, " exit=\x00" as *u8)
347 bg_fputn(fd, exit_code)
348 bg_fputs(fd, " sig=\x00" as *u8)
349 bg_fputn(fd, sig)
350 bg_fputs(fd, " verdict=\x00" as *u8)
351 bg_fputs(fd, word)
352 if reason[0] != (0 as u8) {
353 bg_fputs(fd, " reason=\x00" as *u8)
354 bg_fputs(fd, reason)
355 }
356 bg_fputs(fd, "\n\x00" as *u8)
357 }
358 fdi = fdi + 1
359 }
360 return 0
361}
362
363// Stage the real Mozilla CA bundle from the DURABLE estate copy into /tmp.
364// tmpfs dies on every reboot (proven 2026-06-10: crash wiped it and turned
365// own-site-live + render-own RED); the estate copy does not -- Archivist rule.
366//
367// THE BUNDLE LIVES AT data/mozilla_certdata.txt, RELATIVE TO THE SERVING ROOT (2026-08-27). That is the
368// path forty-odd organs in this estate open it at (nx_browse, nx_crawl_web, nx_page_verify, the tls12
369// gates, nx_cleanview with the absolute fallback spelled out). This gate asked for
370// knowledge/library/mozilla_certdata.txt, which is ABSENT-PROVEN on the NAS -- so its live-TLS rows
371// could never have validated a chain here, and the stage line printed bytes=-1 on every run. Probed
372// at both vantages (see the roots note above); the chosen source path is announced.
373//
374// ALWAYS restage (idempotent, rule 10): the old "size>1MB -> skip" check
375// refused to refresh a STALE-but-large /tmp bundle, so once a rotted bundle
376// sat in /tmp the live-TLS rows rotted with it -- this was the entire
377// "TLS-FIN bedrock regression" (2026-06-13: stale /tmp bundle drove
378// recv_hs_probe exit=44 + ch_compat exit=144 while example.com/github.com
379// validated fine against the fresh durable bundle). /tmp now ends byte-equal
380// to the estate copy every run; the reported byte count also makes freshness
381// self-evident in the log instead of a silent "bytes=0".
382// Returns bytes staged, negative on missing/unwritable target. `used` receives the source path.
383func bg_stage_certdata(used: *u8) -> i64 {
384 var srcpath: *u8 = "data/mozilla_certdata.txt\x00" as *u8
385 var src: i64 = sys_openat_rd(srcpath)
386 if src < 0 {
387 srcpath = "../data/mozilla_certdata.txt\x00" as *u8
388 src = sys_openat_rd(srcpath)
389 }
390 var uo: i64 = bg_cat(used, 0, srcpath)
391 used[uo] = 0 as u8
392 if src < 0 { return 0 - 1 }
393 let dst: i64 = sys_openat_wr("/tmp/mozilla_certdata.txt\x00" as *u8, BG_MODE_644)
394 if dst < 0 { sys_close(src); return 0 - 2 }
395 let buf: *u8 = sys_mmap(BG_COPY_CHUNK)
396 var total: i64 = 0
397 var n: i64 = sys_read(src, buf, BG_COPY_CHUNK)
398 while n > 0 {
399 sys_write(dst, buf, n)
400 total = total + n
401 n = sys_read(src, buf, BG_COPY_CHUNK)
402 }
403 sys_close(src)
404 sys_close(dst)
405 return total
406}
407
408// THE BROWSER ARTIFACT TO JUDGE. argv[1] when given (the caller says which build); otherwise the
409// PROMOTED binary, probed at both vantages. Writes the path it settled on into out; 1 = it exists.
410func bg_subject_resolve(argc: i64, argv: *i64, out: *u8) -> i64 {
411 var o: i64 = 0
412 if argc >= 2 {
413 o = bg_cat(out, 0, argv[1] as *u8)
414 out[o] = 0 as u8
415 return bg_exists(out)
416 }
417 o = bg_cat(out, 0, "nx_browser.elf\x00" as *u8)
418 out[o] = 0 as u8
419 if bg_exists(out) == 1 { return 1 }
420 o = bg_cat(out, 0, "../nx_browser.elf\x00" as *u8)
421 out[o] = 0 as u8
422 if bg_exists(out) == 1 { return 1 }
423 return 0
424}
425
426// The shot fixture, ASSEMBLED AT RUN TIME (a detector that scans source finds its own fixtures):
427// a heading, a paragraph with an inline link, and a two-cell table -- block, inline and table layout
428// in one page, so a browser that has lost any of the three cannot lay it out into real boxes.
429// Returns bytes written, negative when /tmp is unwritable.
430func bg_write_shot_fixture(path: *u8) -> i64 {
431 let fd: i64 = sys_openat_wr(path, BG_MODE_644)
432 if fd < 0 { return 0 - 1 }
433 let body: *u8 = "<!doctype html><html><head><title>bg shot</title><style>body{font-family:sans-serif} td{padding:4px}</style></head><body><h1>Browser gate shot fixture</h1><p>One paragraph with an <a href=/x>inline link</a> and enough words to wrap at a narrow viewport width when laid out.</p><table><tr><td>cell one</td><td>cell two</td></tr></table></body></html>\n\x00"
434 var n: i64 = 0
435 while body[n] != (0 as u8) { n = n + 1 }
436 let w: i64 = sys_write(fd, body, n)
437 sys_close(fd)
438 return w
439}
440
441// The durable log, at whichever vantage holds knowledge/status/.
442func bg_open_log() -> i64 {
443 var fd: i64 = sys_openat_append("knowledge/status/browser_gate.log\x00" as *u8, BG_MODE_644)
444 if fd < 0 { fd = sys_openat_append("../knowledge/status/browser_gate.log\x00" as *u8, BG_MODE_644) }
445 return fd
446}
447
448func main(argc: i64, argv: *i64) -> i64 {
449 let names: *i64 = sys_mmap(8 * (BG_ROWS + 1)) as *i64
450 names[0] = "_arg7_minrepro\x00" as *u8 as i64
451 names[1] = "nx_tls13_kdf_test\x00" as *u8 as i64
452 names[2] = "nx_tls13_schedule_test\x00" as *u8 as i64
453 names[3] = "nx_jpeg_ascii_test\x00" as *u8 as i64
454 names[4] = "nx_browser_page_demo_test\x00" as *u8 as i64
455 names[5] = "nx_browser_own_site_live_test\x00" as *u8 as i64
456 names[6] = "nx_browser_render_own_test\x00" as *u8 as i64
457 names[7] = "nx_tls13_recv_hs_probe_test\x00" as *u8 as i64
458 names[8] = "nx_p256_keyshare_test\x00" as *u8 as i64
459 names[9] = "nx_tls13_ch_compat_test\x00" as *u8 as i64
460 names[10] = "_derefcast_minrepro\x00" as *u8 as i64
461 names[11] = "nx_tls13_p256_loopback_test\x00" as *u8 as i64
462 names[12] = "nx_browser:shot\x00" as *u8 as i64
463 // Which rows need the staged CA bundle: own-site-live, recv-hs-probe and ch-compat open
464 // /tmp/mozilla_certdata.txt directly (measured: 2 references each in their source), and render-own
465 // consumes the /tmp/nishi_own.html that own-site-live writes, so it needs it transitively.
466 // A row that needs an absent fixture is NOT RUN with the fixture named, never run-and-convicted.
467 let needs_ca: *i64 = sys_mmap(8 * (BG_ROWS + 1)) as *i64
468 var zi: i64 = 0
469 while zi < BG_ROWS { needs_ca[zi] = 0; zi = zi + 1 }
470 needs_ca[5] = 1
471 needs_ca[6] = 1
472 needs_ca[7] = 1
473 needs_ca[9] = 1
474
475 let envp: *i64 = sys_mmap(8 * 4) as *i64
476 envp[0] = "PATH=/usr/bin:/bin\x00" as *u8 as i64
477 envp[1] = 0
478 let devnull: i64 = sys_openat_wr("/dev/null\x00" as *u8, BG_MODE_644)
479 let logfd: i64 = bg_open_log()
480 let buildlog: *u8 = "/tmp/bg_row_build.log\x00"
481
482 if logfd > 0 {
483 bg_fputs(logfd, "BROWSER-GATE epoch=\x00" as *u8)
484 bg_fputn(logfd, sys_now_realtime_sec())
485 bg_fputs(logfd, " run-start\n\x00" as *u8)
486 }
487
488 let cdsrc: *u8 = sys_mmap(BG_PATH_CAP)
489 let staged: i64 = bg_stage_certdata(cdsrc)
490 var fdi: i64 = 0
491 while fdi < 2 {
492 var fd: i64 = 1
493 if fdi == 1 { fd = logfd }
494 if fd > 0 {
495 bg_fputs(fd, "BROWSER-GATE stage=certdata src=\x00" as *u8)
496 bg_fputs(fd, cdsrc)
497 bg_fputs(fd, " bytes=\x00" as *u8)
498 bg_fputn(fd, staged)
499 bg_fputs(fd, "\n\x00" as *u8)
500 }
501 fdi = fdi + 1
502 }
503
504 // ---- PRECONDITIONS BEFORE ROWS ---------------------------------------------------------------
505 // The counters are created HERE rather than at the end, so gv_need can record a missing
506 // precondition and gv_verdict can actually REACH its SKIP branch. The base class's ordering is
507 // untouched and this can only ever un-bless: a real row failure still escalates SKIP -> RED, so an
508 // absent precondition can never amnesty a defect. (The D001 migration had left a fresh gv_ctr() at
509 // the END, which silently discarded ctr[2] and sent a toolchain-less run back out as RED.)
510 let ctr: *i64 = gv_ctr()
511 let sov: *u8 = bg_sov_resolve()
512 var sov_ok: i64 = 0
513 if (sov as i64) != 0 { sov_ok = 1 }
514 gv_need("the sovereign builder nx_sov_build_run.elf (probed at _offc/ and ../_offc/, then the serving root and ../) -- without it no row can be built here" as *u8, sov_ok, ctr)
515 var cd_ok: i64 = 0
516 if staged > 0 { cd_ok = 1 }
517 gv_need("data/mozilla_certdata.txt (the estate's CA bundle, serving root or ../) -- own-site-live, render-own, recv-hs-probe and ch-compat validate a live chain against it" as *u8, cd_ok, ctr)
518 if sov_ok == 1 {
519 bg_puts("BROWSER-GATE builder=\x00" as *u8)
520 bg_puts(sov)
521 bg_puts("\n\x00" as *u8)
522 }
523 let subj: *u8 = sys_mmap(BG_PATH_CAP)
524 let subj_ok: i64 = bg_subject_resolve(argc, argv, subj)
525 gv_need("a browser artifact to judge (argv[1], else the promoted nx_browser.elf at ./ or ../) -- the browser-shot row forks it" as *u8, subj_ok, ctr)
526 bg_puts("BROWSER-GATE subject=\x00" as *u8)
527 bg_puts(subj)
528 bg_puts(" present=\x00" as *u8)
529 bg_putn(subj_ok)
530 bg_puts("\n\x00" as *u8)
531 let shot_fixture: *u8 = "/tmp/bg_shot_fixture.html\x00"
532
533 // WITH NO BUILDER THE BUILD ROWS ARE NOT RUN AT ALL: running them would fork a missing binary
534 // twelve times and report twelve rows about a browser nothing examined. The shot row needs no
535 // builder and still runs.
536 var nbuild: i64 = BG_BUILD_ROWS
537 if sov_ok == 0 { nbuild = 0 }
538
539 var run: i64 = 0
540 var passed: i64 = 0
541 var nr_ca: i64 = 0
542 var nr_admit: i64 = 0
543 var nr_canon: i64 = 0
544 var nr_timeout: i64 = 0
545 var nr_exec: i64 = 0
546 var nr_subject: i64 = 0
547 var nr_builder: i64 = 0
548 let art: *u8 = sys_mmap(BG_PATH_CAP)
549 let reason: *u8 = sys_mmap(BG_LINE_CAP)
550 var ri: i64 = 0
551 while ri < BG_ROWS {
552 let name: *u8 = names[ri] as *u8
553 reason[0] = 0 as u8
554 var exit_code: i64 = 0 - 1
555 var sig: i64 = 0 - 1
556 var brc: i64 = 0 - 1
557 var word: *u8 = "FAIL" as *u8
558 var attempted: i64 = 1
559 if ri < BG_BUILD_ROWS { if ri >= nbuild {
560 attempted = 0
561 nr_builder = nr_builder + 1
562 word = "NOT-RUN" as *u8
563 bg_cat(reason, 0, "no sovereign builder on this host\x00" as *u8)
564 } }
565 if needs_ca[ri] == 1 { if cd_ok == 0 { if attempted == 1 {
566 attempted = 0
567 nr_ca = nr_ca + 1
568 word = "NOT-RUN" as *u8
569 bg_cat(reason, 0, "needs the staged CA bundle\x00" as *u8)
570 } } }
571 if ri == BG_SHOT_ROW { if attempted == 1 {
572 attempted = 0
573 brc = BG_B_OK
574 if subj_ok == 0 {
575 nr_subject = nr_subject + 1
576 word = "NOT-RUN" as *u8
577 bg_cat(reason, 0, "no browser artifact to judge\x00" as *u8)
578 } else {
579 let fw: i64 = bg_write_shot_fixture(shot_fixture)
580 if fw <= 0 {
581 nr_exec = nr_exec + 1
582 word = "NOT-RUN" as *u8
583 bg_cat(reason, 0, "could not write the shot fixture under /tmp\x00" as *u8)
584 } else {
585 run = run + 1
586 let sa: *i64 = sys_mmap(8 * BG_SHOT_ARGV_SLOTS) as *i64
587 sa[0] = subj as i64
588 sa[1] = "shot\x00" as *u8 as i64
589 sa[2] = shot_fixture as i64
590 sa[3] = "-\x00" as *u8 as i64
591 sa[4] = 0
592 let sraw: i64 = bg_run(subj, sa, envp, devnull, devnull)
593 exit_code = (sraw >> BG_EXIT_SHIFT) & BG_EXIT_MASK
594 sig = sraw & BG_SIG_MASK
595 if exit_code == 0 { if sig == 0 { word = "PASS" as *u8; passed = passed + 1 } }
596 }
597 }
598 } }
599 if attempted == 1 {
600 brc = bg_build(name, sov, envp, buildlog, art)
601 if brc != BG_B_OK { bg_last_line(buildlog, reason, BG_LINE_CAP) }
602 var not_run: i64 = 0
603 if brc == BG_B_ADMIT_REFUSED { not_run = 1; nr_admit = nr_admit + 1 }
604 if brc == BG_B_CANON_REFUSED { not_run = 1; nr_canon = nr_canon + 1 }
605 if brc == BG_B_BUILD_TIMEOUT { not_run = 1; nr_timeout = nr_timeout + 1 }
606 if brc == BG_B_BUILDER_EXEC { not_run = 1; nr_exec = nr_exec + 1 }
607 if not_run == 1 { word = "NOT-RUN" as *u8 }
608 if not_run == 0 {
609 run = run + 1
610 if brc == BG_B_OK {
611 let a: *i64 = sys_mmap(8 * 3) as *i64
612 a[0] = art as i64
613 a[1] = 0
614 let raw: i64 = bg_run(art, a, envp, devnull, devnull)
615 exit_code = (raw >> BG_EXIT_SHIFT) & BG_EXIT_MASK
616 sig = raw & BG_SIG_MASK
617 if exit_code == 0 { if sig == 0 { word = "PASS" as *u8; passed = passed + 1 } }
618 }
619 }
620 }
621 bg_report(logfd, name, brc, exit_code, sig, word, reason)
622 ri = ri + 1
623 }
624
625 // Every NOT-RUN class is a precondition of its own, so the verdict line NAMES which one kept a row
626 // from running instead of folding them into one count with opposite remedies.
627 var admit_ok: i64 = 1
628 if nr_admit > 0 { admit_ok = 0 }
629 gv_need("build admission GRANTED for every row (nx_build_admit, consulted by the builder) -- a refusal is a fact about the box: re-run when it clears, never spin" as *u8, admit_ok, ctr)
630 var canon_ok: i64 = 1
631 if nr_canon > 0 { canon_ok = 0 }
632 gv_need("tree canon converged for every row -- the builder refuses to compile a forked copy" as *u8, canon_ok, ctr)
633 var tmo_ok: i64 = 1
634 if nr_timeout > 0 { tmo_ok = 0 }
635 gv_need("the builder finished every row inside its own build deadline" as *u8, tmo_ok, ctr)
636 var exec_ok: i64 = 1
637 if nr_exec > 0 { exec_ok = 0 }
638 gv_need("the builder exec'd cleanly for every row (no exec failure, no signal death)" as *u8, exec_ok, ctr)
639
640 // THE PARTITION IS PRINTED AND MUST SUM: run + not_run == of_declared, with not_run split by cause.
641 // permil is taken over the rows ACTUALLY RUN and is -1 when none were -- dividing by the DECLARED
642 // BG_ROWS is what once made a skipped run print "rows=12 passed=0 permil=0 verdict=RED", a fully
643 // populated failure report about twelve tests that never executed. The verdict word stays in the
644 // base class's vocabulary (GREEN/RED/SKIP) and is derived from the SAME conditions gv_verdict
645 // applies, so this line and the verdict line cannot disagree.
646 let not_run: i64 = BG_ROWS - run
647 var permil: i64 = 0 - 1
648 if run > 0 { permil = (passed * 1000) / run }
649 var word: *u8 = "GREEN" as *u8
650 if passed < run { word = "RED" as *u8 } else { if ctr[2] > 0 { word = "SKIP" as *u8 } }
651 fdi = 0
652 while fdi < 2 {
653 var fd: i64 = 1
654 if fdi == 1 { fd = logfd }
655 if fd > 0 {
656 bg_fputs(fd, "BROWSER-GATE rows=\x00" as *u8)
657 bg_fputn(fd, run)
658 bg_fputs(fd, " of_declared=\x00" as *u8)
659 bg_fputn(fd, BG_ROWS)
660 bg_fputs(fd, " passed=\x00" as *u8)
661 bg_fputn(fd, passed)
662 bg_fputs(fd, " not_run=\x00" as *u8)
663 bg_fputn(fd, not_run)
664 bg_fputs(fd, " (ca=\x00" as *u8)
665 bg_fputn(fd, nr_ca)
666 bg_fputs(fd, " admit=\x00" as *u8)
667 bg_fputn(fd, nr_admit)
668 bg_fputs(fd, " canon=\x00" as *u8)
669 bg_fputn(fd, nr_canon)
670 bg_fputs(fd, " timeout=\x00" as *u8)
671 bg_fputn(fd, nr_timeout)
672 bg_fputs(fd, " exec=\x00" as *u8)
673 bg_fputn(fd, nr_exec)
674 bg_fputs(fd, " subject=\x00" as *u8)
675 bg_fputn(fd, nr_subject)
676 bg_fputs(fd, " builder_absent=\x00" as *u8)
677 bg_fputn(fd, nr_builder)
678 bg_fputs(fd, "; run+not_run==of_declared) permil=\x00" as *u8)
679 bg_fputn(fd, permil)
680 bg_fputs(fd, " verdict=\x00" as *u8)
681 bg_fputs(fd, word)
682 bg_fputs(fd, "\n\x00" as *u8)
683 }
684 fdi = fdi + 1
685 }
686 if logfd > 0 { sys_close(logfd) }
687 // The ONE shared base class emits the verdict; the counters are the ones created BEFORE the rows,
688 // so every gv_need above survives to here and the third state is reachable.
689 ctr[0] = passed
690 ctr[1] = run
691 let rc__dry: i64 = gv_verdict("BROWSER-GATE" as *u8, ctr, "every declared row built through the sovereign lane and exited 0" as *u8)
692 sys_exit(rc__dry)
693 return rc__dry
694}