code wiki / _hdl_build / nx_ark_snapshot.nx
nx_ark_snapshot.nx source
↩ module page · 345 lines · 12595 B
1// nx_ark_snapshot.nx -- X-REP-005 (CALLOUT-002 REP-1): the ARK. Packs the
2// data-driven survival set (knowledge/registry/ark_set.tsv) into ONE file
3// with a CID manifest -- the dead-laptop seed's first organ.
4// snap (default): read every set row -> entry CIDs -> manifest text. If
5// the manifest is BYTE-IDENTICAL to the existing one, SKIP the pack
6// write entirely (unchanged=1; the resource-conscious beat is a read
7// pass). Else write pack.new + manifest.new and renameat both (atomic;
8// the lane's temp-rename discipline).
9// verify: walk the pack against the manifest -- entry count, path, size,
10// recomputed CID must all match; any mismatch = named row + RED. An
11// ark that cannot prove itself is hope, not backup.
12// Pack format NXARK1: "NXARK1\n" then per entry "ENT <pathlen> <bytes>
13// <cid>\n" + path bytes + raw content. Manifest rows are DETERMINISTIC
14// (no epoch -- epoch lives in the ARKSNAP log row only) so unchanged
15// detection is a byte compare.
16// argv[1]=mode snap|verify argv[2]=set argv[3]=pack argv[4]=manifest
17// Evidence: ARKSNAP / ARKVERIFY rows -> stdout + knowledge/status/ark.log.
18// license_tier: ORIGINAL
19
20import "nx_syscalls.nx"
21const ARK_MAGIC_65536: i64 = 65536
22const ARK_MAGIC_1469598103: i64 = 1469598103
23const ARK_MAGIC_262144: i64 = 262144
24
25const ARK_PACKCAP: i64 = 16777216
26const ARK_MAXENT: i64 = 128
27
28func ak_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 }
29func ak_wn(fd: i64, v: i64) -> i64 { let bb: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m; sys_write(fd,"-" as *u8,1)}; let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48;k=1}; while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1}; var i: i64=0; while i<k{bb[i]=t[k-1-i];i=i+1}; sys_write(fd,bb,k); return 0 }
30
31func ak_cat(dst: *u8, off: i64, s: *u8) -> i64 {
32 var i: i64 = 0
33 while s[i] != (0 as u8) { dst[off + i] = s[i]; i = i + 1 }
34 dst[off + i] = 0 as u8
35 return off + i
36}
37
38func ak_catn(dst: *u8, off: i64, v: i64) -> i64 {
39 var m: i64 = v
40 if m < 0 { m = 0 }
41 let t: *u8 = sys_mmap(28)
42 var k: i64 = 0
43 if m == 0 { t[0] = 48 as u8; k = 1 }
44 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
45 var o: i64 = off
46 var i: i64 = 0
47 while i < k { dst[o] = t[k - 1 - i]; o = o + 1; i = i + 1 }
48 dst[o] = 0 as u8
49 return o
50}
51
52func ak_readall(path: *u8, szout: *i64) -> *u8 {
53 let fd: i64 = sys_openat_rd(path)
54 if fd < 0 { szout[0] = 0 - 1; return 0 as *u8 }
55 let sz: i64 = sys_lseek(fd, 0, 2)
56 sys_lseek(fd, 0, 0)
57 let buf: *u8 = sys_mmap(sz + 64)
58 var got: i64 = 0
59 var n: i64 = 1
60 while n > 0 {
61 n = sys_read(fd, (buf as i64 + got) as *u8, ARK_MAGIC_65536)
62 if n > 0 { got = got + n }
63 }
64 sys_close(fd)
65 szout[0] = got
66 return buf
67}
68
69func ak_fnv(b: *u8, n: i64) -> i64 {
70 var h: i64 = ARK_MAGIC_1469598103
71 var i: i64 = 0
72 while i < n { h = (h * 131 + (b[i] as i64)) & 0x7fffffffffff; i = i + 1 }
73 return h
74}
75
76func ak_writefile(path: *u8, b: *u8, n: i64) -> i64 {
77 let fd: i64 = sys_openat_wr(path, 0x1a4)
78 if fd < 0 { return 0 }
79 var off: i64 = 0
80 while off < n {
81 let w: i64 = sys_write(fd, b + off, n - off)
82 if w <= 0 { sys_close(fd); return 0 }
83 off = off + w
84 }
85 sys_fsync(fd)
86 sys_close(fd)
87 return 1
88}
89
90// parse set rows into arena (256B/slot: path NUL note-ignored); returns count
91func ak_set(setpath: *u8, arena: *u8) -> i64 {
92 let szp: *i64 = sys_mmap(16) as *i64
93 let b: *u8 = ak_readall(setpath, szp)
94 let n: i64 = szp[0]
95 if n <= 0 { return 0 }
96 var cnt: i64 = 0
97 var i: i64 = 0
98 while i < n {
99 var le: i64 = i
100 var go: i64 = 1
101 while go == 1 {
102 if le >= n { go = 0 } else {
103 if b[le] == (10 as u8) { go = 0 } else { le = le + 1 }
104 }
105 }
106 if le > i {
107 if b[i] != (35 as u8) {
108 if cnt < ARK_MAXENT {
109 let dst: *u8 = (arena as i64 + cnt * 256) as *u8
110 var k: i64 = 0
111 var p: i64 = i
112 var go2: i64 = 1
113 while go2 == 1 {
114 if p >= le { go2 = 0 } else {
115 if b[p] == (9 as u8) { go2 = 0 } else {
116 if k < 255 { dst[k] = b[p]; k = k + 1 }
117 p = p + 1
118 }
119 }
120 }
121 dst[k] = 0 as u8
122 if k > 0 { cnt = cnt + 1 }
123 }
124 }
125 }
126 i = le + 1
127 }
128 return cnt
129}
130
131// build manifest text for the CURRENT sources; returns length (neg on miss)
132func ak_manifest_build(arena: *u8, cnt: i64, mb: *u8) -> i64 {
133 var o: i64 = ak_cat(mb, 0, "ARKMANIFEST v1 entries=" as *u8)
134 o = ak_catn(mb, o, cnt)
135 o = ak_cat(mb, o, "\n" as *u8)
136 let szp: *i64 = sys_mmap(16) as *i64
137 var e: i64 = 0
138 while e < cnt {
139 let path: *u8 = (arena as i64 + e * 256) as *u8
140 let b: *u8 = ak_readall(path, szp)
141 if szp[0] < 0 {
142 ak_w(1, "ARK-MISS path=" as *u8)
143 ak_w(1, path)
144 ak_w(1, "\n" as *u8)
145 return 0 - 1
146 }
147 o = ak_cat(mb, o, "ARKENT path=" as *u8)
148 o = ak_cat(mb, o, path)
149 o = ak_cat(mb, o, " bytes=" as *u8)
150 o = ak_catn(mb, o, szp[0])
151 o = ak_cat(mb, o, " cid=" as *u8)
152 o = ak_catn(mb, o, ak_fnv(b, szp[0]))
153 o = ak_cat(mb, o, "\n" as *u8)
154 e = e + 1
155 }
156 return o
157}
158
159// SNAP: returns 0 written, 1 unchanged-skip, neg fail
160func ak_snap(arena: *u8, cnt: i64, packp: *u8, manp: *u8) -> i64 {
161 let mb: *u8 = sys_mmap(ARK_MAGIC_262144)
162 let ml: i64 = ak_manifest_build(arena, cnt, mb)
163 if ml < 0 { return 0 - 1 }
164 // unchanged? byte-compare against the existing manifest
165 let oszp: *i64 = sys_mmap(16) as *i64
166 let old: *u8 = ak_readall(manp, oszp)
167 if oszp[0] == ml {
168 var same: i64 = 1
169 var i: i64 = 0
170 while i < ml {
171 if old[i] != mb[i] { same = 0; i = ml }
172 i = i + 1
173 }
174 if same == 1 { return 1 }
175 }
176 // build the pack
177 let pk: *u8 = sys_mmap(ARK_PACKCAP)
178 var po: i64 = ak_cat(pk, 0, "NXARK1\n" as *u8)
179 let szp: *i64 = sys_mmap(16) as *i64
180 var e: i64 = 0
181 while e < cnt {
182 let path: *u8 = (arena as i64 + e * 256) as *u8
183 let b: *u8 = ak_readall(path, szp)
184 if szp[0] < 0 { return 0 - 1 }
185 var pl: i64 = 0
186 while path[pl] != (0 as u8) { pl = pl + 1 }
187 if po + pl + szp[0] + 64 > ARK_PACKCAP {
188 ak_w(1, "ARK-CAP-REFUSED (pack would exceed cap -- raise ARK_PACKCAP deliberately)\n" as *u8)
189 return 0 - 2
190 }
191 po = ak_cat(pk, po, "ENT " as *u8)
192 po = ak_catn(pk, po, pl)
193 po = ak_cat(pk, po, " " as *u8)
194 po = ak_catn(pk, po, szp[0])
195 po = ak_cat(pk, po, " " as *u8)
196 po = ak_catn(pk, po, ak_fnv(b, szp[0]))
197 po = ak_cat(pk, po, "\n" as *u8)
198 var k: i64 = 0
199 while k < pl { pk[po] = path[k]; po = po + 1; k = k + 1 }
200 k = 0
201 while k < szp[0] { pk[po] = b[k]; po = po + 1; k = k + 1 }
202 e = e + 1
203 }
204 // atomic: .new then rename (pack first, manifest second)
205 let tmpp: *u8 = sys_mmap(512)
206 var to: i64 = ak_cat(tmpp, 0, packp)
207 to = ak_cat(tmpp, to, ".new" as *u8)
208 if ak_writefile(tmpp, pk, po) != 1 { return 0 - 3 }
209 sys_renameat(tmpp, packp)
210 let tmpm: *u8 = sys_mmap(512)
211 to = ak_cat(tmpm, 0, manp)
212 to = ak_cat(tmpm, to, ".new" as *u8)
213 if ak_writefile(tmpm, mb, ml) != 1 { return 0 - 3 }
214 sys_renameat(tmpm, manp)
215 return 0
216}
217
218// VERIFY: pack entries vs manifest; returns bad count (neg = unreadable)
219func ak_verify(packp: *u8, manp: *u8, lfd: i64) -> i64 {
220 let pszp: *i64 = sys_mmap(16) as *i64
221 let pk: *u8 = ak_readall(packp, pszp)
222 let pn: i64 = pszp[0]
223 if pn <= 7 { return 0 - 1 }
224 let mszp: *i64 = sys_mmap(16) as *i64
225 let mn0: *u8 = ak_readall(manp, mszp)
226 let mn: i64 = mszp[0]
227 if mn <= 0 { return 0 - 1 }
228 var bad: i64 = 0
229 var ents: i64 = 0
230 var i: i64 = 7
231 while i < pn {
232 // expect "ENT <pl> <bytes> <cid>\n"
233 if i + 4 > pn { i = pn } else {
234 i = i + 4
235 var pl: i64 = 0
236 while pk[i] != (32 as u8) { pl = pl * 10 + ((pk[i] as i64) - 48); i = i + 1 }
237 i = i + 1
238 var bs: i64 = 0
239 while pk[i] != (32 as u8) { bs = bs * 10 + ((pk[i] as i64) - 48); i = i + 1 }
240 i = i + 1
241 var cid: i64 = 0
242 while pk[i] != (10 as u8) { cid = cid * 10 + ((pk[i] as i64) - 48); i = i + 1 }
243 i = i + 1
244 let pathp: *u8 = (pk as i64 + i) as *u8
245 i = i + pl
246 let datap: *u8 = (pk as i64 + i) as *u8
247 i = i + bs
248 ents = ents + 1
249 let real: i64 = ak_fnv(datap, bs)
250 if real != cid {
251 bad = bad + 1
252 ak_w(1, "ARKVERIFY-BAD entry=" as *u8)
253 ak_wn(1, ents)
254 ak_w(1, " cid-mismatch\n" as *u8)
255 if lfd >= 0 { ak_w(lfd, "ARKVERIFY-BAD entry cid-mismatch\n" as *u8) }
256 }
257 // the manifest must contain the exact row for this entry
258 let row: *u8 = sys_mmap(512)
259 var ro: i64 = ak_cat(row, 0, "ARKENT path=" as *u8)
260 var k: i64 = 0
261 while k < pl { row[ro] = pathp[k]; ro = ro + 1; k = k + 1 }
262 ro = ak_cat(row, ro, " bytes=" as *u8)
263 ro = ak_catn(row, ro, bs)
264 ro = ak_cat(row, ro, " cid=" as *u8)
265 ro = ak_catn(row, ro, cid)
266 var found: i64 = 0
267 var j: i64 = 0
268 while j + ro <= mn {
269 var m: i64 = 0
270 var hit: i64 = 1
271 while m < ro {
272 if mn0[j + m] != row[m] { hit = 0; m = ro } else { m = m + 1 }
273 }
274 if hit == 1 { found = 1; j = mn }
275 j = j + 1
276 }
277 if found == 0 {
278 bad = bad + 1
279 ak_w(1, "ARKVERIFY-BAD manifest-row-missing\n" as *u8)
280 if lfd >= 0 { ak_w(lfd, "ARKVERIFY-BAD manifest-row-missing\n" as *u8) }
281 }
282 }
283 }
284 if ents == 0 { return 0 - 1 }
285 return bad
286}
287
288func main(argc: i64, argv: *i64) -> i64 {
289 var mode: *u8 = "snap" as *u8
290 var setp: *u8 = "knowledge/registry/ark_set.tsv" as *u8
291 var packp: *u8 = "knowledge/ark_pack.bin" as *u8
292 var manp: *u8 = "knowledge/ark_manifest.txt" as *u8
293 if argc >= 2 { mode = argv[1] as *u8 }
294 if argc >= 3 { setp = argv[2] as *u8 }
295 if argc >= 4 { packp = argv[3] as *u8 }
296 if argc >= 5 { manp = argv[4] as *u8 }
297 let lfd: i64 = sys_openat_append("knowledge/status/ark.log" as *u8, 0x1a4)
298
299 var rc: i64 = 0
300 if mode[0] == (118 as u8) {
301 let bad: i64 = ak_verify(packp, manp, lfd)
302 var verdict: *u8 = "GREEN" as *u8
303 if bad != 0 { verdict = "RED" as *u8; rc = 1 }
304 var p: i64 = 0
305 while p < 2 {
306 var fd: i64 = 1
307 if p == 1 { fd = lfd }
308 if fd >= 0 {
309 ak_w(fd, "ARKVERIFY bad=" as *u8); ak_wn(fd, bad)
310 ak_w(fd, " epoch=" as *u8); ak_wn(fd, sys_now_realtime_sec())
311 ak_w(fd, " verdict=" as *u8); ak_w(fd, verdict)
312 ak_w(fd, "\n" as *u8)
313 }
314 p = p + 1
315 }
316 } else {
317 let arena: *u8 = sys_mmap(ARK_MAXENT * 256)
318 let cnt: i64 = ak_set(setp, arena)
319 if cnt <= 0 {
320 ak_w(1, "ARKSNAP verdict=RED reason=set-missing\n" as *u8)
321 sys_exit(101)
322 }
323 let r: i64 = ak_snap(arena, cnt, packp, manp)
324 var verdict: *u8 = "GREEN" as *u8
325 var unch: i64 = 0
326 if r == 1 { unch = 1 }
327 if r < 0 { verdict = "RED" as *u8; rc = 1 }
328 var p: i64 = 0
329 while p < 2 {
330 var fd: i64 = 1
331 if p == 1 { fd = lfd }
332 if fd >= 0 {
333 ak_w(fd, "ARKSNAP entries=" as *u8); ak_wn(fd, cnt)
334 ak_w(fd, " unchanged=" as *u8); ak_wn(fd, unch)
335 ak_w(fd, " epoch=" as *u8); ak_wn(fd, sys_now_realtime_sec())
336 ak_w(fd, " verdict=" as *u8); ak_w(fd, verdict)
337 ak_w(fd, "\n" as *u8)
338 }
339 p = p + 1
340 }
341 }
342 if lfd >= 0 { sys_close(lfd) }
343 sys_exit(rc)
344 return rc
345}