nx_srcfresh.nx source
↩ module page · 162 lines · 7700 B
1// nx_srcfresh.nx -- LIB: IS THIS ARTIFACT BUILT FROM THIS SOURCE? The canonical source-vs-artifact
2// staleness predicate, extracted 2026-08-07 so callers that are not installers can ask the question.
3//
4// WHY IT EXISTS AS A LIB. The predicate already existed and was already PROVEN -- oi_src_stale in
5// nx_offc_install.nx, locked by _freshness_gate.nx (T1 stale / T2 neg-control fresh / T3 artifact-absent /
6// T4 source-absent / T5 mtime round-trip). But it lives inside an INSTALLER that owns main(), so no other
7// organ can import it, and the mtime-stat idiom is consequently open-coded in 10+ places (gv_mtime in
8// nx_gate_verdict_lib, gb_mtime in nx_gatebuilt_gate, nx_pipeline_census, nx_datacurator x3, nx_netobs,
9// nx_imgbook_promote, nx_janitor_sprawl, nx_sov_tree_audit, nx_eff_lib, nx_mem_healer) -- and those copies
10// do NOT agree: several read only st_mtim.tv_sec, so two writes in the same second compare EQUAL and a
11// rebuild-within-a-second reads FRESH when it is stale.
12// A PROVEN PREDICATE TRAPPED INSIDE AN ORGAN THAT OWNS main() IS NOT A SHARED CAPABILITY, IT IS A COPY
13// WAITING TO BE MADE -- AND EVERY COPY IS FREE TO DISAGREE WITH THE ONE THE GATE PROVED.
14//
15// SEMANTICS ARE LIFTED VERBATIM from oi_src_stale rather than re-derived, so the behaviour _freshness_gate
16// already locked carries over unchanged; nx_srcfresh_gate re-asserts all five of its cases against THIS
17// copy (equivalence is asserted, not assumed) and adds the same-second case the second-granularity copies
18// silently fail.
19// Imports ONLY nx_syscalls.nx -- a lib that drags a dependency tree behind it does not get adopted.
20// license_tier: ORIGINAL No hw writes (Rule 26).
21import "nx_syscalls.nx"
22import "nx_sha256.nx" // CONTENT identity -- the only thing a clock cannot establish
23const SF_MAGIC_4194304: i64 = 4194304
24
25const SF_STATBUF: i64 = 256
26const SF_MTIME_SEC_OFF: i64 = 88
27const SF_MTIME_NSEC_OFF: i64 = 96
28const SF_NS_PER_SEC: i64 = 1000000000
29
30// mtime in NANOSECONDS, or -1 if the path cannot be stat'd. Nanoseconds, not seconds, because the whole
31// point of this predicate is ordering two writes that are often seconds apart -- and sometimes less.
32func sf_mtime_ns(path: *u8) -> i64 {
33 let sb: *u8 = sys_mmap(SF_STATBUF)
34 if sys_fstatat(path, sb) < 0 { sys_munmap(sb, SF_STATBUF); return 0 - 1 }
35 let secp: *i64 = ((sb as i64) + SF_MTIME_SEC_OFF) as *i64
36 let nsecp: *i64 = ((sb as i64) + SF_MTIME_NSEC_OFF) as *i64
37 let v: i64 = secp[0] * SF_NS_PER_SEC + nsecp[0]
38 sys_munmap(sb, SF_STATBUF)
39 return v
40}
41
42// 1 if `artifact` is STALE vs `source`; 0 if fresh or there is no source to be stale against.
43// The four cases, verbatim from the proven oi_src_stale:
44// source absent -> 0 nothing to be stale against
45// artifact absent -> 1 never built (and the source exists)
46// artifact older -> 1 stale
47// otherwise -> 0 fresh
48// The asymmetry is deliberate and load-bearing: absent-source is NOT stale, absent-artifact IS.
49func sf_src_stale(artifact: *u8, source: *u8) -> i64 {
50 let sm: i64 = sf_mtime_ns(source)
51 if sm < 0 { return 0 }
52 let am: i64 = sf_mtime_ns(artifact)
53 if am < 0 { return 1 }
54 if am < sm { return 1 }
55 return 0
56}
57
58// SECONDS the artifact is behind the source: 0 when fresh, -1 when the artifact is ABSENT (no measurable
59// lag, a different fact from "no lag") and -1 when there is no source.
60// WHY A MAGNITUDE AND NOT ONLY A FLAG: a boolean forces every caller to re-derive "how far behind", and the
61// decisions differ -- a 74-second lag is a sibling mid-edit, a 9885-minute lag is abandoned. Measured
62// 2026-08-07: the gate census showed both clusters in one run and the flag alone could not separate them.
63// A DETECTOR THAT REPORTS ONLY A BOOLEAN MAKES THE OPERATOR REDO THE MEASUREMENT IT ALREADY HAD.
64func sf_lag_sec(artifact: *u8, source: *u8) -> i64 {
65 let sm: i64 = sf_mtime_ns(source)
66 if sm < 0 { return 0 - 1 }
67 let am: i64 = sf_mtime_ns(artifact)
68 if am < 0 { return 0 - 1 }
69 if am >= sm { return 0 }
70 return (sm - am) / SF_NS_PER_SEC
71}
72
73// ============ PROVENANCE: what a clock cannot tell you (2026-08-07, debt 1786113214) ==================
74// EVERYTHING ABOVE ORDERS TWO WRITES. None of it can say that one was MADE FROM the other, and that gap has
75// a FALSE-FRESH direction: any promote by any seat resets an artifact mtime and erases the evidence of an
76// unadopted source edit. FRESH is the answer that stops you looking, so a false FRESH is the dangerous one.
77// PROVEN ON THIS ORGAN THE DAY IT SHIPPED: nx_gatefresh answered FRESH lag_sec=0 for nx_mgmt_api while its
78// source carried an edit no binary had ever been built from -- a concurrent promote had reset the mtime.
79// /api/build now records src_sha256 at the moment of production into <target>.provenance, so freshness can
80// be an EXACT identity comparison with no clock in it.
81// A CLOCK CAN ORDER TWO WRITES BUT NOT ESTABLISH THAT ONE WAS MADE FROM THE OTHER.
82const SF_PROV_CAP: i64 = 4096
83const SF_HEXLEN: i64 = 64
84const SF_HEXBUF: i64 = 80
85
86func sf_hex32(dig: *u8, out: *u8) -> i64 {
87 let tab: *u8 = "0123456789abcdef" as *u8
88 var i: i64 = 0
89 while i < 32 {
90 let v: i64 = dig[i] as i64
91 out[i * 2] = tab[v / 16]
92 out[i * 2 + 1] = tab[v % 16]
93 i = i + 1
94 }
95 out[64] = 0 as u8
96 return 64
97}
98// sha256 of a file, hex into `out` (needs SF_HEXBUF). 1 = ok, 0 = unreadable/empty.
99func sf_file_sha_hex(path: *u8, out: *u8) -> i64 {
100 let fd: i64 = sys_openat_rd(path)
101 if fd < 0 { return 0 }
102 let cap: i64 = SF_MAGIC_4194304
103 let buf: *u8 = sys_mmap(cap + 1)
104 var total: i64 = 0
105 var nrd: i64 = sys_read(fd, buf, cap)
106 while nrd > 0 {
107 total = total + nrd
108 if total >= cap { nrd = 0 }
109 else { nrd = sys_read(fd, ((buf as i64) + total) as *u8, cap - total) }
110 }
111 sys_close(fd)
112 if total <= 0 { return 0 }
113 let dig: *u8 = sys_mmap(32)
114 sha256_digest(buf, total, dig)
115 sf_hex32(dig, out)
116 return 1
117}
118// extract the src_sha256=<64hex> field from a sidecar. 1 = found, 0 = absent/malformed.
119// FAIL CLOSED ON A MALFORMED SIDECAR: a half-written receipt must read as UNKNOWN, never as agreement.
120func sf_prov_src_sha(provpath: *u8, out: *u8) -> i64 {
121 let fd: i64 = sys_openat_rd(provpath)
122 if fd < 0 { return 0 }
123 let buf: *u8 = sys_mmap(SF_PROV_CAP + 1)
124 let n: i64 = sys_read(fd, buf, SF_PROV_CAP)
125 sys_close(fd)
126 if n <= 0 { return 0 }
127 let key: *u8 = "src_sha256=" as *u8
128 var i: i64 = 0
129 while i < n {
130 var k: i64 = 0
131 var hit: i64 = 1
132 while key[k] != (0 as u8) {
133 if i + k >= n { hit = 0 }
134 if hit == 1 { if buf[i + k] != key[k] { hit = 0 } }
135 k = k + 1
136 }
137 if hit == 1 {
138 let s: i64 = i + k
139 if s + SF_HEXLEN > n { return 0 }
140 var j: i64 = 0
141 while j < SF_HEXLEN { out[j] = buf[s + j]; j = j + 1 }
142 out[SF_HEXLEN] = 0 as u8
143 return 1
144 }
145 i = i + 1
146 }
147 return 0
148}
149// PROVENANCE verdict: 1 STALE (recorded digest != current source digest), 0 FRESH (exact match),
150// -1 UNKNOWN (no sidecar, malformed sidecar, or unreadable source).
151// -1 MUST NOT BE READ AS FRESH by any caller -- that is the whole defect this replaces.
152func sf_prov_stale(provpath: *u8, srcpath: *u8) -> i64 {
153 let rec: *u8 = sys_mmap(SF_HEXBUF)
154 if sf_prov_src_sha(provpath, rec) == 0 { return 0 - 1 }
155 let cur: *u8 = sys_mmap(SF_HEXBUF)
156 if sf_file_sha_hex(srcpath, cur) == 0 { return 0 - 1 }
157 var i: i64 = 0
158 while i < SF_HEXLEN {
159 if rec[i] != cur[i] { return 1 }
160 i = i + 1
161 }
162 return 0
163}