code wiki / _hdl_build / _wiki_archive_gate.nx

_wiki_archive_gate.nx source

↩ module page · 253 lines · 12441 B

1// _wiki_archive_gate.nx -- THE NO-LINK-ROT GATE. 2// 3// Re-proves, from a REAL run (no fabricated GREEN), that the sovereign wiki 4// archive makes link rot structurally impossible via content-addressing: 5// 6// by_cid_ok archive pages -> each retrievable by its CID, and the 7// readback bytes are BYTE-EQUAL to the original 8// idempotent_ok re-archive identical bytes -> SAME CID, and the store does 9// not grow a NEW distinguishable blob (content-address dedup) 10// rot_clean rot audit over a clean inter-linked set -> rot == 0 11// rot_detected LIAR-KILL: a page with [[ZzqNonexistentTarget]] -> rot >= 1 12// (a real broken internal link MUST be caught, else RED) 13// noloss_ok archive a page, DROP its slug pointer (tombstone), retrieve 14// by CID -> STILL returns the exact bytes (nothing is lost) 15// 16// EXCEED vs Wikipedia: Wikipedia depends on the Internet Archive to paper over 17// rot after links die. Here a CID is derived from the content, so a CID link 18// resolves to the SAME bytes forever -- rot is a non-event by construction. 19// 20// Determinism: each run uses a FRESH store prefix keyed by the wall-clock epoch 21// so the gate starts from an empty archive (idempotent re-run, Rule 10). The 22// PRODUCTION archive prefix is knowledge/store/wikiarchive- (in nx_wiki_archive). 23// 24// Verdict line (judged by this marker, to stdout + knowledge/status): 25// WIKIARCHIVE archived=<n> by_cid_ok=<0|1> idempotent_ok=<0|1> 26// rot_clean=<0|1> rot_detected=<0|1> noloss_ok=<0|1> verdict=GREEN|RED 27// 28// Pure NishiLang, NO SQL, NO .sh/.py/.js, no new .tsv/.conf. nx_sites_daemon 29// UNTOUCHED. Imports the proven trio + the archive module under test. 30// license_tier: ORIGINAL 31import "nx_syscalls.nx" 32import "nx_canon_cid.nx" 33import "nx_seg_store.nx" 34import "nx_wiki_archive.nx" 35 36const WAG_LOG: *u8 = "knowledge/status/wiki_archive_gate.log" 37const WAG_CAP: i64 = 64 // manifest segment-scan cap (>> our few commits) 38 39// ---- io helpers ---- 40func wag_w(fd: i64, s: *u8) -> i64 { 41 var n: i64 = 0 42 while s[n] != (0 as u8) { n = n + 1 } 43 sys_write(fd, s, n) 44 return 0 45} 46func wag_n(fd: i64, v: i64) -> i64 { 47 let bb: *u8 = sys_mmap(28) 48 var m: i64 = v 49 if m < 0 { m = 0 - m; sys_write(fd, "-" as *u8, 1) } 50 let t: *u8 = sys_mmap(28) 51 var k: i64 = 0 52 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 55 while i < k { bb[i] = t[k - 1 - i]; i = i + 1 } 56 sys_write(fd, bb, k) 57 return 0 58} 59// write to BOTH stdout (gate evidence) and the durable log 60func wag_w2(lfd: i64, s: *u8) -> i64 { wag_w(1, s); if lfd >= 0 { wag_w(lfd, s) } return 0 } 61func wag_n2(lfd: i64, v: i64) -> i64 { wag_n(1, v); if lfd >= 0 { wag_n(lfd, v) } return 0 } 62 63func wag_p(s: *u8) -> i64 { wag_w(1, s); return 0 } 64func wag_pn(v: i64) -> i64 { wag_n(1, v); return 0 } 65 66func wag_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 67 68// concat NUL-terminated s into dst at off; returns new off 69func wag_cat(dst: *u8, off: i64, s: *u8) -> i64 { 70 var i: i64 = 0 71 while s[i] != (0 as u8) { dst[off + i] = s[i]; i = i + 1 } 72 return off + i 73} 74// append decimal of v into dst at off; returns new off 75func wag_catn(dst: *u8, off: i64, v: i64) -> i64 { 76 var m: i64 = v 77 var o: i64 = off 78 if m < 0 { dst[o] = 45 as u8; o = o + 1; m = 0 - m } 79 let t: *u8 = sys_mmap(28) 80 var k: i64 = 0 81 if m == 0 { t[0] = 48 as u8; k = 1 } 82 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 83 var i: i64 = 0 84 while i < k { dst[o + i] = t[k - 1 - i]; i = i + 1 } 85 return o + k 86} 87 88// byte-equal compare of two buffers of equal length n 89func wag_bytes_eq(a: *u8, b: *u8, n: i64) -> i64 { 90 var i: i64 = 0 91 var eq: i64 = 1 92 while i < n { 93 if a[i] != b[i] { eq = 0 } 94 i = i + 1 95 } 96 return eq 97} 98 99func main() -> i64 { 100 wag_p("WIKIARCHIVE-GATE: start (no-link-rot, content-addressed archive)\n" as *u8) 101 102 // ---- FRESH per-run store prefix: knowledge/store/wikiarchive-gate-<epoch>- ---- 103 // (empty archive each run => deterministic; idempotent re-run.) 104 let prefix: *u8 = sys_mmap(256) 105 var po: i64 = 0 106 po = wag_cat(prefix, po, "knowledge/store/wikiarchive-gate-" as *u8) 107 po = wag_catn(prefix, po, sys_now_realtime_sec()) 108 po = wag_cat(prefix, po, "-" as *u8) 109 prefix[po] = 0 as u8 110 wag_p(" store prefix = " as *u8); wag_p(prefix); wag_p("\n" as *u8) 111 112 // ===== STEP 1: seed a small inter-linked corpus and ARCHIVE it ===== 113 // Three clean pages whose [[..]] links all point at archived slugs. 114 let s_alpha: *u8 = "alpha" as *u8 115 let s_beta: *u8 = "beta" as *u8 116 let s_gamma: *u8 = "gamma" as *u8 117 let b_alpha: *u8 = "# Alpha\nSee [[beta]] and [[gamma]]. Ref http://example.com/x here.\n" as *u8 118 let b_beta: *u8 = "# Beta\nBack to [[alpha]]. Also https://nishi.example/y external.\n" as *u8 119 let b_gamma: *u8 = "# Gamma\nLinks to [[alpha]] and [[beta]] only.\n" as *u8 120 121 let w: *i64 = ss_begin() 122 let cid_a: *u8 = sys_mmap(80) 123 let cid_b: *u8 = sys_mmap(80) 124 let cid_g: *u8 = sys_mmap(80) 125 let ra: i64 = war_archive_page(w, s_alpha, b_alpha, wag_slen(b_alpha), cid_a) 126 let rb: i64 = war_archive_page(w, s_beta, b_beta, wag_slen(b_beta), cid_b) 127 let rg: i64 = war_archive_page(w, s_gamma, b_gamma, wag_slen(b_gamma), cid_g) 128 let c1: i64 = ss_commit(prefix, w, 0) 129 var archived: i64 = 0 130 if ra == 0 { archived = archived + 1 } 131 if rb == 0 { archived = archived + 1 } 132 if rg == 0 { archived = archived + 1 } 133 wag_p(" archived pages = " as *u8); wag_pn(archived); wag_p(" (commit rc=" as *u8); wag_pn(c1); wag_p(")\n" as *u8) 134 wag_p(" cid(alpha) = " as *u8); wag_p(cid_a); wag_p("\n" as *u8) 135 136 // ===== STEP 2: BY-CID retrieval + BYTE-EQUALITY (ground truth) ===== 137 // Read each page back by its CID; the bytes MUST equal the originals. 138 var by_cid_ok: i64 = 1 139 let pa: *i64 = sys_mmap(16) as *i64 140 let pb: *i64 = sys_mmap(16) as *i64 141 let pg: *i64 = sys_mmap(16) as *i64 142 let la: i64 = war_get_by_cid(prefix, cid_a, pa, WAG_CAP) 143 let lb: i64 = war_get_by_cid(prefix, cid_b, pb, WAG_CAP) 144 let lg: i64 = war_get_by_cid(prefix, cid_g, pg, WAG_CAP) 145 if la != wag_slen(b_alpha) { by_cid_ok = 0 } 146 if lb != wag_slen(b_beta) { by_cid_ok = 0 } 147 if lg != wag_slen(b_gamma) { by_cid_ok = 0 } 148 if la == wag_slen(b_alpha) { if wag_bytes_eq(pa[0] as *u8, b_alpha, la) != 1 { by_cid_ok = 0 } } 149 if lb == wag_slen(b_beta) { if wag_bytes_eq(pb[0] as *u8, b_beta, lb) != 1 { by_cid_ok = 0 } } 150 if lg == wag_slen(b_gamma) { if wag_bytes_eq(pg[0] as *u8, b_gamma, lg) != 1 { by_cid_ok = 0 } } 151 wag_p(" by_cid_ok = " as *u8); wag_pn(by_cid_ok); wag_p(" (lens " as *u8); wag_pn(la); wag_p("/" as *u8); wag_pn(lb); wag_p("/" as *u8); wag_pn(lg); wag_p(")\n" as *u8) 152 153 // ===== STEP 3: IDEMPOTENT re-archive (content-address dedup) ===== 154 // Re-archive alpha's identical bytes -> SAME cid; readback still byte-equal; 155 // a DIFFERENT body for the same slug -> DIFFERENT cid (content drives the key). 156 var idempotent_ok: i64 = 1 157 let w2: *i64 = ss_begin() 158 let cid_a2: *u8 = sys_mmap(80) 159 war_archive_page(w2, s_alpha, b_alpha, wag_slen(b_alpha), cid_a2) 160 let c2: i64 = ss_commit(prefix, w2, 1) 161 if c2 != 0 { idempotent_ok = 0 } 162 // same bytes -> identical cid string 163 if wag_bytes_eq(cid_a, cid_a2, 69) != 1 { idempotent_ok = 0 } 164 // and the blob is still retrievable + byte-equal (no shadow/duplication broke it) 165 let pa2: *i64 = sys_mmap(16) as *i64 166 let la2: i64 = war_get_by_cid(prefix, cid_a2, pa2, WAG_CAP) 167 if la2 != wag_slen(b_alpha) { idempotent_ok = 0 } 168 if la2 == wag_slen(b_alpha) { if wag_bytes_eq(pa2[0] as *u8, b_alpha, la2) != 1 { idempotent_ok = 0 } } 169 // content-address PROOF: different content -> different cid 170 let cid_diff: *u8 = sys_mmap(80) 171 let b_diff: *u8 = "# Alpha (edited)\nDifferent bytes entirely.\n" as *u8 172 war_cid_of_body(b_diff, wag_slen(b_diff), cid_diff) 173 if wag_bytes_eq(cid_a, cid_diff, 69) == 1 { idempotent_ok = 0 } // MUST differ 174 wag_p(" idempotent_ok = " as *u8); wag_pn(idempotent_ok); wag_p("\n" as *u8) 175 176 // ===== STEP 4: ROT AUDIT -- clean set (rot==0) ===== 177 // All three clean pages link only to archived slugs => zero rot. External 178 // http(s) links are COUNTED as external_pending, never as rot. 179 let oa: *i64 = sys_mmap(8 * 8) as *i64 180 let brok: *u8 = sys_mmap(128) 181 var rot_total: i64 = 0 182 var ext_total: i64 = 0 183 nx_wiki_rot_audit_page(prefix, b_alpha, wag_slen(b_alpha), WAG_CAP, oa, brok) 184 rot_total = rot_total + oa[1]; ext_total = ext_total + oa[2] 185 nx_wiki_rot_audit_page(prefix, b_beta, wag_slen(b_beta), WAG_CAP, oa, brok) 186 rot_total = rot_total + oa[1]; ext_total = ext_total + oa[2] 187 nx_wiki_rot_audit_page(prefix, b_gamma, wag_slen(b_gamma), WAG_CAP, oa, brok) 188 rot_total = rot_total + oa[1]; ext_total = ext_total + oa[2] 189 var rot_clean: i64 = 0 190 if rot_total == 0 { rot_clean = 1 } 191 wag_p(" rot_clean = " as *u8); wag_pn(rot_clean); wag_p(" (rot=" as *u8); wag_pn(rot_total); wag_p(" external_pending=" as *u8); wag_pn(ext_total); wag_p(")\n" as *u8) 192 193 // ===== STEP 5: NEGATIVE CONTROL / LIAR-KILL -- a real broken link ===== 194 // A page that links to a slug NEVER archived MUST report rot>=1; if the 195 // audit fails to catch it, the gate goes RED (no false-green). 196 let b_bad: *u8 = "# Broken\nThis points at [[ZzqNonexistentTarget]] which was never archived.\n" as *u8 197 let ob: *i64 = sys_mmap(8 * 8) as *i64 198 let brok2: *u8 = sys_mmap(128) 199 nx_wiki_rot_audit_page(prefix, b_bad, wag_slen(b_bad), WAG_CAP, ob, brok2) 200 var rot_detected: i64 = 0 201 if ob[1] >= 1 { rot_detected = 1 } 202 wag_p(" rot_detected = " as *u8); wag_pn(rot_detected); wag_p(" (broken_rot=" as *u8); wag_pn(ob[1]); wag_p(" first_broken=" as *u8); wag_p(brok2); wag_p(")\n" as *u8) 203 204 // ===== STEP 6: NO-LOSS -- drop the slug pointer, retrieve by CID ===== 205 // Tombstone wikicid:<gamma> (slug pointer GONE) then prove the blob is STILL 206 // retrievable by its CID -- the content is never lost, only the name pointer. 207 var noloss_ok: i64 = 1 208 let w3: *i64 = ss_begin() 209 let ck_g: *u8 = sys_mmap(128) 210 var cko: i64 = 0 211 cko = wag_cat(ck_g, cko, "wikicid:" as *u8) 212 cko = wag_cat(ck_g, cko, s_gamma) 213 ck_g[cko] = 0 as u8 214 ss_add(w3, 2, ck_g, "" as *u8, 0) // kind 2 = tombstone the pointer 215 let c3: i64 = ss_commit(prefix, w3, 2) 216 if c3 != 0 { noloss_ok = 0 } 217 // slug pointer must now read as GONE... 218 let cid_chk: *u8 = sys_mmap(80) 219 let slug_now: i64 = war_cid_of_slug(prefix, s_gamma, cid_chk, WAG_CAP) 220 if slug_now != (0 - 1) { noloss_ok = 0 } // expect -1 (dropped) 221 // ...yet the CONTENT is still retrievable by its CID, byte-equal. 222 let pg2: *i64 = sys_mmap(16) as *i64 223 let lg2: i64 = war_get_by_cid(prefix, cid_g, pg2, WAG_CAP) 224 if lg2 != wag_slen(b_gamma) { noloss_ok = 0 } 225 if lg2 == wag_slen(b_gamma) { if wag_bytes_eq(pg2[0] as *u8, b_gamma, lg2) != 1 { noloss_ok = 0 } } 226 wag_p(" noloss_ok = " as *u8); wag_pn(noloss_ok); wag_p(" (slug_gamma_now=" as *u8); wag_pn(slug_now); wag_p(" cid_readback_len=" as *u8); wag_pn(lg2); wag_p(")\n" as *u8) 227 228 // ===== VERDICT ===== 229 var green: i64 = 1 230 if archived != 3 { green = 0 } 231 if by_cid_ok != 1 { green = 0 } 232 if idempotent_ok != 1 { green = 0 } 233 if rot_clean != 1 { green = 0 } 234 if rot_detected != 1 { green = 0 } 235 if noloss_ok != 1 { green = 0 } 236 237 let lfd: i64 = sys_openat_append(WAG_LOG, 420) 238 wag_w2(lfd, "WIKIARCHIVE archived=" as *u8); wag_n2(lfd, archived) 239 wag_w2(lfd, " by_cid_ok=" as *u8); wag_n2(lfd, by_cid_ok) 240 wag_w2(lfd, " idempotent_ok=" as *u8); wag_n2(lfd, idempotent_ok) 241 wag_w2(lfd, " rot_clean=" as *u8); wag_n2(lfd, rot_clean) 242 wag_w2(lfd, " rot_detected=" as *u8); wag_n2(lfd, rot_detected) 243 wag_w2(lfd, " noloss_ok=" as *u8); wag_n2(lfd, noloss_ok) 244 if green == 1 { wag_w2(lfd, " verdict=GREEN\n" as *u8) } else { wag_w2(lfd, " verdict=RED\n" as *u8) } 245 if lfd >= 0 { sys_close(lfd) } 246 247 if green == 1 { 248 sys_exit(0) 249 return 0 250 } 251 sys_exit(1) 252 return 1 253}