code wiki / (root) / nx_closurehash.nx

nx_closurehash.nx

buildroot/runtime/nx_closurehash.nx

12675 B241 linesdepth 5pulls 6 transitivereach 0 importersview sourcekind tool
docsdependenciesstructsconstsfunctions

about

nx_closurehash.nx -- PROVENANCE AT PRODUCTION, GENERALISED FROM ONE FILE TO THE WHOLE IMPORT CLOSURE. THE DEFECT THIS RETIRES (measured all day 2026-08-07): the build lane decides "has this tree forked?" by comparing the LIVE NAS tree against `treecanon_laptop_hash.mf` -- a manifest generated from the LAPTOP mirror and pushed across. But edits are made with nx_fs_write, which writes DIRECTLY to the NAS, and builds compile FROM the NAS. So the newest truth is on the NAS and the judge is the stale mirror: *THE CANON GUARD RUNS BACKWARDS RELATIVE TO HOW EDITS ACTUALLY FLOW, AND CALLS THE TRUTH A FORK.* Consequences seen today: builds blocked by my own newer edit · a manifest push losing a file-lock race · four different hashes for one header inside ten minutes · ~3,800 NAS-ahead files nobody can reconcile. Every guard involved was working correctly on a bad premise. nx_mgmt_api already states the right principle, for ONE file: "PROVENANCE, CAPTURED AT THE MOMENT OF PRODUCTION ... A CLOCK CANNOT ESTABLISH PROVENANCE. RECORD IT WHERE IT IS CHEAP: HERE. LATER IT IS UNRECOVERABLE." It emits src_sha256/src_stable for the target's own source. That is the whole idea, one file wide. This widens it to every source the compiler will actually read. *A MANIFEST IS A CLAIM ABOUT A TREE AT A PAST INSTANT; A CLOSURE HASH IS A FACT ABOUT THE BINARY IN YOUR HAND.* With the closure recorded at build time you can answer "was this artifact built from these exact bytes?" with no manifest, no push, no watermark, and nothing to go stale -- and the question a census can never answer, "which of the two trees was I compiled from", becomes trivial. SOURCE RESOLUTION ORDER IS LOAD-BEARING and copied from nx_stalesweep's header rather than guessed: _hdl_build/<t>.nx FIRST, then runtime/<t>.nx -- because buildrun compiles the _hdl_build twin when BOTH exist, so hashing the runtime twin would describe a file the compiler never opened. UNRESOLVED IMPORTS ARE THEIR OWN BUCKET AND ARE COUNTED. An import that resolves nowhere is exactly the kind of thing this must not silently omit -- omitting it would make a BROKEN closure hash the same shape as a healthy one. *A PROVENANCE RECORD THAT DROPS WHAT IT COULD NOT FIND IS A FORGERY.* nx_closurehash <target> [root] root default "." (run from the buildroot) -> one line per source, then: closure_sha=<64hex> files=N unresolved=M bytes=B exit 0 complete | 1 closure has unresolved imports | 2 target source not found | 3 usage license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0

dependencies 3 imports · 0 importers

nx_syscalls.nx nx_sha256.nx nx_incclosure_lib.nx nx_closurehash.nx

imports: nx_syscalls.nxnx_sha256.nxnx_incclosure_lib.nx

imported by: nobody (leaf or entry point)

call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown

main che sys_write sys_exit sys_mmap nxa_die sys_write ↻ sys_exit ↻ nxa_lock_take nxa_lock_addr sys_write ↻ nxa_lock_give nxa_lock_addr ↻ nxa_report_overrun sys_write ↻ nxa_dump_printable sys_write ↻ nxa_dump_sizes sys_write ↻ chslot chcat chresolve icl_resolve icl_cat icl_exists sys_openat_rd sys_close sys_mmap ↻ sys_openat_rd ↻ sys_getdents64 sys_close ↻ chp sys_write ↻ chread sys_openat_rd ↻ sys_read sys_close ↻ sha256_digest sys_mmap ↻ sha256_init

structs

none

consts

44const CH_MAGIC_262144: i64 = 262144
45const CH_MAGIC_1024: i64 = 1024
47const CH_MAXF: i64 = 1024 // closure entries; refuses past this rather than silently truncating
48const CH_NAMEW: i64 = 192 // bytes per stored name
49const CH_FCAP: i64 = 4194304 // 4MB per source file
50const CH_ACC: i64 = 262144 // accumulator for the combined digest input

functions

52func chp(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
called by 2: chnmain calls 1: sys_write
53func che(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(2,s,n); return 0 }
called by 1: main calls 1: sys_write
54func chn(v: i64) -> i64
called by 1: main calls 3: chpsys_mmapsys_write
67func chcat(d: *u8, o: i64, s: *u8) -> i64 { var x: i64=o; var i: i64=0; while s[i]!=(0 as u8){d[x]=s[i];x=x+1;i=i+1} return x }
called by 1: main
68func chlen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
69func chslot(b: *u8, i: i64) -> *u8 { return ((b as i64) + i*CH_NAMEW) as *u8 }
called by 1: main
70func chseq(a: *u8, b: *u8) -> i64
called by 1: main
82func chhex(dig: *u8, out: *u8) -> i64
called by 1: main
94func chexists(p: *u8) -> i64 { let fd: i64 = sys_openat_rd(p); if fd < 0 { return 0 } sys_close(fd); return 1 }
95func chread(path: *u8, buf: *u8, cap: i64) -> i64
called by 1: main calls 3: sys_openat_rdsys_readsys_close
119func chresolve(root: *u8, name: *u8, out: *u8) -> i64 { return icl_resolve(root, name, out) }
called by 1: main calls 1: icl_resolve
121func main(argc: i64, argv: *i64) -> i64