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}