code wiki / (root) / nx_provenance.nx

nx_provenance.nx source

↩ module page · 255 lines · 8700 B

1// nx_provenance.nx -- license-class enforcement gate. 2// 3// Per user directive 2026-05-14: "i dont want any wolfram or other 4// polluters that will try to sue i want academic sources that are 5// open etc but i dont want to have to deal with agl or apl or mit 6// or whatever licenses". 7// 8// This primitive reads nxc2/specs/nx_source_allowlist.txt (TSV: 9// source_id <TAB> license_class <TAB> year <TAB> description) and 10// exposes a yes/no gate for any caller that wants to attach a 11// source_id to a record before emit. 12// 13// Allowed classes (emit proceeds): 14// pd_classical, pd_explicit, pd_govdoc, academic_open, expired_patent 15// Refused classes (emit blocked): 16// forbidden, unknown (default for missing source_id) 17// 18// Compositionally fits between source-bridges (lean / coq / etc.) and 19// the ingest_runner: the bridge attaches a source_id; the runner asks 20// the provenance gate; if allowed, the row goes to the shard. 21// 22// genealogy_id: substrate_provenance_gate_2026_05_14 23// lineage_id: license_class_filter 24 25// nx_safety_envelope: 26// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 27// sil_target: SIL1 28// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 29// verdict: NOT_YET_EVALUATED 30 31import "nx_syscalls.nx" 32import "nx_runtime.nx" 33import "nx_tier.nx" 34import "nx_str.nx" 35 36// ===== license class codes (substrate-internal enum) ================ 37 38const NX_LC_UNKNOWN: nx_int = 0 39const NX_LC_FORBIDDEN: nx_int = 1 40const NX_LC_PD_CLASSICAL: nx_int = 2 41const NX_LC_PD_EXPLICIT: nx_int = 3 42const NX_LC_PD_GOVDOC: nx_int = 4 43const NX_LC_ACADEMIC_OPEN: nx_int = 5 44const NX_LC_EXPIRED_PATENT: nx_int = 6 45 46// Allowed lower bound: any class >= NX_LC_PD_CLASSICAL (2). Below that 47// (UNKNOWN=0 / FORBIDDEN=1) is refused. Hardcoded as the literal value 48// here because NishiLang's const initialisers don't accept other consts. 49const NX_LC_ALLOW_MIN: nx_int = 2 50 51// ===== struct ======================================================= 52 53struct NxSourceRecord { 54 source_id: *u8, // null-terminated owned copy 55 license_class: nx_int, 56 year: nx_int, 57} 58 59const NX_PROV_REC_BYTES: nx_size = 24 60const NX_PROV_MAX_SRCS: nx_int = 256 61const NX_PROV_SRC_ID_CAP: nx_size = 96 62 63struct NxProvenanceRegistry { 64 records: *NxSourceRecord, 65 n: nx_int, 66 capacity: nx_int, 67} 68 69// ===== string -> license_class mapping ============================= 70 71func nx_lc_from_str(s: *u8) -> nx_int { 72 if nx_str_eq(s, "pd_classical" as *u8) == 1 { return NX_LC_PD_CLASSICAL } 73 if nx_str_eq(s, "pd_explicit" as *u8) == 1 { return NX_LC_PD_EXPLICIT } 74 if nx_str_eq(s, "pd_govdoc" as *u8) == 1 { return NX_LC_PD_GOVDOC } 75 if nx_str_eq(s, "academic_open" as *u8) == 1 { return NX_LC_ACADEMIC_OPEN } 76 if nx_str_eq(s, "expired_patent" as *u8) == 1 { return NX_LC_EXPIRED_PATENT } 77 if nx_str_eq(s, "forbidden" as *u8) == 1 { return NX_LC_FORBIDDEN } 78 return NX_LC_UNKNOWN 79} 80 81// ===== TSV line parser ============================================= 82 83// One TSV row -> populate `out`. Returns 1 on success, 0 on malformed. 84// Field separators: \t. Recognized comment lead: '#'. 85func nx_prov_parse_line(buf: *u8, lo: nx_int, hi: nx_int, 86 id_buf: *u8, lc_buf: *u8, out: *NxSourceRecord) -> nx_int { 87 // Skip leading whitespace 88 var p: nx_int = lo 89 while p < hi { 90 let c: nx_int = buf[p] as nx_int 91 if c != 32 { 92 if c != 9 { p = hi + 1 } 93 if c == 9 { p = p + 1 } 94 } 95 if c == 32 { p = p + 1 } 96 } 97 p = lo 98 var ws_done: nx_int = 0 99 while ws_done == 0 { 100 if p >= hi { ws_done = 1 } 101 if ws_done == 0 { 102 let c: nx_int = buf[p] as nx_int 103 if c == 32 { p = p + 1 } 104 if c != 32 { 105 if c == 9 { p = p + 1 } 106 if c != 9 { ws_done = 1 } 107 } 108 } 109 } 110 if p >= hi { return 0 } 111 if buf[p] == 35 { return 0 } // '#' comment 112 if buf[p] == 10 { return 0 } // blank line 113 114 // Field 1: source_id (up to TAB or NL) 115 var f1_end: nx_int = p 116 var d1: nx_int = 0 117 while d1 == 0 { 118 if f1_end >= hi { d1 = 1 } 119 if d1 == 0 { 120 let c: nx_int = buf[f1_end] as nx_int 121 if c == 9 { d1 = 1 } 122 if c == 10 { d1 = 1 } 123 if d1 == 0 { f1_end = f1_end + 1 } 124 } 125 } 126 if f1_end >= hi { return 0 } 127 if buf[f1_end] == 10 { return 0 } // no separator -> malformed 128 let id_len: nx_size = (f1_end - p) as nx_size 129 if id_len == 0 { return 0 } 130 if id_len >= NX_PROV_SRC_ID_CAP { return 0 } 131 var i: nx_int = 0 132 while i < (id_len as nx_int) { 133 id_buf[i] = buf[p + i] 134 i = i + 1 135 } 136 id_buf[id_len] = 0 137 138 // Field 2: license_class 139 p = f1_end + 1 140 var f2_end: nx_int = p 141 var d2: nx_int = 0 142 while d2 == 0 { 143 if f2_end >= hi { d2 = 1 } 144 if d2 == 0 { 145 let c: nx_int = buf[f2_end] as nx_int 146 if c == 9 { d2 = 1 } 147 if c == 10 { d2 = 1 } 148 if d2 == 0 { f2_end = f2_end + 1 } 149 } 150 } 151 let lc_len: nx_size = (f2_end - p) as nx_size 152 if lc_len == 0 { return 0 } 153 if lc_len >= 32 { return 0 } 154 i = 0 155 while i < (lc_len as nx_int) { 156 lc_buf[i] = buf[p + i] 157 i = i + 1 158 } 159 lc_buf[lc_len] = 0 160 161 let lc: nx_int = nx_lc_from_str(lc_buf) 162 163 // Copy id into owned buffer 164 let owned_id: *u8 = sys_mmap(id_len + 1) 165 nx_str_cpy(owned_id, id_buf) 166 167 out.source_id = owned_id 168 out.license_class = lc 169 out.year = 0 // ignored for now 170 return 1 171} 172 173// ===== registry construction ======================================== 174 175// Load the manifest file from disk into a fresh registry. 176func nx_provenance_load(path: *u8) -> *NxProvenanceRegistry { 177 let raw: *u8 = sys_mmap(24) 178 let reg: *NxProvenanceRegistry = raw as *NxProvenanceRegistry 179 reg.records = (sys_mmap((NX_PROV_MAX_SRCS as nx_size) * NX_PROV_REC_BYTES)) as *NxSourceRecord 180 reg.n = 0 181 reg.capacity = NX_PROV_MAX_SRCS 182 183 let len_p: *nx_size = (sys_mmap(NX_SIZEOF_NX_SIZE)) as *nx_size 184 len_p[0] = 0 185 let buf: *u8 = sys_read_file(path, len_p) 186 if (buf as nx_size) == 0 { return reg } // empty registry is "deny all" 187 let n: nx_int = len_p[0] as nx_int 188 189 let id_buf: *u8 = sys_mmap(NX_PROV_SRC_ID_CAP) 190 let lc_buf: *u8 = sys_mmap(32) 191 var p: nx_int = 0 192 while p < n { 193 // Find end of current line 194 var eol: nx_int = p 195 var de: nx_int = 0 196 while de == 0 { 197 if eol >= n { de = 1 } 198 if de == 0 { 199 if buf[eol] == 10 { de = 1 } 200 if de == 0 { eol = eol + 1 } 201 } 202 } 203 // Parse it 204 if reg.n < reg.capacity { 205 let raw_rec: *u8 = ((reg.records as nx_size) + (reg.n as nx_size) * NX_PROV_REC_BYTES) as *u8 206 let rec: *NxSourceRecord = raw_rec as *NxSourceRecord 207 let parsed: nx_int = nx_prov_parse_line(buf, p, eol, id_buf, lc_buf, rec) 208 if parsed == 1 { reg.n = reg.n + 1 } 209 } 210 p = eol + 1 211 } 212 return reg 213} 214 215// ===== queries ===================================================== 216 217// Find a record by source_id. Returns the license class, or 218// NX_LC_UNKNOWN if not in the registry. 219func nx_provenance_check(reg: *NxProvenanceRegistry, source_id: *u8) -> nx_int { 220 var i: nx_int = 0 221 while i < reg.n { 222 let raw_rec: *u8 = ((reg.records as nx_size) + (i as nx_size) * NX_PROV_REC_BYTES) as *u8 223 let rec: *NxSourceRecord = raw_rec as *NxSourceRecord 224 if nx_str_eq(rec.source_id, source_id) == 1 { 225 return rec.license_class 226 } 227 i = i + 1 228 } 229 return NX_LC_UNKNOWN 230} 231 232// Returns 1 if source_id is allowed for emit; 0 if blocked. 233func nx_provenance_is_allowed(reg: *NxProvenanceRegistry, source_id: *u8) -> nx_int { 234 let lc: nx_int = nx_provenance_check(reg, source_id) 235 if lc >= NX_LC_ALLOW_MIN { return 1 } 236 return 0 237} 238 239// Observability: count records by class (sum 0..n for now). 240func nx_provenance_n_records(reg: *NxProvenanceRegistry) -> nx_int { 241 return reg.n 242} 243 244// Count records by class -- caller passes a class constant. 245func nx_provenance_n_by_class(reg: *NxProvenanceRegistry, lc: nx_int) -> nx_int { 246 var c: nx_int = 0 247 var i: nx_int = 0 248 while i < reg.n { 249 let raw_rec: *u8 = ((reg.records as nx_size) + (i as nx_size) * NX_PROV_REC_BYTES) as *u8 250 let rec: *NxSourceRecord = raw_rec as *NxSourceRecord 251 if rec.license_class == lc { c = c + 1 } 252 i = i + 1 253 } 254 return c 255}