code wiki / (root) / nx_container_registry.nx

nx_container_registry.nx source

↩ module page · 351 lines · 15988 B

1// nx_container_registry.nx -- C2 of the Nishi Container Host: SOVEREIGN content-addressed container REGISTRY 2// (the Docker Hub / OCI distribution analog). Closes the last census MISSING axis (registry_distribution). 3// 4// A container artifact (an emitted governance PE, a spec, any blob) is stored by its SHA-256 DIGEST -- the digest IS 5// the address (content-addressed store / CAS). Two properties fall out BY CONSTRUCTION, which Docker layers bolt on: 6// 1. DEDUP: identical bytes -> identical digest -> ONE blob (a re-put of the same content writes the same address). 7// 2. INTEGRITY: a pull re-hashes the blob and REFUSES it if the bytes no longer match the manifest digest -- a 8// tampered or bit-rotted blob is caught at get time, never silently served (fail-closed, never-brick spirit). 9// A flat manifest (name|digest|size) maps friendly names -> digests (the "tag -> image" index). Pull is BY NAME. 10// 11// CLI: put <name> <srcfile> -> hash + store in CAS + index the name 12// get <name> <outfile> -> resolve name -> digest -> verify -> write (REFUSES on digest mismatch) 13// list -> print the manifest 14// (no args) -> run the self-test GATE (round-trip + tamper-detect + dedup + liar-kill) 15// 16// Store: knowledge/containers/registry.index (manifest) + knowledge/containers/cas_<hex>.blob (CAS blobs). 17// Distribution: the CAS is the on-disk half; P2P distribution rides the LIVE torrent plane (announce a blob's 18// infohash to the DHT, peers pull by digest) -- that wiring is the next rung; the sovereign CAS + integrity is here. 19// NO fake greens: the gate mechanically proves round-trip fidelity + that the integrity check DISCRIMINATES a tamper. 20// license_tier: ORIGINAL 21import "nx_syscalls.nx" 22import "nx_runtime.nx" 23import "nx_sha256.nx" 24const REG_MAGIC_65536: i64 = 65536 25const REG_MAGIC_65535: i64 = 65535 26const REG_MAGIC_131072: i64 = 131072 27const REG_MAGIC_1024: i64 = 1024 28 29const REG_INDEX: *u8 = "knowledge/containers/registry.index" 30const REG_CAS_PFX: *u8 = "knowledge/containers/cas_" 31const RT_INDEX: *u8 = "knowledge/containers/rt_registry.index" 32const RT_CAS_PFX: *u8 = "knowledge/containers/rt_cas_" 33const REG_MAXBLOB: i64 = 1048576 34 35func rg_w(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 } 36func rg_wn(fd: i64, v: i64) -> i64 { 37 let bb: *u8 = sys_mmap(28) 38 var m: i64 = v 39 if m < 0 { sys_write(fd, "-" as *u8, 1); m = 0 - m } 40 let tt: *u8 = sys_mmap(28) 41 var k: i64 = 0 42 if m == 0 { tt[0] = 48 as u8; k = 1 } 43 while m > 0 { tt[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 44 var i: i64 = 0 45 while i < k { bb[i] = tt[k-1-i]; i = i + 1 } 46 sys_write(fd, bb, k) 47 return 0 48} 49func rg_p(s: *u8) -> i64 { return rg_w(1, s) } 50func rg_pn(v: i64) -> i64 { return rg_wn(1, v) } 51func rg_strlen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 52func rg_streq(a: *u8, b: *u8) -> i64 { 53 var i: i64 = 0 54 while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 } 55 if b[i] != (0 as u8) { return 0 } 56 return 1 57} 58func rg_read(path: *u8, buf: *u8, cap: i64) -> i64 { 59 let fd: i64 = sys_openat_rd(path) 60 if fd < 0 { return 0 - 1 } 61 var tot: i64 = 0 62 var go: i64 = 1 63 while go == 1 { 64 let r: i64 = sys_read(fd, (buf as i64 + tot) as *u8, cap - tot) 65 if r <= 0 { go = 0 } else { tot = tot + r } 66 if tot >= cap { go = 0 } 67 } 68 sys_close(fd) 69 return tot 70} 71func rg_write(path: *u8, buf: *u8, n: i64) -> i64 { 72 let fd: i64 = sys_openat_wr(path, 0x1a4) 73 if fd < 0 { return 0 - 1 } 74 sys_write(fd, buf, n) 75 sys_close(fd) 76 return 0 77} 78// copy null-terminated s into dst at off; return new off (no null written) 79func rg_scpy(dst: *u8, off: i64, s: *u8) -> i64 { 80 var i: i64 = 0 81 while s[i] != (0 as u8) { dst[off + i] = s[i]; i = i + 1 } 82 return off + i 83} 84// 32 raw digest bytes -> 64 lowercase hex chars + null 85func rg_hex(dig: *u8, hexout: *u8) -> i64 { 86 let H: *u8 = "0123456789abcdef" as *u8 87 var i: i64 = 0 88 while i < 32 { 89 let b: i64 = dig[i] as i64 90 hexout[i*2] = H[(b / 16) - ((b / 16) / 16) * 16] 91 hexout[i*2+1] = H[b - (b / 16) * 16] 92 i = i + 1 93 } 94 hexout[64] = 0 as u8 95 return 64 96} 97func rg_hexeq(a: *u8, b: *u8) -> i64 { var i: i64 = 0; while i < 64 { if a[i] != b[i] { return 0 } i = i + 1 } return 1 } 98// build CAS blob path = pfx + hex + ".blob" (null-terminated) into out 99func rg_caspath(out: *u8, pfx: *u8, hex: *u8) -> i64 { 100 var o: i64 = rg_scpy(out, 0, pfx) 101 o = rg_scpy(out, o, hex) 102 o = rg_scpy(out, o, ".blob" as *u8) 103 out[o] = 0 as u8 104 return o 105} 106 107// manifest line col0 (name up to '|') equals `name`? 108func rg_line_name_eq(buf: *u8, off: i64, end: i64, name: *u8) -> i64 { 109 let nl: i64 = rg_strlen(name) 110 if off + nl > end { return 0 } 111 var i: i64 = 0 112 while i < nl { if buf[off + i] != name[i] { return 0 } i = i + 1 } 113 if off + nl >= end { return 0 } 114 if buf[off + nl] != (124 as u8) { return 0 } // 124 = '|' 115 return 1 116} 117// find `name` in manifest -> copy its digest hex (col1) to hexout(65), size to szbox. returns 1/0. 118func rg_find(idxpath: *u8, name: *u8, hexout: *u8, szbox: *i64) -> i64 { 119 let mbuf: *u8 = sys_mmap(REG_MAGIC_65536) 120 let mn: i64 = rg_read(idxpath, mbuf, REG_MAGIC_65535) 121 if mn <= 0 { return 0 } 122 var pos: i64 = 0 123 while pos < mn { 124 var eol: i64 = pos 125 var fe: i64 = 0 126 while fe == 0 { if eol >= mn { fe = 1 } else { if mbuf[eol] == (10 as u8) { fe = 1 } else { eol = eol + 1 } } } 127 if eol > pos { 128 if rg_line_name_eq(mbuf, pos, eol, name) == 1 { 129 // col1 = after the first '|' 130 var b1: i64 = pos 131 while mbuf[b1] != (124 as u8) { b1 = b1 + 1 } 132 b1 = b1 + 1 133 var k: i64 = 0 134 while b1 < eol { 135 if mbuf[b1] == (124 as u8) { b1 = eol } else { if k < 64 { hexout[k] = mbuf[b1]; k = k + 1 } b1 = b1 + 1 } 136 } 137 hexout[k] = 0 as u8 138 // col2 = size after the second '|' 139 var b2: i64 = pos 140 var bars: i64 = 0 141 while b2 < eol { if mbuf[b2] == (124 as u8) { bars = bars + 1; if bars == 2 { b2 = eol } } b2 = b2 + 1 } 142 // (size parse is best-effort; not load-bearing for verify) 143 szbox[0] = 0 144 return 1 145 } 146 } 147 pos = eol + 1 148 } 149 return 0 150} 151// idempotent manifest put: rewrite manifest keeping every line whose name != `name`, then append name|hex|size. 152func rg_index_put(idxpath: *u8, name: *u8, hex: *u8, size: i64) -> i64 { 153 let mbuf: *u8 = sys_mmap(REG_MAGIC_65536) 154 let mn: i64 = rg_read(idxpath, mbuf, REG_MAGIC_65535) 155 let out: *u8 = sys_mmap(REG_MAGIC_131072) 156 var o: i64 = 0 157 if mn > 0 { 158 var pos: i64 = 0 159 while pos < mn { 160 var eol: i64 = pos 161 var fe: i64 = 0 162 while fe == 0 { if eol >= mn { fe = 1 } else { if mbuf[eol] == (10 as u8) { fe = 1 } else { eol = eol + 1 } } } 163 if eol > pos { 164 if rg_line_name_eq(mbuf, pos, eol, name) == 0 { 165 var q: i64 = pos 166 while q < eol { out[o] = mbuf[q]; o = o + 1; q = q + 1 } 167 out[o] = 10 as u8; o = o + 1 168 } 169 } 170 pos = eol + 1 171 } 172 } 173 o = rg_scpy(out, o, name) 174 out[o] = 124 as u8; o = o + 1 175 o = rg_scpy(out, o, hex) 176 out[o] = 124 as u8; o = o + 1 177 // size as decimal 178 let tt: *u8 = sys_mmap(28) 179 var m: i64 = size 180 var k: i64 = 0 181 if m == 0 { tt[0] = 48 as u8; k = 1 } 182 while m > 0 { tt[k] = (48 + (m - (m/10)*10)) as u8; m = m / 10; k = k + 1 } 183 var j: i64 = 0 184 while j < k { out[o] = tt[k-1-j]; o = o + 1; j = j + 1 } 185 out[o] = 10 as u8; o = o + 1 186 return rg_write(idxpath, out, o) 187} 188// PUT: hash srcfile, store in CAS under caspfx, index name. returns 0 ok / <0 err. 189func rg_put(idxpath: *u8, caspfx: *u8, name: *u8, srcpath: *u8) -> i64 { 190 let art: *u8 = sys_mmap(REG_MAXBLOB) 191 let an: i64 = rg_read(srcpath, art, REG_MAXBLOB) 192 if an <= 0 { return 0 - 1 } 193 let dig: *u8 = sys_mmap(32) 194 sha256_digest(art, an, dig) 195 let hex: *u8 = sys_mmap(72) 196 rg_hex(dig, hex) 197 let cp: *u8 = sys_mmap(512) 198 rg_caspath(cp, caspfx, hex) 199 if rg_write(cp, art, an) != 0 { return 0 - 2 } 200 if rg_index_put(idxpath, name, hex, an) != 0 { return 0 - 3 } 201 return an 202} 203// GET: resolve name -> digest -> read blob -> VERIFY re-hash == stored digest -> write outfile. returns bytes / <0. 204// -1 name not found; -2 blob unreadable; -3 INTEGRITY FAILURE (re-hash != manifest digest) = fail-closed refuse. 205func rg_get(idxpath: *u8, caspfx: *u8, name: *u8, outpath: *u8) -> i64 { 206 let hex: *u8 = sys_mmap(72) 207 let szbox: *i64 = sys_mmap(8) as *i64 208 if rg_find(idxpath, name, hex, szbox) == 0 { return 0 - 1 } 209 let cp: *u8 = sys_mmap(512) 210 rg_caspath(cp, caspfx, hex) 211 let blob: *u8 = sys_mmap(REG_MAXBLOB) 212 let bn: i64 = rg_read(cp, blob, REG_MAXBLOB) 213 if bn <= 0 { return 0 - 2 } 214 let dig: *u8 = sys_mmap(32) 215 sha256_digest(blob, bn, dig) 216 let hex2: *u8 = sys_mmap(72) 217 rg_hex(dig, hex2) 218 if rg_hexeq(hex, hex2) == 0 { return 0 - 3 } // integrity: served bytes must match the addressed digest 219 if (outpath as i64) != 0 { if rg_write(outpath, blob, bn) != 0 { return 0 - 4 } } 220 return bn 221} 222 223// ---- self-test GATE (no args) -------------------------------------------------------------------------- 224func rg_gate() -> i64 { 225 rg_p("=== nx_container_registry: sovereign content-addressed container registry (Docker-Hub analog) ===\n" as *u8) 226 // synthetic deterministic artifact A (512 bytes) 227 let A: *u8 = sys_mmap(512) 228 var i: i64 = 0 229 while i < 512 { A[i] = ((i * 7 + 3) - (((i * 7 + 3) / 251) * 251)) as u8; i = i + 1 } 230 // write A to a temp source file, then PUT it (exercises the real file path) 231 let srcp: *u8 = "knowledge/containers/rt_src_alpha.bin" as *u8 232 rg_write(srcp, A, 512) 233 // fresh gate manifest (truncate) 234 rg_write(RT_INDEX, "" as *u8, 0) 235 let pa: i64 = rg_put(RT_INDEX, RT_CAS_PFX, "test-alpha" as *u8, srcp) 236 // GET it back, verifying integrity, into an out file 237 let outp: *u8 = "knowledge/containers/rt_out_alpha.bin" as *u8 238 let ga: i64 = rg_get(RT_INDEX, RT_CAS_PFX, "test-alpha" as *u8, outp) 239 // T1 round-trip: got 512 bytes and they equal A 240 var t1: i64 = 0 241 if ga == 512 { 242 let B: *u8 = sys_mmap(REG_MAGIC_1024) 243 let bn: i64 = rg_read(outp, B, REG_MAGIC_1024) 244 if bn == 512 { 245 var eq: i64 = 1 246 var j: i64 = 0 247 while j < 512 { if B[j] != A[j] { eq = 0; j = 512 } else { j = j + 1 } } 248 t1 = eq 249 } 250 } 251 // digest of alpha (for dedup + tamper tests) 252 let digA: *u8 = sys_mmap(32) 253 sha256_digest(A, 512, digA) 254 let hexA: *u8 = sys_mmap(72) 255 rg_hex(digA, hexA) 256 // T2 integrity DISCRIMINATES: tamper the CAS blob on disk (flip 1 byte) -> get must REFUSE (-3) 257 let cp: *u8 = sys_mmap(512) 258 rg_caspath(cp, RT_CAS_PFX, hexA) 259 let tb: *u8 = sys_mmap(REG_MAGIC_1024) 260 let tbn: i64 = rg_read(cp, tb, REG_MAGIC_1024) 261 tb[0] = (tb[0] + 1) as u8 // corrupt one byte 262 rg_write(cp, tb, tbn) 263 let gt: i64 = rg_get(RT_INDEX, RT_CAS_PFX, "test-alpha" as *u8, 0 as *u8) 264 var t2: i64 = 0 265 if gt == (0 - 3) { t2 = 1 } // integrity failure detected + refused (not silently served) 266 // restore the blob so downstream stays consistent 267 rg_write(cp, A, 512) 268 // T3 DEDUP: put a SECOND name with identical content -> same digest -> same CAS address 269 let pb: i64 = rg_put(RT_INDEX, RT_CAS_PFX, "test-beta" as *u8, srcp) 270 let hexB: *u8 = sys_mmap(72) 271 let szb: *i64 = sys_mmap(8) as *i64 272 let fb: i64 = rg_find(RT_INDEX, "test-beta" as *u8, hexB, szb) 273 var t3: i64 = 0 274 if fb == 1 { if rg_hexeq(hexA, hexB) == 1 { t3 = 1 } } // identical bytes -> identical address (dedup) 275 // NEG-CONTROL liar-kill: a name NOT in the manifest must NOT resolve (no false hit) 276 let hexN: *u8 = sys_mmap(72) 277 let szn: *i64 = sys_mmap(8) as *i64 278 var neg1: i64 = 0 279 if rg_find(RT_INDEX, "no-such-container-9Z" as *u8, hexN, szn) == 0 { neg1 = 1 } 280 // NEG-CONTROL 2: hashing DIFFERENT bytes yields a DIFFERENT address (the hash is not a constant) 281 let A2: *u8 = sys_mmap(512) 282 var z: i64 = 0 283 while z < 512 { A2[z] = (A[z] + 1) as u8; z = z + 1 } 284 let dig2: *u8 = sys_mmap(32) 285 sha256_digest(A2, 512, dig2) 286 let hex2: *u8 = sys_mmap(72) 287 rg_hex(dig2, hex2) 288 var neg2: i64 = 0 289 if rg_hexeq(hexA, hex2) == 0 { neg2 = 1 } 290 291 rg_p(" put_alpha_bytes=" as *u8); rg_pn(pa) 292 rg_p(" digest=" as *u8); sys_write(1, hexA, 16); rg_p("...\n" as *u8) 293 rg_p(" T1 round-trip get==put: " as *u8); rg_pn(t1) 294 rg_p(" | T2 tamper-detected+refused: " as *u8); rg_pn(t2) 295 rg_p(" | T3 dedup(same content->same addr): " as *u8); rg_pn(t3) 296 rg_p(" | neg1 missing-name-no-hit: " as *u8); rg_pn(neg1) 297 rg_p(" | neg2 hash-discriminates: " as *u8); rg_pn(neg2); rg_p("\n" as *u8) 298 299 // durable status row 300 let sfd: i64 = sys_openat_wr("knowledge/status/container_registry.tsv" as *u8, 0x1a4) 301 if sfd >= 0 { 302 rg_w(sfd, "# nx_container_registry -- content-addressed CAS + integrity-verified pull-by-name (Docker-Hub analog)\n" as *u8) 303 rg_w(sfd, "roundtrip\t" as *u8); rg_wn(sfd, t1); rg_w(sfd, "\n" as *u8) 304 rg_w(sfd, "integrity_refuse_tamper\t" as *u8); rg_wn(sfd, t2); rg_w(sfd, "\n" as *u8) 305 rg_w(sfd, "dedup_content_addressed\t" as *u8); rg_wn(sfd, t3); rg_w(sfd, "\n" as *u8) 306 sys_close(sfd) 307 } 308 309 var pass: i64 = 0 310 if t1 == 1 { if t2 == 1 { if t3 == 1 { if neg1 == 1 { if neg2 == 1 { pass = 1 } } } } } 311 if pass == 1 { 312 rg_p("CONTAINERREGISTRYGATE verdict=GREEN (content-addressed store; pull-by-name; integrity refuses tamper; dedup; liar-killed)\n" as *u8) 313 return 0 314 } 315 rg_p("CONTAINERREGISTRYGATE verdict=RED (round-trip/integrity/dedup/neg-control failed)\n" as *u8) 316 return 1 317} 318 319func rg_cli_list() -> i64 { 320 let mbuf: *u8 = sys_mmap(REG_MAGIC_65536) 321 let mn: i64 = rg_read(REG_INDEX, mbuf, REG_MAGIC_65535) 322 if mn <= 0 { rg_p("(registry empty)\n" as *u8); return 0 } 323 sys_write(1, mbuf, mn) 324 return 0 325} 326 327func main(argc: i64, argv: *i64) -> i64 { 328 if argc >= 2 { 329 let cmd: *u8 = argv[1] as *u8 330 if rg_streq(cmd, "put" as *u8) == 1 { 331 if argc < 4 { rg_w(2, "usage: nx_container_registry put <name> <srcfile>\n" as *u8); return 2 } 332 let r: i64 = rg_put(REG_INDEX, REG_CAS_PFX, argv[2] as *u8, argv[3] as *u8) 333 if r < 0 { rg_w(2, "PUT-FAIL\n" as *u8); return 3 } 334 rg_p("PUT ok name=" as *u8); rg_p(argv[2] as *u8); rg_p(" bytes=" as *u8); rg_pn(r); rg_p("\n" as *u8) 335 return 0 336 } 337 if rg_streq(cmd, "get" as *u8) == 1 { 338 if argc < 4 { rg_w(2, "usage: nx_container_registry get <name> <outfile>\n" as *u8); return 2 } 339 let r: i64 = rg_get(REG_INDEX, REG_CAS_PFX, argv[2] as *u8, argv[3] as *u8) 340 if r == (0 - 1) { rg_w(2, "GET-FAIL name not in registry\n" as *u8); return 4 } 341 if r == (0 - 3) { rg_w(2, "GET-REFUSED integrity: blob digest mismatch (tampered/corrupt)\n" as *u8); return 5 } 342 if r < 0 { rg_w(2, "GET-FAIL\n" as *u8); return 6 } 343 rg_p("GET ok name=" as *u8); rg_p(argv[2] as *u8); rg_p(" bytes=" as *u8); rg_pn(r); rg_p(" (integrity-verified)\n" as *u8) 344 return 0 345 } 346 if rg_streq(cmd, "list" as *u8) == 1 { return rg_cli_list() } 347 rg_w(2, "usage: nx_container_registry put|get|list (no args = self-test gate)\n" as *u8) 348 return 2 349 } 350 return rg_gate() 351}