code wiki / _hdl_build / nx_gen_cid_gate.nx

nx_gen_cid_gate.nx source

↩ module page · 155 lines · 7613 B

1// nx_gen_cid_gate.nx -- REFEREE for the GALLERY CID CANON (debt 1785881333 / 1785881657). 2// 3// WHAT IT PROTECTS. The gallery CID is NOT a hash of the pixels, despite the name: it is a hash of a 4// CANONICAL FORM built from the PNG's tEXt fields. Twice on 2026-08-04 that canon was missing a 5// field that genuinely changes the image, and the consequence each time was SILENT DATA LOSS -- the 6// second render collided onto the first cid and gp_one's openat_wr truncated the earlier blob: 7// * resolution: 512x512 (md5 a9fa671a) then 768x1024 under the SAME cid -> the 512 image was gone. 8// * cfg_scale: cfg 1.0 (md5 f10849c2) vs 5.0 (md5 6471d466), two different pictures, ONE cid. 9// Fixed by adding `size` and then `args` (the exact generator-facing argument string) to the canon. 10// 11// ★WHY A GATE AND NOT JUST THE FIX: the second collision existed the whole time the first was being 12// celebrated as fixed. Nothing was checking, so a narrower trigger survived. This gate encodes the 13// INVARIANT rather than the two instances: ANY tEXt field that differs must move the cid. 14// 15// ★HERMETIC AND GPU-FREE. It builds tiny synthetic PNGs in memory and drives the REAL 16// png_insert_genrec + nx_store_ingest_compute_cid -- the same code the daemon runs. No render, no 17// engine, no network, so it is safe on any cadence. (A gate that needs a GPU is a gate nobody runs.) 18// 19// license_tier: ORIGINAL expect_exit: 0 20// module: nishi-core.gen.cidgate 21import "nx_syscalls.nx" 22import "nx_png_textw.nx" 23import "nx_store_ingest.nx" 24 25const CG_BUF: i64 = 262144 26 27func cw(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 28func cn(v: i64) -> i64 { 29 let t: *u8 = sys_mmap(32); let b: *u8 = sys_mmap(32) 30 var m: i64 = v; var k: i64 = 0 31 if m == 0 { t[0] = 48 as u8; k = 1 } 32 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 33 var i: i64 = 0 34 while i < k { b[i] = t[k - 1 - i]; i = i + 1 } 35 sys_write(1, b, k); return 0 36} 37 38// minimal but STRUCTURALLY VALID PNG: signature + IHDR + IEND. png_insert_genrec splices the tEXt 39// chunks in before IEND, exactly as it does for a real render. 40func cg_be32(b: *u8, o: i64, v: i64) -> i64 { 41 b[o] = ((v >> 24) & 0xff) as u8; b[o+1] = ((v >> 16) & 0xff) as u8 42 b[o+2] = ((v >> 8) & 0xff) as u8; b[o+3] = (v & 0xff) as u8 43 return o + 4 44} 45func cg_stub(dst: *u8, w: i64, h: i64) -> i64 { 46 dst[0]=137 as u8; dst[1]=80 as u8; dst[2]=78 as u8; dst[3]=71 as u8 47 dst[4]=13 as u8; dst[5]=10 as u8; dst[6]=26 as u8; dst[7]=10 as u8 48 var o: i64 = 8 49 o = cg_be32(dst, o, 13) 50 dst[o]=73 as u8; dst[o+1]=72 as u8; dst[o+2]=68 as u8; dst[o+3]=82 as u8 // IHDR 51 o = o + 4 52 o = cg_be32(dst, o, w) 53 o = cg_be32(dst, o, h) 54 dst[o]=8 as u8; dst[o+1]=6 as u8; dst[o+2]=0 as u8; dst[o+3]=0 as u8; dst[o+4]=0 as u8 55 o = o + 5 56 o = cg_be32(dst, o, 0) 57 o = cg_be32(dst, o, 0) 58 dst[o]=73 as u8; dst[o+1]=69 as u8; dst[o+2]=78 as u8; dst[o+3]=68 as u8 // IEND 59 o = o + 4 60 o = cg_be32(dst, o, 0) 61 return o 62} 63 64// build a genrec'd PNG and return its CID text in cidout (69 bytes) 65func cg_cid(seed: *u8, model: *u8, sampler: *u8, steps: *u8, host: *u8, prompt: *u8, size: *u8, args: *u8, cidout: *u8) -> i64 { 66 let stub: *u8 = sys_mmap(CG_BUF) 67 let sn: i64 = cg_stub(stub, 64, 64) 68 let withtext: *u8 = sys_mmap(CG_BUF) 69 let wlen: i64 = png_insert_genrec(stub, sn, seed, model, sampler, steps, host, prompt, size, args, withtext) 70 if wlen < 0 { return 0 - 1 } 71 let canon: *u8 = sys_mmap(65536) 72 let rk: *u8 = sys_mmap(128) 73 return nx_store_ingest_compute_cid(withtext, wlen, cidout, canon, rk) 74} 75 76func cg_same(a: *u8, b: *u8) -> i64 { 77 var i: i64 = 0 78 while i < 69 { if a[i] != b[i] { return 0 } i = i + 1 } 79 return 1 80} 81 82func main(argc: i64, argv: *i64) -> i64 { 83 var pass: i64 = 0 84 var total: i64 = 0 85 let S: *u8 = "1234\x00" as *u8 86 let M: *u8 = "z_image\x00" as *u8 87 let SA: *u8 = "euler\x00" as *u8 88 let ST: *u8 = "8\x00" as *u8 89 let H: *u8 = "laptop\x00" as *u8 90 let P: *u8 = "a lighthouse\x00" as *u8 91 let A1: *u8 = "a lighthouse <args>{\"cfg_scale\":1.0}\x00" as *u8 92 let A2: *u8 = "a lighthouse <args>{\"cfg_scale\":5.0}\x00" as *u8 93 let c1: *u8 = sys_mmap(128) 94 let c2: *u8 = sys_mmap(128) 95 96 cw("GEN-CID-GATE (canon must separate anything that changes the image)\n" as *u8) 97 98 // T1 DETERMINISM: identical inputs -> identical cid (else every tooth below is noise) 99 total = total + 1 100 if cg_cid(S,M,SA,ST,H,P,"512x512\x00" as *u8,A1,c1) > 0 { 101 if cg_cid(S,M,SA,ST,H,P,"512x512\x00" as *u8,A1,c2) > 0 { 102 if cg_same(c1,c2) == 1 { pass = pass + 1; cw("T1 deterministic OK\n" as *u8) } else { cw("T1 deterministic FAIL\n" as *u8) } 103 } 104 } 105 106 // T2 ★THE RESOLUTION CASE (the original data loss): size differs -> cid MUST differ 107 total = total + 1 108 cg_cid(S,M,SA,ST,H,P,"512x512\x00" as *u8,A1,c1) 109 cg_cid(S,M,SA,ST,H,P,"768x1024\x00" as *u8,A1,c2) 110 if cg_same(c1,c2) == 0 { pass = pass + 1; cw("T2 size separates OK\n" as *u8) } else { cw("T2 size COLLIDES -- DATA LOSS FAIL\n" as *u8) } 111 112 // T3 ★THE cfg CASE (survived the first fix): engine args differ -> cid MUST differ 113 total = total + 1 114 cg_cid(S,M,SA,ST,H,P,"512x512\x00" as *u8,A1,c1) 115 cg_cid(S,M,SA,ST,H,P,"512x512\x00" as *u8,A2,c2) 116 if cg_same(c1,c2) == 0 { pass = pass + 1; cw("T3 args/cfg separates OK\n" as *u8) } else { cw("T3 args COLLIDES -- DATA LOSS FAIL\n" as *u8) } 117 118 // T4 seed still separates (the oldest guarantee -- a regression here breaks every gallery) 119 total = total + 1 120 cg_cid(S,M,SA,ST,H,P,"512x512\x00" as *u8,A1,c1) 121 cg_cid("9999\x00" as *u8,M,SA,ST,H,P,"512x512\x00" as *u8,A1,c2) 122 if cg_same(c1,c2) == 0 { pass = pass + 1; cw("T4 seed separates OK\n" as *u8) } else { cw("T4 seed COLLIDES FAIL\n" as *u8) } 123 124 // T5 prompt still separates 125 total = total + 1 126 cg_cid(S,M,SA,ST,H,P,"512x512\x00" as *u8,A1,c1) 127 cg_cid(S,M,SA,ST,H,"a harbour\x00" as *u8,"512x512\x00" as *u8,A1,c2) 128 if cg_same(c1,c2) == 0 { pass = pass + 1; cw("T5 prompt separates OK\n" as *u8) } else { cw("T5 prompt COLLIDES FAIL\n" as *u8) } 129 130 // T6 ★NEG-CONTROL: the cid must NOT be a hash of the pixels either -- two DIFFERENT stub sizes 131 // with the SAME declared metadata still collide, which is WHY the metadata must be complete. 132 // This states the model honestly instead of implying content-addressing we do not have. 133 total = total + 1 134 let s1: *u8 = sys_mmap(CG_BUF) 135 let n1: i64 = cg_stub(s1, 64, 64) 136 let s2: *u8 = sys_mmap(CG_BUF) 137 let n2: i64 = cg_stub(s2, 128, 128) 138 let w1: *u8 = sys_mmap(CG_BUF) 139 let w2: *u8 = sys_mmap(CG_BUF) 140 let l1: i64 = png_insert_genrec(s1, n1, S,M,SA,ST,H,P,"512x512\x00" as *u8,A1, w1) 141 let l2: i64 = png_insert_genrec(s2, n2, S,M,SA,ST,H,P,"512x512\x00" as *u8,A1, w2) 142 let cA: *u8 = sys_mmap(128) 143 let cB: *u8 = sys_mmap(128) 144 let ca: *u8 = sys_mmap(65536) 145 let rka: *u8 = sys_mmap(128) 146 nx_store_ingest_compute_cid(w1, l1, cA, ca, rka) 147 nx_store_ingest_compute_cid(w2, l2, cB, ca, rka) 148 if cg_same(cA,cB) == 1 { pass = pass + 1; cw("T6 NEG-CONTROL cid is canon-based, NOT pixel-based (documented, not assumed) OK\n" as *u8) } 149 else { cw("T6 unexpected: differing pixels changed the cid -- the model in the comments is wrong FAIL\n" as *u8) } 150 151 cw("GENCIDGATE " as *u8); cn(pass); cw("/" as *u8); cn(total) 152 if pass == total { cw(" verdict=GREEN\n" as *u8); return 0 } 153 cw(" verdict=RED\n" as *u8) 154 return 1 155}