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,"<" as *u8) } else { if c==0x3e { o=ap(dst,o,">" as *u8) } else { if c==0x26 { o=ap(dst,o,"&" 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>🕐 Nishi Time Machine</h1><p class=sub>page3.com — 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 · <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> → 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 · CID <b>" as *u8); o=apn(ob,o,cid); o=ap(ob,o,"</b> · <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 — 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 · fetched by the Nishi sovereign TLS-1.3 client · content-addressed (CID) · 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}