code wiki / _hdl_build / nx_cms_tls_gate.nx
nx_cms_tls_gate.nx source
↩ module page · 464 lines · 22168 B
1// nx_cms_tls_gate.nx -- CMS C8 GATE (re-runnable, evidence-driven): the CMS behind OUR TLS 1.3
2// front, proven with OUR TLS client doing FULL certificate validation. The gate generates a
3// self-signed Ed25519 cert sovereignly (nx_x509_build, the andelinwest daemon's cert shape), makes
4// it the client's ONLY trust anchor, boots the real admin (loopback) behind nx_cms_tls_front (TLS
5// terminator), and walks the admin flow over the encrypted wire: page serve, login (cookie must now
6// carry Secure), editor, edit-publish. NEGATIVE CONTROL: a client with an EMPTY trust store must
7// FAIL the handshake -- proves the validation is real, not pass-through. Appends
8// "CMSGATE row=nx_cms_tls ... verdict=PASS|FAIL". Exit 0 iff all rows pass. license_tier: ORIGINAL
9import "nx_syscalls.nx"
10import "nx_connect.nx" // bounded connect: a raw sys_connect hangs ~127s on a black-holed host
11import "nx_csprng.nx"
12import "nx_sha256.nx"
13import "nx_ed25519_signature.nx"
14import "hub/nx_x509_build.nx"
15import "nx_x509.nx"
16import "nx_x509_trust_store.nx"
17import "nx_tls13_client_validate_certificate.nx"
18import "nx_tls13_client_session_run.nx"
19import "nx_tls13_read_record_from_fd.nx"
20import "nx_cms_store.nx"
21import "_hdl_build/nx_kill_portable.nx"
22import "nx_gate_verdict.nx" // D001: inherit the shared verdict contract instead of hand-rolling it
23
24// PORT MOVED 8447 -> 19861 (2026-08-01). ★★8447 IS NOT FREE AND NEVER WAS: it is the LIVE, SUPERVISED
25// nx_translate_daemon (nx_hostctl HC_XLATE, watched by nx_keeper, on the status page, in nx_port_audit).
26// This gate squatted a PRODUCTION service's port from the day it was written, so its front could never
27// bind and every TLS row was red for a reason with nothing to do with TLS. ★A GATE THAT PICKS A PORT BY
28// TASTE INSTEAD OF BY REGISTRY EVENTUALLY PICKS ONE THAT IS ALREADY OWNED -- and then grades the collision
29// as a failure of its subject. 19861 verified unused across a full 21433-file corpus scan.
30const TG_FRONT_PORT: i64 = 19861
31const TG_BACK_PORT: i64 = 8086
32const TG_SITE: *u8 = "/tmp/_cms_tls_site"
33const TG_SNI: *u8 = "cms.gate.local"
34const TG_SNI_N: i64 = 14
35
36func tg_w(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
37func tg_len(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
38func tg_cat(dst: *u8, off: i64, s: *u8) -> i64 {
39 var o: i64 = off
40 var k: i64 = 0
41 while s[k] != (0 as u8) { dst[o] = s[k]; o = o + 1; k = k + 1 }
42 return o
43}
44func tg_catn(dst: *u8, off: i64, s: *u8, n: i64) -> i64 {
45 var o: i64 = off
46 var k: i64 = 0
47 while k < n { dst[o] = s[k]; o = o + 1; k = k + 1 }
48 return o
49}
50func tg_index(hay: *u8, n: i64, needle: *u8) -> i64 {
51 let nl: i64 = tg_len(needle)
52 var i: i64 = 0
53 while i + nl <= n {
54 var q: i64 = 0
55 var ok: i64 = 1
56 while q < nl { if (hay[i+q] as i64) != (needle[q] as i64) { ok = 0; q = nl } q = q + 1 }
57 if ok == 1 { return i }
58 i = i + 1
59 }
60 return 0 - 1
61}
62func tg_has(hay: *u8, n: i64, needle: *u8) -> i64 { if tg_index(hay, n, needle) >= 0 { return 1 } return 0 }
63
64func tg_row(id: i64, ok: i64, what: *u8) -> i64 {
65 tg_w("TLSROW " as *u8)
66 let d: *u8 = sys_mmap(8); d[0] = (48 + id/10) as u8; d[1] = (48 + id%10) as u8; d[2] = 32 as u8; d[3] = 0 as u8
67 tg_w(d)
68 if ok == 1 { tg_w("PASS " as *u8) }
69 if ok == 0 { tg_w("FAIL " as *u8) }
70 tg_w(what); tg_w("\n" as *u8)
71 return ok
72}
73
74func tg_mkdir(path: *u8, mode: i64) -> i64 {
75 let nb: *i64 = sys_mmap(8) as *i64
76 nb[0] = 258
77 return __syscall(nb[0], 0 - 100, path, mode, 0, 0, 0)
78}
79
80// build + run a module via the sovereign runner (blocking, output discarded)
81func tg_build(name: *u8) -> i64 {
82 let pid: i64 = sys_fork()
83 if pid == 0 {
84 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4)
85 if dn >= 0 { sys_dup3(dn, 1, 0); sys_dup3(dn, 2, 0) }
86 let bargv: *i64 = sys_mmap(32) as *i64
87 bargv[0] = "_offc/nx_sov_build_run.elf" as *u8 as i64
88 bargv[1] = name as i64
89 bargv[2] = 0
90 let benvp: *i64 = sys_mmap(16) as *i64
91 benvp[0] = 0
92 sys_execve("_offc/nx_sov_build_run.elf" as *u8, bargv, benvp); sys_exit(127)
93 }
94 let st: *i64 = sys_mmap(16) as *i64
95 sys_wait4(pid, st, 0)
96 return 0
97}
98
99// one HTTPS request through the front: connect, validated TLS 1.3 handshake, ONE app record out,
100// pump records to close collecting plaintext into resp. Returns resp length, or negative verdict
101// (handshake failure surfaces here -- the negative-control row relies on that).
102func tg_https(store: *TrustStore, req: *u8, rn: i64, resp: *u8, cap: i64) -> i64 {
103 let fd: i64 = sys_socket(2, 1, 0)
104 if fd < 0 { return 0 - 91 }
105 sys_set_socket_timeout(fd, 15)
106 let addr: *u8 = sys_mmap(16)
107 addr[0] = 2 as u8; addr[1] = 0 as u8
108 addr[2] = ((TG_FRONT_PORT >> 8) & 0xff) as u8
109 addr[3] = (TG_FRONT_PORT & 0xff) as u8
110 addr[4] = 127 as u8; addr[5] = 0 as u8; addr[6] = 0 as u8; addr[7] = 1 as u8
111 var zi: i64 = 8
112 while zi < 16 { addr[zi] = 0 as u8; zi = zi + 1 }
113 if nx_connect_bounded(fd, addr, 16, NX_CONN_DEFAULT_MS) < 0 { sys_close(fd); return 0 - 92 }
114
115 let cr: *u8 = sys_mmap(32)
116 let cpriv: *u8 = sys_mmap(32)
117 nx_csprng_fill(cr, 32)
118 nx_csprng_fill(cpriv, 32)
119 let vraw: *u8 = sys_mmap(64)
120 let val_ctx: *TlsValidationContext = vraw as *TlsValidationContext
121 val_ctx.store = store
122 val_ctx.sni_host = TG_SNI
123 val_ctx.sni_host_len = TG_SNI_N
124 val_ctx.now_epoch = sys_now_realtime_sec()
125 let r: i64 = nx_tls13_client_session_run(fd, TG_SNI, TG_SNI_N, cr, cpriv, val_ctx)
126 if r < 0 { sys_close(fd); return r }
127 let s: *Tls13ClientSession = r as *Tls13ClientSession
128
129 // send the request as one application_data record (requests are small)
130 let rec: *u8 = sys_mmap(rn + 64)
131 let v: i64 = nx_tls13_record_encrypt_v2(
132 s.cipher_suite, s.client_app_traffic_key, s.client_app_iv,
133 s.client_app_seq, req, rn, NX_TLS13_CT_APPLICATION_DATA, 0,
134 rec, rec + NX_TLS13_RECORD_HEADER_LEN, rec + NX_TLS13_RECORD_HEADER_LEN + rn + 1)
135 s.client_app_seq = s.client_app_seq + 1
136 if v != NX_TLS13_REC_VERDICT_OK { sys_close(fd); return 0 - 93 }
137 let total: i64 = NX_TLS13_RECORD_HEADER_LEN + rn + 1 + NX_TLS13_RECORD_TAG_LEN
138 var off: i64 = 0
139 while off < total {
140 let w: i64 = sys_write(fd, (rec as i64 + off) as *u8, total - off)
141 if w <= 0 { sys_close(fd); return 0 - 94 }
142 off = off + w
143 }
144
145 // pump response records until close/alert
146 var got: i64 = 0
147 var go: i64 = 1
148 while go == 1 {
149 go = 0
150 let rb: *u8 = sys_mmap(20480)
151 let tn: i64 = nx_tls13_read_record_from_fd(fd, rb, 20480)
152 if tn > 0 {
153 let ct_len: i64 = tn - NX_TLS13_RECORD_HEADER_LEN - NX_TLS13_RECORD_TAG_LEN
154 if ct_len > 0 {
155 let pl: *u8 = sys_mmap(ct_len + 16)
156 let ctp: *i64 = sys_mmap(16) as *i64
157 let lnp: *i64 = sys_mmap(16) as *i64
158 let dv: i64 = nx_tls13_record_decrypt_v2(
159 s.cipher_suite, s.server_app_traffic_key, s.server_app_iv,
160 s.server_app_seq, rb, rb + NX_TLS13_RECORD_HEADER_LEN, ct_len,
161 rb + tn - NX_TLS13_RECORD_TAG_LEN, pl, ctp, lnp)
162 s.server_app_seq = s.server_app_seq + 1
163 if dv == NX_TLS13_REC_VERDICT_OK {
164 if *ctp == NX_TLS13_CT_APPLICATION_DATA {
165 var i: i64 = 0
166 while i < *lnp { if got + i < cap { resp[got + i] = pl[i] } i = i + 1 }
167 got = got + *lnp
168 go = 1
169 }
170 if *ctp != NX_TLS13_CT_APPLICATION_DATA { if *ctp != NX_TLS13_CT_ALERT { go = 1 } }
171 }
172 }
173 }
174 }
175 sys_close(fd)
176 return got
177}
178
179func tg_post(req: *u8, path: *u8, cookie: *u8, body: *u8) -> i64 {
180 var o: i64 = tg_cat(req, 0, "POST " as *u8)
181 o = tg_cat(req, o, path)
182 o = tg_cat(req, o, " HTTP/1.1\r\nHost: cms.gate.local\r\nContent-Type: application/x-www-form-urlencoded\r\n" as *u8)
183 if (cookie[0] as i64) != 0 {
184 o = tg_cat(req, o, "Cookie: nsess=" as *u8)
185 o = tg_catn(req, o, cookie, 32)
186 o = tg_cat(req, o, "\r\n" as *u8)
187 }
188 o = tg_cat(req, o, "Content-Length: " as *u8)
189 let bl: i64 = tg_len(body)
190 let t: *u8 = sys_mmap(24)
191 var m: i64 = bl
192 var k: i64 = 0
193 if m == 0 { t[0] = 48 as u8; k = 1 }
194 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
195 var i: i64 = 0
196 while i < k { req[o] = t[k-1-i]; o = o + 1; i = i + 1 }
197 o = tg_cat(req, o, "\r\n\r\n" as *u8)
198 o = tg_cat(req, o, body)
199 return o
200}
201func tg_get(req: *u8, path: *u8, cookie: *u8) -> i64 {
202 var o: i64 = tg_cat(req, 0, "GET " as *u8)
203 o = tg_cat(req, o, path)
204 o = tg_cat(req, o, " HTTP/1.1\r\nHost: cms.gate.local\r\n" as *u8)
205 if (cookie[0] as i64) != 0 {
206 o = tg_cat(req, o, "Cookie: nsess=" as *u8)
207 o = tg_catn(req, o, cookie, 32)
208 o = tg_cat(req, o, "\r\n" as *u8)
209 }
210 o = tg_cat(req, o, "\r\n" as *u8)
211 return o
212}
213
214func main() -> i64 {
215 // ---- 1. sovereign self-signed Ed25519 cert (the andelinwest daemon's cert shape) ----
216 let priv: *u8 = sys_mmap(32)
217 let pub: *u8 = sys_mmap(32)
218 nx_csprng_fill(priv, 32)
219 if ed25519_pub_from_priv(priv, pub) != 0 { tg_w("CMSGATE row=nx_cms_tls KEYGEN-FAIL\n" as *u8); sys_exit(1) }
220 let san_ptrs: *i64 = sys_mmap(16) as *i64
221 let san_lens: *i64 = sys_mmap(16) as *i64
222 san_ptrs[0] = TG_SNI as i64
223 san_lens[0] = TG_SNI_N
224 let iraw: *u8 = sys_mmap(256)
225 let inp: *NxX509BuildInputs = iraw as *NxX509BuildInputs
226 let irc: i64 = nx_x509_inputs_init(inp, TG_SNI, TG_SNI_N, san_ptrs, san_lens, 1,
227 priv, pub, "20260101000000Z" as *u8, "20300101000000Z" as *u8, 7)
228 if irc != NX_X509_OK { tg_w("CMSGATE row=nx_cms_tls INPUTS-FAIL\n" as *u8); sys_exit(1) }
229 let cert: *u8 = sys_mmap(4096)
230 let cnb: *i64 = sys_mmap(16) as *i64
231 cnb[0] = 0
232 let brc: i64 = nx_x509_build_self_signed(inp, cert, 4096, cnb)
233 if brc != NX_X509_OK { tg_w("CMSGATE row=nx_cms_tls CERTBUILD-FAIL\n" as *u8); sys_exit(1) }
234 let cert_n: i64 = cnb[0]
235 let cf: i64 = sys_openat_wr("/tmp/_cms_tls_cert.der" as *u8, 0x1a4)
236 sys_write(cf, cert, cert_n)
237 sys_close(cf)
238 let kf: i64 = sys_openat_wr("/tmp/_cms_tls_key.bin" as *u8, 0x180)
239 sys_write(kf, priv, 32)
240 sys_close(kf)
241
242 // ---- 2. trust store: OUR cert is the ONLY anchor ----
243 let store: *TrustStore = trust_store_alloc(2)
244 let xraw: *u8 = sys_mmap(256)
245 let xc: *X509Cert = xraw as *X509Cert
246 if x509_parse(cert, cert_n, xc) != 0 { tg_w("CMSGATE row=nx_cms_tls SELFPARSE-FAIL\n" as *u8); sys_exit(1) }
247 if trust_store_add(store, cert, xc) != NX_TRUST_STORE_OK { tg_w("CMSGATE row=nx_cms_tls ANCHOR-FAIL\n" as *u8); sys_exit(1) }
248 let empty_store: *TrustStore = trust_store_alloc(1) // the negative control's store
249
250 // ---- 3. site fixture + builds + boots ----
251 tg_mkdir(TG_SITE, 0x1ed)
252 cst_copy("knowledge/cms/andelinwest/template.html" as *u8, "/tmp/_cms_tls_site/template.html" as *u8)
253 cst_copy("knowledge/cms/andelinwest/content.txt" as *u8, "/tmp/_cms_tls_site/content.txt" as *u8)
254 sys_renameat("/tmp/_cms_tls_site/draft.txt" as *u8, "/tmp/_cms_tls_site/draft.stale" as *u8)
255 let dig: *u8 = sys_mmap(32)
256 sha256_digest("tls-gate-pw-1" as *u8, 13, dig)
257 let hexs: *u8 = sys_mmap(80)
258 let hxc: *u8 = "0123456789abcdef" as *u8
259 var hi: i64 = 0
260 while hi < 32 {
261 hexs[hi*2] = hxc[((dig[hi] as i64) >> 4) & 15]
262 hexs[hi*2+1] = hxc[(dig[hi] as i64) & 15]
263 hi = hi + 1
264 }
265 let pf: i64 = sys_openat_wr("/tmp/_cms_tls_site/admin_pw.sha256" as *u8, 0x1a4)
266 sys_write(pf, hexs, 64)
267 sys_close(pf)
268 tg_build("nx_cms_admin" as *u8)
269 tg_build("nx_cms_tls_front" as *u8)
270 // CALLER-GAP FIX 2026-08-01 (seq1267 class): the runner emits buildroot/_build/<t>.sov.elf under
271 // the no-/tmp doctrine while this probed /tmp. Proven by control: _build copy 208962B, /tmp absent.
272 var bchk: i64 = sys_openat_rd("buildroot/_build/nx_cms_admin.sov.elf" as *u8)
273 var badm: *u8 = "buildroot/_build/nx_cms_admin.sov.elf" as *u8
274 if bchk < 0 { bchk = sys_openat_rd("/tmp/nx_cms_admin.sov.elf" as *u8); badm = "/tmp/nx_cms_admin.sov.elf" as *u8 }
275 if bchk < 0 { tg_w("CMSGATE row=nx_cms_tls ADMIN-BUILD-MISSING (probed buildroot/_build AND /tmp)\n" as *u8); sys_exit(1) }
276 sys_close(bchk)
277 // ★SAME CALLER GAP, SECOND ARTIFACT -- and it only surfaced once the FIRST one was fixed.
278 // This gate builds TWO things (nx_cms_admin and nx_cms_tls_front) and probed /tmp for both;
279 // fixing only the admin probe moved the error from ADMIN-BUILD-MISSING to FRONT-BUILD-MISSING.
280 // ★A DEFECT WITH N INSTANCES IN ONE FILE REVEALS THEM ONE AT A TIME, EACH BEHIND THE LAST --
281 // so "the fix worked" must mean the gate REACHED ITS SUBJECT, never merely that the message changed.
282 var fchk: i64 = sys_openat_rd("buildroot/_build/nx_cms_tls_front.sov.elf" as *u8)
283 var bfront: *u8 = "buildroot/_build/nx_cms_tls_front.sov.elf" as *u8
284 if fchk < 0 { fchk = sys_openat_rd("/tmp/nx_cms_tls_front.sov.elf" as *u8); bfront = "/tmp/nx_cms_tls_front.sov.elf" as *u8 }
285 if fchk < 0 { tg_w("CMSGATE row=nx_cms_tls FRONT-BUILD-MISSING (probed buildroot/_build AND /tmp)\n" as *u8); sys_exit(1) }
286 sys_close(fchk)
287
288 let pid_admin: i64 = sys_fork()
289 if pid_admin == 0 {
290 let lg: i64 = sys_openat_wr("/tmp/_cms_admin_tls_gate.log" as *u8, 0x1a4)
291 if lg >= 0 { sys_dup3(lg, 1, 0); sys_dup3(lg, 2, 0) }
292 let argvv: *i64 = sys_mmap(8*7) as *i64
293 argvv[0] = badm as i64
294 argvv[1] = "8086" as *u8 as i64
295 argvv[2] = TG_SITE as i64
296 argvv[3] = "99" as *u8 as i64
297 argvv[4] = "tls" as *u8 as i64
298 argvv[5] = 0
299 let envp: *i64 = sys_mmap(16) as *i64
300 envp[0] = 0
301 sys_execve(badm, argvv, envp)
302 sys_exit(127)
303 }
304 let pid_front: i64 = sys_fork()
305 if pid_front == 0 {
306 let lg2: i64 = sys_openat_wr("/tmp/_cms_tls_front_gate.log" as *u8, 0x1a4)
307 if lg2 >= 0 { sys_dup3(lg2, 1, 0); sys_dup3(lg2, 2, 0) }
308 let argvv2: *i64 = sys_mmap(8*8) as *i64
309 argvv2[0] = bfront as i64
310 argvv2[1] = "19861" as *u8 as i64
311 argvv2[2] = "8086" as *u8 as i64
312 argvv2[3] = "/tmp/_cms_tls_cert.der" as *u8 as i64
313 argvv2[4] = "/tmp/_cms_tls_key.bin" as *u8 as i64
314 argvv2[5] = "99" as *u8 as i64
315 argvv2[6] = 0
316 let envp2: *i64 = sys_mmap(16) as *i64
317 envp2[0] = 0
318 sys_execve(bfront, argvv2, envp2)
319 sys_exit(127)
320 }
321
322 let req: *u8 = sys_mmap(65536)
323 let resp: *u8 = sys_mmap(524288)
324 let nocookie: *u8 = sys_mmap(8)
325 nocookie[0] = 0 as u8
326 let sess: *u8 = sys_mmap(64)
327 sess[0] = 0 as u8
328 let csrf: *u8 = sys_mmap(64)
329 csrf[0] = 0 as u8
330
331 // T0: validated HTTPS GET / through the front (boot-poll: handshake fails until both are up)
332 var rn: i64 = 0 - 1
333 var tries: i64 = 0
334 while tries < 60 {
335 let rl0: i64 = tg_get(req, "/" as *u8, nocookie)
336 rn = tg_https(store, req, rl0, resp, 524287)
337 if rn > 0 { tries = 60 }
338 if rn <= 0 { sys_sleep_ms(200); tries = tries + 1 }
339 }
340 // ★★SETUP-FAILED IS NOT A SUBJECT FAILURE -- ADDED 2026-08-01 AFTER THIS GATE LIED BY 4 ROWS.
341 // If the boot-poll never got a single response, the daemons never came up and NOTHING downstream
342 // can be evidence about TLS. Previously the gate ploughed on and reported 1/5: four positive rows
343 // red and the untrusted-anchor row "PASS" -- a NEGATIVE CONTROL THAT PASSES BECAUSE EVERYTHING IS
344 // REFUSED, which cannot tell correct validation from a dead listener. ★A NEG-CONTROL IS ONLY
345 // EVIDENCE WHEN THE POSITIVE PATH WORKS; on a dead subject it is the most dangerous green there is.
346 // MEASURED ROOT CAUSE the first time this fired: both daemon logs said BIND-FAIL -- ports 8086/8447
347 // were still held by ORPHANS from an earlier run that gate_run KILLED ON ITS 12s DEADLINE, so the
348 // gate never reached its own nxk_kill teardown. ★A GATE KILLED BY A DEADLINE SKIPS ITS TEARDOWN AND
349 // ITS ORPHANED DAEMONS POISON EVERY LATER RUN -- run port-binding gates through nx_job_run (uncapped).
350 if rn <= 0 {
351 tg_w("CMSGATE row=nx_cms_tls SETUP-FAILED daemons never served -- NOT a TLS failure. READ THE ERRNO in /tmp/_cms_tls_front_gate.log and /tmp/_cms_admin_tls_gate.log; do NOT assume a cause. 98=EADDRINUSE (something already listens: identify the OWNER before killing anything -- this gate's original port 8447 belonged to the live nx_translate_daemon, and a name-based reaper aimed at it would have killed a supervised production service), 13=EACCES, 99=EADDRNOTAVAIL.\n" as *u8)
352 sys_exit(2)
353 }
354 var pass: i64 = 0
355 var rows: i64 = 0
356 var ok: i64 = 0
357 let ctr: *i64 = gv_ctr()
358 gv_head("nx_cms_tls_gate -- CMS behind a real TLS 1.3 front" as *u8)
359 if rn > 0 { if tg_has(resp, rn, "200 OK" as *u8) == 1 { if tg_has(resp, rn, "Andelin West Law" as *u8) == 1 { ok = 1 } } }
360 rows = rows + 1; pass = pass + tg_row(0, ok, "validated TLS1.3 handshake + proxied GET /" as *u8)
361 gv_check("validated TLS1.3 handshake + proxied GET /" as *u8, ok, ctr)
362
363 // T1: login over TLS -> 303 + Secure cookie
364 var rl: i64 = tg_post(req, "/admin/login" as *u8, nocookie, "pw=tls-gate-pw-1" as *u8)
365 rn = tg_https(store, req, rl, resp, 524287)
366 ok = 0
367 if rn > 0 { if tg_has(resp, rn, "303" as *u8) == 1 { if tg_has(resp, rn, "; Secure" as *u8) == 1 {
368 let sp: i64 = tg_index(resp, rn, "nsess=" as *u8)
369 if sp >= 0 {
370 var m2: i64 = 0
371 while m2 < 32 { sess[m2] = resp[sp+6+m2]; m2 = m2 + 1 }
372 sess[32] = 0 as u8
373 ok = 1
374 }
375 } } }
376 rows = rows + 1; pass = pass + tg_row(1, ok, "login over TLS issues Secure cookie" as *u8)
377 gv_check("login over TLS issues Secure cookie" as *u8, ok, ctr)
378
379 // T2: editor over TLS with the session
380 rl = tg_get(req, "/admin" as *u8, sess)
381 rn = tg_https(store, req, rl, resp, 524287)
382 ok = 0
383 if rn > 0 { if tg_has(resp, rn, "name=\"hero_title\"" as *u8) == 1 {
384 let cp: i64 = tg_index(resp, rn, "name=\"csrf\" value=\"" as *u8)
385 if cp >= 0 {
386 var m3: i64 = 0
387 while m3 < 32 { csrf[m3] = resp[cp+19+m3]; m3 = m3 + 1 }
388 csrf[32] = 0 as u8
389 ok = 1
390 }
391 } }
392 rows = rows + 1; pass = pass + tg_row(2, ok, "editor reached over TLS" as *u8)
393 gv_check("editor reached over TLS" as *u8, ok, ctr)
394
395 // T3: edit-publish over TLS, live page reflects it
396 let body3: *u8 = sys_mmap(4096)
397 var b3: i64 = tg_cat(body3, 0, "csrf=" as *u8)
398 b3 = tg_catn(body3, b3, csrf, 32)
399 b3 = tg_cat(body3, b3, "&hero_title=TLS+Edited+Headline" as *u8)
400 body3[b3] = 0 as u8
401 rl = tg_post(req, "/admin/save" as *u8, sess, body3)
402 rn = tg_https(store, req, rl, resp, 524287)
403 ok = 0
404 if rn > 0 { if tg_has(resp, rn, "303" as *u8) == 1 {
405 let rl3: i64 = tg_get(req, "/" as *u8, nocookie)
406 let rn3: i64 = tg_https(store, req, rl3, resp, 524287)
407 if rn3 > 0 { if tg_has(resp, rn3, "TLS Edited Headline" as *u8) == 1 { ok = 1 } }
408 } }
409 rows = rows + 1; pass = pass + tg_row(3, ok, "edit-publish round trip over TLS" as *u8)
410 gv_check("edit-publish round trip over TLS" as *u8, ok, ctr)
411
412 // T4: NEGATIVE CONTROL -- empty trust store must REFUSE the handshake
413 let rl4: i64 = tg_get(req, "/" as *u8, nocookie)
414 let rn4: i64 = tg_https(empty_store, req, rl4, resp, 524287)
415 ok = 0
416 if rn4 < 0 { ok = 1 }
417 rows = rows + 1; pass = pass + tg_row(4, ok, "untrusted anchor handshake REFUSED (validation real)" as *u8)
418 gv_check("untrusted anchor handshake REFUSED (validation real)" as *u8, ok, ctr)
419
420 // ---- teardown ----
421 nxk_kill(pid_front, 9)
422 nxk_kill(pid_admin, 9)
423 let stbuf: *i64 = sys_mmap(16) as *i64
424 sys_wait4(pid_front, stbuf, 0)
425 sys_wait4(pid_admin, stbuf, 0)
426
427 var verdict: *u8 = "FAIL" as *u8
428 if pass == rows { verdict = "PASS" as *u8 }
429 let line: *u8 = sys_mmap(512)
430 var lo: i64 = tg_cat(line, 0, "CMSGATE row=nx_cms_tls rows=" as *u8)
431 let t2: *u8 = sys_mmap(24)
432 var mm: i64 = rows
433 var kk: i64 = 0
434 if mm == 0 { t2[0] = 48 as u8; kk = 1 }
435 while mm > 0 { t2[kk] = (48 + (mm % 10)) as u8; mm = mm / 10; kk = kk + 1 }
436 var ii: i64 = 0
437 while ii < kk { line[lo] = t2[kk-1-ii]; lo = lo + 1; ii = ii + 1 }
438 lo = tg_cat(line, lo, " pass=" as *u8)
439 mm = pass; kk = 0
440 if mm == 0 { t2[0] = 48 as u8; kk = 1 }
441 while mm > 0 { t2[kk] = (48 + (mm % 10)) as u8; mm = mm / 10; kk = kk + 1 }
442 ii = 0
443 while ii < kk { line[lo] = t2[kk-1-ii]; lo = lo + 1; ii = ii + 1 }
444 lo = tg_cat(line, lo, " verdict=" as *u8)
445 lo = tg_cat(line, lo, verdict)
446 lo = tg_cat(line, lo, " epoch=" as *u8)
447 mm = sys_now_realtime_sec(); kk = 0
448 if mm == 0 { t2[0] = 48 as u8; kk = 1 }
449 while mm > 0 { t2[kk] = (48 + (mm % 10)) as u8; mm = mm / 10; kk = kk + 1 }
450 ii = 0
451 while ii < kk { line[lo] = t2[kk-1-ii]; lo = lo + 1; ii = ii + 1 }
452 line[lo] = 10 as u8
453 lo = lo + 1
454 sys_write(1, line, lo)
455 let gf: i64 = sys_openat_append("knowledge/status/cms_gate.log" as *u8, 0x1a4)
456 if gf >= 0 { sys_write(gf, line, lo); sys_close(gf) }
457 // D001 MIGRATION 2026-08-01 (hand-migrated: nx_gate_dry_apply SKIPped this gate -- "no verdict=GREEN
458 // tail", the same shape gap that blocked the q4k KAT, so the mechanical remedy could not cover it).
459 // The legacy CMSGATE line above is KEPT because other readers already parse it -- API contract
460 // stability (rule 19): add the new contract first, never break the old one in the same change.
461 let grc: i64 = gv_verdict("cms-tls" as *u8, ctr, "CMS served behind a real validated TLS 1.3 front" as *u8)
462 sys_exit(grc)
463 return grc
464}