code wiki / (root) / nx_patent_check.nx

nx_patent_check.nx source

↩ module page · 227 lines · 7470 B

1// nx_patent_check.nx -- offline patent-encumbrance gate. 2// 3// Per user directive 2026-05-14: "add the patent information/table to 4// nishi library for offline referencing". This primitive reads 5// nxc2/specs/nx_patent_table.txt (TSV: algorithm_id, patent_status, 6// patent_ref, filed_year, expiry_year, notes) and answers per-algo 7// safety queries. 8// 9// Status enum: 10// NX_PAT_UNKNOWN = 0 default deny (no row in table) 11// NX_PAT_ACTIVE = 1 patent in force -- UNSAFE; refuse 12// NX_PAT_EXPIRED = 2 was patented; now safe 13// NX_PAT_PD = 3 never patented; always safe 14// 15// Safe predicate: status >= NX_PAT_EXPIRED (i.e., PD or EXPIRED). 16// 17// Composes with nx_provenance: license filter (#2) blocks tainted 18// source text; patent filter (#3) blocks encumbered algorithm 19// IDEAS. Together they close the IP-exposure surface. 20// 21// genealogy_id: substrate_patent_gate_2026_05_14 22// lineage_id: ip_compliance_filter 23 24// nx_safety_envelope: 25// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 26// sil_target: SIL1 27// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 28// verdict: NOT_YET_EVALUATED 29 30import "nx_syscalls.nx" 31import "nx_runtime.nx" 32import "nx_tier.nx" 33import "nx_str.nx" 34 35const NX_PAT_UNKNOWN: nx_int = 0 36const NX_PAT_ACTIVE: nx_int = 1 37const NX_PAT_EXPIRED: nx_int = 2 38const NX_PAT_PD: nx_int = 3 39 40// Safe lower bound (PD or EXPIRED). Below = refused. Hardcoded as 41// literal because NishiLang const initialisers don't accept other consts. 42const NX_PAT_SAFE_MIN: nx_int = 2 43 44const NX_PAT_MAX_RECORDS: nx_int = 1024 45const NX_PAT_ALGO_ID_CAP: nx_size = 96 46const NX_PAT_STATUS_CAP: nx_size = 16 47 48// ===== struct ======================================================= 49 50struct NxPatentRecord { 51 algorithm_id: *u8, 52 patent_status: nx_int, 53 expiry_year: nx_int, // 0 if pd or unknown 54} 55 56const NX_PAT_REC_BYTES: nx_size = 24 57 58struct NxPatentTable { 59 records: *NxPatentRecord, 60 n: nx_int, 61 capacity: nx_int, 62} 63 64// ===== string -> status ============================================= 65 66func nx_pat_status_from_str(s: *u8) -> nx_int { 67 if nx_str_eq(s, "pd" as *u8) == 1 { return NX_PAT_PD } 68 if nx_str_eq(s, "expired" as *u8) == 1 { return NX_PAT_EXPIRED } 69 if nx_str_eq(s, "active" as *u8) == 1 { return NX_PAT_ACTIVE } 70 return NX_PAT_UNKNOWN 71} 72 73// ===== TSV row parser =============================================== 74 75// Parse one TSV row into out_rec. Skips blank / comment lines. 76// Returns 1 on success, 0 on malformed / skipped. 77func nx_pat_parse_line(buf: *u8, lo: nx_int, hi: nx_int, 78 id_buf: *u8, status_buf: *u8, 79 out: *NxPatentRecord) -> nx_int { 80 // Skip leading whitespace 81 var p: nx_int = lo 82 var ws_done: nx_int = 0 83 while ws_done == 0 { 84 if p >= hi { ws_done = 1 } 85 if ws_done == 0 { 86 let c: nx_int = buf[p] as nx_int 87 if c == 32 { p = p + 1 } 88 if c != 32 { 89 if c == 9 { p = p + 1 } 90 if c != 9 { ws_done = 1 } 91 } 92 } 93 } 94 if p >= hi { return 0 } 95 if buf[p] == 35 { return 0 } // '#' comment 96 if buf[p] == 10 { return 0 } // blank line 97 98 // Field 1: algorithm_id (up to TAB or NL) 99 var f1_end: nx_int = p 100 var d1: nx_int = 0 101 while d1 == 0 { 102 if f1_end >= hi { d1 = 1 } 103 if d1 == 0 { 104 let c: nx_int = buf[f1_end] as nx_int 105 if c == 9 { d1 = 1 } 106 if c == 10 { d1 = 1 } 107 if d1 == 0 { f1_end = f1_end + 1 } 108 } 109 } 110 if f1_end >= hi { return 0 } 111 if buf[f1_end] == 10 { return 0 } // no separator 112 let id_len: nx_int = f1_end - p 113 if id_len == 0 { return 0 } 114 if id_len >= (NX_PAT_ALGO_ID_CAP as nx_int) { return 0 } 115 var i: nx_int = 0 116 while i < id_len { 117 id_buf[i] = buf[p + i] 118 i = i + 1 119 } 120 id_buf[id_len] = 0 121 122 // Field 2: patent_status 123 p = f1_end + 1 124 var f2_end: nx_int = p 125 var d2: nx_int = 0 126 while d2 == 0 { 127 if f2_end >= hi { d2 = 1 } 128 if d2 == 0 { 129 let c: nx_int = buf[f2_end] as nx_int 130 if c == 9 { d2 = 1 } 131 if c == 10 { d2 = 1 } 132 if d2 == 0 { f2_end = f2_end + 1 } 133 } 134 } 135 let st_len: nx_int = f2_end - p 136 if st_len == 0 { return 0 } 137 if st_len >= (NX_PAT_STATUS_CAP as nx_int) { return 0 } 138 i = 0 139 while i < st_len { 140 status_buf[i] = buf[p + i] 141 i = i + 1 142 } 143 status_buf[st_len] = 0 144 145 // Own the algorithm_id copy. 146 let owned_id: *u8 = sys_mmap((id_len + 1) as nx_size) 147 nx_str_cpy(owned_id, id_buf) 148 out.algorithm_id = owned_id 149 out.patent_status = nx_pat_status_from_str(status_buf) 150 out.expiry_year = 0 // present in file but unused at gate time 151 return 1 152} 153 154// ===== load + query ================================================= 155 156func nx_patent_load(path: *u8) -> *NxPatentTable { 157 let raw: *u8 = sys_mmap(24) 158 let t: *NxPatentTable = raw as *NxPatentTable 159 t.records = (sys_mmap((NX_PAT_MAX_RECORDS as nx_size) * NX_PAT_REC_BYTES)) as *NxPatentRecord 160 t.n = 0 161 t.capacity = NX_PAT_MAX_RECORDS 162 163 let len_p: *nx_size = (sys_mmap(NX_SIZEOF_NX_SIZE)) as *nx_size 164 len_p[0] = 0 165 let buf: *u8 = sys_read_file(path, len_p) 166 if (buf as nx_size) == 0 { return t } // empty -> default-deny everything 167 let n: nx_int = len_p[0] as nx_int 168 169 let id_buf: *u8 = sys_mmap(NX_PAT_ALGO_ID_CAP) 170 let status_buf: *u8 = sys_mmap(NX_PAT_STATUS_CAP) 171 var p: nx_int = 0 172 while p < n { 173 var eol: nx_int = p 174 var de: nx_int = 0 175 while de == 0 { 176 if eol >= n { de = 1 } 177 if de == 0 { 178 if buf[eol] == 10 { de = 1 } 179 if de == 0 { eol = eol + 1 } 180 } 181 } 182 if t.n < t.capacity { 183 let raw_rec: *u8 = ((t.records as nx_size) + (t.n as nx_size) * NX_PAT_REC_BYTES) as *u8 184 let rec: *NxPatentRecord = raw_rec as *NxPatentRecord 185 let parsed: nx_int = nx_pat_parse_line(buf, p, eol, id_buf, status_buf, rec) 186 if parsed == 1 { t.n = t.n + 1 } 187 } 188 p = eol + 1 189 } 190 return t 191} 192 193// Look up algorithm_id; returns status enum, or NX_PAT_UNKNOWN. 194func nx_patent_check(t: *NxPatentTable, algo_id: *u8) -> nx_int { 195 var i: nx_int = 0 196 while i < t.n { 197 let raw_rec: *u8 = ((t.records as nx_size) + (i as nx_size) * NX_PAT_REC_BYTES) as *u8 198 let rec: *NxPatentRecord = raw_rec as *NxPatentRecord 199 if nx_str_eq(rec.algorithm_id, algo_id) == 1 { 200 return rec.patent_status 201 } 202 i = i + 1 203 } 204 return NX_PAT_UNKNOWN 205} 206 207// Returns 1 if algo_id is patent-safe (PD or EXPIRED), 0 otherwise. 208func nx_patent_is_safe(t: *NxPatentTable, algo_id: *u8) -> nx_int { 209 let s: nx_int = nx_patent_check(t, algo_id) 210 if s >= NX_PAT_SAFE_MIN { return 1 } 211 return 0 212} 213 214// Observability 215func nx_patent_n_records(t: *NxPatentTable) -> nx_int { return t.n } 216 217func nx_patent_n_by_status(t: *NxPatentTable, status: nx_int) -> nx_int { 218 var c: nx_int = 0 219 var i: nx_int = 0 220 while i < t.n { 221 let raw_rec: *u8 = ((t.records as nx_size) + (i as nx_size) * NX_PAT_REC_BYTES) as *u8 222 let rec: *NxPatentRecord = raw_rec as *NxPatentRecord 223 if rec.patent_status == status { c = c + 1 } 224 i = i + 1 225 } 226 return c 227}