code wiki / (root) / nx_buildecho.nx

nx_buildecho.nx source

↩ module page · 108 lines · 4809 B

1// nx_buildecho.nx -- READ WHAT THE BUILDER SAID IT COMPILED (2026-08-07). 2// 3// Extracted from nx_mgmt_api's ma_do_build so the cross-target guard can be BITE-TESTED. It was written 4// inline inside a 296KB daemon that binds a port and needs a live estate, which made the one piece of 5// logic standing between "wrong organ promoted under the right name" and "refused" the piece nobody 6// could exercise. 7// *A GUARD BURIED IN A DAEMON IS A GUARD THAT ONLY GETS TESTED IN PRODUCTION.* 8// 9// WHAT IT PARSES -- the builder's own echo, the ONLY witness to what was actually compiled: 10// [hostctl] buildrun: compile 'nx_seg_store_gate' on the NAS (nx_cc -> nxasm) 11// 12// WHY THE ECHO AND NOT THE OBVIOUS FIELDS (all three measured 2026-08-07): 13// expect_sha256 -- proves the staged bytes are the ones just built. When the WRONG source was compiled 14// that digest IS the wrong binary's digest, so promote installs it and says PROMOTED. 15// A GUARD THAT CHECKS THE ARTIFACT AGAINST THE BUILD CANNOT TELL YOU THE BUILD WAS OF 16// THE WRONG THING. 17// src_path -- ma_gate_src DERIVES it from the REQUESTED name, so it agrees BY CONSTRUCTION 18// whatever the compiler read. A FIELD COMPUTED FROM THE QUESTION CANNOT CORROBORATE 19// THE ANSWER. 20// 21// ABSENCE IS NOT A MISMATCH. A capture without the marker returns 0 and the caller proceeds exactly as 22// before. *A GUARD THAT CANNOT SEE ITS EVIDENCE MUST ABSTAIN, NEVER ACCUSE* -- turning "I could not look" 23// into "it is wrong" would break every build the day the echo format changes. 24// license_tier: ORIGINAL No hw writes (Rule 26). 25import "nx_syscalls.nx" 26 27const BE_NAMEMAX: i64 = 250 28 29// Returns 1 and fills `out` (NUL-terminated) with the compiled target name, or 0 when the marker is 30// absent/empty. Scans the WHOLE buffer: the echo is never at offset 0, and a capture that merges stdout 31// with stderr moves it again -- classifying by the first bytes of a merged stream read every SUCCESS as 32// unparsed once already today. 33func be_compiled_name(buf: *u8, n: i64, out: *u8) -> i64 { 34 let k: *u8 = "compile '" as *u8 35 var i: i64 = 0 36 while i + 9 <= n { 37 var m: i64 = 0 38 var hit: i64 = 1 39 while m < 9 { if buf[i+m] != k[m] { hit = 0; m = 9 } else { m = m + 1 } } 40 if hit == 1 { 41 var s: i64 = i + 9 42 var o: i64 = 0 43 while s < n { 44 let c: i64 = buf[s] as i64 45 if c == 39 { s = n } else { 46 if o < BE_NAMEMAX { out[o] = buf[s]; o = o + 1 } 47 s = s + 1 48 } 49 } 50 out[o] = 0 as u8 51 if o == 0 { return 0 } 52 return 1 53 } 54 i = i + 1 55 } 56 return 0 57} 58 59// Extract the 64 hex chars following `key` (e.g. "closure_sha=") from a captured stream into `out`. 60// Returns 1 on success, 0 when the key is absent OR the value is short/non-hex. 61// *A TRUNCATED DIGEST MUST NOT BE PUBLISHED AS A DIGEST* -- accepting a short value would let a receipt 62// carry a number that looks authoritative and identifies nothing, which is worse than carrying none. 63// Scans the whole buffer: captured streams merge stdout and stderr, so the value is not at offset 0. 64func be_after_hex(buf: *u8, n: i64, key: *u8, out: *u8) -> i64 { 65 var ln: i64 = 0 66 while key[ln] != (0 as u8) { ln = ln + 1 } 67 if n < ln + 64 { return 0 } 68 var i: i64 = 0 69 while i + ln + 64 <= n { 70 var m: i64 = 0 71 var hit: i64 = 1 72 while m < ln { if buf[i+m] != key[m] { hit = 0; m = ln } else { m = m + 1 } } 73 if hit == 1 { 74 let s: i64 = i + ln 75 var o: i64 = 0 76 var ok: i64 = 1 77 while o < 64 { 78 let c: i64 = buf[s+o] as i64 79 var ishex: i64 = 0 80 if c >= 48 { if c <= 57 { ishex = 1 } } 81 if c >= 97 { if c <= 102 { ishex = 1 } } 82 if ishex == 0 { ok = 0; o = 64 } else { out[o] = buf[s+o]; o = o + 1 } 83 } 84 if ok == 0 { return 0 } 85 out[64] = 0 as u8 86 return 1 87 } 88 i = i + 1 89 } 90 return 0 91} 92 93// 1 when the builder's echo names exactly `want`. 0 on mismatch. Callers must treat be_compiled_name()==0 94// (no evidence) differently from this returning 0 (contradicted evidence) -- they are not the same claim. 95func be_echo_matches(buf: *u8, n: i64, want: *u8) -> i64 { 96 let got: *u8 = sys_mmap(BE_NAMEMAX + 8) 97 if be_compiled_name(buf, n, got) == 0 { return 0 } 98 var i: i64 = 0 99 var go: i64 = 1 100 while go == 1 { 101 let a: i64 = got[i] as i64 102 let b: i64 = want[i] as i64 103 if a != b { return 0 } 104 if a == 0 { go = 0 } 105 i = i + 1 106 } 107 return 1 108}