code wiki / _hdl_build / nx_browser_profile_gate.nx
nx_browser_profile_gate.nx source
↩ module page · 132 lines · 6342 B
1// nx_browser_profile_gate.nx -- gate for the browser PERSISTENT PROFILE STORE (R4 of the comms-parity
2// ladder). Proves on FRESH /tmp fixtures: (T1) contact roundtrip byte-identical, (T2) setting last-wins,
3// (T3) chat multi-key, (T4) ★THE SPLIT: clearing the CACHE family (delete every cache-prefix file = the
4// history-clear) leaves the PROFILE family byte-identical -- the operator's "different cache than the
5// history-clearing cache" requirement, proven mechanically, (T5) tombstone-delete misses, (T6) NEG bogus
6// key misses (liar-kill), (T7) re-put after tombstone works (additive law: current view correct).
7// expect_exit: 0 license_tier: ORIGINAL
8import "nx_browser_profile_store.nx"
9
10const GP: *u8 = "/tmp/bp_gate_profile-"
11const GC: *u8 = "/tmp/bp_gate_cache-"
12
13func g_unlink(path: *u8) -> i64 { __syscall(263, AT_FDCWD, path, 0, 0, 0, 0) return 0 }
14func g_pw(s: *u8) -> i64 { sys_write(1, s, bp_len(s)); return 0 }
15func g_pn(v: i64) -> i64 {
16 let b: *u8=sys_mmap(28); var m: i64=v; if m<0{sys_write(1,"-" as *u8,1);m=0-m}
17 let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48 as u8;k=1} while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1}
18 var i: i64=0; while i<k{b[i]=t[k-1-i];i=i+1} sys_write(1,b,k); return 0 }
19func g_eq(a: *u8, alen: i64, b: *u8, blen: i64) -> i64 {
20 if alen != blen { return 0 }
21 var i: i64=0; while i<alen { if a[i]!=b[i] { return 0 } i=i+1 }
22 return 1 }
23
24// delete every file in /tmp whose name starts with `stem` (e.g. "bp_gate_cache-"). Returns count.
25func g_clear_family(stem: *u8) -> i64 {
26 let dfd: i64 = sys_openat_rd("/tmp" as *u8)
27 if dfd < 0 { return 0 - 1 }
28 let buf: *u8 = sys_mmap(65536)
29 let full: *u8 = sys_mmap(4096)
30 var killed: i64 = 0
31 var go: i64 = 1
32 while go == 1 {
33 let n: i64 = sys_getdents64(dfd, buf, 65536)
34 if n <= 0 { go = 0 } else {
35 var off: i64 = 0
36 while off < n {
37 let reclen: i64 = (buf[off+16] as i64) + ((buf[off+17] as i64) * 256)
38 let name: *u8 = buf + off + 19
39 // prefix match name against stem
40 var m: i64 = 1
41 var i: i64 = 0
42 while stem[i] != (0 as u8) { if name[i] != stem[i] { m = 0; i = bp_len(stem) } else { i = i + 1 } }
43 if m == 1 {
44 var o: i64 = 0
45 let t: *u8 = "/tmp/\x00" as *u8
46 var j: i64 = 0
47 while t[j] != (0 as u8) { full[o] = t[j]; o = o + 1; j = j + 1 }
48 j = 0
49 while name[j] != (0 as u8) { full[o] = name[j]; o = o + 1; j = j + 1 }
50 full[o] = 0 as u8
51 g_unlink(full)
52 killed = killed + 1
53 }
54 off = off + reclen
55 }
56 }
57 }
58 sys_close(dfd)
59 return killed
60}
61
62func chk(name: *u8, ok: i64, pass: *i64) -> i64 {
63 g_pw(" " as *u8)
64 if ok==1 { g_pw("PASS " as *u8); pass[0]=pass[0]+1 } else { g_pw("FAIL " as *u8) }
65 g_pw(name); g_pw("\n" as *u8)
66 return 0 }
67
68func main() -> i64 {
69 g_pw("=== nx_browser_profile_gate: persistent profile store + cache-split (R4) ===\n" as *u8)
70 // fresh fixtures per run: clear BOTH families first (gate-only /tmp artifacts)
71 g_clear_family("bp_gate_profile-" as *u8)
72 g_clear_family("bp_gate_cache-" as *u8)
73 let pass: *i64 = sys_mmap(16) as *i64
74 pass[0] = 0
75 let out: *u8 = sys_mmap(65536)
76
77 // T1 contact roundtrip
78 let alice: *u8 = "Alice West;pub=ab12cd34;tel=+1-555-0100"
79 bp_put(GP, "contact:alice" as *u8, alice, bp_len(alice))
80 var n: i64 = bp_get(GP, "contact:alice" as *u8, out, 65536)
81 chk("T1 contact roundtrip byte-identical " as *u8, g_eq(out, n, alice, bp_len(alice)), pass)
82
83 // T2 setting last-wins
84 bp_put(GP, "setting:theme" as *u8, "dark" as *u8, 4)
85 bp_put(GP, "setting:theme" as *u8, "light" as *u8, 5)
86 n = bp_get(GP, "setting:theme" as *u8, out, 65536)
87 chk("T2 setting overwrite last-wins " as *u8, g_eq(out, n, "light" as *u8, 5), pass)
88
89 // T3 chat multi-key (two messages, distinct ts keys, one commit via bp_put2 = the batch seam)
90 bp_put2(GP, "chat:family:1000" as *u8, "hello room" as *u8, 10, "chat:family:2000" as *u8, "second msg" as *u8, 10)
91 n = bp_get(GP, "chat:family:1000" as *u8, out, 65536)
92 var t3: i64 = g_eq(out, n, "hello room" as *u8, 10)
93 n = bp_get(GP, "chat:family:2000" as *u8, out, 65536)
94 if g_eq(out, n, "second msg" as *u8, 10) == 0 { t3 = 0 }
95 chk("T3 chat multi-key (batched single commit) " as *u8, t3, pass)
96
97 // T4 ★ THE SPLIT: cache write -> history-clear (delete cache family) -> profile INTACT
98 bp_put(GC, "cache:page1" as *u8, "<html>stub</html>" as *u8, 17)
99 n = bp_get(GC, "cache:page1" as *u8, out, 65536)
100 var t4: i64 = 0
101 if n > 0 {
102 let killed: i64 = g_clear_family("bp_gate_cache-" as *u8)
103 g_pw(" (history-clear removed " as *u8); g_pn(killed); g_pw(" cache files)\n" as *u8)
104 let cgone: i64 = bp_get(GC, "cache:page1" as *u8, out, 65536)
105 n = bp_get(GP, "contact:alice" as *u8, out, 65536)
106 if cgone < 0 { if g_eq(out, n, alice, bp_len(alice)) == 1 { t4 = 1 } }
107 }
108 chk("T4 SPLIT: cache cleared, profile intact " as *u8, t4, pass)
109
110 // T5 tombstone-delete misses
111 bp_del(GP, "contact:alice" as *u8)
112 n = bp_get(GP, "contact:alice" as *u8, out, 65536)
113 var t5: i64 = 0
114 if n < 0 { t5 = 1 }
115 chk("T5 tombstone-delete: current view misses " as *u8, t5, pass)
116
117 // T6 NEG bogus key
118 n = bp_get(GP, "contact:nobody_zz" as *u8, out, 65536)
119 var t6: i64 = 0
120 if n < 0 { t6 = 1 }
121 chk("T6 NEG bogus key misses (no fabrication) " as *u8, t6, pass)
122
123 // T7 re-put after tombstone (additive: history kept, current view correct)
124 bp_put(GP, "contact:alice" as *u8, "Alice West;pub=NEWKEY99" as *u8, 23)
125 n = bp_get(GP, "contact:alice" as *u8, out, 65536)
126 chk("T7 re-put after tombstone reads new value " as *u8, g_eq(out, n, "Alice West;pub=NEWKEY99" as *u8, 23), pass)
127
128 g_pw("BROWSER-PROFILE-GATE pass=" as *u8); g_pn(pass[0]); g_pw("/7" as *u8)
129 if pass[0]==7 { g_pw(" verdict=GREEN -- profile store + cache split proven (clear cache, keep profile)\n" as *u8); return 0 }
130 g_pw(" verdict=RED\n" as *u8)
131 return 1
132}