code wiki / (root) / nx_asset_prov_clock_candidate_t346.nx

nx_asset_prov_clock_candidate_t346.nx source

↩ module page · 246 lines · 10723 B

1// nx_asset_prov_lib.nx -- THE PER-ASSET PROVENANCE ROW AND ITS FAIL-CLOSED VERDICT (/compare/modding MD9, 2026-09-06). 2// ONE rights table in this estate (knowledge/model_license.conf, read by nx_licgate_lib) and ONE journal of ingested assets: 3// a row is `asset<TAB>sha256<TAB>source<TAB>licence_id<TAB>origin<TAB>epoch`, APPEND-ONLY -- a later row for the same sha 4// SUPERSEDES the earlier one (a relicence is a new row, never an edit) and the LAST row wins. Field bytes are sanitised on 5// write (a tab or newline inside a source url becomes a space) so no caller can forge a licence column through a url. 6// THE VERDICT IS FAIL-CLOSED BY CONSTRUCTION: no journal or no row -> REFUSE (NO-JOURNAL / NO-ROW); a licence id the rights 7// table does not carry -> REFUSE (UNKNOWN-LICENCE), so "unknown" is never a table row, it is the absence of one; a licence 8// whose weights_redist right is NO -> REFUSE (NON-REDISTRIBUTABLE); CONDITIONAL -> REVIEW; an UNVERIFIED licence is capped 9// at CONDITIONAL, the same law nx_licgate_lib applies to models (a licence nobody read cannot grant an outright YES). 10// Every export and publish door asks pv_verdict in-process; PRIVATE USE IS ALWAYS ALLOWED (res[PV_RES_PRIVATE]=1) -- the 11// refusal is about redistribution, never about looking at what you downloaded. 12// The journal path is a PARAMETER: a gate drives this lib on its own scratch journal and never on the production one. 13// license_tier: ORIGINAL No hw writes (Rule 26). 14import "nx_syscalls.nx" 15import "nx_licgate_lib.nx" 16import "nx_sha256.nx" 17 18const PV_JRNL_DEFAULT: *u8 = "knowledge/provenance/assets.jrnl" 19const PV_DIR_MODE: i64 = 493 20const PV_FILE_MODE: i64 = 420 21const PV_OPEN_APPEND: i64 = 1089 // O_WRONLY | O_CREAT | O_APPEND 22const PV_SYS_OPENAT: i64 = 257 23const PV_SYS_TIME: i64 = 201 24const PV_SHA_BYTES: i64 = 32 25const PV_SHA_HEX: i64 = 64 26const PV_TAG: *u8 = "asset" 27const PV_TAB: i64 = 9 28const PV_NL: i64 = 10 29const PV_SPACE: i64 = 32 30const PV_SLASH: i64 = 47 31const PV_LIC_CAP: i64 = 128 32const PV_LINE_CAP: i64 = 4096 33const PV_PATH_CAP: i64 = 1024 34const PV_F_TAG: i64 = 0 35const PV_F_SHA: i64 = 1 36const PV_F_SOURCE: i64 = 2 37const PV_F_LIC: i64 = 3 38const PV_F_ORIGIN: i64 = 4 39const PV_F_EPOCH: i64 = 5 40// verdict codes are nx_licgate_lib's own exit vocabulary: LG_RC_OK 0 / LG_RC_REVIEW 3 / LG_RC_REFUSE 4 41const PV_R_OK: i64 = 0 42const PV_R_NO_ROW: i64 = 1 43const PV_R_UNKNOWN_LIC: i64 = 2 44const PV_R_NON_REDIST: i64 = 3 45const PV_R_CONDITIONAL: i64 = 4 46const PV_R_UNVERIFIED: i64 = 5 47const PV_R_TABLE_UNREADABLE: i64 = 6 48const PV_R_NO_JOURNAL: i64 = 7 49const PV_RES_REASON: i64 = 0 50const PV_RES_REDIST: i64 = 1 51const PV_RES_LICIDX: i64 = 2 52const PV_RES_PRIVATE: i64 = 3 53const PV_RES_ROWS: i64 = 4 54const PV_RES_N: i64 = 8 55// box slots for pv_row_find 56const PV_BOX_BUF: i64 = 0 57const PV_BOX_S: i64 = 1 58const PV_BOX_E: i64 = 2 59const PV_BOX_ROWS: i64 = 3 60const PV_BOX_N: i64 = 4 61 62func pv_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 63func pv_puts(s: *u8) -> i64 { sys_write(1, s, pv_len(s)); return 0 } 64func pv_putn(v: i64) -> i64 { let t: *u8 = sys_mmap(32); var e: i64 = 0; if v < 0 { t[0] = 45 as u8; e = lg_putd(t, 1, 0 - v) } else { e = lg_putd(t, 0, v) } sys_write(1, t, e); return 0 } 65func pv_hexdig(v: i64) -> u8 { if v < 10 { return (48 + v) as u8 } return (87 + v) as u8 } 66func pv_hex(dig: *u8, out: *u8) -> i64 { 67 var i: i64 = 0 68 while i < PV_SHA_BYTES { 69 let b: i64 = (dig[i] & 0xff) as i64 70 out[i * 2] = pv_hexdig(b / 16) 71 out[i * 2 + 1] = pv_hexdig(b % 16) 72 i = i + 1 73 } 74 out[PV_SHA_HEX] = 0 as u8 75 return PV_SHA_HEX 76} 77func pv_hash_bytes(b: *u8, n: i64, out: *u8) -> i64 { 78 let d: *u8 = sys_mmap(PV_SHA_BYTES) 79 sha256_digest(b, n, d) 80 return pv_hex(d, out) 81} 82// returns the file length hashed, or -1 when the file cannot be read (an unreadable asset has NO sha and NO row) 83func pv_hash_file(path: *u8, out: *u8) -> i64 { 84 let lp: *i64 = sys_mmap(16) as *i64 85 let buf: *u8 = sys_read_file(path, lp) 86 if (buf as i64) == 0 { return 0 - 1 } 87 pv_hash_bytes(buf, lp[0], out) 88 return lp[0] 89} 90func pv_now() -> i64 { return sys_now_realtime_sec() } 91func pv_is_sha_hex(s: *u8) -> i64 { 92 if pv_len(s) != PV_SHA_HEX { return 0 } 93 var i: i64 = 0 94 while i < PV_SHA_HEX { 95 let c: i64 = s[i] as i64 96 var ok: i64 = 0 97 if c >= 48 { if c <= 57 { ok = 1 } } 98 if c >= 97 { if c <= 102 { ok = 1 } } 99 if ok == 0 { return 0 } 100 i = i + 1 101 } 102 return 1 103} 104// a field byte that would open a new column or a new row is replaced by a space: the schema cannot be forged from a value 105func pv_put_field(o: *u8, at: i64, s: *u8, cap: i64) -> i64 { 106 var a: i64 = at 107 var i: i64 = 0 108 while s[i] != (0 as u8) { 109 var c: i64 = s[i] as i64 110 if c == PV_TAB { c = PV_SPACE } 111 if c == PV_NL { c = PV_SPACE } 112 if c == 13 { c = PV_SPACE } 113 if a < cap - 1 { o[a] = c as u8; a = a + 1 } 114 i = i + 1 115 } 116 return a 117} 118func pv_mkdir_of(jrnl: *u8) -> i64 { 119 let d: *u8 = sys_mmap(PV_PATH_CAP) 120 var last: i64 = 0 - 1 121 var i: i64 = 0 122 while jrnl[i] != (0 as u8) { if (jrnl[i] as i64) == PV_SLASH { last = i } i = i + 1 } 123 if last <= 0 { return 0 } 124 var k: i64 = 0 125 while k < last { d[k] = jrnl[k]; k = k + 1 } 126 d[last] = 0 as u8 127 sys_mkdir(d, PV_DIR_MODE) 128 return 1 129} 130// append one row; returns the bytes written or -1 when the journal cannot be opened for append 131func pv_row_write(jrnl: *u8, sha: *u8, source: *u8, lic: *u8, origin: *u8, epoch: i64) -> i64 { 132 pv_mkdir_of(jrnl) 133 let line: *u8 = sys_mmap(PV_LINE_CAP) 134 var a: i64 = lg_put(line, 0, PV_TAG) 135 line[a] = PV_TAB as u8; a = a + 1 136 a = pv_put_field(line, a, sha, PV_LINE_CAP) 137 line[a] = PV_TAB as u8; a = a + 1 138 a = pv_put_field(line, a, source, PV_LINE_CAP) 139 line[a] = PV_TAB as u8; a = a + 1 140 a = pv_put_field(line, a, lic, PV_LINE_CAP) 141 line[a] = PV_TAB as u8; a = a + 1 142 a = pv_put_field(line, a, origin, PV_LINE_CAP) 143 line[a] = PV_TAB as u8; a = a + 1 144 a = lg_putd(line, a, epoch) 145 line[a] = PV_NL as u8; a = a + 1 146 let fd: i64 = __syscall(PV_SYS_OPENAT, AT_FDCWD, jrnl, PV_OPEN_APPEND, PV_FILE_MODE, 0, 0) 147 if fd < 0 { return 0 - 1 } 148 var done: i64 = 0 149 while done < a { let k: i64 = sys_write(fd, line + done, a - done); if k <= 0 { break } done = done + k } 150 sys_close(fd) 151 return done 152} 153// the LAST row carrying this sha wins. returns 1 found / 0 no row / -1 no journal; box carries buf, line start, line end, rows 154func pv_row_find(jrnl: *u8, sha: *u8, box: *i64) -> i64 { 155 box[PV_BOX_BUF] = 0; box[PV_BOX_S] = 0; box[PV_BOX_E] = 0; box[PV_BOX_ROWS] = 0 156 let lp: *i64 = sys_mmap(16) as *i64 157 let buf: *u8 = sys_read_file(jrnl, lp) 158 if (buf as i64) == 0 { return 0 - 1 } 159 let n: i64 = lp[0] 160 box[PV_BOX_BUF] = buf as i64 161 let fb: *i64 = sys_mmap(32) as *i64 162 var rows: i64 = 0 163 var p: i64 = 0 164 while p < n { 165 var q: i64 = p 166 while q < n { if (buf[q] as i64) == PV_NL { break } q = q + 1 } 167 if q > p { 168 if lg_field(buf, p, q, PV_F_TAG, fb) == 1 { 169 if lg_seq(buf, fb[0], fb[1], PV_TAG) == 1 { 170 if lg_field(buf, p, q, PV_F_SHA, fb) == 1 { 171 if lg_seq(buf, fb[0], fb[1], sha) == 1 { box[PV_BOX_S] = p; box[PV_BOX_E] = q; rows = rows + 1 } 172 } 173 } 174 } 175 } 176 p = q + 1 177 } 178 box[PV_BOX_ROWS] = rows 179 if rows > 0 { return 1 } 180 return 0 181} 182func pv_field_copy(buf: *u8, s: i64, e: i64, idx: i64, out: *u8, cap: i64) -> i64 { 183 let fb: *i64 = sys_mmap(32) as *i64 184 out[0] = 0 as u8 185 if lg_field(buf, s, e, idx, fb) != 1 { return 0 } 186 var n: i64 = fb[1] 187 if n > cap - 1 { n = cap - 1 } 188 var i: i64 = 0 189 while i < n { out[i] = buf[fb[0] + i]; i = i + 1 } 190 out[n] = 0 as u8 191 return n 192} 193// THE VERDICT. res: reason, redist right after the unverified cap, licence row index, private_use (always 1), rows for this sha 194func pv_verdict(jrnl: *u8, sha: *u8, res: *i64) -> i64 { 195 var i: i64 = 0 196 while i < PV_RES_N { res[i] = 0; i = i + 1 } 197 res[PV_RES_PRIVATE] = 1 198 res[PV_RES_LICIDX] = 0 - 1 199 res[PV_RES_REDIST] = LG_NO 200 let ctx: *i64 = lg_ctx() 201 if ctx[3] < 0 { res[PV_RES_REASON] = PV_R_TABLE_UNREADABLE; return LG_RC_REFUSE } 202 let box: *i64 = sys_mmap(8 * PV_BOX_N) as *i64 203 let f: i64 = pv_row_find(jrnl, sha, box) 204 if f < 0 { res[PV_RES_REASON] = PV_R_NO_JOURNAL; return LG_RC_REFUSE } 205 res[PV_RES_ROWS] = box[PV_BOX_ROWS] 206 if f == 0 { res[PV_RES_REASON] = PV_R_NO_ROW; return LG_RC_REFUSE } 207 let licb: *u8 = sys_mmap(PV_LIC_CAP) 208 pv_field_copy(box[PV_BOX_BUF] as *u8, box[PV_BOX_S], box[PV_BOX_E], PV_F_LIC, licb, PV_LIC_CAP) 209 let li: i64 = lg_find_license(ctx, licb) 210 if li < 0 { res[PV_RES_REASON] = PV_R_UNKNOWN_LIC; return LG_RC_REFUSE } 211 res[PV_RES_LICIDX] = li 212 var redist: i64 = lg_lic_redist(ctx, li) 213 var reason: i64 = PV_R_OK 214 if lg_lic_verified(ctx, li) == LG_UNVERIFIED { if redist > LG_COND { redist = LG_COND; reason = PV_R_UNVERIFIED } } 215 res[PV_RES_REDIST] = redist 216 if redist == LG_NO { res[PV_RES_REASON] = PV_R_NON_REDIST; return LG_RC_REFUSE } 217 if redist == LG_COND { if reason == PV_R_OK { reason = PV_R_CONDITIONAL } res[PV_RES_REASON] = reason; return LG_RC_REVIEW } 218 res[PV_RES_REASON] = PV_R_OK 219 return LG_RC_OK 220} 221func pv_verdict_name(rc: i64) -> *u8 { 222 if rc == LG_RC_OK { return "SHIP_OK" as *u8 } 223 if rc == LG_RC_REVIEW { return "REVIEW" as *u8 } 224 return "REFUSE" as *u8 225} 226func pv_reason_name(r: i64) -> *u8 { 227 if r == PV_R_OK { return "OK" as *u8 } 228 if r == PV_R_NO_ROW { return "NO-ROW" as *u8 } 229 if r == PV_R_UNKNOWN_LIC { return "UNKNOWN-LICENCE" as *u8 } 230 if r == PV_R_NON_REDIST { return "NON-REDISTRIBUTABLE" as *u8 } 231 if r == PV_R_CONDITIONAL { return "CONDITIONAL" as *u8 } 232 if r == PV_R_UNVERIFIED { return "UNVERIFIED-LICENCE-CAPPED" as *u8 } 233 if r == PV_R_TABLE_UNREADABLE { return "RIGHTS-TABLE-UNREADABLE" as *u8 } 234 if r == PV_R_NO_JOURNAL { return "NO-JOURNAL" as *u8 } 235 return "UNNAMED" as *u8 236} 237// one line, key=value, the verdict token spelled exactly once so a positional reader and a grep agree 238func pv_print(sha: *u8, rc: i64, res: *i64) -> i64 { 239 pv_puts("ASSET-PROV sha=" as *u8); pv_puts(sha) 240 pv_puts(" verdict=" as *u8); pv_puts(pv_verdict_name(rc)) 241 pv_puts(" reason=" as *u8); pv_puts(pv_reason_name(res[PV_RES_REASON])) 242 pv_puts(" redist=" as *u8); pv_putn(res[PV_RES_REDIST]) 243 pv_puts(" rows=" as *u8); pv_putn(res[PV_RES_ROWS]) 244 pv_puts(" private_use=allowed\n" as *u8) 245 return 0 246}