code wiki / _hdl_build / nx_reader_sync_gate.nx

nx_reader_sync_gate.nx source

↩ module page · 205 lines · 11255 B

1import "nx_gate_grow.nx" 2import "nx_gate_gn.nx" 3import "nx_gate_read.nx" 4// nx_reader_sync_gate.nx -- SOVEREIGN in-process referee for the R1 SYNC SPINE (nx_reader_sync). No socket/shell: 5// chdir /tmp, build the fixture book world (reader/sgate/ with a cbx.json marker), drive the handlers with 6// crafted request bytes + a FILE fd as cfd, assert response bytes AND on-disk state. Per-user isolation is the 7// core Whispersync property under test. 8// T0 ukey: deterministic + sanitize-collision-safe ("Elder!" vs "Elder_" -> DIFFERENT keys) + path-safe 9// T1 PUT then GET round-trip (user A): stored verbatim, auto empty 10// T2 auto-record from a chapter fetch path -> GET shows auto=chap007.txt + the exact epoch 11// T3 USER ISOLATION: user B sees pos:null + auto:"" for the same book 12// T4 NEG traversal slug -> 403 (GET and PUT) 13// T5 NEG oversized body (> cap) -> 413 + nothing stored ; PUT to a nonexistent book -> 404 14// T6 LWW: second PUT overwrites; GET returns v2; no .new temp left behind 15// GREEN iff 7/7. license_tier: ORIGINAL expect_exit: 0 16import "nx_syscalls.nx" 17import "nx_reader_sync.nx" // sy_* under test (+ zs_* transitively) 18 19func gw(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 20func g_has(hay: *u8, n: i64, needle: *u8) -> i64 { 21 var nn: i64 = 0; while needle[nn] != (0 as u8) { nn = nn + 1 } 22 if nn == 0 { return 0 } 23 var i: i64 = 0 24 while i + nn <= n { var m: i64 = 1; var j: i64 = 0; while j < nn { if (hay[i + j] as i64) != (needle[j] as i64) { m = 0; j = nn } else { j = j + 1 } } if m == 1 { return 1 } i = i + 1 } 25 return 0 26} 27func g_exists(path: *u8) -> i64 { let fd: i64 = sys_openat_rd(path); if fd < 0 { return 0 } sys_close(fd); return 1 } 28func g_mkdir(path: *u8) -> i64 { __syscall(258, 0 - 100, path as i64, 0x1ed, 0, 0, 0); return 0 } 29func g_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 30func g_unlink(path: *u8) -> i64 { __syscall(263, 0 - 100, path, 0, 0, 0, 0); return 0 } 31func g_streq(a: *u8, b: *u8) -> i64 { var i: i64 = 0; while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 } if b[i] == (0 as u8) { return 1 } return 0 } 32 33// run a GET handler call; response captured via a file fd 34func g_get(p: *u8, ukey: *u8, out: *u8, cap: i64) -> i64 { 35 let rp: *u8 = "sy_rsp.tmp" as *u8 36 g_unlink(rp) 37 let fd: i64 = sys_openat_wr("sy_rsp.tmp" as *u8, 0x1a4) 38 if fd < 0 { return 0 - 1 } 39 sy_handle_pos_get(fd, p, g_slen(p), ukey) 40 return g_read("sy_rsp.tmp" as *u8, out, cap) 41} 42// run a PUT handler call with body 43func g_put(p: *u8, body: *u8, blen: i64, ukey: *u8, out: *u8, cap: i64) -> i64 { 44 let rp: *u8 = "sy_rsp.tmp" as *u8 45 g_unlink(rp) 46 let fd: i64 = sys_openat_wr("sy_rsp.tmp" as *u8, 0x1a4) 47 if fd < 0 { return 0 - 1 } 48 sy_handle_pos_put(fd, p, g_slen(p), body, 0, blen, ukey) 49 return g_read("sy_rsp.tmp" as *u8, out, cap) 50} 51// annotation variants 52func g_ann_get(p: *u8, ukey: *u8, out: *u8, cap: i64) -> i64 { 53 let rp: *u8 = "sy_rsp.tmp" as *u8 54 g_unlink(rp) 55 let fd: i64 = sys_openat_wr("sy_rsp.tmp" as *u8, 0x1a4) 56 if fd < 0 { return 0 - 1 } 57 sy_handle_ann_get(fd, p, g_slen(p), ukey) 58 return g_read("sy_rsp.tmp" as *u8, out, cap) 59} 60func g_ann_put(p: *u8, body: *u8, blen: i64, ukey: *u8, out: *u8, cap: i64) -> i64 { 61 let rp: *u8 = "sy_rsp.tmp" as *u8 62 g_unlink(rp) 63 let fd: i64 = sys_openat_wr("sy_rsp.tmp" as *u8, 0x1a4) 64 if fd < 0 { return 0 - 1 } 65 sy_handle_ann_put(fd, p, g_slen(p), body, 0, blen, ukey) 66 return g_read("sy_rsp.tmp" as *u8, out, cap) 67} 68 69func main() -> i64 { 70 gw("reader-sync SOVEREIGN in-process gate (fixture world -> per-user position spine asserts)\n" as *u8) 71 if sys_chdir("/tmp" as *u8) != 0 { gw("CHDIR /tmp FAIL\n" as *u8); sys_exit(1) } 72 73 // fixture: book dir with a cbx.json marker (the PUT existence probe) 74 g_mkdir("knowledge" as *u8) 75 g_mkdir("knowledge/staging" as *u8) 76 g_mkdir("knowledge/staging/media" as *u8) 77 g_mkdir("knowledge/staging/media/reader" as *u8) 78 g_mkdir("knowledge/staging/media/reader/sgate" as *u8) 79 let mf: i64 = sys_openat_wr("knowledge/staging/media/reader/sgate/cbx.json" as *u8, 0x1a4) 80 if mf >= 0 { sys_write(mf, "{\"pages\":[]}" as *u8, 12); sys_close(mf) } 81 82 var pass: i64 = 0 83 let rsp: *u8 = sys_mmap(65536) 84 85 // ---- T0 ukey determinism + sanitize-collision safety ---- 86 let kA: *u8 = sys_mmap(64); let kA2: *u8 = sys_mmap(64); let kB: *u8 = sys_mmap(64); let kC: *u8 = sys_mmap(64) 87 sy_user_key("Elder!" as *u8, 6, kA) 88 sy_user_key("Elder!" as *u8, 6, kA2) 89 sy_user_key("Elder_" as *u8, 6, kB) 90 sy_user_key("familymember" as *u8, 12, kC) 91 var t0: i64 = 1 92 if g_streq(kA, kA2) == 0 { t0 = 0 } // deterministic 93 if g_streq(kA, kB) == 1 { t0 = 0 } // "Elder!" vs "Elder_" MUST differ (fnv suffix disambiguates the sanitize collision) 94 if g_streq(kA, kC) == 1 { t0 = 0 } 95 gw(" ukeyA=" as *u8); gw(kA); gw(" ukeyB=" as *u8); gw(kB); gw("\n" as *u8) 96 pass = pass + grow("T0 ukey deterministic + collision-safe after sanitize\x00" as *u8, t0) 97 98 // fixture cleanliness: wipe any prior run's per-user state so T1 exercises the COLD path deterministically 99 let clp: *u8 = sys_mmap(1152) 100 sy_path("sgate" as *u8, "pos" as *u8, kA, ".json" as *u8, clp); g_unlink(clp) 101 sy_path("sgate" as *u8, "auto" as *u8, kA, ".txt" as *u8, clp); g_unlink(clp) 102 sy_path("sgate" as *u8, "pos" as *u8, kC, ".json" as *u8, clp); g_unlink(clp) 103 sy_path("sgate" as *u8, "auto" as *u8, kC, ".txt" as *u8, clp); g_unlink(clp) 104 sy_path("sgate" as *u8, "ann" as *u8, kA, ".json" as *u8, clp); g_unlink(clp) 105 sy_path("sgate" as *u8, "ann" as *u8, kC, ".json" as *u8, clp); g_unlink(clp) 106 107 // ---- T1 PUT -> GET round-trip (user A) ---- 108 var rn: i64 = g_put("/api/pos?slug=sgate" as *u8, "{\"page\":3,\"ts\":111}" as *u8, 19, kA, rsp, 65536) 109 var t1: i64 = 0 110 if rn > 0 { if g_has(rsp, rn, "HTTP/1.1 200" as *u8) == 1 { if g_has(rsp, rn, "\"ok\":1" as *u8) == 1 { 111 rn = g_get("/api/pos?slug=sgate" as *u8, kA, rsp, 65536) 112 if g_has(rsp, rn, "HTTP/1.1 200" as *u8) == 1 { if g_has(rsp, rn, "\"pos\":{\"page\":3,\"ts\":111}" as *u8) == 1 { if g_has(rsp, rn, "\"auto\":\"\"" as *u8) == 1 { t1 = 1 } } } 113 } } } 114 pass = pass + grow("T1 PUT->GET round-trip verbatim (user A) + auto empty\x00" as *u8, t1) 115 116 // ---- T2 auto-record from a chapter fetch ---- 117 let ar: i64 = sy_record_auto("/api/book?p=reader/sgate/chap007.txt" as *u8, 36, kA, 1234567) 118 rn = g_get("/api/pos?slug=sgate" as *u8, kA, rsp, 65536) 119 var t2: i64 = 0 120 if ar == 1 { if g_has(rsp, rn, "\"auto\":\"chap007.txt\"" as *u8) == 1 { if g_has(rsp, rn, "\"auto_ts\":1234567" as *u8) == 1 { t2 = 1 } } } 121 pass = pass + grow("T2 chapter fetch auto-records -> auto=chap007.txt ts=1234567 (zero-JS sync)\x00" as *u8, t2) 122 123 // ---- T3 user isolation ---- 124 rn = g_get("/api/pos?slug=sgate" as *u8, kC, rsp, 65536) 125 var t3: i64 = 0 126 if g_has(rsp, rn, "\"pos\":null" as *u8) == 1 { if g_has(rsp, rn, "\"auto\":\"\"" as *u8) == 1 { t3 = 1 } } 127 pass = pass + grow("T3 USER ISOLATION: user C sees pos:null + auto:'' on the same book\x00" as *u8, t3) 128 129 // ---- T4 NEG traversal slug ---- 130 rn = g_get("/api/pos?slug=..sgate" as *u8, kA, rsp, 65536) 131 var t4: i64 = 0 132 if g_has(rsp, rn, "HTTP/1.1 403" as *u8) == 1 { 133 rn = g_put("/api/pos?slug=..sgate" as *u8, "{}" as *u8, 2, kA, rsp, 65536) 134 if g_has(rsp, rn, "HTTP/1.1 403" as *u8) == 1 { t4 = 1 } 135 } 136 pass = pass + grow("T4 NEG traversal slug -> 403 on GET and PUT\x00" as *u8, t4) 137 138 // ---- T5 NEG oversize body -> 413 + nothing stored ; nonexistent book -> 404 ---- 139 let big: *u8 = sys_mmap(4096) 140 var bi: i64 = 0 141 while bi < 2100 { big[bi] = 120 as u8; bi = bi + 1 } // 'x' * 2100 > cap 2048 142 big[2100] = 0 as u8 143 rn = g_put("/api/pos?slug=sgate" as *u8, big, 2100, kC, rsp, 65536) 144 var t5: i64 = 0 145 if g_has(rsp, rn, "HTTP/1.1 413" as *u8) == 1 { 146 let cp: *u8 = sys_mmap(1152) 147 sy_path("sgate" as *u8, "pos" as *u8, kC, ".json" as *u8, cp) 148 if g_exists(cp) == 0 { 149 rn = g_put("/api/pos?slug=nobook" as *u8, "{}" as *u8, 2, kA, rsp, 65536) 150 if g_has(rsp, rn, "HTTP/1.1 404" as *u8) == 1 { t5 = 1 } 151 } 152 } 153 pass = pass + grow("T5 NEG oversize -> 413 + not stored ; nonexistent book -> 404\x00" as *u8, t5) 154 155 // ---- T6 LWW overwrite + no temp left ---- 156 g_put("/api/pos?slug=sgate" as *u8, "{\"page\":9,\"ts\":222}" as *u8, 19, kA, rsp, 65536) 157 rn = g_get("/api/pos?slug=sgate" as *u8, kA, rsp, 65536) 158 var t6: i64 = 0 159 if g_has(rsp, rn, "\"pos\":{\"page\":9,\"ts\":222}" as *u8) == 1 { 160 let pp: *u8 = sys_mmap(1152) 161 let pl: i64 = sy_path("sgate" as *u8, "pos" as *u8, kA, ".json" as *u8, pp) 162 let tp: *u8 = sys_mmap(1200) 163 var vo: i64 = 0 164 while vo < pl { tp[vo] = pp[vo]; vo = vo + 1 } 165 vo = 0 166 let sfx: *u8 = ".new" as *u8 167 while sfx[vo] != (0 as u8) { tp[pl + vo] = sfx[vo]; vo = vo + 1 } 168 tp[pl + vo] = 0 as u8 169 if g_exists(tp) == 0 { t6 = 1 } 170 } 171 pass = pass + grow("T6 LWW: second PUT wins on GET + no .new temp left (atomic)\x00" as *u8, t6) 172 173 // ---- T7 R2 ANNOTATIONS: PUT -> GET verbatim + user isolation + GET-as-export ---- 174 let abody: *u8 = "[{\"page\":2,\"text\":\"great panel\",\"anchor\":\"page002\",\"ts\":333}]" as *u8 175 rn = g_ann_put("/api/ann?slug=sgate" as *u8, abody, g_slen(abody), kA, rsp, 65536) 176 var t7: i64 = 0 177 if g_has(rsp, rn, "HTTP/1.1 200" as *u8) == 1 { if g_has(rsp, rn, "\"ok\":1" as *u8) == 1 { 178 rn = g_ann_get("/api/ann?slug=sgate" as *u8, kA, rsp, 65536) 179 if g_has(rsp, rn, "\"ann\":[{\"page\":2,\"text\":\"great panel\",\"anchor\":\"page002\",\"ts\":333}]" as *u8) == 1 { 180 rn = g_ann_get("/api/ann?slug=sgate" as *u8, kC, rsp, 65536) 181 if g_has(rsp, rn, "\"ann\":null" as *u8) == 1 { t7 = 1 } 182 } 183 } } 184 pass = pass + grow("T7 annotations PUT->GET verbatim (export-by-construction) + user isolation\x00" as *u8, t7) 185 186 // ---- T8 R2 NEG: traversal 403 + oversize 413 + not stored ---- 187 var t8: i64 = 0 188 rn = g_ann_put("/api/ann?slug=..sgate" as *u8, "[]" as *u8, 2, kA, rsp, 65536) 189 if g_has(rsp, rn, "HTTP/1.1 403" as *u8) == 1 { 190 let hb: *u8 = sys_mmap(70000) 191 var hbi: i64 = 0 192 while hbi < 66000 { hb[hbi] = 121 as u8; hbi = hbi + 1 } // 'y' * 66000 > 65536 193 rn = g_ann_put("/api/ann?slug=sgate" as *u8, hb, 66000, kC, rsp, 65536) 194 if g_has(rsp, rn, "HTTP/1.1 413" as *u8) == 1 { 195 let cp2: *u8 = sys_mmap(1152) 196 sy_path("sgate" as *u8, "ann" as *u8, kC, ".json" as *u8, cp2) 197 if g_exists(cp2) == 0 { t8 = 1 } 198 } 199 } 200 pass = pass + grow("T8 NEG annotations: traversal 403 + oversize 413 + not stored\x00" as *u8, t8) 201 202 gw("pass=" as *u8); gn(pass); gw("/9\n" as *u8) 203 if pass == 9 { gw("verdict=GREEN (R1+R2 sync spine: per-user position + annotations, verbatim + isolated + fail-closed, all atomic)\n" as *u8); sys_exit(0); return 0 } 204 gw("verdict=RED\n" as *u8); sys_exit(1); return 1 205}