code wiki / (root) / nx_refcorpus.nx

nx_refcorpus.nx source

↩ module page · 234 lines · 10635 B

1// nx_refcorpus.nx -- THE MEASURED-FACTS PLANE + ITS ADMISSION CONTRACT. 2// 3// WHY THIS EXISTS. The Reciprocal Corpus Program (published 2026-08-04 at 4// /code/research_reciprocal_corpus_program) makes two promises in public: 5// (1) we retain ZERO asset bytes from any reference corpus -- only measurements; 6// (2) we publish only AGGREGATE statistics, never anything reconstructable to one creator. 7// A promise in prose is worth nothing. This organ is where those promises become MECHANICAL: 8// every fact entering the corpus store passes an admission contract that REFUSES anything that 9// could carry asset payload or describe too few sources, and the gate proves the refusals fire. 10// 11// ★THE CORE INSIGHT: a measurement is SMALL AND PLAIN. A vertex count, a texel resolution, a 12// slider mean -- these are short numeric or short-label values. Asset payload (base64 texture, 13// mesh blob, embedded binary) is LONG or NON-PLAIN. So an admission rule of "short + plain + 14// declared sample size" is not a heuristic dressed as a guarantee: it is a structural wall that 15// a payload cannot pass through while every legitimate measurement passes trivially. 16// 17// ROW SHAPE (append-only journal; a journal, not a registry -- ws_sync.jrnl precedent): 18// epoch <TAB> source <TAB> fact_key <TAB> value <TAB> unit <TAB> n <TAB> provenance <TAB> license 19// 20// nx_refcorpus put <source> <fact_key> <value> <unit> <n> <provenance> <license> 21// nx_refcorpus admit <value> <n> -- dry-run the contract, print the verdict 22// nx_refcorpus list [max] 23// 24// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0 25import "nx_syscalls.nx" 26const RC_MAGIC_1024: i64 = 1024 27 28const RC_JRNL: *u8 = "knowledge/status/refcorpus.jrnl" 29// A measurement is short. 64 chars holds any number, any unit, any reasonable label -- and 30// cannot hold a texture, a mesh, or a meaningful fragment of either. 31const RC_MAX_VAL: i64 = 64 32const RC_MAX_FIELD: i64 = 160 33// k-anonymity floor: a DISTRIBUTION fact (one derived from a population of community works) may 34// not be published unless it summarises at least this many independent sources. Below it, a 35// "statistic" can describe an individual's asset -- which is the thing we promised never to do. 36const RC_KMIN: i64 = 8 37const RC_MODE: i64 = 0x1a4 38const RC_EXIT_USAGE: i64 = 2 39const RC_EXIT_REFUSED: i64 = 5 40const RC_EXIT_IO: i64 = 1 41 42func rc_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 43func rc_w(s: *u8) -> i64 { sys_write(1, s, rc_slen(s)); return 0 } 44func rc_e(s: *u8) -> i64 { sys_write(2, s, rc_slen(s)); return 0 } 45func rc_num(v: i64) -> i64 { 46 if v == 0 { sys_write(1, "0" as *u8, 1); return 0 } 47 var m: i64 = v 48 if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m } 49 let t: *u8 = sys_mmap(32) 50 var k: i64 = 0 51 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 52 let o: *u8 = sys_mmap(32) 53 var i: i64 = 0 54 while i < k { o[i] = t[k - 1 - i]; i = i + 1 } 55 sys_write(1, o, k) 56 return 0 57} 58func rc_atoi(s: *u8) -> i64 { 59 var v: i64 = 0 60 var i: i64 = 0 61 var neg: i64 = 0 62 if s[0] == (45 as u8) { neg = 1; i = 1 } 63 while s[i] != (0 as u8) { 64 let c: i64 = (s[i] & 0xff) as i64 65 if c < 48 { return 0 - 1 } 66 if c > 57 { return 0 - 1 } 67 v = v*10 + (c - 48) 68 i = i + 1 69 } 70 if neg == 1 { return 0 - v } 71 return v 72} 73 74// ---- THE ADMISSION CONTRACT ---------------------------------------------------------------- 75// Returns 0 = ADMIT, or a negative REASON code. Every rejection reason is distinct so a caller 76// (and the gate) can assert WHICH wall fired, not merely that something failed. 77// -1 value too long (payload-shaped: a measurement is short) 78// -2 value not plain (bytes outside the printable measurement alphabet) 79// -3 sample size below the k-anonymity floor 80// -4 empty value 81func rc_admit(value: *u8, n: i64) -> i64 { 82 let L: i64 = rc_slen(value) 83 if L == 0 { return 0 - 4 } 84 if L > RC_MAX_VAL { return 0 - 1 } 85 var i: i64 = 0 86 while i < L { 87 let c: i64 = (value[i] & 0xff) as i64 88 var ok: i64 = 0 89 if c >= 48 { if c <= 57 { ok = 1 } } // 0-9 90 if c >= 65 { if c <= 90 { ok = 1 } } // A-Z 91 if c >= 97 { if c <= 122 { ok = 1 } } // a-z 92 if c == 46 { ok = 1 } // . 93 if c == 44 { ok = 1 } // , 94 if c == 45 { ok = 1 } // - 95 if c == 95 { ok = 1 } // _ 96 if c == 47 { ok = 1 } // / 97 if c == 58 { ok = 1 } // : 98 if c == 32 { ok = 1 } // space 99 if c == 37 { ok = 1 } // % 100 if ok == 0 { return 0 - 2 } 101 i = i + 1 102 } 103 if n < RC_KMIN { return 0 - 3 } 104 return 0 105} 106func rc_reason(code: i64) -> i64 { 107 if code == 0 - 1 { rc_e("REFUSED value exceeds the measurement length wall -- a fact is a number or a short label, never a payload\n" as *u8) } 108 if code == 0 - 2 { rc_e("REFUSED value carries bytes outside the plain measurement alphabet -- binary/encoded payload cannot enter\n" as *u8) } 109 if code == 0 - 3 { rc_e("REFUSED sample size below the k-anonymity floor -- a statistic over too few sources can describe one creator's asset\n" as *u8) } 110 if code == 0 - 4 { rc_e("REFUSED empty value\n" as *u8) } 111 return 0 112} 113 114func rc_apps(buf: *u8, p: i64, s: *u8) -> i64 { 115 var k: i64 = 0 116 var q: i64 = p 117 while s[k] != (0 as u8) { buf[q] = s[k]; q = q + 1; k = k + 1 } 118 return q 119} 120func rc_appn(buf: *u8, p: i64, v: i64) -> i64 { 121 var q: i64 = p 122 if v == 0 { buf[q] = 48 as u8; return q + 1 } 123 var m: i64 = v 124 if m < 0 { buf[q] = 45 as u8; q = q + 1; m = 0 - m } 125 let t: *u8 = sys_mmap(32) 126 var k: i64 = 0 127 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 128 var i: i64 = 0 129 while i < k { buf[q] = t[k - 1 - i]; q = q + 1; i = i + 1 } 130 return q 131} 132 133// The whole put path -- admission wall, field bounds, journal append, receipt -- as ONE callable 134// so composing organs (nx_mediafacts sweep mode) reach the SAME wall in-process instead of 135// duplicating the append or shelling out. Factored 2026-08-04, additive: the CLI behavior below is 136// byte-identical. Returns 0 written, RC_EXIT_REFUSED admission/bounds, RC_EXIT_IO journal failure; 137// prints the same receipts/refusals the CLI always printed. 138func rc_put_row(src: *u8, key: *u8, val: *u8, unit: *u8, n: i64, prov: *u8, lic: *u8) -> i64 { 139 // the wall: EVERY value passes the contract before a byte reaches the journal 140 let rc: i64 = rc_admit(val, n) 141 if rc != 0 { 142 rc_reason(rc) 143 rc_e("REFCORPUS-REFUSED nothing written -- the journal never holds a row that failed admission\n" as *u8) 144 return RC_EXIT_REFUSED 145 } 146 // the other fields are bounded too: a provenance URL or licence tag is short by nature, and 147 // an unbounded field is a payload channel wearing a different name. 148 if rc_slen(src) > RC_MAX_FIELD { rc_e("REFUSED source field too long\n" as *u8); return RC_EXIT_REFUSED } 149 if rc_slen(key) > RC_MAX_FIELD { rc_e("REFUSED fact_key too long\n" as *u8); return RC_EXIT_REFUSED } 150 if rc_slen(prov) > RC_MAX_FIELD { rc_e("REFUSED provenance too long\n" as *u8); return RC_EXIT_REFUSED } 151 if rc_slen(lic) > RC_MAX_FIELD { rc_e("REFUSED license tag too long\n" as *u8); return RC_EXIT_REFUSED } 152 153 let buf: *u8 = sys_mmap(RC_MAGIC_1024) 154 var p: i64 = 0 155 p = rc_appn(buf, p, sys_now_realtime_sec()) 156 buf[p] = 9 as u8; p = p + 1 157 p = rc_apps(buf, p, src) 158 buf[p] = 9 as u8; p = p + 1 159 p = rc_apps(buf, p, key) 160 buf[p] = 9 as u8; p = p + 1 161 p = rc_apps(buf, p, val) 162 buf[p] = 9 as u8; p = p + 1 163 p = rc_apps(buf, p, unit) 164 buf[p] = 9 as u8; p = p + 1 165 p = rc_appn(buf, p, n) 166 buf[p] = 9 as u8; p = p + 1 167 p = rc_apps(buf, p, prov) 168 buf[p] = 9 as u8; p = p + 1 169 p = rc_apps(buf, p, lic) 170 buf[p] = 10 as u8; p = p + 1 171 172 let fd: i64 = sys_openat_append(RC_JRNL, RC_MODE) 173 if fd < 0 { rc_e("REFCORPUS-RED cannot open journal for append\n" as *u8); return RC_EXIT_IO } 174 var w: i64 = 0 175 while w < p { 176 let k2: i64 = sys_write(fd, ((buf as i64) + w) as *u8, p - w) 177 if k2 <= 0 { sys_close(fd); rc_e("REFCORPUS-RED short write\n" as *u8); return RC_EXIT_IO } 178 w = w + k2 179 } 180 sys_close(fd) 181 rc_w("REFCORPUS-PUT " as *u8); rc_w(src); rc_w(" " as *u8); rc_w(key); rc_w("=" as *u8); rc_w(val) 182 rc_w(" (n=" as *u8); rc_num(n); rc_w(", " as *u8); rc_w(lic); rc_w(")\n" as *u8) 183 return 0 184} 185 186func main(argc: i64, argv: *i64) -> i64 { 187 if argc < 2 { 188 rc_e("usage: nx_refcorpus put <source> <fact_key> <value> <unit> <n> <provenance> <license>\n" as *u8) 189 rc_e(" nx_refcorpus admit <value> <n> -- dry-run the admission contract\n" as *u8) 190 rc_e(" nx_refcorpus list [max]\n" as *u8) 191 rc_e(" Facts only: short, plain, aggregate. The contract REFUSES asset payload by construction.\n" as *u8) 192 sys_exit(RC_EXIT_USAGE) 193 return RC_EXIT_USAGE 194 } 195 let verb: *u8 = argv[1] as *u8 196 197 if verb[0] == (97 as u8) { // admit 198 if argc < 4 { rc_e("admit needs <value> <n>\n" as *u8); sys_exit(RC_EXIT_USAGE); return RC_EXIT_USAGE } 199 let v: *u8 = argv[2] as *u8 200 let n: i64 = rc_atoi(argv[3] as *u8) 201 let rc: i64 = rc_admit(v, n) 202 if rc == 0 { rc_w("ADMIT ok len=" as *u8); rc_num(rc_slen(v)); rc_w(" n=" as *u8); rc_num(n); rc_w("\n" as *u8); sys_exit(0); return 0 } 203 rc_reason(rc) 204 sys_exit(RC_EXIT_REFUSED) 205 return RC_EXIT_REFUSED 206 } 207 208 if verb[0] == (108 as u8) { // list 209 let lp: *i64 = sys_mmap(16) as *i64 210 let b: *u8 = sys_read_file(RC_JRNL, lp) 211 if (b as i64) == 0 { rc_w("REFCORPUS empty (no journal yet)\n" as *u8); sys_exit(0); return 0 } 212 sys_write(1, b, lp[0]) 213 sys_exit(0) 214 return 0 215 } 216 217 // put 218 if argc < 9 { 219 rc_e("put needs <source> <fact_key> <value> <unit> <n> <provenance> <license>\n" as *u8) 220 sys_exit(RC_EXIT_USAGE) 221 return RC_EXIT_USAGE 222 } 223 let src: *u8 = argv[2] as *u8 224 let key: *u8 = argv[3] as *u8 225 let val: *u8 = argv[4] as *u8 226 let unit: *u8 = argv[5] as *u8 227 let n: i64 = rc_atoi(argv[6] as *u8) 228 let prov: *u8 = argv[7] as *u8 229 let lic: *u8 = argv[8] as *u8 230 231 let rc2: i64 = rc_put_row(src, key, val, unit, n, prov, lic) 232 sys_exit(rc2) 233 return rc2 234}