nx_extllm_lib.nx source
↩ module page · 292 lines · 13085 B
1// nx_extllm_lib.nx -- THE EGRESS DECISION LIBRARY: may THESE BYTES leave, to THIS PROVIDER, for THIS USE?
2// The CODE half of two conf files authored 2026-08-24 that have had no reader since (measured 2026-08-25:
3// xl_egress matches=0 over 61,475 files, coverage_complete=1 corpus_complete=1).
4// xl_class_of -- CLASSIFY AT THE DOOR over the operator's release manifest. C by default; U only on an
5// explicit row whose sha256 still matches the file. A STALE row admits NOTHING.
6// xl_use_verdict -- the provider terms ledger as data. UNKNOWN is a VALUE and REFUSES every gated use.
7// xl_budget_* -- CUMULATIVE disclosure per provider. Splitting work into parts defeats a SINGLE call
8// and NOT aggregation across calls: a provider holding a perpetual licence retains every
9// fragment and reassembles at leisure. The bound is on the SUM, never on the call.
10// WHY A NEW LIB RATHER THAN EXTENDING AN INCUMBENT (checked before building, 2026-08-25): the estate's three
11// shipping classifiers cannot answer an OBJECT-side release question. ag_uid_to_level grades a READER (a 0..3
12// RBAC ladder, never a byte's sensitivity); nx_site_lock_lib slk_resolve returns 0 for an unmatched path and
13// ag_allow turns that into ALLOW for everyone ("no match => PUBLIC"); the docportal consent bit is OPT-OUT
14// (keep initialises to 1, so an undeclared document is SERVED). Two of the three default to release, so this
15// decision must fail closed INDEPENDENTLY of all of them rather than compose one.
16// Verdicts are exit-code shaped so a caller can branch without parsing. license_tier: ORIGINAL. No hw writes (Rule 26).
17import "nx_syscalls.nx"
18import "nx_sovjson_lib.nx"
19
20// ---- the three states. UNKNOWN is NOT allow: an axis that cannot see must abstain, never acquit. ----
21const XL_ALLOW: i64 = 0
22const XL_REFUSE: i64 = 1
23const XL_UNKNOWN: i64 = 3
24
25// ---- classification (no-write-down: a provider is a sink cleared to U) ----
26const XL_CLASS_U: i64 = 0
27const XL_CLASS_C: i64 = 1
28
29// ---- use classes, as spelled in the allowed_uses column ----
30const XL_USE_WORKER: i64 = 1
31const XL_USE_JUDGE: i64 = 2
32const XL_USE_DISTILL: i64 = 3
33
34// ---- bytes we must construct rather than spell: the nx_cc lexer forbids these inside a string literal ----
35const XL_BYTE_HASH: i64 = 35
36const XL_BYTE_PIPE: i64 = 124
37const XL_BYTE_NL: i64 = 10
38
39// ---- extllm_terms.conf: 12 pipe-separated columns, declared in the file's own header ----
40const XL_TC_PROVIDER: i64 = 0
41const XL_TC_TIER: i64 = 1
42const XL_TC_TRAINS_IN: i64 = 2
43const XL_TC_OUT_OURS: i64 = 3
44const XL_TC_PUBRESTRICT: i64 = 4
45const XL_TC_USES: i64 = 9
46const XL_TC_REFKEY: i64 = 10
47const XL_TC_PIN: i64 = 11
48const XL_TC_COLS: i64 = 12
49
50// ---- extllm_release.conf: release|<path>|<sha256>|<declared by>|<date>|<why> ----
51const XL_RC_PATH: i64 = 1
52const XL_RC_SHA: i64 = 2
53const XL_RC_COLS: i64 = 6
54
55// A pin is the sha256 of the mirrored contract, spelled h then 64 hex. The terms conf's own header requires
56// a pin check, so a row that was never pinned to a mirror ADMITS NOTHING: PIN-PENDING is a refusal, not a
57// placeholder that quietly passes. We do not get to assert a provider's posture we have not pinned to evidence.
58const XL_PIN_MIN: i64 = 65
59const XL_BYTE_H: i64 = 104
60
61// A column span is TWO i64 slots (start, end), so its scratch is 2 x 8 bytes. Named for that PURPOSE,
62// not for its value: nx_magic would hoist this to XL_MAGIC_16, and a constant named after its own value
63// is still a magic number -- it has only moved. The tool says so itself: its generated names are
64// mechanical and the rename to a domain term is the owner judgment rule 11 actually asks for.
65const XL_SPAN_SLOTS: i64 = 2
66const XL_SPAN_BYTES: i64 = 16
67
68func xl_terms_path() -> *u8 { return "knowledge/extllm_terms.conf" as *u8 }
69func xl_release_path() -> *u8 { return "knowledge/extllm_release.conf" as *u8 }
70
71// A conf comment. Spelled as a byte because the lexer will not carry the character in a literal.
72func xl_is_comment(q: *u8, ls: i64, le: i64) -> i64 {
73 if ls >= le { return 1 }
74 if (q[ls] as i64) == XL_BYTE_HASH { return 1 }
75 return 0
76}
77
78// Column c of a PIPE-separated row. sj_col is TAB-only by construction, so the separator travels here.
79func xl_col(q: *u8, ls: i64, le: i64, c: i64, out: *i64) -> i64 {
80 var col: i64 = 0
81 var p: i64 = ls
82 while col < c {
83 var s: i64 = 1
84 while s == 1 { if p >= le { return 0 } if (q[p] as i64) == XL_BYTE_PIPE { s = 0 } else { p = p + 1 } }
85 p = p + 1
86 col = col + 1
87 }
88 var e: i64 = p
89 var s2: i64 = 1
90 while s2 == 1 { if e >= le { s2 = 0 } else { if (q[e] as i64) == XL_BYTE_PIPE { s2 = 0 } else { e = e + 1 } } }
91 out[0] = p
92 out[1] = e
93 return 1
94}
95
96// How many columns does this row actually have? A row of the wrong width is MALFORMED, never silently short.
97func xl_ncols(q: *u8, ls: i64, le: i64) -> i64 {
98 var n: i64 = 1
99 var i: i64 = ls
100 while i < le { if (q[i] as i64) == XL_BYTE_PIPE { n = n + 1 } i = i + 1 }
101 return n
102}
103
104// The literal spelled in the allowed_uses column for a use class.
105func xl_use_name(use: i64) -> *u8 {
106 if use == XL_USE_WORKER { return "WORKER" as *u8 }
107 if use == XL_USE_JUDGE { return "JUDGE" as *u8 }
108 if use == XL_USE_DISTILL { return "DISTILL" as *u8 }
109 return "" as *u8
110}
111
112func xl_verdict_name(v: i64) -> *u8 {
113 if v == XL_ALLOW { return "ALLOW" as *u8 }
114 if v == XL_REFUSE { return "REFUSE" as *u8 }
115 return "UNKNOWN-REFUSED" as *u8
116}
117
118func xl_class_name(c: i64) -> *u8 {
119 if c == XL_CLASS_U { return "U" as *u8 }
120 return "C" as *u8
121}
122
123// Find the terms row for provider+tier. Returns 1 and writes row start/end into out, else 0.
124func xl_row_find(q: *u8, n: i64, provider: *u8, tier: *u8, out: *i64) -> i64 {
125 let c: *i64 = sys_mmap(XL_SPAN_BYTES) as *i64
126 var i: i64 = 0
127 while i < n {
128 let le: i64 = sj_le(q, i, n)
129 if xl_is_comment(q, i, le) == 0 {
130 if xl_col(q, i, le, XL_TC_PROVIDER, c) == 1 {
131 if sj_lit_eq(q, c[0], c[1], provider) == 1 {
132 if xl_col(q, i, le, XL_TC_TIER, c) == 1 {
133 if sj_lit_eq(q, c[0], c[1], tier) == 1 {
134 out[0] = i
135 out[1] = le
136 return 1
137 }
138 }
139 }
140 }
141 }
142 i = le + 1
143 }
144 return 0
145}
146
147// Is a terms cell exactly this token? Used for YES / NO / CONDITIONAL / UNKNOWN.
148func xl_cell_is(q: *u8, ls: i64, le: i64, col: i64, lit: *u8) -> i64 {
149 let c: *i64 = sys_mmap(XL_SPAN_BYTES) as *i64
150 if xl_col(q, ls, le, col, c) == 0 { return 0 }
151 return sj_lit_eq(q, c[0], c[1], lit)
152}
153
154// Is this use listed in allowed_uses? An empty list lists nothing (github/models, retired).
155func xl_use_listed(q: *u8, ls: i64, le: i64, use: i64) -> i64 {
156 let c: *i64 = sys_mmap(XL_SPAN_BYTES) as *i64
157 if xl_col(q, ls, le, XL_TC_USES, c) == 0 { return 0 }
158 if c[0] >= c[1] { return 0 }
159 return sj_span_has(q, c[0], c[1], xl_use_name(use))
160}
161
162func xl_pin_ok(q: *u8, ls: i64, le: i64) -> i64 {
163 let c: *i64 = sys_mmap(XL_SPAN_BYTES) as *i64
164 if xl_col(q, ls, le, XL_TC_PIN, c) == 0 { return 0 }
165 if c[1] - c[0] < XL_PIN_MIN { return 0 }
166 if (q[c[0]] as i64) != XL_BYTE_H { return 0 }
167 return 1
168}
169
170// THE TERMS DECISION. Every refusal is attributable to exactly one named conjunct, so a caller is never
171// told "no" without being told which rule said it.
172// 0 row absent -> UNKNOWN (a provider with no mirrored row must never read ALLOW)
173// 1 row malformed -> UNKNOWN
174// 2 use not listed -> REFUSE
175// 3 DISTILL demands outputs_train_ours=YES; anything else, including UNKNOWN, REFUSES
176func xl_use_verdict(q: *u8, n: i64, provider: *u8, tier: *u8, use: i64) -> i64 {
177 let r: *i64 = sys_mmap(XL_SPAN_BYTES) as *i64
178 if xl_row_find(q, n, provider, tier, r) == 0 { return XL_UNKNOWN }
179 if xl_ncols(q, r[0], r[1]) != XL_TC_COLS { return XL_UNKNOWN }
180 if xl_pin_ok(q, r[0], r[1]) == 0 { return XL_UNKNOWN }
181 if xl_use_listed(q, r[0], r[1], use) == 0 { return XL_REFUSE }
182 if use == XL_USE_DISTILL {
183 if xl_cell_is(q, r[0], r[1], XL_TC_OUT_OURS, "YES" as *u8) == 0 { return XL_REFUSE }
184 }
185 return XL_ALLOW
186}
187
188// Does this provider train on what we send it? Reported separately from the verdict because it does not
189// refuse a WORKER use -- it decides whether a fragment sent is a fragment donated permanently.
190func xl_trains_on_input(q: *u8, n: i64, provider: *u8, tier: *u8) -> i64 {
191 let r: *i64 = sys_mmap(XL_SPAN_BYTES) as *i64
192 if xl_row_find(q, n, provider, tier, r) == 0 { return XL_UNKNOWN }
193 if xl_cell_is(q, r[0], r[1], XL_TC_TRAINS_IN, "NO" as *u8) == 1 { return XL_ALLOW }
194 if xl_cell_is(q, r[0], r[1], XL_TC_TRAINS_IN, "YES" as *u8) == 1 { return XL_REFUSE }
195 return XL_UNKNOWN
196}
197
198// ---- CLASSIFY AT THE DOOR -------------------------------------------------------------------------
199// A path is U only when the operator's manifest carries a row for it AND the declared sha256 still equals
200// the file's bytes at call time. Absent row -> C. Stale row -> C, which is the whole point of the hash:
201// a file that changed since it was declared has NOT been declared.
202func xl_release_row(q: *u8, n: i64, path: *u8, out: *i64) -> i64 {
203 let c: *i64 = sys_mmap(XL_SPAN_BYTES) as *i64
204 var i: i64 = 0
205 while i < n {
206 let le: i64 = sj_le(q, i, n)
207 if xl_is_comment(q, i, le) == 0 {
208 if xl_ncols(q, i, le) == XL_RC_COLS {
209 if xl_col(q, i, le, XL_RC_PATH, c) == 1 {
210 if sj_lit_eq(q, c[0], c[1], path) == 1 { out[0] = i; out[1] = le; return 1 }
211 }
212 }
213 }
214 i = le + 1
215 }
216 return 0
217}
218
219// actual_sha is supplied by the caller (nx_filehash of the file at call time), never trusted from the row.
220func xl_class_of(q: *u8, n: i64, path: *u8, actual_sha: *u8) -> i64 {
221 let r: *i64 = sys_mmap(XL_SPAN_BYTES) as *i64
222 if xl_release_row(q, n, path, r) == 0 { return XL_CLASS_C }
223 let c: *i64 = sys_mmap(XL_SPAN_BYTES) as *i64
224 if xl_col(q, r[0], r[1], XL_RC_SHA, c) == 0 { return XL_CLASS_C }
225 if sj_lit_eq(q, c[0], c[1], actual_sha) == 0 { return XL_CLASS_C }
226 return XL_CLASS_U
227}
228
229// How many rows has the operator actually declared? Zero is the correct answer today and the caller must
230// be able to SEE that, because a manifest with no rows admits nothing and that is not a malfunction.
231func xl_release_rows(q: *u8, n: i64) -> i64 {
232 var rows: i64 = 0
233 var i: i64 = 0
234 while i < n {
235 let le: i64 = sj_le(q, i, n)
236 if xl_is_comment(q, i, le) == 0 { if xl_ncols(q, i, le) == XL_RC_COLS { rows = rows + 1 } }
237 i = le + 1
238 }
239 return rows
240}
241
242// ---- CUMULATIVE DISCLOSURE ------------------------------------------------------------------------
243// The ledger row is: <unix>|<provider>|<tier>|<subsystem>|<bytes>|<class>|<prompt-sha>|<verdict>
244const XL_LC_PROVIDER: i64 = 1
245const XL_LC_SUBSYSTEM: i64 = 3
246const XL_LC_BYTES: i64 = 4
247const XL_LC_COLS: i64 = 8
248
249// Total bytes this provider has EVER been shown, optionally narrowed to one subsystem. This is the number
250// a per-call check structurally cannot compute, and it is the only number that bounds reassembly.
251func xl_spent(q: *u8, n: i64, provider: *u8, subsystem: *u8, all_subsystems: i64) -> i64 {
252 let c: *i64 = sys_mmap(XL_SPAN_BYTES) as *i64
253 var tot: i64 = 0
254 var i: i64 = 0
255 while i < n {
256 let le: i64 = sj_le(q, i, n)
257 if xl_is_comment(q, i, le) == 0 {
258 if xl_ncols(q, i, le) == XL_LC_COLS {
259 if xl_col(q, i, le, XL_LC_PROVIDER, c) == 1 {
260 if sj_lit_eq(q, c[0], c[1], provider) == 1 {
261 var take: i64 = all_subsystems
262 if take == 0 {
263 if xl_col(q, i, le, XL_LC_SUBSYSTEM, c) == 1 {
264 if sj_lit_eq(q, c[0], c[1], subsystem) == 1 { take = 1 }
265 }
266 }
267 if take == 1 {
268 if xl_col(q, i, le, XL_LC_BYTES, c) == 1 { tot = tot + sj_atoi_span(q, c[0], c[1]) }
269 }
270 }
271 }
272 }
273 }
274 i = le + 1
275 }
276 return tot
277}
278
279// The budget refuses on the SUM, which is what makes fragmenting honest rather than theatre. A bound of 0
280// is UNKNOWN, not unlimited -- an unconfigured budget must not read as permission.
281func xl_budget_verdict(spent: i64, add: i64, bound: i64) -> i64 {
282 if bound <= 0 { return XL_UNKNOWN }
283 if spent + add > bound { return XL_REFUSE }
284 return XL_ALLOW
285}
286
287// Remaining headroom, clamped at 0 so a caller never sees a negative budget and reads it as a large one.
288func xl_budget_left(spent: i64, bound: i64) -> i64 {
289 if bound <= 0 { return 0 }
290 if spent >= bound { return 0 }
291 return bound - spent
292}