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}