nx_toolreg_reap.nx source
↩ module page · 262 lines · 12681 B
1// nx_toolreg_reap.nx -- REMOVE named scaffold rows from tool_allowlist.conf. The counterpart the registry
2// never had: nx_toolreg_reconcile only ever ADDS (rows=830 registered=0 already=815), /api has a
3// tools/register route and NO unregister, and nx_fs_write refuses the allowlist by construction -- correctly,
4// since a tool that can EDIT the capability allowlist can GRANT ITSELF ANYTHING. So scaffold registrations
5// accumulated forever with no sanctioned way to clear them (debt 1785976684).
6//
7// WHY THIS IS SAFE TO EXIST AT ALL, and why it is a separate organ rather than a verb on the registrar:
8// this organ can ONLY REMOVE, never add and never rewrite a target. Removal is strictly DE-privileging --
9// the worst case is a tool stops being callable, which is a recoverable annoyance, whereas an organ that
10// could add or repoint a row would be a privilege-granting oracle. Single responsibility is the security
11// property here, not a style preference.
12//
13// FAIL-CLOSED PREDICATE: a row is reapable ONLY if its elf path ends in ".new" -- the staged-artifact shape
14// that scaffold aliases have. A row pointing at a promoted ".elf" is REFUSED and reported, so a fat-fingered
15// name can never unregister a live tool. Names are EXPLICIT: there is no wildcard sweep, because rows like
16// nx_meshmerge_staged and nx_ccingest_r7 also point at .new and other seats depend on them.
17// nx_toolreg_reap <name> [<name>...] | nx_toolreg_reap --list (show reapable rows, change nothing)
18//
19// ---- THE SUPERSEDED LANE (2026-08-15) ---------------------------------------------------------------
20// nx_toolreg_reap --superseded <name> <superseded-by>
21// WHY IT WAS ADDED: a rename can leave a PROMOTED duplicate registered (measured: nx_promotestale and
22// nx_servedrift, both superseded by nx_artifactdrift, three registry rows for one capability). The add
23// lane exists (/api/tools/register, nx_toolreg_reconcile), the scaffold-reap lane exists, and there was
24// NO sovereign path to unregister a promoted duplicate -- so duplicates could only accumulate.
25// WHY IT DOES NOT WEAKEN THE DEFAULT: the bare-name lane still refuses every promoted row, unchanged.
26// This lane is opt-in, takes exactly ONE name, has no wildcard, and REFUSES unless the caller also names
27// a DIFFERENT row that is present AND promoted -- so the capability is proven to still have a home. The
28// original property survives: a fat-fingered single name can never unregister a live tool, because one
29// name is no longer enough. And removal stays strictly de-privileging, which is this organ's whole
30// security argument.
31// ⚠THE CEILING, STATED WITH THE REMEDY: this proves a promoted alternative EXISTS, never that it is
32// semantically equivalent. That last step is the caller's judgement and the organ says so rather than
33// implying it checked. ★★★★★A GUARD THAT CANNOT VERIFY THE CLAIM IT GATES MUST NAME WHAT IT DID VERIFY,
34// OR THE NEXT READER CREDITS IT WITH THE STRONGER CHECK.
35// license_tier: ORIGINAL expect_exit: 0
36import "nx_syscalls.nx"
37
38const TR_CONF: *u8 = "tool_allowlist.conf"
39const TR_TMP: *u8 = "tool_allowlist.conf.reaptmp"
40const TR_CAP: i64 = 1048576
41const TR_MODE: i64 = 0x1a4
42const TR_MAXNAME: i64 = 128
43
44func tr_w(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
45func tr_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
46func tr_wn(v: i64) -> i64 {
47 let b: *u8=sys_mmap(32); var x: i64=v; var i: i64=31
48 if x==0 { b[i]=48 as u8; i=i-1 }
49 while x>0 { b[i]=(48+x%10) as u8; x=x/10; i=i-1 }
50 sys_write(1, ((b as i64)+i+1) as *u8, 31-i); return 0
51}
52// does the row that starts at ls have EXACTLY this name in field 0? (exact, never a prefix:
53// nx_csgate_r1 must not match nx_csgate_r11)
54func tr_name_is(buf: *u8, ls: i64, le: i64, nm: *u8) -> i64 {
55 let n: i64 = tr_slen(nm)
56 if ls+n > le { return 0 }
57 var k: i64 = 0
58 while k<n { if buf[ls+k]!=nm[k] { return 0 } k=k+1 }
59 if ls+n == le { return 0 } // name with no fields = malformed, leave it alone
60 if buf[ls+n] != (9 as u8) { return 0 } // must be followed by TAB = end of field 0
61 return 1
62}
63// is this row's elf field a STAGED artifact (ends .new)? that is the only reapable shape.
64func tr_row_staged(buf: *u8, ls: i64, le: i64) -> i64 {
65 // field 1 runs from the first TAB to the second TAB (or end of line)
66 var t1: i64 = 0-1
67 var j: i64 = ls
68 while j<le { if buf[j]==(9 as u8) { t1=j; j=le } else { j=j+1 } }
69 if t1<0 { return 0 }
70 var t2: i64 = le
71 var k: i64 = t1+1
72 while k<le { if buf[k]==(9 as u8) { t2=k; k=le } else { k=k+1 } }
73 if t2-t1 < 5 { return 0 }
74 // compare the last 4 bytes of field 1 against ".new"
75 if buf[t2-4]!=(46 as u8) { return 0 }
76 if buf[t2-3]!=(110 as u8) { return 0 }
77 if buf[t2-2]!=(101 as u8) { return 0 }
78 if buf[t2-1]!=(119 as u8) { return 0 }
79 return 1
80}
81func tr_emit_field1(buf: *u8, ls: i64, le: i64) -> i64 {
82 var t1: i64 = 0-1
83 var j: i64 = ls
84 while j<le { if buf[j]==(9 as u8) { t1=j; j=le } else { j=j+1 } }
85 if t1<0 { return 0 }
86 var t2: i64 = le
87 var k: i64 = t1+1
88 while k<le { if buf[k]==(9 as u8) { t2=k; k=le } else { k=k+1 } }
89 sys_write(1, ((buf as i64)+t1+1) as *u8, t2-t1-1)
90 return 0
91}
92func tr_eq(a: *u8, b: *u8) -> i64 {
93 let la: i64 = tr_slen(a)
94 let lb: i64 = tr_slen(b)
95 if la != lb { return 0 }
96 var i: i64 = 0
97 while i < la { if a[i] != b[i] { return 0 } i = i + 1 }
98 return 1
99}
100// 0 = no such row | 1 = present and STAGED (.new) | 2 = present and PROMOTED
101// ★THREE STATES, NOT TWO: "absent" and "present but staged" are different refusals for the survivor
102// argument below, and collapsing them would report a typo'd name as a policy violation.
103func tr_find(buf: *u8, n: i64, nm: *u8) -> i64 {
104 var i: i64 = 0
105 while i < n {
106 let ls: i64 = i
107 var le: i64 = ls
108 var s: i64 = 1
109 while s == 1 { if le >= n { s = 0 } else { if buf[le] == (10 as u8) { s = 0 } else { le = le + 1 } } }
110 i = le + 1
111 if le > ls {
112 if tr_name_is(buf, ls, le, nm) == 1 {
113 if tr_row_staged(buf, ls, le) == 1 { return 1 }
114 return 2
115 }
116 }
117 }
118 return 0
119}
120
121func main(argc: i64, argv: *i64) -> i64 {
122 if argc < 2 {
123 tr_w("usage: nx_toolreg_reap <name> [<name>...] | --list | --superseded <name> <superseded-by>\n" as *u8)
124 tr_w(" removes named rows from tool_allowlist.conf. ONLY rows whose elf ends .new are reapable;\n" as *u8)
125 tr_w(" a row pointing at a promoted .elf is REFUSED so a live tool can never be unregistered.\n" as *u8)
126 tr_w(" --superseded removes ONE promoted row, and only when a DIFFERENT promoted row is named as\n" as *u8)
127 tr_w(" the surviving home of the capability -- for duplicates left behind by a rename.\n" as *u8)
128 return 0
129 }
130 let buf: *u8 = sys_mmap(TR_CAP)
131 let fd: i64 = sys_openat_rd(TR_CONF)
132 if fd < 0 { tr_w("REAP-FAIL cannot read tool_allowlist.conf\n" as *u8); sys_exit(1); return 1 }
133 var n: i64 = 0
134 var go: i64 = 1
135 while go==1 {
136 let r: i64 = sys_read(fd, ((buf as i64)+n) as *u8, TR_CAP-n)
137 if r<=0 { go=0 } else { n=n+r }
138 if n>=TR_CAP { go=0 }
139 }
140 sys_close(fd)
141 if n<=0 { tr_w("REAP-FAIL empty allowlist -- refusing to write\n" as *u8); sys_exit(1); return 1 }
142
143 var listonly: i64 = 0
144 var supmode: i64 = 0
145 var supname: *u8 = 0 as *u8
146 var supby: *u8 = 0 as *u8
147 let a1: *u8 = argv[1] as *u8
148 if tr_eq(a1, "--superseded" as *u8) == 1 { supmode = 1 } else {
149 if a1[0]==(45 as u8) { listonly = 1 }
150 }
151 if supmode == 1 {
152 if argc < 4 {
153 tr_w("usage: nx_toolreg_reap --superseded <name> <superseded-by>\n" as *u8)
154 tr_w(" removes ONE promoted row, and only when a DIFFERENT promoted row is named as the\n" as *u8)
155 tr_w(" surviving home of the capability. Both names must already be in the registry.\n" as *u8)
156 sys_exit(2); return 2
157 }
158 supname = argv[2] as *u8
159 supby = argv[3] as *u8
160 if tr_eq(supname, supby) == 1 {
161 tr_w("REAP-REFUSED -- a row cannot supersede itself; name the OTHER tool that keeps the capability\n" as *u8)
162 sys_exit(1); return 1
163 }
164 let fs: i64 = tr_find(buf, n, supname)
165 if fs == 0 {
166 tr_w("REAP-REFUSED -- no such row: " as *u8); tr_w(supname)
167 tr_w(" (nothing removed; a typo must never silently match a neighbour)\n" as *u8)
168 sys_exit(1); return 1
169 }
170 let fb: i64 = tr_find(buf, n, supby)
171 if fb == 0 {
172 tr_w("REAP-REFUSED -- the named survivor is not registered: " as *u8); tr_w(supby)
173 tr_w(" -- removing " as *u8); tr_w(supname)
174 tr_w(" would delete the capability, not relocate it\n" as *u8)
175 sys_exit(1); return 1
176 }
177 if fb == 1 {
178 tr_w("REAP-REFUSED -- the named survivor " as *u8); tr_w(supby)
179 tr_w(" is itself a STAGED .new row, which promotion deletes; it cannot be the durable home\n" as *u8)
180 sys_exit(1); return 1
181 }
182 }
183
184 let outb: *u8 = sys_mmap(TR_CAP)
185 var o: i64 = 0
186 var rows: i64 = 0
187 var reaped: i64 = 0
188 var refused: i64 = 0
189 var i: i64 = 0
190 while i<n {
191 let ls: i64 = i
192 var le: i64 = ls
193 var s: i64 = 1
194 while s==1 { if le>=n { s=0 } else { if buf[le]==(10 as u8) { s=0 } else { le=le+1 } } }
195 i = le+1
196 var drop: i64 = 0
197 if le>ls {
198 rows = rows + 1
199 if listonly==1 {
200 if tr_row_staged(buf, ls, le)==1 {
201 tr_w(" reapable " as *u8); sys_write(1, ((buf as i64)+ls) as *u8, le-ls); tr_w("\n" as *u8)
202 }
203 } else {
204 if supmode==1 {
205 if tr_name_is(buf, ls, le, supname)==1 {
206 drop = 1
207 reaped = reaped + 1
208 tr_w(" REAPED-SUPERSEDED " as *u8); tr_w(supname)
209 tr_w(" -> " as *u8); tr_emit_field1(buf, ls, le)
210 tr_w(" (capability remains registered as " as *u8); tr_w(supby); tr_w(")\n" as *u8)
211 }
212 } else {
213 var ai: i64 = 1
214 while ai<argc {
215 let want: *u8 = argv[ai] as *u8
216 if tr_name_is(buf, ls, le, want)==1 {
217 if tr_row_staged(buf, ls, le)==1 {
218 drop = 1
219 reaped = reaped + 1
220 tr_w(" REAPED " as *u8); tr_w(want); tr_w(" -> " as *u8); tr_emit_field1(buf, ls, le); tr_w("\n" as *u8)
221 } else {
222 refused = refused + 1
223 tr_w(" REFUSED " as *u8); tr_w(want)
224 tr_w(" -- elf is NOT a staged .new artifact (" as *u8); tr_emit_field1(buf, ls, le)
225 tr_w("); a promoted tool is never unregistered by this organ\n" as *u8)
226 }
227 ai = argc
228 } else { ai = ai + 1 }
229 }
230 }
231 }
232 }
233 if drop==0 {
234 var c: i64 = ls
235 while c<le { outb[o]=buf[c]; o=o+1; c=c+1 }
236 if le<n { outb[o]=10 as u8; o=o+1 }
237 }
238 }
239 if listonly==1 {
240 tr_w("NX-TOOLREG-REAP --list rows=" as *u8); tr_wn(rows); tr_w(" (nothing written)\n" as *u8)
241 return 0
242 }
243 if reaped==0 {
244 tr_w("NX-TOOLREG-REAP rows=" as *u8); tr_wn(rows)
245 tr_w(" reaped=0 refused=" as *u8); tr_wn(refused)
246 tr_w(" -- NOTHING WRITTEN (a no-op must not rewrite the file other seats are reading)\n" as *u8)
247 return 0
248 }
249 // atomic install: write the tmp in full, then rename over the live conf. A partial write can never be
250 // observed as the allowlist, so a crash mid-reap cannot leave the estate with an unreadable registry.
251 let wfd: i64 = sys_openat_wr(TR_TMP, TR_MODE)
252 if wfd < 0 { tr_w("REAP-FAIL cannot open tmp -- LIVE CONF UNTOUCHED\n" as *u8); sys_exit(1); return 1 }
253 sys_write(wfd, outb, o)
254 sys_close(wfd)
255 if sys_renameat(TR_TMP, TR_CONF) < 0 { tr_w("REAP-FAIL rename -- LIVE CONF UNTOUCHED\n" as *u8); sys_exit(1); return 1 }
256 tr_w("NX-TOOLREG-REAP rows_in=" as *u8); tr_wn(rows)
257 tr_w(" reaped=" as *u8); tr_wn(reaped)
258 tr_w(" refused=" as *u8); tr_wn(refused)
259 tr_w(" bytes_out=" as *u8); tr_wn(o)
260 tr_w("\n" as *u8)
261 return 0
262}