code wiki / (root) / nx_archive_viewer.nx

nx_archive_viewer.nx source

↩ module page · 194 lines · 12349 B

1// nx_archive_viewer.nx -- make the time-machine VISIBLE. Pulls page3.com's real Wayback CDX, browses as-of-1997, 2// fetches the actual archived page, and EMITS a viewable web artifact under web_assets/archive/: 3// page.html = the reconstructed page (its <img src> rewritten to the archive's image endpoint so it renders) 4// index.html = a dashboard: provenance (bytes/CID/dates), the capture timeline, the rendered page in an iframe, 5// and the raw archived HTML (exactly what we grabbed). 6// Sovereign TLS-1.3. Status-aware retry (handles transient 503 rate-limits) + a DISK CACHE so we fetch each artifact 7// once and never re-hammer the archive. Read-only except the cache + the two output files. license_tier: ORIGINAL 8import "nx_syscalls.nx" 9import "nx_x509_trust_store.nx" 10import "nx_trust_store_load_from_certdata.nx" 11import "nx_https_fetch_follow.nx" 12import "nx_cdx_parse.nx" 13import "nx_temporal_index.nx" 14const K_MAGIC_1000000007: i64 = 1000000007 15const K_MAGIC_4194304: i64 = 4194304 16const K_MAGIC_4096: i64 = 4096 17const K_MAGIC_19970601: i64 = 19970601 18const K_MAGIC_2048: i64 = 2048 19 20func gw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 21func gn(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 } 22func 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 } 23func apn(buf: *u8, off: i64, v: i64) -> i64 { if v==0 { buf[off]=0x30 as u8; return off+1 } var m: i64=v; if m<0 { buf[off]=0x2d as u8; off=off+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 } var o: i64=off; var q: i64=k-1; while q>=0 { buf[o]=t[q]; o=o+1; q=q-1 } return o } 24func apslice(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 } 25func cid_hash(buf: *u8, n: i64) -> i64 { var h: i64=0; var i: i64=0; while i<n { h=(h*131+(buf[i] as i64))%K_MAGIC_1000000007; i=i+1 } return h } 26func dstarts(buf: *u8, off: i64, n: i64, lit: *u8, litlen: i64) -> i64 { if off+litlen>n { return 0 } var i: i64=0; while i<litlen { var a: i64=buf[off+i] as i64; if a>=0x41 { if a<=0x5a { a=a+0x20 } } if a != (lit[i] as i64) { return 0 } i=i+1 } return 1 } 27func count_img(buf: *u8, n: i64) -> i64 { var c: i64=0; var i: i64=0; while i+4<=n { if dstarts(buf,i,n,"<img" as *u8,4)==1 { c=c+1; i=i+4 } else { i=i+1 } } return c } 28func esc_html(dst: *u8, doff: i64, src: *u8, n: i64) -> i64 { 29 var o: i64=doff; var i: i64=0 30 while i<n { 31 let c: i64=src[i] as i64 32 if c==0x3c { o=ap(dst,o,"&lt;" as *u8) } else { if c==0x3e { o=ap(dst,o,"&gt;" as *u8) } else { if c==0x26 { o=ap(dst,o,"&amp;" as *u8) } else { dst[o]=c as u8; o=o+1 } } } 33 i=i+1 34 } 35 return o 36} 37func rewrite_render(dst: *u8, doff: i64, src: *u8, n: i64, ts: *u8, tso: i64, tsl: i64) -> i64 { 38 var o: i64=doff; var i: i64=0 39 while i<n { 40 var handled: i64=0 41 if dstarts(src, i, n, "src=" as *u8, 4)==1 { 42 if i+4<n { 43 if (src[i+4] as i64)==0x22 { 44 o=ap(dst,o,"src=" as *u8); dst[o]=0x22 as u8; o=o+1 45 o=ap(dst,o,"https://web.archive.org/web/" as *u8); o=apslice(dst,o,ts,tso,tsl); o=ap(dst,o,"im_/" as *u8) 46 i=i+5 47 var go: i64=1 48 while go==1 { if i<n { let cc: i64=src[i] as i64; if cc==0x22 { go=0 } else { dst[o]=cc as u8; o=o+1; i=i+1 } } else { go=0 } } 49 handled=1 50 } 51 } 52 } 53 if handled==0 { dst[o]=src[i] as u8; o=o+1; i=i+1 } 54 } 55 return o 56} 57func save_file(path: *u8, buf: *u8, n: i64) -> i64 { 58 let fd: i64 = sys_openat_wr(path, 0x1a4) 59 if fd<0 { return 0 } 60 var off: i64=0 61 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 } 62 sys_close(fd); return 1 63} 64func read_file(path: *u8, buf: *u8, cap: i64, lenbox: *i64) -> i64 { 65 let fd: i64 = sys_openat_rd(path) 66 if fd<0 { return 0 } 67 let n: i64 = sys_read(fd, buf, cap) 68 sys_close(fd) 69 if n<=0 { return 0 } 70 lenbox[0]=n 71 return n 72} 73// fetch with retry on transient error OR non-200 status (503 rate-limit). Slow archive requests self-space the retries. 74func fetch_retry(url: *u8, store: *TrustStore, out: *u8, cap: i64, st: *i64, tries: i64) -> i64 { 75 var t: i64=0; var n: i64=0-1 76 while t<tries { 77 n=nx_https_fetch_follow(url, store, out, cap, 6, st) 78 if n>0 { if st[0]==200 { t=tries } else { gw(" [status " as *u8); gn(st[0]); gw(" - retry]\n" as *u8); t=t+1 } } else { gw(" [err " as *u8); gn(n); gw(" - retry]\n" as *u8); t=t+1 } 79 } 80 return n 81} 82 83func main() -> i64 { 84 gw("=== nx_archive_viewer: render the time-machine to web_assets/archive/ ===\n" as *u8) 85 sys_mkdir("web_assets" as *u8, 0x1ed) 86 sys_mkdir("web_assets/archive" as *u8, 0x1ed) 87 let r: i64 = nx_trust_store_load_from_certdata("data/mozilla_certdata.txt" as *u8, 512, K_MAGIC_4194304) 88 if r<=0 { gw("trust store load failed\n" as *u8); return 1 } 89 let store: *TrustStore = r as *TrustStore 90 let cap: i64 = K_MAGIC_4194304 91 let cdxbuf: *u8 = sys_mmap(cap) 92 let st: *i64 = sys_mmap(8) as *i64 93 let lb: *i64 = sys_mmap(8) as *i64 94 95 // -- CDX (cache-first, then fetch) -- 96 var ncdx: i64 = read_file("web_assets/archive/_cache_cdx.txt" as *u8, cdxbuf, cap, lb) 97 if ncdx>0 { gw("[1] CDX from cache (" as *u8); gn(ncdx); gw(" bytes)\n" as *u8) } else { 98 gw("[1] fetch page3.com CDX ...\n" as *u8) 99 ncdx = fetch_retry("https://web.archive.org/cdx/search/cdx?url=page3.com&limit=120" as *u8, store, cdxbuf, cap, st, 6) 100 gw(" CDX status=" as *u8); gn(st[0]); gw(" bytes=" as *u8); gn(ncdx); gw("\n" as *u8) 101 if ncdx<=0 { gw(" CDX fetch failed -- abort\n" as *u8); return 1 } 102 if st[0]!=200 { gw(" web.archive.org rate-limited (status not 200) -- wait ~1 min and re-run\n" as *u8); return 1 } 103 save_file("web_assets/archive/_cache_cdx.txt" as *u8, cdxbuf, ncdx) 104 } 105 106 let RS: *i64=sys_mmap(K_MAGIC_4096) as *i64 107 let RE: *i64=sys_mmap(K_MAGIC_4096) as *i64 108 let nrows: i64=cdx_rows(cdxbuf, ncdx, RS, RE, 480) 109 let keys: *i64=sys_mmap(K_MAGIC_4096) as *i64 110 let dates: *i64=sys_mmap(K_MAGIC_4096) as *i64 111 let tso: *i64=sys_mmap(K_MAGIC_4096) as *i64 112 let tsl: *i64=sys_mmap(K_MAGIC_4096) as *i64 113 let b2: *i64=sys_mmap(16) as *i64 114 var i: i64=0 115 var m: i64=0 116 while i<nrows { 117 cdx_field(cdxbuf, RS[i], RE[i], 4, b2) 118 var is200: i64=0 119 if b2[1]==3 { if (cdxbuf[b2[0]] as i64)==0x32 { if (cdxbuf[b2[0]+1] as i64)==0x30 { if (cdxbuf[b2[0]+2] as i64)==0x30 { is200=1 } } } } 120 if is200==1 { 121 cdx_field(cdxbuf, RS[i], RE[i], 0, b2) 122 let ko: i64=b2[0]; let kl: i64=b2[1] 123 let kb: *u8=sys_mmap(256) 124 var j: i64=0 125 while j<kl { kb[j]=cdxbuf[ko+j]; j=j+1 } 126 kb[kl]=0 as u8 127 keys[m]=kb as i64 128 cdx_field(cdxbuf, RS[i], RE[i], 1, b2); dates[m]=cdx_ts8(cdxbuf, b2[0], b2[1]); tso[m]=b2[0]; tsl[m]=b2[1] 129 m=m+1 130 } 131 i=i+1 132 } 133 gw(" captures(200)=" as *u8); gn(m); gw("\n" as *u8) 134 if m<=0 { gw(" no captures -- abort\n" as *u8); return 1 } 135 136 let q: i64 = ti_query(keys, dates, m, "com,page3)/" as *u8, K_MAGIC_19970601) 137 if q<0 { gw(" no as-of-1997 capture -- abort\n" as *u8); return 1 } 138 gw("[2] as-of 1997-06 -> capture " as *u8); gn(dates[q]); gw("\n" as *u8) 139 140 // -- archived content (cache-first, then fetch) -- 141 let cont: *u8=sys_mmap(cap) 142 var ncont: i64 = read_file("web_assets/archive/_cache_page.txt" as *u8, cont, cap, lb) 143 if ncont>0 { gw("[3] content from cache (" as *u8); gn(ncont); gw(" bytes)\n" as *u8) } else { 144 let url: *u8=sys_mmap(K_MAGIC_2048) 145 var uo: i64=0 146 uo=ap(url, uo, "https://web.archive.org/web/" as *u8) 147 uo=apslice(url, uo, cdxbuf, tso[q], tsl[q]) 148 uo=ap(url, uo, "id_/http://www.page3.com:80/" as *u8) 149 url[uo]=0 as u8 150 gw("[3] fetch archived page ...\n" as *u8) 151 ncont=fetch_retry(url, store, cont, cap, st, 6) 152 gw(" content status=" as *u8); gn(st[0]); gw(" bytes=" as *u8); gn(ncont); gw("\n" as *u8) 153 if ncont<=0 { gw(" content fetch failed -- abort\n" as *u8); return 1 } 154 if st[0]!=200 { gw(" archive rate-limited on content -- wait ~1 min and re-run (CDX is cached now)\n" as *u8); return 1 } 155 save_file("web_assets/archive/_cache_page.txt" as *u8, cont, ncont) 156 } 157 let cid: i64=cid_hash(cont, ncont) 158 let nimg: i64=count_img(cont, ncont) 159 160 // -- emit page.html (rendered reconstruction) -- 161 let pbuf: *u8=sys_mmap(cap) 162 let po: i64=rewrite_render(pbuf, 0, cont, ncont, cdxbuf, tso[q], tsl[q]) 163 if save_file("web_assets/archive/page.html" as *u8, pbuf, po)==0 { gw(" page.html write failed\n" as *u8); return 1 } 164 165 // -- emit index.html (dashboard) -- 166 let ob: *u8=sys_mmap(cap) 167 var o: i64=0 168 o=ap(ob,o,"<!DOCTYPE html><html><head><meta charset=utf-8><title>Nishi Time Machine - page3.com</title><style>" as *u8) 169 o=ap(ob,o,"body{font-family:system-ui,Arial,sans-serif;margin:0;background:#0e0f13;color:#e7e7ea}.wrap{max-width:1040px;margin:0 auto;padding:28px}" as *u8) 170 o=ap(ob,o,"h1{font-size:26px;margin:0 0 4px}h3{margin:26px 0 8px;color:#cdd}.sub{color:#8a8f9a;margin:0 0 18px}" as *u8) 171 o=ap(ob,o,".prov{background:#171a21;border:1px solid #2a2d36;border-radius:10px;padding:16px 18px;line-height:1.6}.prov b{color:#7bd88f}" as *u8) 172 o=ap(ob,o,".tl{display:flex;flex-wrap:wrap;gap:6px;margin:10px 0}.tl span{background:#23262f;border:1px solid #343846;border-radius:5px;padding:3px 8px;font-size:12px;color:#bcd}" as *u8) 173 o=ap(ob,o,"iframe{width:100%;height:520px;border:1px solid #2a2d36;border-radius:10px;background:#fff}" as *u8) 174 o=ap(ob,o,"pre{white-space:pre-wrap;word-break:break-all;background:#0b0c10;border:1px solid #2a2d36;border-radius:10px;padding:14px;font-size:12px;color:#9fe6c8;max-height:360px;overflow:auto}" as *u8) 175 o=ap(ob,o,"</style></head><body><div class=wrap>" as *u8) 176 o=ap(ob,o,"<h1>&#128336; Nishi Time Machine</h1><p class=sub>page3.com &mdash; reconstructed from the archive by our own sovereign stack (TLS-1.3, no curl/SDK)</p>" as *u8) 177 o=ap(ob,o,"<div class=prov><b>What we pulled</b><br>CDX index: " as *u8); o=apn(ob,o,ncdx); o=ap(ob,o," bytes &middot; <b>" as *u8); o=apn(ob,o,m); o=ap(ob,o,"</b> status-200 captures<br>" as *u8) 178 o=ap(ob,o,"Browsing <b>as-of 1997-06</b> &rarr; capture <b>" as *u8); o=apn(ob,o,dates[q]); o=ap(ob,o,"</b><br>Reconstructed page: <b>" as *u8); o=apn(ob,o,ncont); o=ap(ob,o,"</b> bytes &middot; CID <b>" as *u8); o=apn(ob,o,cid); o=ap(ob,o,"</b> &middot; <b>" as *u8); o=apn(ob,o,nimg); o=ap(ob,o,"</b> media references</div>" as *u8) 179 o=ap(ob,o,"<h3>Captures we found</h3><div class=tl>" as *u8) 180 var d: i64=0 181 while d<m { if d<60 { o=ap(ob,o,"<span>" as *u8); o=apn(ob,o,dates[d]); o=ap(ob,o,"</span>" as *u8) } d=d+1 } 182 o=ap(ob,o,"</div>" as *u8) 183 o=ap(ob,o,"<h3>Reconstructed page (images served live from the archive)</h3><iframe src=page.html></iframe>" as *u8) 184 o=ap(ob,o,"<h3>Raw archived HTML &mdash; exactly what we grabbed</h3><pre>" as *u8) 185 o=esc_html(ob, o, cont, ncont) 186 o=ap(ob,o,"</pre>" as *u8) 187 o=ap(ob,o,"<div class=prov>Source: Internet Archive Wayback Machine &middot; fetched by the Nishi sovereign TLS-1.3 client &middot; content-addressed (CID) &middot; never-delete</div>" as *u8) 188 o=ap(ob,o,"</div></body></html>" as *u8) 189 if save_file("web_assets/archive/index.html" as *u8, ob, o)==0 { gw(" index.html write failed\n" as *u8); return 1 } 190 191 gw("\nWROTE web_assets/archive/index.html (" as *u8); gn(o); gw(" bytes) + page.html (" as *u8); gn(po); gw(" bytes)\n" as *u8) 192 gw("VIEWER DONE -- open web_assets/archive/index.html in a browser.\n" as *u8) 193 return 0 194}