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}