nx_browser_gate.nx source
↩ module page · 285 lines · 11357 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 compiles it with the NATIVE
6// browser-lane compiler (_offc/nx_compile_x86_native.elf), assembles
7// + links, runs the ELF, and judges via the RAW wait4 status (the
8// sovereign exit-judging law -- never $? through a shell).
9//
10// Rows (the browser claim set, 2026-06-10):
11// arg7-tailcall compiler regression gate: >6-arg tail-call drop
12// (the defect that zeroed every TLS Derive-Secret)
13// tls-kdf RFC 8448 Derive-Secret KATs
14// tls-schedule TLS 1.3 key-schedule KATs
15// jpeg-ascii deterministic synthetic-JPEG decode + raster
16// page-demo live HTTP fetch + html_to_text (example.com:80)
17// own-site-live LIVE sovereign HTTPS fetch of nishifamily.com
18// (TLS 1.3 + real Mozilla CA chain validation)
19// render-own paints the FETCHED page's text -> framebuffer
20// (consumes own-site-live's /tmp/nishi_own.html)
21// recv-hs-probe B1 rung: full encrypted-handshake walk vs
22// example.com (per-record/per-message markers,
23// cert-pipeline verdict + chain sig-alg census);
24// exit 0 = handshake to WAIT_CLIENT_FIN
25// p256-keyshare B4 rung: P-256 ECDH (RFC 5903 KAT both
26// directions, boundary rejects) + dual key_share
27// wire format (x25519 + secp256r1 in one CH)
28// derefcast compiler regression gate: `*p as T` precedence
29// (pre-fix: cast bound inside the deref -> byte-
30// load of the pointer cell -> SIGSEGV; fixed in
31// parse_unary 2026-06-10)
32// p256-loopback B4 server side: OUR server completes a P-256-only
33// client (no HRR), both roles derive byte-identical
34// traffic secrets, off-curve share rejected (the
35// nishifamily P-256 gap, healed + loopback-proven)
36// ch-compat B2 rung, two live legs: httpbin.org census
37// (TLS-1.2-only signature classified -- the CH
38// was never the defect; auto-escalates to a full
39// handshake if they enable 1.3) + github.com
40// P-256-only CH walked to verified server
41// Finished (GR=23: live proof of the B4 path)
42//
43// Verdict lines stream to stdout AND append to the durable log
44// knowledge/status/browser_gate.log (Archivist durability rule --
45// /tmp dies on reboot, knowledge/ does not).
46//
47// Exit 0 iff every row passes. ORCHESTRATION is pure NishiLang
48// fork/dup3/execve/wait4 -- NO shell, NO .sh.
49//
50// license_tier: ORIGINAL
51
52import "nx_syscalls.nx"
53import "nx_gate_verdict.nx"
54
55const BG_ROWS: i64 = 12
56
57func bg_puts(s: *u8) -> i64 {
58 var n: i64 = 0
59 while s[n] != (0 as u8) { n = n + 1 }
60 sys_write(1, s, n)
61 return 0
62}
63
64func bg_fputs(fd: i64, s: *u8) -> i64 {
65 var n: i64 = 0
66 while s[n] != (0 as u8) { n = n + 1 }
67 sys_write(fd, s, n)
68 return 0
69}
70
71func bg_fputn(fd: i64, v: i64) -> i64 {
72 let bb: *u8 = sys_mmap(28)
73 var m: i64 = v
74 if m < 0 { m = 0 - m; sys_write(fd, "-" as *u8, 1) }
75 let t: *u8 = sys_mmap(28)
76 var k: i64 = 0
77 if m == 0 { t[0] = 48 as u8; k = 1 }
78 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
79 var i: i64 = 0
80 while i < k { bb[i] = t[k - 1 - i]; i = i + 1 }
81 sys_write(fd, bb, k)
82 return 0
83}
84
85func bg_putn(v: i64) -> i64 { bg_fputn(1, v); return 0 }
86
87func bg_cat(dst: *u8, off: i64, s: *u8) -> i64 {
88 var i: i64 = 0
89 while s[i] != (0 as u8) { dst[off + i] = s[i]; i = i + 1 }
90 return off + i
91}
92
93// fork + redirects + execve; parent waits; returns RAW wait status.
94func bg_run(path: *u8, argv: *i64, envp: *i64, redir_out: i64, redir_err: i64) -> i64 {
95 let pid: i64 = sys_fork()
96 if pid == 0 {
97 if redir_out >= 0 { sys_dup3(redir_out, 1, 0) }
98 if redir_err >= 0 { sys_dup3(redir_err, 2, 0) }
99 sys_execve(path, argv, envp)
100 sys_exit(127)
101 }
102 let st: *i64 = sys_mmap(16) as *i64
103 sys_wait4(pid, st, 0)
104 return st[0]
105}
106
107// Compile runtime/<mod>.nx -> /tmp/bg_row.elf via native lane.
108// Returns 0 ok, 1 compile-fail, 2 as-fail, 3 ld-fail.
109func bg_build(mod: *u8, envp: *i64, devnull: i64) -> i64 {
110 let src: *u8 = sys_mmap(512)
111 var o: i64 = 0
112 o = bg_cat(src, o, "runtime/\x00" as *u8)
113 o = bg_cat(src, o, mod)
114 o = bg_cat(src, o, ".nx\x00" as *u8)
115 src[o] = 0 as u8
116
117 let cc: *u8 = "_offc/nx_compile_x86_native.elf\x00"
118 let a1: *i64 = sys_mmap(8 * 4) as *i64
119 a1[0] = cc as i64; a1[1] = src as i64; a1[2] = 0
120 let sfd: i64 = sys_openat_wr("/tmp/bg_row.s\x00", 0x1a4)
121 let st1: i64 = bg_run(cc, a1, envp, sfd, devnull)
122 sys_close(sfd)
123 if st1 != 0 { return 1 }
124
125 let asbin: *u8 = "/usr/bin/as\x00"
126 let a2: *i64 = sys_mmap(8 * 6) as *i64
127 a2[0] = asbin as i64
128 a2[1] = "/tmp/bg_row.s\x00" as *u8 as i64
129 a2[2] = "-o\x00" as *u8 as i64
130 a2[3] = "/tmp/bg_row.o\x00" as *u8 as i64
131 a2[4] = 0
132 if bg_run(asbin, a2, envp, 0 - 1, devnull) != 0 { return 2 }
133
134 let ldbin: *u8 = "/usr/bin/ld\x00"
135 let a3: *i64 = sys_mmap(8 * 6) as *i64
136 a3[0] = ldbin as i64
137 a3[1] = "/tmp/bg_row.o\x00" as *u8 as i64
138 a3[2] = "-o\x00" as *u8 as i64
139 a3[3] = "/tmp/bg_row.elf\x00" as *u8 as i64
140 a3[4] = 0
141 if bg_run(ldbin, a3, envp, 0 - 1, devnull) != 0 { return 3 }
142 return 0
143}
144
145// Emit one row verdict to stdout + durable log.
146func bg_report(logfd: i64, name: *u8, build_rc: i64, exit_code: i64, sig: i64, pass: i64) -> i64 {
147 var fdi: i64 = 0
148 while fdi < 2 {
149 var fd: i64 = 1
150 if fdi == 1 { fd = logfd }
151 if fd > 0 {
152 bg_fputs(fd, "BROWSER-GATE row=\x00" as *u8)
153 bg_fputs(fd, name)
154 bg_fputs(fd, " build=\x00" as *u8)
155 bg_fputn(fd, build_rc)
156 bg_fputs(fd, " exit=\x00" as *u8)
157 bg_fputn(fd, exit_code)
158 bg_fputs(fd, " sig=\x00" as *u8)
159 bg_fputn(fd, sig)
160 if pass == 1 { bg_fputs(fd, " verdict=PASS\n\x00" as *u8) }
161 if pass != 1 { bg_fputs(fd, " verdict=FAIL\n\x00" as *u8) }
162 }
163 fdi = fdi + 1
164 }
165 return 0
166}
167
168// Stage the real Mozilla CA bundle from the DURABLE repo copy into /tmp.
169// tmpfs dies on every reboot (proven 2026-06-10: crash wiped it and turned
170// own-site-live + render-own RED); knowledge/ does not -- Archivist rule.
171//
172// ALWAYS restage (idempotent, rule 10): the old "size>1MB -> skip" check
173// refused to refresh a STALE-but-large /tmp bundle, so once a rotted bundle
174// sat in /tmp the live-TLS rows rotted with it -- this was the entire
175// "TLS-FIN bedrock regression" (2026-06-13: stale /tmp bundle drove
176// recv_hs_probe exit=44 + ch_compat exit=144 while example.com/github.com
177// validated fine against the fresh durable bundle). /tmp now ends byte-equal
178// to knowledge/ every run; the reported byte count also makes freshness
179// self-evident in the log instead of a silent "bytes=0".
180// Returns bytes staged, negative on missing/unwritable target.
181func bg_stage_certdata() -> i64 {
182 let src: i64 = sys_openat_rd("knowledge/library/mozilla_certdata.txt\x00" as *u8)
183 if src < 0 { return 0 - 1 }
184 let dst: i64 = sys_openat_wr("/tmp/mozilla_certdata.txt\x00" as *u8, 0x1a4)
185 if dst < 0 { sys_close(src); return 0 - 2 }
186 let buf: *u8 = sys_mmap(65536)
187 var total: i64 = 0
188 var n: i64 = sys_read(src, buf, 65536)
189 while n > 0 {
190 sys_write(dst, buf, n)
191 total = total + n
192 n = sys_read(src, buf, 65536)
193 }
194 sys_close(src)
195 sys_close(dst)
196 return total
197}
198
199func main() -> i64 {
200 let names: *i64 = sys_mmap(8 * (BG_ROWS + 1)) as *i64
201 names[0] = "_arg7_minrepro\x00" as *u8 as i64
202 names[1] = "nx_tls13_kdf_test\x00" as *u8 as i64
203 names[2] = "nx_tls13_schedule_test\x00" as *u8 as i64
204 names[3] = "nx_jpeg_ascii_test\x00" as *u8 as i64
205 names[4] = "nx_browser_page_demo_test\x00" as *u8 as i64
206 names[5] = "nx_browser_own_site_live_test\x00" as *u8 as i64
207 names[6] = "nx_browser_render_own_test\x00" as *u8 as i64
208 names[7] = "nx_tls13_recv_hs_probe_test\x00" as *u8 as i64
209 names[8] = "nx_p256_keyshare_test\x00" as *u8 as i64
210 names[9] = "nx_tls13_ch_compat_test\x00" as *u8 as i64
211 names[10] = "_derefcast_minrepro\x00" as *u8 as i64
212 names[11] = "nx_tls13_p256_loopback_test\x00" as *u8 as i64
213
214 let envp: *i64 = sys_mmap(8 * 4) as *i64
215 envp[0] = "PATH=/usr/bin:/bin\x00" as *u8 as i64
216 envp[1] = 0
217 let devnull: i64 = sys_openat_wr("/dev/null\x00" as *u8, 0x1a4)
218 let logfd: i64 = sys_openat_append("knowledge/status/browser_gate.log\x00" as *u8, 0x1a4)
219
220 if logfd > 0 {
221 bg_fputs(logfd, "BROWSER-GATE epoch=\x00" as *u8)
222 bg_fputn(logfd, sys_now_realtime_sec())
223 bg_fputs(logfd, " run-start\n\x00" as *u8)
224 }
225
226 let staged: i64 = bg_stage_certdata()
227 bg_fputs(1, "BROWSER-GATE stage=certdata bytes=\x00" as *u8)
228 bg_putn(staged)
229 bg_fputs(1, "\n\x00" as *u8)
230 if logfd > 0 {
231 bg_fputs(logfd, "BROWSER-GATE stage=certdata bytes=\x00" as *u8)
232 bg_fputn(logfd, staged)
233 bg_fputs(logfd, "\n\x00" as *u8)
234 }
235
236 var passed: i64 = 0
237 var ri: i64 = 0
238 while ri < BG_ROWS {
239 let name: *u8 = names[ri] as *u8
240 let brc: i64 = bg_build(name, envp, devnull)
241 var exit_code: i64 = 0 - 1
242 var sig: i64 = 0 - 1
243 var pass: i64 = 0
244 if brc == 0 {
245 let elf: *u8 = "/tmp/bg_row.elf\x00"
246 let a: *i64 = sys_mmap(8 * 3) as *i64
247 a[0] = elf as i64; a[1] = 0
248 let raw: i64 = bg_run(elf, a, envp, devnull, devnull)
249 exit_code = (raw >> 8) & 0xff
250 sig = raw & 0x7f
251 if exit_code == 0 { if sig == 0 { pass = 1 } }
252 }
253 bg_report(logfd, name, brc, exit_code, sig, pass)
254 passed = passed + pass
255 ri = ri + 1
256 }
257
258 let permil: i64 = (passed * 1000) / BG_ROWS
259 var fdi: i64 = 0
260 while fdi < 2 {
261 var fd: i64 = 1
262 if fdi == 1 { fd = logfd }
263 if fd > 0 {
264 bg_fputs(fd, "BROWSER-GATE rows=\x00" as *u8)
265 bg_fputn(fd, BG_ROWS)
266 bg_fputs(fd, " passed=\x00" as *u8)
267 bg_fputn(fd, passed)
268 bg_fputs(fd, " permil=\x00" as *u8)
269 bg_fputn(fd, permil)
270 if passed == BG_ROWS { bg_fputs(fd, " verdict=GREEN\n\x00" as *u8) }
271 if passed != BG_ROWS { bg_fputs(fd, " verdict=RED\n\x00" as *u8) }
272 }
273 fdi = fdi + 1
274 }
275 if logfd > 0 { sys_close(logfd) }
276 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check
277 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled
278 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify.
279 let ctr__dry: *i64 = gv_ctr()
280 ctr__dry[0] = passed
281 ctr__dry[1] = BG_ROWS
282 let rc__dry: i64 = gv_verdict("BROWSER-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8)
283 sys_exit(rc__dry)
284 return rc__dry
285}