code wiki / _hdl_build / nx_page_verify.nx

nx_page_verify.nx source

↩ module page · 1006 lines · 59259 B

1// nx_page_verify.nx -- BROWSER-GRADE LIVE PAGE VERIFIER (the "my verify was shit" cure, operator 2026-07-16). 2// Status-200-on-the-page is NOT verification: a page can 200 while every image on it is broken (the /render3d 3// incident: the edge 301-strips trailing slashes, so RELATIVE img srcs resolve to the site root -> 404/fallback 4// -> broken images that a direct asset fetch never catches). This organ verifies a page THE WAY A BROWSER 5// EXPERIENCES IT: 6// 1. fetch the page (sovereign TLS, redirects followed) -- must be 200 7// 2. extract every asset ref (img/script src=, link href=) 8// 3. LAW CHECK: a RELATIVE asset ref = RED by construction on our slash-stripping edge (use absolute /path) 9// 4. fetch every same-origin asset -- 200 required 10// 5. DEEP-DECODE: PNG assets run through the full sovereign decoder (nx_png_decode: signature, IHDR, 11// zlib inflate, unfilter incl Paeth) -- "200 + magic bytes" is not proof; DECODED WxH is 12// 6. an asset that answers with HTML (the 404-fallback class) = RED even though its status is 200 13// Composes nx_https_fetch_follow (the games_url_check fetch spine) + nx_png_decode. Exit = broken count. 14// usage: nx_page_verify <https-url> [connect-host:port] (run from repo root: needs data/mozilla_certdata.txt) 15// [connect-host:port] = OPTIONAL connect-override (curl --connect-to): page + every asset fetch opens 16// TCP+TLS to THIS endpoint while SNI/Host/cert-name stay the URL host -- use 127.0.0.1:8443 to verify 17// our OWN vhosts deterministically from the sovereign edge (kills the DSM-nginx :443 coin-flip false-RED). 18// license_tier: ORIGINAL 19import "nx_syscalls.nx" 20import "nx_x509_trust_store.nx" 21import "nx_trust_store_load_from_certdata.nx" 22import "nx_https_url_connect.nx" 23import "nx_https_fetch_follow.nx" 24import "nx_png_decoder.nx" 25// ONE DEFINITION OF -AN IMG WITH USABLE ATTRIBUTES-, shared with the compare generator that WRITES 26// them, so the emitter and this ruler cannot drift into two answers (2026-08-26). 27import "nx_imgattr_lib.nx" 28import "nx_pageref_lib.nx" // the asset-reference vocabulary: ONE owner, shared with nx_pageref_gate 29import "nx_sha256.nx" // canonical artifact/evidence identity hashing 30 31const PV_CAP: i64 = 8388608 // the EYE must out-read the pages it audits (the 2MiB-truncation lesson) 32const PV_MAXREF: i64 = 256 // per-page ref ceiling; the compare gallery lane emits 15+ figures beside 33 // chrome refs and pushed 64 -- reaching this cap now ANNOUNCES (never a silent floor) 34// hoisted to satisfy the magic-number ratchet (rule 11 / LAW L001+L006, enforced at the 35// build gate by nx_magicratchet -- it REFUSED the seq911 build until the count went down). 36// Named for what they bound, not for their value, so a future reader can judge the size. 37const PV_CERTDATA_CAP: i64 = 4194304 // trust-store parse arena for mozilla_certdata.txt 38const PV_URL_CAP: i64 = 4096 // one absolute URL (origin, and each resolved ref) 39const PV_REF_MAX: i64 = 2048 // longest relative ref we will resolve 40// scratch for nx_pageref_lib's pr_attr_hit: [0]=bytes to skip to the value, [1]=the closing quote. 41// Two slots because the vocabulary answers two questions per hit, and one i64 is eight bytes. 42const PV_PROUT_SLOTS: i64 = 2 43const PV_WORD_BYTES: i64 = 8 44const PV_FULLURL_CAP: i64 = 8192 // origin + ref joined 45// ATTRIBUTE DELIMITERS (2026-08-25) -- named so the BARE (unquoted) attribute scan reads as intent 46// rather than as character codes. HTML permits an unquoted attribute value, terminated by whitespace 47// or '>'. The 2026-08-18 fix taught this scanner BOTH QUOTE STYLES and stopped at two; our own 48// sites/nishifamily/world/foundation.html emits <img src=nx_worldpipe.png ...> with NO quotes, and 49// that page reported img tags=16 but refs resolved=1. 50const PV_ATTR_DQ: i64 = 34 // " 51const PV_ATTR_SQ: i64 = 39 // ' 52const PV_ATTR_GT: i64 = 62 // > 53const PV_ATTR_SP: i64 = 32 // space 54const PV_ATTR_TAB: i64 = 9 // tab 55const PV_ATTR_LF: i64 = 10 // newline 56const PV_ATTR_CR: i64 = 13 57const PV_ATTR_LT: i64 = 60 // < 58// PV_TAG_LOOKBACK / pv_in_tag -- ADDED 2026-08-25, after the first POPULATION-SCALE run of the new 59// nx_sitesweep measured THIS scanner's bare-attribute branch firing on INLINE JAVASCRIPT. Adjudicated 60// by hand over four pages: /world/skull (3 refs) and /world/models (9) were REAL broken images, while 61// /atlashygiene reported AI_SYNTHESIZED / PRIMARY_EMPIRICAL x3 / COORDINATION_ROW and /listen reported 62// a fragment of minified script -- six refs that are JavaScript, not markup. 63// THE CAUSE IS STRUCTURAL, NOT A VOCABULARY GAP: a BARE attribute has no delimiter, so el.src=url 64// inside a script is byte-for-byte the same SHAPE as an unquoted src= on an img tag. A DETECTOR WITH 65// FALSE POSITIVES IS WORSE THAN NONE -- it teaches everyone to ignore it -- and this branch had just 66// been handed the whole site to shout at. 67// THE FIX IS HTML, NOT A BLOCKLIST: an attribute can only occur INSIDE an open tag, so scan back and 68// require the nearest angle bracket to be the opener. Inline script always sits after the bracket that 69// closed its script tag, so every JS occurrence is rejected BY CONSTRUCTION and no keyword list can 70// ever go stale. 71// APPLIED ONLY TO THE BARE FORM. The quoted forms are deliberately untouched: a quoted src assigned in 72// a script IS an asset the browser really fetches, and a relative one really does break under the same 73// edge rule -- narrowing those would LOSE true positives instead of removing false ones. 74// UNDECIDABLE REJECTS. A genuine attribute is always a few dozen bytes from its own opening bracket, 75// so the bound is unreachable for real markup; and an accusation is the wrong thing to make on a guess. 76const PV_TAG_LOOKBACK: i64 = 4096 77func pv_in_tag(page: *u8, at: i64) -> i64 { 78 var b: i64 = at 79 var back: i64 = 0 80 while back < PV_TAG_LOOKBACK { 81 if b <= 0 { return 0 } 82 b = b - 1 83 let c: i64 = page[b] as i64 84 if c == PV_ATTR_LT { return 1 } 85 if c == PV_ATTR_GT { return 0 } 86 back = back + 1 87 } 88 return 0 89} // carriage return 90 91// ============================================================================================ 92// THE THIRD STATE -- "I COULD NOT REACH IT" IS NOT "IT IS BROKEN" (2026-08-22). 93// MEASURED: a seat ran this organ on a HEALTHY page from a CWD where the domain registry did not 94// resolve, the auto-override silently fell to DNS, :443 on the NAS is the co-squatting side, the 95// fetch NEVER CONNECTED (status=0 bytes=-2), and the verdict said RED. Two untouched control pages 96// returned byte-identical output; the loopback vantage served the same page 200 with every asset. 97// The page was fine. The verifier could not see it. And it reported that in the same word as a 98// page that is genuinely broken -- which is the failure mode that teaches everyone to ignore a 99// detector. AN AXIS THAT CANNOT SEE MUST ABSTAIN, NOT ACQUIT -- AND EQUALLY MUST NOT CONVICT. 100// The discriminator is already in the fetch layer's return contract and this organ was throwing it 101// away: nx_https_fetch_follow returns -NX_FF_<stage> with out_status[0] UNTOUCHED (0) when no HTTP 102// response was ever received. So: (n<0 AND status==0) = no server answered = UNOBSERVABLE, exit 3. 103// Any run where status!=0 means a server DID answer, and a wrong answer stays RED. 104// Exit 3 is the fleet's SKIP/UNOBSERVABLE code (nx_gate_verdict returns 3; /api/gate_run maps 3 -> 105// SKIP, excluded from RED). The trust-store-load failure below already exits 3 for the same reason: 106// it is a "could not look", not a "looked and it is broken". The RED exit (1) and the broken-count 107// exit are UNCHANGED, so every consumer that branches on 0/1 keeps its meaning; only runs that 108// previously LIED now say so. The verdict line stays LAST (gv_last_line anchors by position). 109const PV_EXIT_GREEN: i64 = 0 110const PV_EXIT_RED: i64 = 1 111const PV_EXIT_USAGE: i64 = 2 112const PV_EXIT_UNOBSERVABLE: i64 = 3 113const PV_EXIT_UNMEASURED: i64 = 4 // our-domain host with a malformed registry endpoint 114// fetch-layer stage codes, mirrored from nx_https_fetch_follow so the UNOBSERVABLE line can NAME 115// the stage that never completed instead of printing a bare negative number 116const PV_FF_BAD_URL: i64 = 1 117const PV_FF_CONNECT: i64 = 2 118const PV_FF_HANDSHAKE: i64 = 3 119const PV_FF_GET: i64 = 4 120const PV_VANTAGE_CAP: i64 = 320 // "dns:<host>" or "pinned:<a.b.c.d:port>" for the verdict line 121const PV_NO_STATUS: i64 = 0 // out_status when no HTTP response was ever parsed 122 123// append NUL-terminated s into the vantage buffer at off; bounded by PV_VANTAGE_CAP (a vantage 124// string that does not fit is truncated AND announced by the cap, never silently overrun) 125func pv_vcat(d: *u8, off: i64, s: *u8) -> i64 { 126 var o: i64 = off 127 var i: i64 = 0 128 while s[i] != (0 as u8) { if o < PV_VANTAGE_CAP - 1 { d[o] = s[i]; o = o + 1 } i = i + 1 } 129 d[o] = 0 as u8 130 return o 131} 132func pv_stage_name(n: i64) -> *u8 { 133 let code: i64 = 0 - n 134 if code == PV_FF_BAD_URL { return "bad-url" as *u8 } 135 if code == PV_FF_CONNECT { return "connect" as *u8 } 136 if code == PV_FF_HANDSHAKE { return "tls-handshake" as *u8 } 137 if code == PV_FF_GET { return "http-get" as *u8 } 138 return "fetch" as *u8 139} 140// 1 iff the fetch never produced an HTTP response: negative length AND no status was parsed. 141// A non-200 WITH a status is a server that answered (404, 500, a redirect loop) -> that is RED 142// territory and must NOT land here. 143func pv_unobservable(n: i64, status: i64) -> i64 { 144 if n < 0 { if status == PV_NO_STATUS { return 1 } } 145 return 0 146} 147 148func hw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 149// pv_normpath -- RFC 3986 5.2.4 remove_dot_segments over an ABSOLUTE PATH (2026-08-18). A browser 150// resolves src='/compare/graphics/../../world/x.png' to /world/x.png before it ever asks the edge; this 151// EYE fetched the literal string, got the client's own bad-url body (status=0, 82 bytes) and reported 152// BROKEN x5 / VERDICT=RED on a page whose every asset served 200 -- a false red on the ruler, the kind 153// everyone learns to ignore. Segments: '.' dropped, '..' pops one (never above root), else copied. In 154// place, same buffer, returns the new length. Only path bytes are touched (stops at '?' or '#'). 155func pv_normpath(p: *u8) -> i64 { 156 var r: i64 = 0 157 var wpos: i64 = 0 158 var n: i64 = 0 159 while p[n] != (0 as u8) { if p[n] == (63 as u8) { break } if p[n] == (35 as u8) { break } n = n + 1 } 160 let tail: i64 = n 161 while r < n { 162 // r sits on a '/', segment = bytes after it up to next '/' or end 163 var e: i64 = r + 1 164 while e < n { if p[e] == (47 as u8) { break } e = e + 1 } 165 let sl: i64 = e - r - 1 166 var dot: i64 = 0 167 if sl == 1 { if p[r+1] == (46 as u8) { dot = 1 } } 168 if sl == 2 { if p[r+1] == (46 as u8) { if p[r+2] == (46 as u8) { dot = 2 } } } 169 if dot == 2 { 170 // pop the last written segment (leave at least the root '/') 171 if wpos > 0 { wpos = wpos - 1; while wpos > 0 { if p[wpos] == (47 as u8) { break } wpos = wpos - 1 } } 172 } else { if dot == 0 { 173 var k: i64 = r 174 while k < e { p[wpos] = p[k]; wpos = wpos + 1; k = k + 1 } 175 } } 176 r = e 177 } 178 if wpos == 0 { p[0] = 47 as u8; wpos = 1 } 179 // carry query/fragment forward untouched 180 var t: i64 = tail 181 var w2: i64 = wpos 182 while p[t] != (0 as u8) { p[w2] = p[t]; w2 = w2 + 1; t = t + 1 } 183 p[w2] = 0 as u8 184 return w2 185} 186func pn(v: i64) -> i64 { let t: *u8=sys_mmap(24); var m: i64=v; if m<0{sys_write(1,"-" as *u8,1);m=0-m} var k: i64=0; if m==0{t[0]=48 as u8;k=1} while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} let b: *u8=sys_mmap(24); var j: i64=0; while j<k{b[j]=t[k-1-j];j=j+1} sys_write(1,b,k); return 0 } 187func pw(s: *u8, n: i64) -> i64 { sys_write(1, s, n); return 0 } 188 189func sw_starts(b: *u8, off: i64, lit: *u8) -> i64 { 190 var i: i64 = 0 191 while lit[i] != (0 as u8) { if b[off+i] != lit[i] { return 0 } i = i + 1 } 192 return 1 193} 194 195// PV_RAW_* / pv_in_rawtext -- ADDED 2026-08-25 after the SECOND fleet sweep, because pv_in_tag ALONE 196// still admits inline JavaScript, and A DETECTOR WITH FALSE POSITIVES IS WORSE THAN NONE. MEASURED 197// from source, not inferred, at sites/nishifamily/listen/index.html line 29, whose script assigns 198// au.src from a variable inside a function body; the extractor read the JavaScript that follows as a 199// RELATIVE ASSET REF and the page reported RED with law=1. Five live pages carried this one shape. 200// pv_in_tag cannot see it and is not wrong to miss it: JavaScript uses the less-than character as a 201// comparison operator, so inside a script the nearest preceding bracket genuinely IS an opener. 202// THE FIX IS HTML AGAIN, NEVER A KEYWORD LIST: script and style are RAW TEXT elements -- a browser 203// tokenizer does not look for attributes inside them at all -- so a position between such an open tag 204// and its matching close is not attribute context, whatever the surrounding bytes look like. 205// SCOPED TO THE BARE FORM ONLY, DELIBERATELY, AND THIS DIRECTION CANNOT LOSE A TRUE POSITIVE: a 206// QUOTED src assigned in a script IS a literal the browser really fetches and a relative one really 207// does break under the same edge rule, so narrowing those would lose real findings; JavaScript has no 208// UNQUOTED string literal, so a bare src= inside a script can never have been a real ref. 209// COST: one backward scan per bare candidate that ALREADY passed pv_in_tag -- that is, only on the 210// false positives this exists to remove. Bounded by page size (PV_CAP) times PV_MAXREF, both bounded. 211const PV_RAW_NONE: i64 = 0 212const PV_RAW_SCRIPT: i64 = 1 213const PV_RAW_STYLE: i64 = 2 214func pv_in_rawtext(page: *u8, at: i64) -> i64 { 215 var b: i64 = at 216 while b > 0 { 217 b = b - 1 218 if page[b] == (PV_ATTR_LT as u8) { 219 if sw_starts(page, b, "</script" as *u8) == 1 { return PV_RAW_NONE } 220 if sw_starts(page, b, "</style" as *u8) == 1 { return PV_RAW_NONE } 221 if sw_starts(page, b, "<script" as *u8) == 1 { return PV_RAW_SCRIPT } 222 if sw_starts(page, b, "<style" as *u8) == 1 { return PV_RAW_STYLE } 223 } 224 } 225 return PV_RAW_NONE 226} 227 228// origin = "https://host" prefix of url (through the 3rd slash, exclusive) 229func pv_origin(url: *u8, out: *u8) -> i64 { 230 var i: i64 = 0 231 var slashes: i64 = 0 232 while url[i] != (0 as u8) { 233 if url[i] == (47 as u8) { 234 slashes = slashes + 1 235 if slashes == 3 { out[i] = 0 as u8; return i } 236 } 237 out[i] = url[i] 238 i = i + 1 239 } 240 out[i] = 0 as u8 241 return i 242} 243 244// check one asset URL: fetch + validate. kind: 1=img-ish (png expected ok), 0=other. returns 0 ok / 1 broken 245// assets this run could not READ. Counted apart from `broken` because they are a different question with 246// a different remedy, and a reader who cannot tell them apart cannot audit either. 247static PV_UNOBS: i64 248// HEAD-RANGE VERIFICATION (2026-09-05). MEASURED on the live surface: /world/ref9d.nxa is 10,758,232 B 249// against PV_CAP 8,388,608, so the whole-body fetch returned -NX_FF_GET and the declared rig read 250// UNOBSERVABLE -- the seed-san.vrm class named in pv_check_asset, now hit by the FIRST asset the declared-asset 251// rule ever resolved. The proof a rig asset needs is its MAGIC, eight bytes at offset 0; reading ten megabytes 252// to check eight is the cap-as-a-guess defect wearing a verifier's clothes. So when the whole-body read does 253// not complete, the asset is re-fetched as a HEAD RANGE (bytes 0..PV_HEAD_CAP-1) through the fetch spine's own 254// range entry, and a head that carries a known magic is VERIFIED-BY-HEAD: counted apart from the whole-body 255// checks and apart from unobservable, because it answers a narrower question and a reader must see which. 256// A head that reads but carries no known magic stays UNOBSERVABLE (abstain -- a partial read may not convict), 257// and a head that cannot be read at all keeps the old UNOBSERVABLE line. The window is the fetch spine's own 258// padding unit (NX_MAGIC_65536, the smallest read it already sizes for), not a fresh number. MEASURED LIVE 259// 2026-09-05 on /world/rigview: the retry's TLS 1.3 handshake completed on the pinned edge and the GET stage 260// failed with NO status (head-range status=0 bytes=-4) -- the edge answered the Range request with the whole 261// 10 MB body and the spine's completing reader FAILS on overflow rather than handing back a prefix, so on 262// this edge the retry adds one honest measurement per asset and no verdict. It can never remove one: every 263// path out of the retry is the old UNOBSERVABLE line or a magic-carrying head. The verdict it was built for 264// needs an edge that honours Range (206 + Content-Range) or a bounded-prefix reader in the fetch spine. 265// The reader landed the same day (nx_https_fetch_head over nx_https_req_prefix, browser BR48) and this retry 266// composes it; the edge half is browser BR49, the traffic owner's, and lights the 206 branch when it lands. 267const PV_HEAD_CAP: i64 = 65536 268const PV_HTTP_PARTIAL: i64 = 206 // RFC 9110 Partial Content: the server honoured the Range 269const PV_HTTP_OK: i64 = 200 // a server that ignored the Range and answered whole; the spine bounds the body 270static PV_HEADVER: i64 271 272// NXA (Nishi Animated 3D) magic: the eight ASCII bytes NXANIM01, owned by nx_nxa.nx. Compared byte for byte here 273// so the verifier proves a declared rig asset is a rig and not the edge's error page, without forking a decoder. 274func pv_nxa_magic(b: *u8, n: i64) -> i64 { 275 if n < 8 { return 0 } 276 let m: *u8 = "NXANIM01" as *u8 277 var i: i64 = 0 278 while i < 8 { if b[i] != m[i] { return 0 } i = i + 1 } 279 return 1 280} 281 282func pv_check_asset(store: *TrustStore, aurl: *u8, body: *u8) -> i64 { 283 let st: *i64 = sys_mmap(8) as *i64 284 let n: i64 = nx_https_fetch_follow(aurl, store, body, PV_CAP, 6, st) 285 hw(" asset " as *u8); hw(aurl) 286 hw(" -> status=" as *u8); pn(st[0]); hw(" bytes=" as *u8); pn(n) 287 // A READ THAT NEVER COMPLETED IS NOT A BROKEN ASSET. A negative n is a fetch-STAGE code, not a body: 288 // the asset may serve perfectly and merely exceed PV_CAP. MEASURED 2026-08-26 -- seed-san.vrm is 289 // 10,917,800 B against a PV_CAP of 8,388,608 and came back n=-4, which this function spelled 290 // "BROKEN (non-200)", while an independent client fetching the same URL got 200 OK with the full 291 // Content-Length. This organ already carries the third state on the PAGE path (could not connect -> 292 // UNOBSERVABLE, never RED); it was never carried on the ASSET path, and the half of a law left undone 293 // is the half that ships. n >= 0 with a non-200 status is a REAL failure and still counts as broken -- 294 // that is exactly how a 404 whose body we could read is caught. 295 if n < 0 { 296 // HEAD-RANGE RETRY (see PV_HEAD_CAP): the whole body did not arrive; ask for its head and let the magic decide. 297 let hst: *i64 = sys_mmap(8) as *i64 298 hst[0] = PV_NO_STATUS 299 let hn: i64 = nx_https_fetch_head(aurl, store, body, PV_HEAD_CAP, 0, PV_HEAD_CAP - 1, hst, 0) 300 hw(" head-range status=" as *u8); pn(hst[0]); hw(" bytes=" as *u8); pn(hn) 301 var hok: i64 = 0 302 if hst[0] == PV_HTTP_PARTIAL { hok = 1 } 303 if hst[0] == PV_HTTP_OK { hok = 1 } 304 if hn > 0 { if hok == 1 { 305 if pv_nxa_magic(body, hn) == 1 { 306 hw(" NXA MAGIC-OK VERIFIED-BY-HEAD (the whole body exceeds the read budget; the head carries the rig magic)\n" as *u8) 307 PV_HEADVER = PV_HEADVER + 1 308 return 0 309 } 310 hw(" head read but carries no known magic -- a partial read may not convict, so this stays UNOBSERVABLE\n" as *u8) 311 } } 312 hw(" UNOBSERVABLE (could not read this asset; fetch stage " as *u8); pn(0 - n) 313 hw(" -- it may exceed the read budget, so it is NOT counted as broken)\n" as *u8) 314 PV_UNOBS = PV_UNOBS + 1 315 return 0 316 } 317 if st[0] != 200 { hw(" BROKEN (non-200)\n" as *u8); return 1 } 318 if n <= 0 { hw(" BROKEN (empty body)\n" as *u8); return 1 } 319 // PNG? deep-decode (signature + IHDR + inflate + unfilter) -- the real proof 320 if n > 8 { 321 if body[0] == (137 as u8) { 322 if body[1] == (80 as u8) { 323 let res: *NxPngResult = nx_png_decode(body, n) 324 if (res as i64) == 0 { hw(" BROKEN (png decode null)\n" as *u8); return 1 } 325 if res.error_code != 0 { hw(" BROKEN (png decode err=" as *u8); pn(res.error_code); hw(")\n" as *u8); return 1 } 326 let hdr: *NxPngHeader = res.header 327 if hdr.width <= 0 { hw(" BROKEN (png w=0)\n" as *u8); return 1 } 328 hw(" PNG DECODE-OK " as *u8); pn(hdr.width); hw("x" as *u8); pn(hdr.height); hw("\n" as *u8) 329 return 0 330 } 331 } 332 // NXA (Nishi Animated 3D): a declared rig asset must open with the NXANIM01 magic (pv_nxa_magic above) 333 if pv_nxa_magic(body, n) == 1 { hw(" NXA MAGIC-OK bytes=" as *u8); pn(n); hw("\n" as *u8); return 0 } 334 // AN ARTIFACT THAT ANSWERS HTML IS THE 404-FALLBACK CLASS, and the status was 200. The decision 335 // moved into nx_pageref_lib so this organ and nx_pageref_gate read ONE table: the chain that used 336 // to live here knew four extensions -- png, jpg, css, js -- so a mesh, a VRM, a wasm module or a 337 // viewer blob that answered the edge's error page with status 200 read as OK, and those are 338 // precisely what an /exceed asset page is made of. The extension match is also exact to 339 // end-of-url now, where the old chain accepted any suffix beginning at the dot. 340 if pr_is_fallback(aurl, body, n) == 1 { 341 hw(" BROKEN (artifact answers HTML -- 404-fallback class, extension class " as *u8) 342 pn(pr_ext_class(aurl)) 343 hw(")\n" as *u8) 344 return 1 345 } 346 } 347 hw(" OK\n" as *u8) 348 return 0 349} 350 351// a11y-lite: integer substring presence + count over the page (axe-core / Lighthouse-class checks, NO float) 352func pv_has(b: *u8, n: i64, lit: *u8) -> i64 { 353 var i: i64 = 0 354 while i < n { if sw_starts(b, i, lit) == 1 { return 1 } i = i + 1 } 355 return 0 356} 357func pv_count(b: *u8, n: i64, lit: *u8) -> i64 { 358 var c: i64 = 0 359 var i: i64 = 0 360 while i < n { if sw_starts(b, i, lit) == 1 { c = c + 1 } i = i + 1 } 361 return c 362} 363 364// heading-level skip detector (axe/Lighthouse structural rule: no downward jump >1, e.g. h1->h3) -- integer scan 365func pv_hskip(b: *u8, n: i64) -> i64 { 366 var prev: i64 = 0 367 var skips: i64 = 0 368 var i: i64 = 0 369 while i < n - 3 { 370 if b[i] == (60 as u8) { if b[i+1] == (104 as u8) { 371 let d: i64 = b[i+2] as i64 372 if d >= 49 { if d <= 54 { 373 let lvl: i64 = d - 48 374 if prev > 0 { if lvl > prev + 1 { skips = skips + 1 } } 375 prev = lvl 376 } } 377 } } 378 i = i + 1 379 } 380 return skips 381} 382 383// does s start with pfx? returns the offset just past pfx, or -1. Lets the optional 384// args be recognised by NAME in any position, so adding them cannot disturb the existing 385// positional [connect-host:port] callers (rule 19: adding is safe, reordering is not). 386func pv_pfx(s: *u8, pfx: *u8) -> i64 { 387 var i: i64 = 0 388 while pfx[i] != (0 as u8) { 389 if s[i] != pfx[i] { return 0 - 1 } 390 i = i + 1 391 } 392 return i 393} 394// ============================================================================================ 395// AUTO CONNECT-OVERRIDE -- RECOVERED 2026-07-25 (debt seq1008 / frontier F1144). 396// The DEPLOYED nx_page_verify.elf implemented this; NO SOURCE IN THE TREE DID. It was built 397// ~2026-07-20, deployed and documented, and its source was lost, so the ordinary /api/build + 398// /api/promote loop would have SILENTLY DELETED it -- turning every our-domain verification back 399// into a :443 coin flip against the co-squatting DSM nginx. That failure mode already manufactured 400// one false sev-7 debt (seq984, retracted). Reimplemented here from the contract documented in the 401// registry header so the binary is reproducible from source again. 402// CONTRACT: rows are <host><TAB><a.b.c.d:port>, '#' comments skipped, an explicit argv[2] override 403// ALWAYS wins, and an absent registry is FAIL-SAFE (verification still runs) -- but never silently: 404// see the vantage line in main(), which is now unconditional. 405const PV_DOMCONF: *u8 = "knowledge/registry/our_domains.conf" 406const PV_DOMCAP: i64 = 65536 407const PV_EP_CAP: i64 = 256 408 409// host of "https://HOST[:port]/path" -> out. 1 ok, 0 if not an https url. 410func pv_host(url: *u8, out: *u8, cap: i64) -> i64 { 411 if sw_starts(url, 0, "https://" as *u8) != 1 { return 0 } 412 var i: i64 = 8 413 var o: i64 = 0 414 var go: i64 = 1 415 while go == 1 { 416 let c: i64 = url[i] as i64 417 if c == 0 { go = 0 } 418 else { 419 if c == 47 { go = 0 } 420 else { 421 if c == 58 { go = 0 } 422 else { 423 if o < (cap - 1) { out[o] = url[i]; o = o + 1 } 424 i = i + 1 425 } 426 } 427 } 428 } 429 out[o] = 0 as u8 430 if o == 0 { return 0 } 431 return 1 432} 433 434// 1 = hit (ep filled) | 0 = not one of ours | -1 = registry unreadable (caller must SAY SO) 435func pv_dom_lookup(host: *u8, ep: *u8, epcap: i64) -> i64 { 436 let fd: i64 = sys_openat_rd(PV_DOMCONF) 437 if fd < 0 { return 0 - 1 } 438 let buf: *u8 = sys_mmap(PV_DOMCAP) 439 var total: i64 = 0 440 var go: i64 = 1 441 while go == 1 { 442 if total >= PV_DOMCAP { go = 0 } 443 else { 444 let n: i64 = sys_read(fd, (buf as i64 + total) as *u8, PV_DOMCAP - total) 445 if n > 0 { total = total + n } else { go = 0 } 446 } 447 } 448 sys_close(fd) 449 var hl: i64 = 0 450 while host[hl] != (0 as u8) { hl = hl + 1 } 451 var found: i64 = 0 452 var ls: i64 = 0 453 var i: i64 = 0 454 while i <= total { 455 var nl: i64 = 0 456 if i >= total { nl = 1 } else { if buf[i] == (10 as u8) { nl = 1 } } 457 if nl == 1 { 458 if found == 0 { 459 let len: i64 = i - ls 460 if len > 0 { 461 if buf[ls] != (35 as u8) { 462 var t: i64 = ls 463 var tab: i64 = 0 - 1 464 while t < i { if buf[t] == (9 as u8) { if tab < 0 { tab = t } } t = t + 1 } 465 if tab > 0 { 466 if (tab - ls) == hl { 467 var k: i64 = 0 468 var same: i64 = 1 469 while k < hl { if buf[ls + k] != host[k] { same = 0 } k = k + 1 } 470 if same == 1 { 471 var o: i64 = 0 472 var p: i64 = tab + 1 473 while p < i { 474 if buf[p] != (13 as u8) { if o < (epcap - 1) { ep[o] = buf[p]; o = o + 1 } } 475 p = p + 1 476 } 477 ep[o] = 0 as u8 478 found = 1 479 } 480 } 481 } 482 } 483 } 484 } 485 ls = i + 1 486 } 487 i = i + 1 488 } 489 return found 490} 491 492func pv_atoi(a: *u8) -> i64 { 493 var v: i64 = 0 494 var i: i64 = 0 495 while a[i] != (0 as u8) { 496 let c: i64 = a[i] as i64 497 if c >= 48 { if c <= 57 { v = v * 10 + (c - 48) } } 498 i = i + 1 499 } 500 return v 501} 502 503 504const PV_CONTRACT_OK: i64 = 0 505const PV_CONTRACT_RED: i64 = 1 506const PV_CONTRACT_USAGE: i64 = 2 507const PV_CONTRACT_HEX_BYTES: i64 = 32 508const PV_CONTRACT_HEX_CHARS: i64 = 64 509const PV_CONTRACT_PATH_CAP: i64 = 4096 510const PV_CONTRACT_EVIDENCE_CAP: i64 = 262144 511const PV_CONTRACT_DEFAULT_MAX_AGE: i64 = 900 512 513func pv_contract_hex(c: i64) -> i64 { 514 if c >= 48 { if c <= 57 { return c - 48 } } 515 if c >= 65 { if c <= 70 { return c - 55 } } 516 if c >= 97 { if c <= 102 { return c - 87 } } 517 return 0 - 1 518} 519func pv_contract_copy(dst: *u8, cap: i64, src: *u8, off: i64) -> i64 { 520 var i: i64 = 0 521 while src[off+i] != (0 as u8) { 522 if i >= cap - 1 { return 0 - 1 } 523 dst[i] = src[off+i] 524 i = i + 1 525 } 526 dst[i] = 0 as u8 527 return i 528} 529func pv_contract_hex_equal(text: *u8, digest: *u8) -> i64 { 530 var i: i64 = 0 531 while i < PV_CONTRACT_HEX_BYTES { 532 let bv: i64 = digest[i] as i64 533 let hi: i64 = (bv >> 4) & 15 534 let lo: i64 = bv & 15 535 var hc: i64 = 48 + hi 536 if hi >= 10 { hc = 87 + hi } 537 var lc: i64 = 48 + lo 538 if lo >= 10 { lc = 87 + lo } 539 if text[i*2] as i64 != hc { return 0 } 540 if text[i*2+1] as i64 != lc { return 0 } 541 i = i + 1 542 } 543 if text[PV_CONTRACT_HEX_CHARS] != (0 as u8) { return 0 } 544 return 1 545} 546func pv_contract_has(buf: *u8, n: i64, lit: *u8) -> i64 { 547 var q: i64 = 0 548 var m: i64 = 0 549 while lit[m] != (0 as u8) { m = m + 1 } 550 if m == 0 { return 1 } 551 while q + m <= n { 552 var j: i64 = 0 553 var same: i64 = 1 554 while j < m { if buf[q+j] != lit[j] { same = 0; j = m } else { j = j + 1 } } 555 if same == 1 { return 1 } 556 q = q + 1 557 } 558 return 0 559} 560func pv_contract_verify(url: *u8, page: *u8, page_n: i64, artifact: *u8, evidence_path: *u8, evidence_sha: *u8, interaction: *u8, runtime: *u8, observed: *u8, max_age: i64) -> i64 { 561 if (artifact as i64) == 0 { hw("served-review RED reason=missing-artifact-identity\n" as *u8); return PV_CONTRACT_RED } 562 if (evidence_path as i64) == 0 { hw("served-review RED reason=missing-evidence-path\n" as *u8); return PV_CONTRACT_RED } 563 if (evidence_sha as i64) == 0 { hw("served-review RED reason=missing-evidence-identity\n" as *u8); return PV_CONTRACT_RED } 564 if (interaction as i64) == 0 { hw("served-review RED reason=missing-interaction-assertion\n" as *u8); return PV_CONTRACT_RED } 565 if (runtime as i64) == 0 { hw("served-review RED reason=missing-runtime-backend\n" as *u8); return PV_CONTRACT_RED } 566 let digest: *u8 = sys_mmap(PV_CONTRACT_HEX_BYTES) 567 if sha256_digest(page, page_n, digest) != 0 { hw("served-review RED reason=artifact-hash-failed\n" as *u8); return PV_CONTRACT_RED } 568 if pv_contract_hex_equal(artifact, digest) == 0 { 569 hw("served-review RED reason=artifact-hash-mismatch actual_sha256=" as *u8) 570 var hx: i64 = 0 571 while hx < PV_CONTRACT_HEX_BYTES { 572 let bv: i64 = digest[hx] as i64 573 let hi: i64 = (bv >> 4) & 15 574 let lo: i64 = bv & 15 575 let hc: *u8 = sys_mmap(2) 576 if hi < 10 { hc[0] = (48 + hi) as u8 } else { hc[0] = (87 + hi) as u8 } 577 if lo < 10 { hc[1] = (48 + lo) as u8 } else { hc[1] = (87 + lo) as u8 } 578 sys_write(1, hc, 2) 579 hx = hx + 1 580 } 581 hw("\n" as *u8) 582 return PV_CONTRACT_RED 583 } 584 let ep_len: *i64 = sys_mmap(8) as *i64 585 let ev: *u8 = sys_read_file(evidence_path, ep_len) 586 if (ev as i64) == 0 { hw("served-review RED reason=evidence-missing\n" as *u8); return PV_CONTRACT_RED } 587 if ep_len[0] <= 0 { hw("served-review RED reason=evidence-empty\n" as *u8); sys_free_file(ev, ep_len[0]); return PV_CONTRACT_RED } 588 let edigest: *u8 = sys_mmap(PV_CONTRACT_HEX_BYTES) 589 if sha256_digest(ev, ep_len[0], edigest) != 0 { hw("served-review RED reason=evidence-hash-failed\n" as *u8); sys_free_file(ev, ep_len[0]); return PV_CONTRACT_RED } 590 if pv_contract_hex_equal(evidence_sha, edigest) == 0 { hw("served-review RED reason=evidence-hash-mismatch\n" as *u8); sys_free_file(ev, ep_len[0]); return PV_CONTRACT_RED } 591 if pv_contract_has(ev, ep_len[0], "interaction=PASS\n" as *u8) == 0 { hw("served-review RED reason=interaction-evidence-missing-or-failed\n" as *u8); sys_free_file(ev, ep_len[0]); return PV_CONTRACT_RED } 592 if pv_contract_has(ev, ep_len[0], "screenshot=" as *u8) == 0 { hw("served-review RED reason=screenshot-evidence-missing\n" as *u8); sys_free_file(ev, ep_len[0]); return PV_CONTRACT_RED } 593 if pv_contract_has(ev, ep_len[0], "backend=" as *u8) == 0 { hw("served-review RED reason=backend-evidence-missing\n" as *u8); sys_free_file(ev, ep_len[0]); return PV_CONTRACT_RED } 594 if pv_contract_has(ev, ep_len[0], "observed_epoch=" as *u8) == 0 { hw("served-review RED reason=observation-time-missing\n" as *u8); sys_free_file(ev, ep_len[0]); return PV_CONTRACT_RED } 595 if pv_contract_has(ev, ep_len[0], interaction) == 0 { hw("served-review RED reason=interaction-assertion-not-in-evidence\n" as *u8); sys_free_file(ev, ep_len[0]); return PV_CONTRACT_RED } 596 if pv_contract_has(ev, ep_len[0], runtime) == 0 { hw("served-review RED reason=runtime-backend-assertion-not-in-evidence\n" as *u8); sys_free_file(ev, ep_len[0]); return PV_CONTRACT_RED } 597 let seen: i64 = pv_atoi(observed) 598 if seen <= 0 { hw("served-review RED reason=invalid-observation-time\n" as *u8); sys_free_file(ev, ep_len[0]); return PV_CONTRACT_RED } 599 let now: i64 = sys_now_realtime_sec() 600 if now < seen { hw("served-review RED reason=observation-in-future\n" as *u8); sys_free_file(ev, ep_len[0]); return PV_CONTRACT_RED } 601 let age: i64 = now - seen 602 if age > max_age { hw("served-review RED reason=stale-evidence age=" as *u8); pn(age); hw("\n" as *u8); sys_free_file(ev, ep_len[0]); return PV_CONTRACT_RED } 603 hw("served-review GREEN url=" as *u8); hw(url) 604 hw(" status=200 artifact_verified=1 interaction_verified=1 screenshot_verified=1 runtime=" as *u8); hw(runtime) 605 hw(" observed_epoch=" as *u8); hw(observed); hw(" evidence=" as *u8); hw(evidence_path); hw(" age=" as *u8); pn(age); hw("\n" as *u8) 606 sys_free_file(ev, ep_len[0]) 607 return PV_CONTRACT_OK 608} 609 610func main(argc: i64, argv: *i64) -> i64 { 611 if argc < 2 { hw("usage: nx_page_verify <https-url> [connect-host:port] [expect=<substring>] [minbytes=<n>]\n" as *u8); sys_exit(PV_EXIT_USAGE); return PV_EXIT_USAGE } 612 let url: *u8 = argv[1] as *u8 613 var contract_mode: i64 = 0 614 let contract_artifact: *u8 = sys_mmap(96) 615 let contract_evidence: *u8 = sys_mmap(PV_CONTRACT_PATH_CAP) 616 let contract_evidence_sha: *u8 = sys_mmap(96) 617 let contract_interaction: *u8 = sys_mmap(96) 618 let contract_runtime: *u8 = sys_mmap(96) 619 let contract_observed: *u8 = sys_mmap(96) 620 var contract_max_age: i64 = PV_CONTRACT_DEFAULT_MAX_AGE 621 hw("=== nx_page_verify -- static HTTP asset checks (no browser execution) for " as *u8); hw(url); hw(" ===\n" as *u8) 622 var expoff: i64 = 0 623 var minbytes: i64 = 0 624 var ov_given: i64 = 0 625 // the vantage this run actually used, carried to the LAST line so a reader of the verdict alone 626 // can tell which server was (or was not) reached -- "RED from :443" and "RED from :8443" are 627 // different facts, and an UNOBSERVABLE is meaningless without the endpoint it failed to reach 628 let vant: *u8 = sys_mmap(PV_VANTAGE_CAP) 629 var vo: i64 = 0 630 var ai: i64 = 2 631 while ai < argc { 632 let a: *u8 = argv[ai] as *u8 633 let co: i64 = pv_pfx(a, "contract" as *u8) 634 if co >= 0 { 635 contract_mode = 1 636 } else { 637 let ao: i64 = pv_pfx(a, "artifact=" as *u8) 638 let ep: i64 = pv_pfx(a, "evidence=" as *u8) 639 let es: i64 = pv_pfx(a, "evidence_sha256=" as *u8) 640 let io: i64 = pv_pfx(a, "interaction=" as *u8) 641 let ro: i64 = pv_pfx(a, "runtime=" as *u8) 642 let ob: i64 = pv_pfx(a, "observed_epoch=" as *u8) 643 let ma: i64 = pv_pfx(a, "max_age=" as *u8) 644 let eo: i64 = pv_pfx(a, "expect=" as *u8) 645 let mo: i64 = pv_pfx(a, "minbytes=" as *u8) 646 if ao >= 0 { pv_contract_copy(contract_artifact, 96, a, ao) } 647 else { 648 if ep >= 0 { pv_contract_copy(contract_evidence, PV_CONTRACT_PATH_CAP, a, ep) } 649 else { 650 if es >= 0 { pv_contract_copy(contract_evidence_sha, 96, a, es) } 651 else { 652 if io >= 0 { pv_contract_copy(contract_interaction, 96, a, io) } 653 else { 654 if ro >= 0 { pv_contract_copy(contract_runtime, 96, a, ro) } 655 else { 656 if ob >= 0 { pv_contract_copy(contract_observed, 96, a, ob) } 657 else { 658 if ma >= 0 { contract_max_age = pv_atoi(((a as i64) + ma) as *u8) } 659 else { 660 if eo >= 0 { expoff = (a as i64) + eo } 661 else { 662 if mo >= 0 { minbytes = pv_atoi(((a as i64) + mo) as *u8) } 663 else { 664 if nx_https_connect_override_set(a) != 1 { hw("RED bad connect-override (want a.b.c.d:port)\n" as *u8); sys_exit(PV_EXIT_USAGE); return PV_EXIT_USAGE } 665 hw("vantage=EXPLICIT connect-override " as *u8); hw(a); hw(" (SNI/Host stay the URL host)\n" as *u8) 666 ov_given = 1 667 vo = pv_vcat(vant, 0, "pinned:" as *u8) 668 vo = pv_vcat(vant, vo, a) 669 } 670 } 671 } 672 } 673 } 674 } 675 } 676 } 677 } 678 } 679 ai = ai + 1 680 } 681 682 // ---- VANTAGE RESOLUTION (recovered + hardened 2026-07-25; seq1008 recovery, seq990 hardening) ---- 683 // The whole point: this organ must NEVER be able to grade one server while the reader believes it 684 // graded another. So the vantage is now stated on EVERY run -- there is no silent path. 685 let pv_h: *u8 = sys_mmap(PV_URL_CAP) 686 pv_h[0] = 0 as u8 687 var pv_isours: i64 = 0 688 var pv_regunread: i64 = 0 689 var pv_pinned: i64 = ov_given 690 if pv_host(url, pv_h, PV_URL_CAP) == 1 { 691 let pv_ep: *u8 = sys_mmap(PV_EP_CAP) 692 let d: i64 = pv_dom_lookup(pv_h, pv_ep, PV_EP_CAP) 693 if d == 1 { pv_isours = 1 } 694 if ov_given == 0 { 695 if d == 1 { 696 if nx_https_connect_override_set(pv_ep) != 1 { 697 hw("VERDICT=UNMEASURED (our-domain " as *u8); hw(pv_h) 698 hw(" but its registry endpoint " as *u8); hw(pv_ep) 699 hw(" is malformed -- refusing to grade an unknown server)\n" as *u8) 700 sys_exit(PV_EXIT_UNMEASURED); return PV_EXIT_UNMEASURED 701 } 702 hw("auto connect-override (our-domain " as *u8); hw(pv_h) 703 hw(" -> " as *u8); hw(pv_ep); hw("; SNI/Host stay the URL host)\n" as *u8) 704 pv_pinned = 1 705 vo = pv_vcat(vant, 0, "pinned:" as *u8) 706 vo = pv_vcat(vant, vo, pv_ep) 707 } 708 if d == (0 - 1) { 709 hw("WARNING registry " as *u8); hw(PV_DOMCONF) 710 hw(" unreadable -- cannot tell if this host is ours; proceeding via DNS (fail-safe)\n" as *u8) 711 pv_regunread = 1 712 } 713 } 714 } 715 if pv_pinned == 1 { hw("vantage=PINNED (deterministic sovereign edge)\n" as *u8) } 716 else { 717 hw("vantage=DNS (no override)" as *u8) 718 vo = pv_vcat(vant, 0, "dns:" as *u8) 719 vo = pv_vcat(vant, vo, pv_h) 720 if pv_regunread == 1 { vo = pv_vcat(vant, vo, ",registry-unreadable-from-cwd" as *u8) } 721 if pv_isours == 1 { 722 // Cannot happen once the registry is readable, but if it ever does, the reader is told 723 // LOUDLY rather than handed a confident RED from whatever answered port 443. 724 hw(" <== WARNING: this IS one of our domains and it is NOT pinned. A 404/RED here may be\n" as *u8) 725 hw(" the co-squatting server on :443, not our edge. TREAT AS UNMEASURED." as *u8) 726 } 727 hw("\n" as *u8) 728 } 729 730 let r: i64 = nx_trust_store_load_from_certdata("data/mozilla_certdata.txt" as *u8, 512, PV_CERTDATA_CAP) 731 if r <= 0 { 732 // the eye has no trust store -> it cannot fetch anything -> it has NO EVIDENCE about the page. 733 // This was already exit 3; it now says so in the fleet's own word, on the last line. 734 hw("cannot load trust store (run from repo root)\n" as *u8) 735 hw("VERDICT=UNOBSERVABLE vantage=" as *u8); hw(vant); hw(" reason=no-trust-store\n" as *u8) 736 sys_exit(PV_EXIT_UNOBSERVABLE); return PV_EXIT_UNOBSERVABLE 737 } 738 let store: *TrustStore = r as *TrustStore 739 740 let page: *u8 = sys_mmap(PV_CAP) 741 let st: *i64 = sys_mmap(8) as *i64 742 st[0] = PV_NO_STATUS 743 let n: i64 = nx_https_fetch_follow(url, store, page, PV_CAP, 6, st) 744 hw("page status=" as *u8); pn(st[0]); hw(" bytes=" as *u8); pn(n) 745 if n < 0 { hw(" fetch-stage=" as *u8); hw(pv_stage_name(n)) } 746 hw("\n" as *u8) 747 // THE THIRD STATE. No status AND a negative length = no server ever answered. That is a 748 // fact about the VANTAGE (closed port, black-holed host, DNS landing on the wrong edge, a CWD 749 // that lost the registry), not about the page. It must not be spelled RED. 750 if pv_unobservable(n, st[0]) == 1 { 751 hw("VERDICT=UNOBSERVABLE vantage=" as *u8); hw(vant) 752 hw(" reason=no-connection(" as *u8); hw(pv_stage_name(n)); hw(")\n" as *u8) 753 sys_exit(PV_EXIT_UNOBSERVABLE); return PV_EXIT_UNOBSERVABLE 754 } 755 if st[0] != 200 { hw("VERDICT=RED (page not 200)\n" as *u8); sys_exit(PV_EXIT_RED); return PV_EXIT_RED } 756 if n <= 0 { hw("VERDICT=RED (empty page)\n" as *u8); sys_exit(PV_EXIT_RED); return PV_EXIT_RED } 757 if contract_mode == 1 { 758 if contract_max_age <= 0 { hw("served-review RED reason=invalid-max-age\n" as *u8); sys_exit(PV_CONTRACT_USAGE); return PV_CONTRACT_USAGE } 759 let crc: i64 = pv_contract_verify(url, page, n, contract_artifact, contract_evidence, contract_evidence_sha, contract_interaction, contract_runtime, contract_observed, contract_max_age) 760 sys_exit(crc) 761 return crc 762 } 763 764 // CONTENT ASSERTION (debt seq911). A 200 DOES NOT MEAN YOUR PAGE. The edge serves a 765 // root fallback for unknown paths, so a failed publish yields 200 with a perfectly 766 // valid, fully-decoding page that simply is not yours -- and every asset on it fetches, 767 // so the old asset-only verdict said GREEN. Measured 2026-07-25: verifying 768 // /research/rt004.html reported status=200 VERDICT=GREEN while the edge was serving the 769 // 835-byte site root and the 10201-byte page had landed nowhere. Asset liveness is not 770 // page identity; the caller must be able to assert WHICH page it expects. 771 if minbytes > 0 { 772 if n < minbytes { 773 hw("VERDICT=RED (page is " as *u8); pn(n) 774 hw(" bytes, below the asserted floor of " as *u8); pn(minbytes) 775 hw(" -- a 200 is not your page)\n" as *u8) 776 sys_exit(PV_EXIT_RED); return PV_EXIT_RED 777 } 778 hw("byte-floor OK (" as *u8); pn(n); hw(" >= " as *u8); pn(minbytes); hw(")\n" as *u8) 779 } 780 if expoff != 0 { 781 if pv_has(page, n, expoff as *u8) == 0 { 782 hw("VERDICT=RED (expected content ABSENT: " as *u8); hw(expoff as *u8) 783 hw(") -- the server returned 200 but this is not the page you published\n" as *u8) 784 sys_exit(PV_EXIT_RED); return PV_EXIT_RED 785 } 786 hw("content-assert OK (expect=" as *u8); hw(expoff as *u8); hw(")\n" as *u8) 787 } 788 789 let origin: *u8 = sys_mmap(PV_URL_CAP) 790 pv_origin(url, origin) 791 792 // --- a11y-lite audit (integer, no float): axe-core / Lighthouse-class page checks --- 793 hw("-- a11y-lite --\n" as *u8) 794 var a11y: i64 = 0 795 if pv_has(page, n, "<title" as *u8) == 1 { hw(" title OK\n" as *u8) } else { hw(" A11Y missing <title>\n" as *u8); a11y = a11y + 1 } 796 if pv_has(page, n, "lang=" as *u8) == 1 { hw(" lang OK\n" as *u8) } else { hw(" A11Y missing lang= on <html>\n" as *u8); a11y = a11y + 1 } 797 if pv_has(page, n, "viewport" as *u8) == 1 { hw(" viewport OK\n" as *u8) } else { hw(" A11Y missing <meta viewport>\n" as *u8); a11y = a11y + 1 } 798 // ELEMENT-SCOPED SINCE 2026-08-26, AND THAT IS THE WHOLE POINT. These two lines used to be 799 // pv_count(page, n, "<img") and pv_count(page, n, " alt=") -- two whole-page SUBSTRING counts. 800 // A SUBSTRING COUNTER CANNOT SEE ATTRIBUTE SCOPE. Measured 2026-08-26 it read img=15 with-alt=15, 801 // a clean bill of health, for /compare/koikatsu, whose first figure served 802 // <img src='...' alt='The card's own portrait -- the oracle we match against' loading='lazy'> 803 // The caption's raw apostrophe ends alt at the two words -The card-, turns the rest of the sentence 804 // into attribute names and swallows loading='lazy'. Every byte the old rule looked for was present, 805 // so it counted a DESTROYED alt as a good one and the page validated GREEN for as long as the row 806 // had existed. 807 // ia_scan walks each img tag with the quoting rules a browser uses, and it is the SAME lib the 808 // compare generator now emits through -- the writer and the reader share one definition and cannot 809 // drift into two answers. It also skips comment, script and style bodies, so an img quoted inside 810 // inline JavaScript is no longer counted as an element; that makes the COVERAGE-GAP comparison 811 // below sharper too, because nimg is now a count of real elements rather than of a 4-byte pattern. 812 let iasl: *i64 = sys_mmap(IA_SCAN_SLOTS * IA_SLOT_BYTES) as *i64 813 ia_scan(page, n, iasl) 814 let nimg: i64 = iasl[IA_S_IMGS] 815 let nalt: i64 = iasl[IA_S_ALT_OK] 816 hw(" img=" as *u8); pn(nimg); hw(" with-alt=" as *u8); pn(nalt); hw("\n" as *u8) 817 if nalt < nimg { hw(" A11Y " as *u8); pn(nimg - nalt); hw(" image(s) missing alt\n" as *u8); a11y = a11y + 1 } 818 // ITS OWN AXIS, not folded into missing-alt: the alt is PRESENT here and merely truncated, so the 819 // remedy is the emitter's escaping and not a missing attribute. One counter for two causes is how a 820 // worklist stops pointing at the repair. 821 if iasl[IA_S_QUOTEBREAK] > 0 { 822 hw(" A11Y " as *u8); pn(iasl[IA_S_QUOTEBREAK]) 823 hw(" img tag(s) whose own data escaped its attribute -- an unescaped quote ends the value early, so the alt a browser reads is a TRUNCATED prefix of the intended one\n" as *u8) 824 a11y = a11y + 1 825 } 826 if iasl[IA_S_MALFORMED] > 0 { 827 hw(" A11Y " as *u8); pn(iasl[IA_S_MALFORMED]) 828 hw(" img tag(s) whose tag or attribute value never closed\n" as *u8) 829 a11y = a11y + 1 830 } 831 let nh1: i64 = pv_count(page, n, "<h1" as *u8) 832 if nh1 == 1 { hw(" single-h1 OK\n" as *u8) } else { hw(" A11Y h1-count=" as *u8); pn(nh1); hw(" (want exactly 1)\n" as *u8); a11y = a11y + 1 } 833 let hsk: i64 = pv_hskip(page, n) 834 if hsk == 0 { hw(" heading-order OK\n" as *u8) } else { hw(" A11Y " as *u8); pn(hsk); hw(" heading-level skip(s)\n" as *u8); a11y = a11y + 1 } 835 if pv_has(page, n, "<main" as *u8) == 1 { hw(" landmark <main> OK\n" as *u8) } else { hw(" A11Y no <main> landmark\n" as *u8); a11y = a11y + 1 } 836 hw(" a11y-issues=" as *u8); pn(a11y); hw("\n" as *u8) 837 838 // extract src="..." / href="..." refs (img/script/link surface) 839 let abuf: *u8 = sys_mmap(PV_CAP) 840 let prout: *i64 = sys_mmap(PV_PROUT_SLOTS * PV_WORD_BYTES) as *i64 841 var broken: i64 = 0 842 var checked: i64 = 0 843 var skipped: i64 = 0 844 var lawred: i64 = 0 845 var declared: i64 = 0 // assets the page DECLARES its script will fetch (meta name=nishi-<kind> content=...; pr_declared_hit, 2026-09-05) 846 // bare src= occurrences REJECTED as not-inside-a-tag (inline script, almost always). Printed on 847 // the summary line so the new filter's effect on real pages is measurable rather than assumed. 848 var barerej: i64 = 0 849 // bare src= occurrences REJECTED as INSIDE script/style RAW TEXT (pv_in_rawtext above). Counted 850 // and printed SEPARATELY from barerej, because two rejection REASONS with opposite remedies must 851 // never share one counter -- a reader who cannot tell them apart cannot audit either. 852 var rawrej: i64 = 0 853 var i: i64 = 0 854 while i < n - 8 { 855 var hit: i64 = 0 856 // BOTH QUOTE STYLES (2026-08-18). This scanner recognised only src=" -- so a generator emitting 857 // src='...' (the whole compare gallery, 15 relative images that 404'd in every browser) was 858 // INVISIBLE to it: not checked, not skipped, not law-red -- and the verdict still said "every 859 // referenced asset fetches". A REF THE EXTRACTOR CANNOT SEE IS A REF THE VERDICT SILENTLY 860 // ACQUITS. The closing delimiter is whichever quote opened the attribute. 861 var bare: i64 = 0 862 var qch: i64 = 34 863 // IN-TAG GUARD ON EVERY ATTRIBUTE BRANCH (2026-08-25). pv_in_tag was added for the BARE branch 864 // and not its siblings -- the same half-a-fix shape one level down. MEASURED: the first fleet 865 // sweep reported 245 broken refs of which 216 were /code/ pages, and those pages carry NO <pre> 866 // at all (count=0) -- each line is a <span class='l'>, and the hits are the literal text src= 867 // inside RENDERED STRING LITERALS, e.g. gf_puts(" verdict=LIB src=" as *u8). Those sit in TEXT 868 // BETWEEN tags, so the nearest preceding bracket is '>' and pv_in_tag already rejects them. 869 // A real <img src="..."> has '<' as its nearest preceding bracket and is unaffected. 870 // ONE VOCABULARY OWNER (nx_pageref_lib, shared with nx_pageref_gate). This was two hand-written 871 // branches that knew only src=. The table now also carries data-glb=, the attribute nx_asset_page 872 // emits for every mesh panel on an /exceed page and the one this extractor had never heard of -- 873 // so four mesh refs per page were acquitted without ever being fetched (measured on the live 874 // /exceed/vrm/seed-san: checked=2 against four data-glb canvases). Behaviour on src= is unchanged 875 // BY CONSTRUCTION: the skip is still the attribute length plus its quote, and the closing 876 // delimiter is still whichever quote opened the attribute. 877 if pr_attr_hit(page, n, i, prout) >= 0 { if pv_in_tag(page, i) == 1 { hit = prout[0]; qch = prout[1] } } 878 // DECLARED ASSETS (2026-09-05): a meta row named nishi-<kind> with a content path promises a script-fetched 879 // artifact (rigview declares its 1.7 MB rig this way and used to be acquitted on one advert PNG); followed like a src 880 if hit == 0 { if pr_declared_hit(page, n, i, prout) >= 0 { if pv_in_tag(page, i) == 1 { hit = prout[0]; qch = prout[1]; declared = declared + 1 } } } 881 // BARE (UNQUOTED) src= -- legal HTML, and our own sites/nishifamily/world/foundation.html emits 882 // <img src=nx_worldpipe.png ...>. Checked ONLY after both quoted forms miss, and only when the 883 // next byte is not a quote / space / '>', so an empty or quoted value can never land here. 884 // The terminator is whitespace or '>', handled in the extraction below. 885 if hit == 0 { if i + 4 < n { if sw_starts(page, i, "src=" as *u8) == 1 { 886 let c0: i64 = page[i+4] as i64 887 if c0 != PV_ATTR_DQ { if c0 != PV_ATTR_SQ { if c0 != PV_ATTR_SP { if c0 != PV_ATTR_GT { 888 // INSIDE AN OPEN TAG OR IT IS NOT AN ATTRIBUTE (2026-08-25, see pv_in_tag above). 889 // Counted, not silently dropped: a filter whose rejections are invisible cannot be 890 // audited, and this one is new enough to deserve being watched on real pages. 891 if pv_in_tag(page, i) == 1 { 892 // IN A TAG AND NOT IN RAW TEXT. Both conjuncts are required and each is counted 893 // under its own name, so a future reader can see WHICH rule fired on real pages. 894 if pv_in_rawtext(page, i) == PV_RAW_NONE { hit = 4; bare = 1 } else { rawrej = rawrej + 1 } 895 } else { barerej = barerej + 1 } 896 } } } } 897 } } } 898 if hit == 0 { if sw_starts(page, i, "href='" as *u8) == 1 { 899 var b1: i64 = i 900 var found1: i64 = 0 901 var back1: i64 = 0 902 while back1 < 80 { 903 if b1 <= 0 { back1 = 80 } else { 904 if page[b1] == (60 as u8) { 905 if sw_starts(page, b1, "<link" as *u8) == 1 { found1 = 1 } 906 back1 = 80 907 } else { b1 = b1 - 1; back1 = back1 + 1 } 908 } 909 } 910 if found1 == 1 { hit = 6; qch = 39 } 911 } } 912 if hit == 0 { if sw_starts(page, i, "href=\"" as *u8) == 1 { 913 // href only counts for <link ...> stylesheet-ish tags: look back for '<link' within 80 bytes 914 var b: i64 = i 915 var found: i64 = 0 916 var back: i64 = 0 917 while back < 80 { 918 if b <= 0 { back = 80 } else { 919 if page[b] == (60 as u8) { 920 if sw_starts(page, b, "<link" as *u8) == 1 { found = 1 } 921 back = 80 922 } else { b = b - 1; back = back + 1 } 923 } 924 } 925 if found == 1 { hit = 6 } 926 } } 927 if hit > 0 { 928 let rs: i64 = i + hit 929 var re: i64 = rs 930 if bare == 0 { 931 while page[re] != (qch as u8) { re = re + 1 } 932 } else { 933 // Unquoted value ends at the first whitespace or '>'. Bounded by n so a malformed tag 934 // cannot run the scan off the end of the buffer. 935 var stop: i64 = 0 936 while stop == 0 { 937 if re >= n { stop = 1 } else { 938 let ce: i64 = page[re] as i64 939 if ce == PV_ATTR_SP { stop = 1 } else { 940 if ce == PV_ATTR_GT { stop = 1 } else { 941 if ce == PV_ATTR_TAB { stop = 1 } else { 942 if ce == PV_ATTR_LF { stop = 1 } else { 943 if ce == PV_ATTR_CR { stop = 1 } else { re = re + 1 } } } } } 944 } 945 } 946 } 947 let rlen: i64 = re - rs 948 if rlen > 0 { if rlen < PV_REF_MAX { if checked + skipped + lawred < PV_MAXREF { 949 let ref: *u8 = sys_mmap(PV_URL_CAP) 950 var k: i64 = 0 951 while k < rlen { ref[k] = page[rs+k]; k = k + 1 } 952 ref[rlen] = 0 as u8 953 if sw_starts(ref, 0, "data:" as *u8) == 1 { skipped = skipped + 1 } else { 954 if sw_starts(ref, 0, "http" as *u8) == 1 { 955 if sw_starts(ref, 0, origin) == 1 { checked = checked + 1; broken = broken + pv_check_asset(store, ref, abuf) } else { hw(" skip foreign " as *u8); hw(ref); hw("\n" as *u8); skipped = skipped + 1 } 956 } else { 957 if ref[0] == (47 as u8) { 958 pv_normpath(ref) 959 let full: *u8 = sys_mmap(PV_FULLURL_CAP) 960 var o: i64 = 0 961 while origin[o] != (0 as u8) { full[o] = origin[o]; o = o + 1 } 962 var q: i64 = 0 963 while ref[q] != (0 as u8) { full[o+q] = ref[q]; q = q + 1 } 964 full[o+q] = 0 as u8 965 checked = checked + 1 966 broken = broken + pv_check_asset(store, full, abuf) 967 } else { 968 // RELATIVE ref = RED BY LAW on our slash-stripping edge (the /render3d broken-image class) 969 hw(" asset " as *u8); hw(ref); hw(" BROKEN-BY-LAW (relative ref; the edge 301-strips trailing slashes so browsers resolve this against the PARENT -- use absolute /path)\n" as *u8) 970 lawred = lawred + 1 971 broken = broken + 1 972 } } } 973 } } } 974 i = re 975 } else { i = i + 1 } 976 } 977 978 hw("checked=" as *u8); pn(checked) 979 hw(" skipped=" as *u8); pn(skipped) 980 hw(" law-violations=" as *u8); pn(lawred) 981 hw(" bare-src-rejected-not-in-tag=" as *u8); pn(barerej) 982 hw(" bare-src-rejected-in-rawtext=" as *u8); pn(rawrej) 983 hw(" declared-assets=" as *u8); pn(declared) 984 hw(" unobservable=" as *u8); pn(PV_UNOBS) 985 hw(" verified-by-head=" as *u8); pn(PV_HEADVER) 986 hw(" broken=" as *u8); pn(broken); hw("\n" as *u8) 987 // COVERAGE IS TWO QUESTIONS: per-asset (above) and whole-page. A verdict that says "every asset" 988 // must be able to say it saw every asset: refs beyond PV_MAXREF were never examined, and a page 989 // whose <img> count exceeds what the extractor resolved has refs the scanner could not read. Both 990 // are announced and turn the whole-page claim into UNPROVEN rather than a silent acquittal. 991 let seen: i64 = checked + skipped + lawred 992 if seen >= PV_MAXREF { hw("COVERAGE-CAP: ref cap PV_MAXREF=" as *u8); pn(PV_MAXREF); hw(" reached -- refs beyond it were NOT examined; whole-page claim UNPROVEN\n" as *u8) } 993 if nimg > seen { hw("COVERAGE-GAP: img tags=" as *u8); pn(nimg); hw(" but refs resolved=" as *u8); pn(seen); hw(" -- some src attributes were not readable by the extractor; whole-page claim UNPROVEN\n" as *u8) } 994 // AN ASSET THAT COULD NOT BE READ ABSTAINS; IT NEVER ACQUITS. Ordered ahead of both GREEN-PARTIAL 995 // arms deliberately: an unread asset is a stronger claim against the page than a capped ref count, 996 // and letting a partial-green outrank it would be the acquittal this whole ladder exists to refuse. 997 // Nothing that reads GREEN today can land here -- a negative read was already counted as broken and 998 // published the page as RED -- so this can only move a verdict toward honesty, never away from it. 999 if broken == 0 { if PV_UNOBS > 0 { hw("VERDICT=UNOBSERVABLE unreadable-assets=" as *u8); pn(PV_UNOBS); hw(" (nothing that was read came back broken, but at least one asset could not be READ, so the whole-page claim is UNPROVEN)\n" as *u8); sys_exit(PV_EXIT_UNOBSERVABLE); return PV_EXIT_UNOBSERVABLE } } 1000 if broken == 0 { if seen >= PV_MAXREF { hw("VERDICT=GREEN-PARTIAL (examined assets fetch AND decode; coverage capped, see above)\n" as *u8); sys_exit(PV_EXIT_GREEN); return PV_EXIT_GREEN } } 1001 if broken == 0 { if nimg > seen { hw("VERDICT=GREEN-PARTIAL (examined assets fetch AND decode; " as *u8); pn(nimg - seen); hw(" img ref(s) unread, see above)\n" as *u8); sys_exit(PV_EXIT_GREEN); return PV_EXIT_GREEN } } 1002 if broken == 0 { hw("VERDICT=GREEN (examined static/declarative assets passed HTTP checks; browser rendering and runtime-only dependencies unverified)\n" as *u8); sys_exit(PV_EXIT_GREEN); return PV_EXIT_GREEN } 1003 hw("VERDICT=RED broken=" as *u8); pn(broken); hw("\n" as *u8) 1004 sys_exit(broken) 1005 return broken 1006}