nx_arcmine.nx source
↩ module page · 468 lines · 27072 B
1// nx_arcmine.nx -- INDEX-FIRST domain media harvester. The rung that makes a sovereign archive EXCEED
2// the Wayback Machine on media completeness (the 2026-06-29 goal: page3.com / megastar.co.uk day-by-day
3// WITH their media).
4//
5// module: nishi-core.archive.arcmine
6// depends: nx_medrec.nx (admission), nx_cdx_parse.nx (rows/fields), nx_https_fetch_follow.nx
7// usage: nx_arcmine <domain> [page] [min_bytes] [max_fetch] [mime]
8// capability: LIVE FETCH, read-only against the archive. Additive + IDEMPOTENT (Rule 10).
9//
10// *WHY INDEX-FIRST, AND WHY IT BEATS THE PAGE-WALK -- the whole point of this organ:
11// nx_archive_site_viewer discovers media by RENDERING a captured page and reading its <img> tags. That
12// ceiling is structural: it can only ever find media referenced by the pages it happens to render, at the
13// one timestamp it renders them. Every image on a page never rendered, on a page never captured, or
14// referenced only from a since-changed layout, is INVISIBLE to it -- and its absence is invisible too.
15// The CDX index knows every image the crawler EVER saw across the whole host and all time. Enumerating
16// from the index is exhaustive BY CONSTRUCTION rather than by luck of which page we happened to open.
17// That is the difference between browsing an archive and MINING one.
18//
19// *DEDUPE BY DIGEST, MEASURED 2026-08-06: captures of one URL are byte-identical. Real sample --
20// page3.com/3zine_pages/html_preview/01/ held 21 status-200 capture rows that collapse to just 6 unique
21// digests (01.jpg alone: 6 captures, ONE digest HSTRJJRQ.., sizes 27236..27245 = 9 bytes of WARC record
22// framing, not pixels). Fetching per-capture would spend 3.5x the bandwidth for byte-identical output.
23// collapse=digest is therefore applied at the QUERY, so the redundancy never crosses the network at all.
24// *AND THE COROLLARY THAT KILLED A PLANNED CHANGE: because captures are byte-identical, choosing the
25// LARGEST capture over the EARLIEST buys exactly nothing on this corpus. That tweak was measured, found
26// worthless, and NOT shipped.
27//
28// *TOMBSTONES ARE REJECTED FROM THE INDEX (nx_medrec), before a single byte is fetched -- a host's
29// "image not found" placeholder is a structurally valid JPEG that magic-byte validation cannot fail.
30//
31// *IDEMPOTENT BY CONSTRUCTION: the output filename IS the content digest, so a re-run rewrites identical
32// bytes and can never duplicate or corrupt. Safe to run twice, or to resume a partial page.
33// *PACING: max_fetch bounds one invocation deliberately. The archive is a shared resource and we have
34// rate-limited ourselves off hosts before by sweeping too fast; the operator advances pages at their own
35// cadence. OWED: wire pace_before(host) from nx_crawl_pace so politeness is structural, not procedural.
36// license_tier: ORIGINAL
37import "nx_medrec.nx"
38import "nx_x509_trust_store.nx"
39import "nx_trust_store_load_from_certdata.nx"
40import "nx_https_fetch_follow.nx"
41
42const AM_CAP: i64 = 4194304
43const AM_ROWCAP: i64 = 4000
44const AM_PATH: i64 = 1024
45const AM_URLBUF: i64 = 4096
46const AM_DIRMODE: i64 = 0x1ed
47const AM_FILEMODE: i64 = 0x1a4
48const AM_DEF_MIN: i64 = 2048
49const AM_DEF_MAX: i64 = 40
50
51func aw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
52func an(v: i64) -> i64 { if v==0 { sys_write(1,"0" as *u8,1); return 0 } var m: i64=v; if m<0 { sys_write(1,"-" as *u8,1); m=0-m } let t: *u8=sys_mmap(24); var k: i64=0; while m>0 { t[k]=(48+(m%10)) as u8; m=m/10; k=k+1 } let o: *u8=sys_mmap(24); var w: i64=0; var q: i64=k-1; while q>=0 { o[w]=t[q]; w=w+1; q=q-1 } sys_write(1,o,w); return 0 }
53func aslice(buf: *u8, off: i64, len: i64) -> i64 { sys_write(1, ((buf as i64)+off) as *u8, len); return 0 }
54func ap(buf: *u8, off: i64, s: *u8) -> i64 { var i: i64=0; while s[i]!=(0 as u8) { buf[off+i]=s[i]; i=i+1 } return off+i }
55func apsl(dst: *u8, off: i64, src: *u8, so: i64, sl: i64) -> i64 { var i: i64=0; while i<sl { dst[off+i]=src[so+i]; i=i+1 } return off+sl }
56func apn(buf: *u8, off: i64, v: i64) -> i64 { if v==0 { buf[off]=0x30 as u8; return off+1 } var m: i64=v; let t: *u8=sys_mmap(24); var k: i64=0; while m>0 { t[k]=(48+(m%10)) as u8; m=m/10; k=k+1 } var o: i64=off; var q: i64=k-1; while q>=0 { buf[o]=t[q]; o=o+1; q=q-1 } return o }
57func s2i(s: *u8) -> i64 { var v: i64=0; var i: i64=0; var any: i64=0; while s[i]!=(0 as u8) { let c: i64=s[i] as i64; if c<0x30 { return 0-1 } if c>0x39 { return 0-1 } v=v*10+(c-0x30); any=1; i=i+1 } if any==0 { return 0-1 } return v }
58
59// real image magic -- the STRUCTURE check. Kept because a fetch can still return markup or a truncated
60// body even after the INDEX admitted the row; nx_medrec covers identity, this covers form.
61func is_image(buf: *u8, n: i64) -> i64 {
62 if n<4 { return 0 }
63 let a: i64=buf[0] as i64; let b: i64=buf[1] as i64; let c: i64=buf[2] as i64; let d: i64=buf[3] as i64
64 if a==0x47 { if b==0x49 { if c==0x46 { return 1 } } }
65 if a==0xff { if b==0xd8 { if c==0xff { return 1 } } }
66 if a==0x89 { if b==0x50 { if c==0x4e { if d==0x47 { return 1 } } } }
67 if a==0x42 { if b==0x4d { return 1 } }
68 return 0
69}
70// *A PARTIAL WRITE MUST NEVER OCCUPY THE FINAL PATH. The skip check is a pure EXISTENCE test, so a
71// truncated file left by a failed write is skipped on every later run -- permanent corruption that
72// reports as "already had" forever and is invisible in the counters. Write to <path>.part and rename
73// into place only once the FULL byte count is down; a crash then leaves a .part (which the skip check
74// ignores and the next run overwrites), never half an image at the real name.
75// *Adding the skip optimisation silently changed the meaning of "the file is there" from "we fetched it"
76// to "something is at that path". This restores the first meaning, which is the one the skip relies on.
77func save_file(path: *u8, buf: *u8, n: i64) -> i64 {
78 let tmp: *u8 = sys_mmap(AM_PATH)
79 var t: i64 = ap(tmp, 0, path)
80 t = ap(tmp, t, ".part" as *u8)
81 tmp[t]=0 as u8
82 let fd: i64=sys_openat_wr(tmp, AM_FILEMODE)
83 if fd<0 { return 0 }
84 var off: i64=0
85 while off<n {
86 let w: i64=sys_write(fd, ((buf as i64)+off) as *u8, n-off)
87 if w<=0 { sys_close(fd); sys_unlinkat(tmp); return 0 }
88 off=off+w
89 }
90 sys_close(fd)
91 if off != n { sys_unlinkat(tmp); return 0 }
92 if sys_renameat(tmp, path) < 0 { sys_unlinkat(tmp); return 0 }
93 return 1
94}
95// *THE DIGEST IS KNOWN FROM THE INDEX BEFORE ANY FETCH, so an already-harvested image can be skipped
96// without spending a request. Measured need: re-running a page to continue past max_fetch was re-fetching
97// every image already stored -- idempotent, but it spends a shared rate-limited resource for zero gain.
98func have_file(path: *u8) -> i64 { let fd: i64=sys_openat_rd(path); if fd<0 { return 0 } sys_close(fd); return 1 }
99// *THE REVERSE INDEX THAT LETS THE PAGE RENDERER SKIP THE NETWORK ENTIRELY.
100// nx_archive_site_viewer rebuilds a captured page and must resolve each <img src> to bytes. Fetching them
101// is what makes it slow enough to be killed mid-run. But this harvester ALREADY stores those bytes,
102// content-addressed by digest -- the only thing missing is a URL -> digest lookup, because the sidecars are
103// keyed the other way. byurl/<canonhash>.txt closes that loop: the renderer becomes a local, network-free,
104// millisecond operation over whatever the harvester has already banked.
105//
106// *CANONICALISATION IS THE WHOLE TRICK. The CDX 'original' column reads http://www.page3.com:80/foo.jpg
107// while the page's own markup says /foo.jpg or http://www.page3.com/foo.jpg. Hashing either verbatim gives
108// two different keys and the join silently never matches -- it would look like "the archive doesn't have it"
109// rather than "we hashed it wrong". So both sides hash the SAME reduced form: scheme dropped, leading www.
110// dropped, :80/:443 dropped, host lowercased, query dropped, path kept verbatim.
111func canon_hash(buf: *u8, off: i64, len: i64) -> i64 {
112 let end0: i64 = off + len
113 var p: i64 = off
114 var i: i64 = off
115 var sch: i64 = 0 - 1
116 while i + 2 < end0 {
117 if buf[i]==(58 as u8) { if buf[i+1]==(47 as u8) { if buf[i+2]==(47 as u8) { if sch < 0 { sch = i + 3 } } } }
118 i = i + 1
119 }
120 // *PATH ONLY -- the host is DELIBERATELY excluded. byurl/ already lives under
121 // web_assets/archive/<host>/, so the host is established by the DIRECTORY and is redundant here.
122 // Worse, including it is actively WRONG: the CDX 'original' column carries an absolute url
123 // (http://www.page3.com:80/x.jpg) while a page's own markup usually carries a relative one (/x.jpg),
124 // so a host+path key hashes those two forms DIFFERENTLY and the join never matches -- silently,
125 // presenting as "the archive lacks this image" rather than "we hashed it wrong". Path-only makes
126 // both forms agree by construction. (This replaced a host+path version that had exactly that bug,
127 // caught by hand-trace before it ever ran.)
128 if sch >= 0 {
129 p = end0
130 i = sch
131 var found: i64 = 0
132 while i < end0 {
133 if found == 0 { if buf[i]==(47 as u8) { p = i; found = 1 } }
134 i = i + 1
135 }
136 }
137 var h: i64 = 1125899906842597
138 i = p
139 while i < end0 {
140 let c: i64 = buf[i] as i64
141 if c == 63 { i = end0 } else {
142 // case folded: measured, megastar serves 3pxShadow.png whose CDX urlkey is 3pxshadow.png,
143 // so case genuinely differs between the two sides of this join.
144 h = (h * 131) + mr_lc(c)
145 i = i + 1
146 }
147 }
148 if h < 0 { h = 0 - h }
149 return h & 0x7fffffffffffffff
150}
151func append_file(path: *u8, buf: *u8, n: i64) -> i64 {
152 let fd: i64=sys_openat_append(path, AM_FILEMODE)
153 if fd<0 { return 0 }
154 var off: i64=0
155 while off<n { let w: i64=sys_write(fd, ((buf as i64)+off) as *u8, n-off); if w<=0 { sys_close(fd); return 0 } off=off+w }
156 sys_close(fd)
157 return 1
158}
159func fetch_retry(url: *u8, store: *TrustStore, out: *u8, cap: i64, st: *i64, tries: i64) -> i64 {
160 var t: i64=0; var n: i64=0-1
161 while t<tries {
162 n=nx_https_fetch_follow(url, store, out, cap, 6, st)
163 if n>0 { if st[0]==200 { t=tries } else { t=t+1 } } else { t=t+1 }
164 }
165 return n
166}
167// *THE EXTENSION MUST COME FROM THE ROW, NOT FROM THE QUERY. Measured 2026-08-06: megastar.co.uk serves
168// babes-2.jpg with recorded mimetype image/PNG at 31,078 B -- a real content image whose extension and
169// content-type disagree. Deriving the stored extension from the query filter would name it .jpg, and
170// nx_archive_server sets Content-Type BY EXTENSION, so the browser would be handed image/jpeg for PNG
171// bytes and render nothing. Per-row mime is in the index, available BEFORE the fetch, so the skip check
172// still works. Era formats included (Rule 25) -- a 90s corpus is not all jpeg.
173func ext_for(mime: *u8) -> *u8 {
174 if mr_ci_has(mime, "png") == 1 { return ".png" }
175 if mr_ci_has(mime, "gif") == 1 { return ".gif" }
176 if mr_ci_has(mime, "webp") == 1 { return ".webp" }
177 if mr_ci_has(mime, "avif") == 1 { return ".avif" }
178 if mr_ci_has(mime, "bmp") == 1 { return ".bmp" }
179 if mr_ci_has(mime, "icon") == 1 { return ".ico" }
180 if mr_ci_has(mime, "svg") == 1 { return ".svg" }
181 if mr_ci_has(mime, "tiff") == 1 { return ".tif" }
182 if mr_ci_has(mime, "xbitmap") == 1 { return ".xbm" }
183 if mr_ci_has(mime, "pcx") == 1 { return ".pcx" }
184 return ".jpg"
185}
186// per-row extension: copy the mimetype slice out of the CDX row, then classify it.
187func ext_from_row(buf: *u8, off: i64, len: i64, scratch: *u8) -> *u8 {
188 var i: i64 = 0
189 while i < len { scratch[i] = buf[off+i]; i = i + 1 }
190 scratch[len] = 0 as u8
191 return ext_for(scratch)
192}
193
194// *THE SEARCHABLE SIGNAL LIVES IN THE FILENAME, AND CONTENT-ADDRESSING THROWS IT AWAY. Storing an image
195// as TEOIGMBT..jpg is exactly right for dedupe and catastrophic for retrieval: megastar.co.uk names its
196// files alex_simwise-1.jpg / alicia_keys.jpg / angels_and_demons-1.jpg -- person and subject names, the
197// precise tokens a query will use. A harvest nobody can query is not a search capability.
198// So each stored image also gets a TEXT SIDECAR carrying the original url, the TOKENISED filename
199// (separators -> spaces so BM25 can match "alex simwise"), host, capture timestamp, digest and size.
200// nx_corpus_ingest <metadir> <host> then ingests that directory into the domain's seg_store shard, and the
201// archive becomes queryable through the SAME front door as every other nishi-search corpus.
202// Rule 15/22: compose the proven ingester; do not write a second index writer.
203
204// THE WHOLE PATH, not just the filename -- query and extension stripped, separators folded to spaces.
205// *MEASURED CORRECTION 2026-08-06: the filename is the searchable signal only SOMETIMES. megastar.co.uk
206// mixes human-readable names (alex_simwise-1.jpg, alicia_keys.jpg, 280200_condoms.jpg) with opaque CMS ids
207// (sMEG01MTE0NDQxOTgxMzc.jpg, which tokenises to nothing a human would ever type). For that second class
208// the PATH is the only signal there is: /babenews/news/2006/04/07/ yields category and date. Tokenising the
209// last segment alone would have indexed a large slice of the corpus as unsearchable noise.
210func tok_name(dst: *u8, o: i64, src: *u8, so: i64, sl: i64) -> i64 {
211 let end0: i64 = so + sl
212 var sch: i64 = 0 - 1
213 var i: i64 = so
214 while i + 2 < end0 {
215 if src[i]==(58 as u8) { if src[i+1]==(47 as u8) { if src[i+2]==(47 as u8) { if sch < 0 { sch = i + 3 } } } }
216 i = i + 1
217 }
218 if sch < 0 { sch = so }
219 var start: i64 = sch
220 var found: i64 = 0
221 i = sch
222 while i < end0 {
223 if found == 0 { if src[i]==(47 as u8) { start = i + 1; found = 1 } }
224 i = i + 1
225 }
226 var end: i64 = end0
227 i = start
228 while i < end { if src[i]==(63 as u8) { end = i } else { i=i+1 } }
229 var dot: i64 = 0-1
230 i = start
231 while i < end { if src[i]==(46 as u8) { dot = i } i=i+1 }
232 if dot > start { end = dot }
233 var p: i64 = o
234 i = start
235 while i < end {
236 let c: i64 = src[i] as i64
237 var w: i64 = c
238 if c==95 { w=32 }
239 if c==45 { w=32 }
240 if c==43 { w=32 }
241 if c==37 { w=32 }
242 if c==47 { w=32 }
243 dst[p]=w as u8
244 p=p+1; i=i+1
245 }
246 return p
247}
248
249func main(argc: i64, argv: *i64) -> i64 {
250 aw("=== nx_arcmine: INDEX-FIRST domain media harvest (exceed Wayback on completeness) ===\n" as *u8)
251 if argc < 2 { aw("usage: nx_arcmine <domain> [page] [min_bytes] [max_fetch] [mime]\n" as *u8); return 2 }
252 let host: *u8 = argv[1] as *u8
253 var page: i64 = 0
254 var minb: i64 = AM_DEF_MIN
255 var maxf: i64 = AM_DEF_MAX
256 var mime: *u8 = "image/.*" as *u8
257 if argc >= 3 { let v: i64=s2i(argv[2] as *u8); if v>=0 { page=v } }
258 if argc >= 4 { let v: i64=s2i(argv[3] as *u8); if v>=0 { minb=v } }
259 if argc >= 5 { let v: i64=s2i(argv[4] as *u8); if v>=0 { maxf=v } }
260 if argc >= 6 { mime = argv[5] as *u8 }
261
262 aw("host=" as *u8); aw(host); aw(" page=" as *u8); an(page)
263 aw(" min_bytes=" as *u8); an(minb); aw(" max_fetch=" as *u8); an(maxf)
264 aw(" mime=" as *u8); aw(mime); aw("\n" as *u8)
265
266 sys_mkdir("web_assets" as *u8, AM_DIRMODE)
267 sys_mkdir("web_assets/archive" as *u8, AM_DIRMODE)
268 let dir: *u8 = sys_mmap(AM_PATH)
269 var dof: i64 = ap(dir, 0, "web_assets/archive/" as *u8)
270 dof = ap(dir, dof, host); dir[dof]=0 as u8
271 sys_mkdir(dir, AM_DIRMODE)
272 let mdir: *u8 = sys_mmap(AM_PATH)
273 var mof: i64 = ap(mdir, 0, dir); mof = ap(mdir, mof, "/media" as *u8); mdir[mof]=0 as u8
274 sys_mkdir(mdir, AM_DIRMODE)
275 let tdir: *u8 = sys_mmap(AM_PATH)
276 var tof: i64 = ap(tdir, 0, dir); tof = ap(tdir, tof, "/meta" as *u8); tdir[tof]=0 as u8
277 sys_mkdir(tdir, AM_DIRMODE)
278 let bdir: *u8 = sys_mmap(AM_PATH)
279 var bof: i64 = ap(bdir, 0, dir); bof = ap(bdir, bof, "/byurl" as *u8); bdir[bof]=0 as u8
280 sys_mkdir(bdir, AM_DIRMODE)
281 aw("media dir: " as *u8); aw(mdir); aw("\n" as *u8)
282 aw("byurl dir: " as *u8); aw(bdir); aw(" (url->digest reverse index for the page renderer)\n" as *u8)
283 aw("meta dir: " as *u8); aw(tdir); aw(" (nx_corpus_ingest <metadir> " as *u8); aw(host); aw(" -> searchable)\n" as *u8)
284
285 let r: i64 = nx_trust_store_load_from_certdata("data/mozilla_certdata.txt" as *u8, 512, AM_CAP)
286 if r<=0 { aw("trust store load failed\n" as *u8); return 1 }
287 let store: *TrustStore = r as *TrustStore
288 let st: *i64 = sys_mmap(8) as *i64
289
290 // ---- 1. ENUMERATE FROM THE INDEX. collapse=digest kills the byte-identical redundancy server-side.
291 let cu: *u8 = sys_mmap(AM_URLBUF)
292 var co: i64 = ap(cu, 0, "https://web.archive.org/cdx/search/cdx?url=" as *u8)
293 co = ap(cu, co, host)
294 co = ap(cu, co, "&matchType=domain&filter=statuscode:200&filter=mimetype:" as *u8)
295 co = ap(cu, co, mime)
296 co = ap(cu, co, "&collapse=digest&output=text&limit=300&page=" as *u8)
297 co = apn(cu, co, page)
298 cu[co]=0 as u8
299 aw("\nCDX: " as *u8); aw(cu); aw("\n" as *u8)
300
301 let cdxbuf: *u8 = sys_mmap(AM_CAP)
302 let n: i64 = fetch_retry(cu, store, cdxbuf, AM_CAP, st, 4)
303 if n<=0 { aw("CDX fetch failed (transport)\n" as *u8); return 1 }
304 if st[0]!=200 { aw("CDX status " as *u8); an(st[0]); aw(" -- rate limited? back off and retry\n" as *u8); return 1 }
305
306 let RS: *i64 = sys_mmap(AM_ROWCAP*8) as *i64
307 let RE: *i64 = sys_mmap(AM_ROWCAP*8) as *i64
308 let rows: i64 = cdx_rows(cdxbuf, n, RS, RE, AM_ROWCAP)
309 aw("index rows (deduped by digest) = " as *u8); an(rows); aw("\n" as *u8)
310 if rows<=0 { aw("page " as *u8); an(page); aw(" is EMPTY -- end of the index for this host/mime\n" as *u8); return 0 }
311
312 // ---- 2. ADMIT FROM THE INDEX, then fetch only what survives.
313 let o2: *i64 = sys_mmap(64) as *i64
314 let ub: *u8 = sys_mmap(AM_URLBUF)
315 let img: *u8 = sys_mmap(AM_CAP)
316 let path: *u8 = sys_mmap(AM_PATH)
317 let mpath: *u8 = sys_mmap(AM_PATH)
318 let doc: *u8 = sys_mmap(AM_URLBUF)
319 let mscratch: *u8 = sys_mmap(AM_PATH)
320 let gpath: *u8 = sys_mmap(AM_PATH)
321 let doc2: *u8 = sys_mmap(AM_URLBUF)
322 let bpath: *u8 = sys_mmap(AM_PATH)
323 let bref: *u8 = sys_mmap(AM_PATH)
324
325 var seen: i64=0; var adm: i64=0; var rjt: i64=0; var rjs: i64=0; var rjz: i64=0; var rju: i64=0
326 var got: i64=0; var bad: i64=0; var nonimg: i64=0; var bytes: i64=0; var skip: i64=0; var metas: i64=0; var byurls: i64=0
327 var i: i64=0
328 while i<rows {
329 seen=seen+1
330 let v: i64 = mr_row_admit(cdxbuf, RS[i], RE[i], minb, o2)
331 if v == MR_REJ_TOMBSTONE { rjt=rjt+1 }
332 if v == MR_REJ_STATUS { rjs=rjs+1 }
333 if v == MR_REJ_TOOSMALL { rjz=rjz+1 }
334 if v == MR_UNKNOWN { rju=rju+1 }
335 if v == MR_ADMIT {
336 adm=adm+1
337 // CONTENT-ADDRESS FIRST. The digest is an INDEX field, so the destination path is known
338 // before any network decision -- which is what makes the skip below free.
339 cdx_field(cdxbuf, RS[i], RE[i], MR_F_MIME, o2)
340 let xt: *u8 = ext_from_row(cdxbuf, o2[0], o2[1], mscratch)
341 cdx_field(cdxbuf, RS[i], RE[i], MR_F_DIGEST, o2)
342 var p: i64 = ap(path, 0, mdir)
343 p = ap(path, p, "/" as *u8)
344 p = apsl(path, p, cdxbuf, o2[0], o2[1])
345 p = ap(path, p, xt)
346 path[p]=0 as u8
347 // SEARCH SIDECAR, BUILT FROM INDEX DATA ONLY and written unconditionally. *This must not live
348 // on the fetch path: images harvested before sidecars existed would then be SKIPPED and never
349 // gain metadata -- permanently unsearchable, and silently so. Depending on no fetch makes the
350 // skip path a BACKFILL instead of a dead end.
351 var mp: i64 = ap(mpath, 0, tdir)
352 mp = ap(mpath, mp, "/" as *u8)
353 cdx_field(cdxbuf, RS[i], RE[i], MR_F_DIGEST, o2)
354 mp = apsl(mpath, mp, cdxbuf, o2[0], o2[1])
355 mp = ap(mpath, mp, ".txt" as *u8)
356 mpath[mp]=0 as u8
357 if have_file(mpath)==0 {
358 cdx_field(cdxbuf, RS[i], RE[i], MR_F_ORIG, o2)
359 let suo: i64=o2[0]; let sul: i64=o2[1]
360 var d: i64 = ap(doc, 0, "archived image\nurl: " as *u8)
361 d = apsl(doc, d, cdxbuf, suo, sul)
362 d = ap(doc, d, "\nname: " as *u8)
363 d = tok_name(doc, d, cdxbuf, suo, sul)
364 d = ap(doc, d, "\nhost: " as *u8)
365 d = ap(doc, d, host)
366 d = ap(doc, d, "\ncaptured: " as *u8)
367 cdx_field(cdxbuf, RS[i], RE[i], MR_F_TS, o2)
368 d = apsl(doc, d, cdxbuf, o2[0], o2[1])
369 d = ap(doc, d, "\ndigest: " as *u8)
370 cdx_field(cdxbuf, RS[i], RE[i], MR_F_DIGEST, o2)
371 d = apsl(doc, d, cdxbuf, o2[0], o2[1])
372 d = ap(doc, d, "\nindex_bytes: " as *u8)
373 d = apn(doc, d, mr_row_size(cdxbuf, RS[i], RE[i], o2))
374 d = ap(doc, d, "\nmedia: " as *u8)
375 d = ap(doc, d, path)
376 d = ap(doc, d, "\n" as *u8)
377 if save_file(mpath, doc, d)==1 { metas=metas+1 }
378 // BROWSABLE SURFACE, built incrementally. Appended inside the SAME have_file guard as the
379 // sidecar, so exactly one tile exists per unique digest no matter how often a page is re-run.
380 var g: i64 = ap(gpath, 0, dir); g = ap(gpath, g, "/index.html" as *u8); gpath[g]=0 as u8
381 if have_file(gpath)==0 {
382 var h: i64 = ap(doc2, 0, "<!doctype html><meta charset=utf-8><title>archive: " as *u8)
383 h = ap(doc2, h, host)
384 h = ap(doc2, h, "</title><style>body{background:#111;color:#ddd;font:14px system-ui;margin:0;padding:16px}h1{font-size:16px;font-weight:600}a{color:#8bf}figure{display:inline-block;margin:6px;vertical-align:top;max-width:240px}img{max-width:240px;height:auto;display:block;background:#222}figcaption{font-size:11px;color:#999;word-break:break-all;padding-top:4px}</style>\n<h1>sovereign archive — " as *u8)
385 h = ap(doc2, h, host)
386 h = ap(doc2, h, " — recovered from the Wayback index</h1>\n" as *u8)
387 append_file(gpath, doc2, h)
388 }
389 var t: i64 = ap(doc2, 0, "<figure><a href=\"media/" as *u8)
390 cdx_field(cdxbuf, RS[i], RE[i], MR_F_DIGEST, o2)
391 t = apsl(doc2, t, cdxbuf, o2[0], o2[1]); t = ap(doc2, t, xt)
392 t = ap(doc2, t, "\"><img loading=lazy src=\"media/" as *u8)
393 t = apsl(doc2, t, cdxbuf, o2[0], o2[1]); t = ap(doc2, t, xt)
394 t = ap(doc2, t, "\"></a><figcaption>" as *u8)
395 cdx_field(cdxbuf, RS[i], RE[i], MR_F_ORIG, o2)
396 t = tok_name(doc2, t, cdxbuf, o2[0], o2[1])
397 t = ap(doc2, t, "<br>" as *u8)
398 cdx_field(cdxbuf, RS[i], RE[i], MR_F_TS, o2)
399 t = apsl(doc2, t, cdxbuf, o2[0], o2[1])
400 t = ap(doc2, t, "</figcaption></figure>\n" as *u8)
401 append_file(gpath, doc2, t)
402 }
403 // *BYURL GETS ITS OWN GUARD -- it must NOT hang off the sidecar's.
404 // Nested under have_file(mpath)==0 it could only ever be written when a SIDECAR WAS NEW, and
405 // every one of the 727 megastar + 34 page3 sidecars already on disk predates byurl. The
406 // reverse index would have been 0 of 761 while every run reported success, and the join it
407 // exists to serve would have looked broken when it had simply never been populated.
408 // *A SINGLE GUARD COVERING N INDEPENDENT ARTIFACTS MEANS N-1 OF THEM CAN NEVER BACKFILL.
409 // The question is not "have I processed this row" but "does THIS artifact exist", and those
410 // stop being the same question the moment a second artifact is added.
411 cdx_field(cdxbuf, RS[i], RE[i], MR_F_ORIG, o2)
412 var bp: i64 = ap(bpath, 0, bdir)
413 bp = ap(bpath, bp, "/" as *u8)
414 bp = apn(bpath, bp, mr_canon_hash(cdxbuf, o2[0], o2[1]))
415 bp = ap(bpath, bp, ".txt" as *u8)
416 bpath[bp]=0 as u8
417 if have_file(bpath)==0 {
418 cdx_field(cdxbuf, RS[i], RE[i], MR_F_DIGEST, o2)
419 var bd: i64 = apsl(bref, 0, cdxbuf, o2[0], o2[1])
420 bd = ap(bref, bd, xt)
421 bref[bd]=0x0a as u8; bd=bd+1
422 if save_file(bpath, bref, bd)==1 { byurls=byurls+1 }
423 }
424 if have_file(path)==1 { skip=skip+1 } else {
425 if got < maxf {
426 // timestamp (field 1) + original url (field 2) -> the RAW archived bytes via id_
427 cdx_field(cdxbuf, RS[i], RE[i], MR_F_TS, o2)
428 let tso: i64=o2[0]; let tsl: i64=o2[1]
429 cdx_field(cdxbuf, RS[i], RE[i], MR_F_ORIG, o2)
430 let uo: i64=o2[0]; let ul: i64=o2[1]
431 var q: i64 = ap(ub, 0, "https://web.archive.org/web/" as *u8)
432 q = apsl(ub, q, cdxbuf, tso, tsl)
433 q = ap(ub, q, "id_/" as *u8)
434 q = apsl(ub, q, cdxbuf, uo, ul)
435 ub[q]=0 as u8
436 let m: i64 = fetch_retry(ub, store, img, AM_CAP, st, 3)
437 if m>0 {
438 if st[0]==200 {
439 if is_image(img, m)==1 {
440 if save_file(path, img, m)==1 { got=got+1; bytes=bytes+m } else { bad=bad+1 }
441 } else { nonimg=nonimg+1 }
442 } else { bad=bad+1 }
443 } else { bad=bad+1 }
444 }
445 }
446 }
447 i=i+1
448 }
449
450 aw("\n--- ARCMINE page " as *u8); an(page); aw(" ---\n" as *u8)
451 aw(" index rows seen = " as *u8); an(seen); aw("\n" as *u8)
452 aw(" ADMITTED = " as *u8); an(adm); aw("\n" as *u8)
453 aw(" rejected tombstone = " as *u8); an(rjt); aw("\n" as *u8)
454 aw(" rejected status = " as *u8); an(rjs); aw("\n" as *u8)
455 aw(" rejected too-small = " as *u8); an(rjz); aw("\n" as *u8)
456 aw(" unknown (fail-closed)= " as *u8); an(rju); aw("\n" as *u8)
457 aw(" FETCHED+STORED = " as *u8); an(got); aw("\n" as *u8)
458 aw(" fetch failed = " as *u8); an(bad); aw("\n" as *u8)
459 aw(" fetched-but-not-image= " as *u8); an(nonimg); aw("\n" as *u8)
460 aw(" already had (skipped)= " as *u8); an(skip); aw("\n" as *u8)
461 aw(" search sidecars = " as *u8); an(metas); aw("\n" as *u8)
462 aw(" byurl reverse-index = " as *u8); an(byurls); aw("\n" as *u8)
463 aw(" bytes stored = " as *u8); an(bytes); aw("\n" as *u8)
464 let remain: i64 = adm - skip - got
465 if remain > 0 { aw(" NOTE: max_fetch capped this run -- " as *u8); an(remain); aw(" admitted rows NOT yet fetched. Re-run to continue.\n" as *u8) }
466 aw(" next: nx_arcmine " as *u8); aw(host); aw(" " as *u8); an(page+1); aw("\n" as *u8)
467 return 0
468}