code wiki / _hdl_build / _wiki_version_gate.nx

_wiki_version_gate.nx source

↩ module page · 297 lines · 15193 B

1// _wiki_version_gate.nx -- unit gate PROVING wiki R2 page VERSIONING: HISTORY 2// (the immutable wikirev:* rows enumerate in order), DIFF (line-level, non-empty 3// for a real change + empty for identical revisions), and ROLLBACK (auth-gated, 4// ADDITIVE -- restores old content as a NEW revision while every prior revision 5// survives), plus the liar-kill: an UNAUTH rollback is REFUSED with the store 6// UNCHANGED. 7// 8// NO mocks, NO live socket: drives the REAL R2 core (nx_wiki_version) on top of 9// the REAL R1 save (nx_wiki_page_save) + REAL doc store + the REAL reused auth 10// (armed NxAuthContext: persistent key bundle + OPAQUE account store + argon2id 11// KSF -- the exact production auth) + the REAL append-only seg_store. The auth 12// recipe mirrors _wiki_edit_gate.nx (armed ctx + register + login -> real token). 13// 14// Per the native-codegen desync landmine, EVERY assertion is judged on a 15// GROUND-TRUTH observable -- the BYTES read back out of the seg_store and the 16// COUNT of enumerated revisions -- never a bare returned status code. 17// 18// Distinct epochs: v1/v2/v3 are saved with distinct now_unix_s stamps 19// (base+10/+20/+30) so their wikirev:<slug>:<epoch> keys are distinct (a single 20// shared epoch would collide one rev key and under-count history). All offsets 21// sit well inside the session TTL so the freshly minted token still validates. 22// 23// Rows / markers: 24// history_ok : >= 3 revisions enumerate for the slug, in ascending epoch 25// order (the v1/v2/v3 epochs come back sorted). 26// diff_ok : nx_wiki_diff(v1,v3) is non-empty AND mentions a changed 27// line; nx_wiki_diff(v2,v2) is empty (0 bytes). 28// rollback_ok : authed nx_wiki_rollback(v1_epoch) -> wikicur readback now 29// equals v1's content (ground-truth bytes). 30// additive_ok : after rollback the revision count GREW by 1 AND v2's and 31// v3's revisions are STILL retrievable (history not truncated). 32// unauth_refused : nx_wiki_rollback with an empty token AND a garbage token 33// -> REFUSED, and wikicur is UNCHANGED (the rollback never 34// touched the store). 35// 36// JUDGED by the printed WIKIVERSION marker (verdict=GREEN only if EVERY 37// assertion holds), NOT by $?. 38// 39// Import set: nx_syscalls + wiki/nx_wiki_version (pulls the R2 core -> R1 save -> 40// reused auth flow -> seg_store) + wiki/nx_wiki_edit_handler (the doc-store + 41// auth context armers, same as the edit gate). Each base module splices exactly 42// ONCE (path-dedup) to avoid the double-import nxasm rc6 trap. 43// license_tier: ORIGINAL 44import "nx_syscalls.nx" 45import "wiki/nx_wiki_version.nx" 46import "wiki/nx_wiki_edit_handler.nx" 47 48// ----- tiny io helpers ----- 49func gw(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 } 50func gn(fd: i64, v: i64) -> i64 { 51 let bb: *u8 = sys_mmap(28); var m: i64 = v; if m < 0 { m = 0 - m; sys_write(fd, "-" as *u8, 1) } 52 let t: *u8 = sys_mmap(28); var k: i64 = 0; if m == 0 { t[0] = 48 as u8; k = 1 } 53 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 54 var i: i64 = 0; while i < k { bb[i] = t[k - 1 - i]; i = i + 1 } 55 sys_write(fd, bb, k); return 0 56} 57func w2(lfd: i64, s: *u8) -> i64 { gw(1, s); if lfd >= 0 { gw(lfd, s) } return 0 } 58func n2(lfd: i64, v: i64) -> i64 { gn(1, v); if lfd >= 0 { gn(lfd, v) } return 0 } 59 60// counted byte-equality 61func bytes_eq(a: *u8, an: i64, b: *u8, bn: i64) -> i64 { 62 if an != bn { return 0 } 63 var i: i64 = 0 64 while i < an { if a[i] != b[i] { return 0 } i = i + 1 } 65 return 1 66} 67 68// does NUL-terminated-free counted haystack contain counted needle? (substring) 69func contains(hay: *u8, hay_n: i64, needle: *u8, needle_n: i64) -> i64 { 70 if needle_n <= 0 { return 0 } 71 if hay_n < needle_n { return 0 } 72 var i: i64 = 0 73 while i + needle_n <= hay_n { 74 var k: i64 = 0 75 var hit: i64 = 1 76 while k < needle_n { if hay[i + k] != needle[k] { hit = 0; k = needle_n } else { k = k + 1 } } 77 if hit == 1 { return 1 } 78 i = i + 1 79 } 80 return 0 81} 82 83func main() -> i64 { 84 gw(1, "WIKIVERSION-GATE: start (history/diff/rollback; rollback additive+auth-gated; unauth refused)\n" as *u8) 85 86 // ===== armed auth context (REAL key bundle + account store; /tmp = fresh) ===== 87 let keys_path: *u8 = "/tmp/nishi_wiki_version_gate_keys.log" as *u8 88 let store_path: *u8 = "/tmp/nishi_wiki_version_gate_store.log" as *u8 89 let fz: i64 = sys_openat_wr(store_path, 0x180) 90 if fz >= 0 { sys_close(fz) } 91 92 let oprf_seed: *u8 = sys_mmap(32) 93 let akp: *u8 = sys_mmap(32) 94 let akb: *u8 = sys_mmap(33) 95 let edp: *u8 = sys_mmap(32) 96 let edb: *u8 = sys_mmap(32) 97 if nx_uas_server_keys_load_or_init(keys_path, oprf_seed, akp, akb, edp, edb) != NX_UAS_OK { 98 gw(1, "WIKIVERSION revs=0 history_ok=0 diff_ok=0 rollback_ok=0 additive_ok=0 unauth_refused=0 verdict=RED (keys init failed)\n" as *u8) 99 sys_exit(1); return 1 100 } 101 let realm: *u8 = "Nishi Wiki" as *u8 102 let ctx: *NxAuthContext = sys_mmap(256) as *NxAuthContext 103 nx_auth_context_init(ctx, realm, 10, realm, 10, 104 store_path as i64, oprf_seed, edp, edb, 105 900, 8192, 1, 1, 5, 1) 106 107 let handle: *u8 = "elderwesto" as *u8 108 let pw: *u8 = "correct horse+battery!" as *u8 // 22 bytes 109 let mn: *u8 = sys_mmap(512) 110 let mn_n: *i64 = sys_mmap(16) as *i64 111 nx_modern_auth_register(ctx, handle, 10, pw, 22, mn, 512, mn_n) 112 113 let tok: *u8 = sys_mmap(NX_MAUTH_SESSION_TOKEN_BYTES + 8) 114 let tok_n: *i64 = sys_mmap(8) as *i64 115 tok_n[0] = 0 116 let login_rc: i64 = nx_modern_auth_login(ctx, handle, 10, pw, 22, 117 tok, NX_MAUTH_SESSION_TOKEN_BYTES, tok_n) 118 let now_s: i64 = sys_now_realtime_sec() 119 var token_valid: i64 = 0 120 if login_rc == NX_MAUTH_OK { 121 if tok_n[0] == NX_MAUTH_SESSION_TOKEN_BYTES { 122 if nx_modern_auth_validate_session(ctx, tok, tok_n[0], now_s, 123 (0 as i64) as *u8, 0, (0 as i64) as *i64) == NX_MAUTH_OK { 124 token_valid = 1 125 } 126 } 127 } 128 gw(1, " minted+validated real session token: "); gn(1, token_valid); gw(1, "\n" as *u8) 129 130 // ===== fresh doc store + UNIQUE slug for this run ===== 131 let doc_store: *NxWikiDocStore = sys_mmap(256) as *NxWikiDocStore 132 if nx_wiki_doc_store_init(doc_store, 100, 65536, 65536, 4194304) != NX_WIB_OK { 133 gw(1, "WIKIVERSION revs=0 history_ok=0 diff_ok=0 rollback_ok=0 additive_ok=0 unauth_refused=0 verdict=RED (doc store init failed)\n" as *u8) 134 sys_exit(1); return 1 135 } 136 // slug = "ver-gate-<epoch>" (unique per run; keeps the shared store clean) 137 let slug: *u8 = sys_mmap(64) 138 var so: i64 = 0 139 let sp: *u8 = "ver-gate-" as *u8 140 while sp[so] != (0 as u8) { slug[so] = sp[so]; so = so + 1 } 141 var ev: i64 = now_s 142 let et: *u8 = sys_mmap(28); var ek: i64 = 0 143 if ev == 0 { et[0] = 48 as u8; ek = 1 } 144 while ev > 0 { et[ek] = (48 + (ev % 10)) as u8; ev = ev / 10; ek = ek + 1 } 145 var ei: i64 = 0 146 while ei < ek { slug[so + ei] = et[ek - 1 - ei]; ei = ei + 1 } 147 let slug_n: i64 = so + ek 148 149 // seed the page into the doc store 150 let url: *u8 = sys_mmap(NX_WPS_URL_CAP) 151 let url_n: i64 = nx_wps_url(slug, slug_n, url) 152 nx_wiki_doc_store_add(doc_store, slug, slug_n, url, url_n, "# seed\nold body\n" as *u8, 16) 153 154 // ===== three distinct AUTHED saves with DISTINCT epochs ===== 155 // v1/v2/v3 share a common first line + differ on line 2 so the diff has a 156 // clean removed/added pair to surface. Epochs base+10/+20/+30. 157 let e1: i64 = now_s + 10 158 let e2: i64 = now_s + 20 159 let e3: i64 = now_s + 30 160 let v1: *u8 = "# title\nalpha one\ncommon tail\n" as *u8 // 30 bytes 161 let v2: *u8 = "# title\nbeta two\ncommon tail\n" as *u8 // 29 bytes 162 let v3: *u8 = "# title\ngamma three\ncommon tail\n" as *u8 // 32 bytes 163 let v1n: i64 = 30 164 let v2n: i64 = 29 165 let v3n: i64 = 32 166 nx_wiki_page_save(doc_store, ctx, slug, slug_n, v1, v1n, tok, tok_n[0], e1) 167 nx_wiki_page_save(doc_store, ctx, slug, slug_n, v2, v2n, tok, tok_n[0], e2) 168 nx_wiki_page_save(doc_store, ctx, slug, slug_n, v3, v3n, tok, tok_n[0], e3) 169 170 // ===== ROW history_ok: >=3 revisions, ascending epoch order ===== 171 let epochs: *i64 = sys_mmap(8 * 256) as *i64 172 let clens: *i64 = sys_mmap(8 * 256) as *i64 173 let nrev: i64 = nx_wiki_history(slug, slug_n, epochs, clens, 256) 174 var order_ok: i64 = 1 175 var oi: i64 = 1 176 while oi < nrev { if epochs[oi] < epochs[oi - 1] { order_ok = 0 } oi = oi + 1 } 177 // confirm our three distinct epochs are all present 178 var have1: i64 = 0 179 var have2: i64 = 0 180 var have3: i64 = 0 181 var hi: i64 = 0 182 while hi < nrev { 183 if epochs[hi] == e1 { have1 = 1 } 184 if epochs[hi] == e2 { have2 = 1 } 185 if epochs[hi] == e3 { have3 = 1 } 186 hi = hi + 1 187 } 188 var history_ok: i64 = 0 189 if nrev >= 3 { if order_ok == 1 { if have1 == 1 { if have2 == 1 { if have3 == 1 { history_ok = 1 } } } } } 190 gw(1, " history revs="); gn(1, nrev) 191 gw(1, " order_ok="); gn(1, order_ok) 192 gw(1, " have(v1,v2,v3)="); gn(1, have1); gn(1, have2); gn(1, have3); gw(1, "\n" as *u8) 193 194 // ===== ROW diff_ok: diff(v1,v3) non-empty+shows change; diff(v2,v2) empty ===== 195 let dbuf: *u8 = sys_mmap(65536) 196 let d13: i64 = nx_wiki_diff(slug, slug_n, e1, e3, dbuf, 65536) 197 // the change is line 2: "alpha one" removed, "gamma three" added 198 var shows_removed: i64 = contains(dbuf, d13, "alpha one" as *u8, 9) 199 var shows_added: i64 = contains(dbuf, d13, "gamma three" as *u8, 11) 200 // common first/last lines must NOT appear (context-free unified diff) 201 var no_common: i64 = 1 202 if contains(dbuf, d13, "common tail" as *u8, 11) == 1 { no_common = 0 } 203 let dbuf2: *u8 = sys_mmap(4096) 204 let d22: i64 = nx_wiki_diff(slug, slug_n, e2, e2, dbuf2, 4096) 205 var diff_ok: i64 = 0 206 if d13 > 0 { if shows_removed == 1 { if shows_added == 1 { if no_common == 1 { if d22 == 0 { diff_ok = 1 } } } } } 207 gw(1, " diff(v1,v3) bytes="); gn(1, d13) 208 gw(1, " removed='alpha one':"); gn(1, shows_removed) 209 gw(1, " added='gamma three':"); gn(1, shows_added) 210 gw(1, " no_common_line="); gn(1, no_common) 211 gw(1, " diff(v2,v2) bytes="); gn(1, d22); gw(1, "\n" as *u8) 212 213 // ===== ROW unauth_refused: rollback with bad tokens -> REFUSED, store UNCHANGED ===== 214 // current head is v3; an unauth rollback must NOT change it. 215 let er: i64 = now_s + 40 // distinct epoch for any rollback 216 let bad_tok: *u8 = sys_mmap(NX_MAUTH_SESSION_TOKEN_BYTES + 8) 217 let ur1: i64 = nx_wiki_rollback(doc_store, ctx, slug, slug_n, e1, bad_tok, 0, er) 218 var refused1: i64 = 0 219 if ur1 == 0 - NX_WV_REFUSED { refused1 = 1 } 220 let garb: *u8 = sys_mmap(NX_MAUTH_SESSION_TOKEN_BYTES + 8) 221 var gi: i64 = 0 222 while gi < NX_MAUTH_SESSION_TOKEN_BYTES { garb[gi] = 65 as u8; gi = gi + 1 } 223 let ur2: i64 = nx_wiki_rollback(doc_store, ctx, slug, slug_n, e1, garb, NX_MAUTH_SESSION_TOKEN_BYTES, er) 224 var refused2: i64 = 0 225 if ur2 == 0 - NX_WV_REFUSED { refused2 = 1 } 226 // ground truth: wikicur still == v3 227 let rbU: *u8 = sys_mmap(NX_WPS_MAX_CONTENT_LEN + 16) 228 let rbU_n: i64 = nx_wiki_page_load_current(slug, slug_n, rbU, NX_WPS_MAX_CONTENT_LEN) 229 var unchanged: i64 = 0 230 if rbU_n >= 0 { if bytes_eq(rbU, rbU_n, v3, v3n) == 1 { unchanged = 1 } } 231 var unauth_refused: i64 = 0 232 if refused1 == 1 { if refused2 == 1 { if unchanged == 1 { unauth_refused = 1 } } } 233 gw(1, " unauth rollback refused(empty)="); gn(1, refused1) 234 gw(1, " refused(garbage)="); gn(1, refused2) 235 gw(1, " head_unchanged(=v3)="); gn(1, unchanged); gw(1, "\n" as *u8) 236 237 // ===== ROW rollback_ok + additive_ok: AUTHED rollback to v1 ===== 238 let nrev_before: i64 = nrev 239 let roll_rc: i64 = nx_wiki_rollback(doc_store, ctx, slug, slug_n, e1, tok, tok_n[0], er) 240 // ground truth #1: wikicur now == v1's content 241 let rbR: *u8 = sys_mmap(NX_WPS_MAX_CONTENT_LEN + 16) 242 let rbR_n: i64 = nx_wiki_page_load_current(slug, slug_n, rbR, NX_WPS_MAX_CONTENT_LEN) 243 var rollback_ok: i64 = 0 244 if roll_rc == NX_WV_OK { if rbR_n >= 0 { if bytes_eq(rbR, rbR_n, v1, v1n) == 1 { rollback_ok = 1 } } } 245 gw(1, " rollback rc="); gn(1, roll_rc) 246 gw(1, " wikicur readback len="); gn(1, rbR_n) 247 gw(1, " eq(v1)="); gn(1, rollback_ok); gw(1, "\n" as *u8) 248 249 // ground truth #2 (ADDITIVE): history grew by 1 AND v2/v3 revs still present 250 let epochs2: *i64 = sys_mmap(8 * 256) as *i64 251 let clens2: *i64 = sys_mmap(8 * 256) as *i64 252 let nrev_after: i64 = nx_wiki_history(slug, slug_n, epochs2, clens2, 256) 253 var grew_by_1: i64 = 0 254 if nrev_after == nrev_before + 1 { grew_by_1 = 1 } 255 // v2 + v3 revisions must STILL be retrievable byte-for-byte (not truncated) 256 let chk: *u8 = sys_mmap(NX_WPS_MAX_CONTENT_LEN + 16) 257 let g2n: i64 = nx_wiki_rev_get(slug, slug_n, e2, chk, NX_WPS_MAX_CONTENT_LEN) 258 var v2_still: i64 = 0 259 if g2n >= 0 { if bytes_eq(chk, g2n, v2, v2n) == 1 { v2_still = 1 } } 260 let chk3: *u8 = sys_mmap(NX_WPS_MAX_CONTENT_LEN + 16) 261 let g3n: i64 = nx_wiki_rev_get(slug, slug_n, e3, chk3, NX_WPS_MAX_CONTENT_LEN) 262 var v3_still: i64 = 0 263 if g3n >= 0 { if bytes_eq(chk3, g3n, v3, v3n) == 1 { v3_still = 1 } } 264 // and the NEW rollback revision (epoch er) holds v1's content 265 let chkr: *u8 = sys_mmap(NX_WPS_MAX_CONTENT_LEN + 16) 266 let grn: i64 = nx_wiki_rev_get(slug, slug_n, er, chkr, NX_WPS_MAX_CONTENT_LEN) 267 var newrev_is_v1: i64 = 0 268 if grn >= 0 { if bytes_eq(chkr, grn, v1, v1n) == 1 { newrev_is_v1 = 1 } } 269 var additive_ok: i64 = 0 270 if grew_by_1 == 1 { if v2_still == 1 { if v3_still == 1 { if newrev_is_v1 == 1 { additive_ok = 1 } } } } 271 gw(1, " additive: revs "); gn(1, nrev_before); gw(1, "->"); gn(1, nrev_after) 272 gw(1, " v2_still="); gn(1, v2_still) 273 gw(1, " v3_still="); gn(1, v3_still) 274 gw(1, " new_rev_eq_v1="); gn(1, newrev_is_v1); gw(1, "\n" as *u8) 275 276 // ===== verdict + marker line ===== 277 var green: i64 = 1 278 if token_valid != 1 { green = 0 } 279 if history_ok != 1 { green = 0 } 280 if diff_ok != 1 { green = 0 } 281 if rollback_ok != 1 { green = 0 } 282 if additive_ok != 1 { green = 0 } 283 if unauth_refused != 1 { green = 0 } 284 285 let lfd: i64 = sys_openat_append("knowledge/status/wiki_version_gate.log" as *u8, 420) 286 w2(lfd, "WIKIVERSION revs=" as *u8); n2(lfd, nrev_after) 287 w2(lfd, " history_ok=" as *u8); n2(lfd, history_ok) 288 w2(lfd, " diff_ok=" as *u8); n2(lfd, diff_ok) 289 w2(lfd, " rollback_ok=" as *u8); n2(lfd, rollback_ok) 290 w2(lfd, " additive_ok=" as *u8); n2(lfd, additive_ok) 291 w2(lfd, " unauth_refused=" as *u8); n2(lfd, unauth_refused) 292 if green == 1 { w2(lfd, " verdict=GREEN\n" as *u8) } else { w2(lfd, " verdict=RED\n" as *u8) } 293 if lfd >= 0 { sys_close(lfd) } 294 295 if green == 1 { sys_exit(0); return 0 } 296 sys_exit(1); return 1 297}