code wiki / _hdl_build / nx_legal_portal_post_gate.nx
nx_legal_portal_post_gate.nx source
↩ module page · 172 lines · 7977 B
1// nx_legal_portal_post_gate.nx -- GATE for the interactive POST routes (lp_handle_post), ENGINEER verify.
2//
3// Proves the portal's MUTATING actions over HTTP (the markup/checklist interactivity),
4// composing D4 (annotate) + D8 (persist), each with a control:
5// T1 POST annotate : POST /portal/annotate adds a note -> 200, store grows
6// T2 POST check : add a required checklist item then POST /portal/check?ann= ->
7// the item is DONE (GET /portal/checklist -> remaining 0 READY)
8// T3 MUTATION PERSISTS : after the POSTs, save + reload into FRESH arrays -> the
9// checklist done-state + the note survive (durable interactivity)
10// T4 AUTHZ : POST to /staff at level 1 -> 403 ; unknown path -> 404
11// T5 NEG : POST /portal/annotate with no doc -> 400 (not silent)
12//
13// lp_handle (GET) is UNCHANGED -- the POST router is a new function, so the existing
14// portal GET gates are not regressed (re-verified separately).
15//
16// Evidence -> knowledge/status/legal_portal_post.log
17// license_tier: ORIGINAL
18import "nx_legal_portal.nx"
19import "nx_legal_portal_boot.nx"
20import "nx_doc_annotate.nx"
21import "nx_doc_envelope.nx"
22import "nx_doc_vault.nx"
23import "nx_legal_compliance.nx"
24import "nx_syscalls.nx"
25import "nx_gate_verdict.nx"
26
27const LPP_LOG: *u8 = "knowledge/status/legal_portal_post.log"
28const LPP_BASE: *u8 = "/tmp/nx_lpp_"
29
30func ew(fd: i64, s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(fd, s, n); return 0 }
31func ewn(fd: i64, v: i64) -> i64 {
32 let bb: *u8 = sys_mmap(28); var m: i64 = v
33 if m < 0 { m = 0 - m; sys_write(fd, "-" as *u8, 1) }
34 let t: *u8 = sys_mmap(28); var k: i64 = 0
35 if m == 0 { t[0] = 48; k = 1 }
36 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
37 var i: i64 = 0; while i < k { bb[i] = t[k - 1 - i]; i = i + 1 }
38 sys_write(fd, bb, k); return 0
39}
40func slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
41func g_cat(d: *u8, o: i64, s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { d[o + i] = s[i]; i = i + 1 } return o + i }
42func g_starts(buf: *u8, n: i64, s: *u8) -> i64 {
43 let sn: i64 = slen(s)
44 if n < sn { return 0 }
45 var i: i64 = 0
46 while i < sn { if buf[i] != s[i] { return 0 } i = i + 1 }
47 return 1
48}
49func g_contains(hay: *u8, n: i64, needle: *u8) -> i64 {
50 let nn: i64 = slen(needle)
51 if nn == 0 { return 1 }
52 var i: i64 = 0
53 while i + nn <= n {
54 var m: i64 = 1
55 var j: i64 = 0
56 while j < nn { if hay[i + j] != needle[j] { m = 0; break } j = j + 1 }
57 if m == 1 { return 1 }
58 i = i + 1
59 }
60 return 0
61}
62
63func main() -> i64 {
64 var ok: i64 = 1
65 let ncap: i64 = 16
66 let vcap: i64 = 8
67 let ecap: i64 = 8
68 let rcap: i64 = 8
69 let eflat: *i64 = sys_mmap(ecap * EF_STRIDE * 8) as *i64
70 let rflat: *i64 = sys_mmap(rcap * RF_STRIDE * 8) as *i64
71 let anflat: *i64 = sys_mmap(ncap * NF_STRIDE * 8) as *i64
72 let vflat: *i64 = sys_mmap(vcap * VF_STRIDE * 8) as *i64
73 let eidbuf: *i64 = sys_mmap(32) as *i64
74 var vc: i64 = nx_vault_add(vflat, 0, vcap, 8601, 5001, 64, 1000)
75
76 let ctx: *NxPortalCtx = sys_mmap(256) as *NxPortalCtx
77 ctx.user_level = 1
78 ctx.eflat = eflat; ctx.ne = 0
79 ctx.rflat = rflat; ctx.nr = 0
80 ctx.anflat = anflat; ctx.na = 0
81 ctx.vflat = vflat; ctx.vc = vc
82 ctx.env_ids = eidbuf; ctx.env_count = 0
83
84 let req: *u8 = sys_mmap(4096)
85 let out: *u8 = sys_mmap(16384)
86
87 // ---- T1: POST annotate adds a note ----
88 var t1: i64 = 1
89 var rn: i64 = g_cat(req, 0, "POST /portal/annotate?doc=8601&ann=50&author=700&typ=0 HTTP/1.1\r\nHost: x\r\n\r\n" as *u8)
90 var on: i64 = lp_handle_post(ctx, ncap, req, rn, out)
91 if g_starts(out, on, "HTTP/1.1 200" as *u8) != 1 { t1 = 0 }
92 if g_contains(out, on, "ANNOTATED doc 8601 ann 50" as *u8) != 1 { t1 = 0 }
93 if nx_ann_count(anflat, ctx.na, 8601, 1) != 1 { t1 = 0 }
94 if t1 != 1 { ok = 0 }
95
96 // ---- T2: POST add a required checklist item, then POST check it done ----
97 var t2: i64 = 1
98 rn = g_cat(req, 0, "POST /portal/annotate?doc=8601&ann=60&author=700&typ=4&req=1 HTTP/1.1\r\nHost: x\r\n\r\n" as *u8)
99 on = lp_handle_post(ctx, ncap, req, rn, out)
100 if g_starts(out, on, "HTTP/1.1 200" as *u8) != 1 { t2 = 0 }
101 if nx_ann_checklist_remaining(anflat, ctx.na, 8601, 1) != 1 { t2 = 0 } // pending
102 rn = g_cat(req, 0, "POST /portal/check?ann=60 HTTP/1.1\r\nHost: x\r\n\r\n" as *u8)
103 on = lp_handle_post(ctx, ncap, req, rn, out)
104 if g_starts(out, on, "HTTP/1.1 200" as *u8) != 1 { t2 = 0 }
105 if g_contains(out, on, "CHECKED ann 60" as *u8) != 1 { t2 = 0 }
106 // GET the checklist via lp_handle -> now READY
107 rn = g_cat(req, 0, "GET /portal/checklist?doc=8601 HTTP/1.1\r\nHost: x\r\n\r\n" as *u8)
108 on = lp_handle(ctx, req, rn, out)
109 if g_contains(out, on, "remaining 0 READY" as *u8) != 1 { t2 = 0 }
110 if t2 != 1 { ok = 0 }
111
112 // ---- T3: the mutations PERSIST across a reboot ----
113 var t3: i64 = 1
114 lp_boot_save(ctx, LPP_BASE, "clientA" as *u8)
115 let anflat2: *i64 = sys_mmap(ncap * NF_STRIDE * 8) as *i64
116 let eflat2: *i64 = sys_mmap(ecap * EF_STRIDE * 8) as *i64
117 let rflat2: *i64 = sys_mmap(rcap * RF_STRIDE * 8) as *i64
118 let vflat2: *i64 = sys_mmap(vcap * VF_STRIDE * 8) as *i64
119 let eidbuf2: *i64 = sys_mmap(32) as *i64
120 let ctx2: *NxPortalCtx = sys_mmap(256) as *NxPortalCtx
121 ctx2.user_level = 1
122 lp_boot_load(ctx2, LPP_BASE, "clientA" as *u8, eflat2, ecap, rflat2, rcap, anflat2, ncap, vflat2, vcap, eidbuf2)
123 if nx_ann_count(anflat2, ctx2.na, 8601, 1) != 2 { t3 = 0 } // note + check persisted
124 rn = g_cat(req, 0, "GET /portal/checklist?doc=8601 HTTP/1.1\r\nHost: x\r\n\r\n" as *u8)
125 on = lp_handle(ctx2, req, rn, out)
126 if g_contains(out, on, "remaining 0 READY" as *u8) != 1 { t3 = 0 } // done-state persisted
127 if t3 != 1 { ok = 0 }
128
129 // ---- T4: authz ----
130 var t4: i64 = 1
131 rn = g_cat(req, 0, "POST /staff/x HTTP/1.1\r\nHost: x\r\n\r\n" as *u8)
132 on = lp_handle_post(ctx, ncap, req, rn, out)
133 if g_starts(out, on, "HTTP/1.1 403" as *u8) != 1 { t4 = 0 }
134 rn = g_cat(req, 0, "POST /portal/bogus HTTP/1.1\r\nHost: x\r\n\r\n" as *u8)
135 on = lp_handle_post(ctx, ncap, req, rn, out)
136 if g_starts(out, on, "HTTP/1.1 404" as *u8) != 1 { t4 = 0 }
137 if t4 != 1 { ok = 0 }
138
139 // ---- T5: NEG -- missing doc -> 400 ----
140 var t5: i64 = 1
141 rn = g_cat(req, 0, "POST /portal/annotate?ann=99 HTTP/1.1\r\nHost: x\r\n\r\n" as *u8)
142 on = lp_handle_post(ctx, ncap, req, rn, out)
143 if g_starts(out, on, "HTTP/1.1 400" as *u8) != 1 { t5 = 0 }
144 if t5 != 1 { ok = 0 }
145
146 // ---- evidence ----
147 var fd: i64 = 1
148 while fd >= 1 {
149 ew(fd, "LEGALPORTALPOSTGATE authored=organ composes=D4+D8 post_annotate=" as *u8); ewn(fd, t1)
150 ew(fd, " post_check_done=" as *u8); ewn(fd, t2)
151 ew(fd, " mutation_persists=" as *u8); ewn(fd, t3)
152 ew(fd, " authz=" as *u8); ewn(fd, t4)
153 ew(fd, " neg_missing_doc=" as *u8); ewn(fd, t5)
154 if ok == 1 { ew(fd, " verdict=GREEN\n" as *u8) } else { ew(fd, " verdict=RED\n" as *u8) }
155 if fd == 1 {
156 let lf: i64 = sys_openat_append(LPP_LOG, 420)
157 if lf >= 1 { fd = lf } else { fd = 0 }
158 } else {
159 sys_close(fd); fd = 0
160 }
161 }
162
163 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check
164 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled
165 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify.
166 let ctr__dry: *i64 = gv_ctr()
167 ctr__dry[0] = ok
168 ctr__dry[1] = 1
169 let rc__dry: i64 = gv_verdict("LEGAL-PORTAL-POST-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8)
170 sys_exit(rc__dry)
171 return rc__dry
172}