code wiki / _hdl_build / nx_quarantine.nx
nx_quarantine.nx source
↩ module page · 265 lines · 12969 B
1// nx_quarantine.nx -- W-TD-3: THE WITHHOLD-WITHOUT-DESTROYING STORE, and the locator resolver.
2//
3// This is where nx_realperson's RP_QUARANTINE finally has somewhere to put something. Until now it
4// was a verdict with no destination (debt 1785893553), which is a promise, not a capability.
5//
6// ★★★★★★WHY MOVE AND NOT DELETE. The operator's correction is the whole design: "for minors it
7// should be flagged and quarantined, as the performer could be 18 and miscategorized." A suspected
8// -age call is a HYPOTHESIS. Deleting on it destroys the very record (2257-style age documentation,
9// the original file, its provenance) that would have EXONERATED a lawful adult -- and it destroys
10// it precisely in the case where we were wrong. So:
11// HOLD = rename into a quarantine prefix. The asset stops being served immediately (safe if
12// the suspicion is right) and survives byte-for-byte (safe if it is wrong).
13// RELEASE = rename back. This path is not a nicety; it is the remedy for the misread performer,
14// and a quarantine with no release is just a slow delete.
15// ⛔THERE IS NO UNLINK IN THIS ORGAN, deliberately. Rule 13: history is sacred. Even the CSAM branch
16// of nx_takedown says PRESERVE+REPORT, never remove-and-forget -- silent deletion is evidence
17// destruction, which is a worse problem than the one it pretends to solve.
18//
19// ★RENAME, NOT COPY+DELETE: atomic, so there is no window where the bytes exist in neither place or
20// in both. A half-finished quarantine is exactly the state you cannot explain to anyone later.
21//
22// verbs:
23// resolve <locator> -- /gen/img/<cid> | blob-<cid>.png | <cid> -> the on-disk asset
24// hold <locator> <notice-id> -- withhold (reversible), append a record
25// release <locator> <notice-id>-- restore, append a record
26// status <locator> -- LIVE | HELD | ABSENT
27// selftest -- hermetic teeth, creates and cleans its own fixtures
28// license_tier: ORIGINAL expect_exit: 0
29// module: nishi-core.hosting.quarantine
30import "nx_syscalls.nx"
31import "nx_estate_path.nx"
32const QT_MAGIC_65536: i64 = 65536
33const QT_MAGIC_1024: i64 = 1024
34
35const QT_GENDIR: *u8 = "/volume1/ai/gen/"
36const QT_QDIR: *u8 = "/volume1/ai/gen/quarantine/"
37const QT_MODE_DIR: i64 = 493
38const QT_CIDCAP: i64 = 128
39
40func qw(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
41func qn(v: i64) -> i64 {
42 let t: *u8 = sys_mmap(32); let b: *u8 = sys_mmap(32)
43 var m: i64 = v; var k: i64 = 0
44 if m < 0 { qw("-\x00" as *u8); m = 0 - m }
45 if m == 0 { t[0] = 48 as u8; k = 1 }
46 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
47 var i: i64 = 0
48 while i < k { b[i] = t[k - 1 - i]; i = i + 1 }
49 sys_write(1, b, k); return 0
50}
51func qt_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
52func qt_cat(d: *u8, o: i64, s: *u8) -> i64 { var i: i64 = 0; var a: i64 = o; while s[i] != (0 as u8) { d[a] = s[i]; a = a + 1; i = i + 1 } return a }
53func qt_catn(d: *u8, o: i64, v: i64) -> i64 {
54 let t: *u8 = sys_mmap(32)
55 var m: i64 = v; var k: i64 = 0
56 if m == 0 { t[0] = 48 as u8; k = 1 }
57 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
58 var a: i64 = o; var j: i64 = k - 1
59 while j >= 0 { d[a] = t[j]; a = a + 1; j = j - 1 }
60 return a
61}
62
63// LOCATOR -> CID. Accepts what a complainant or a log would realistically hand us:
64// https://host/gen/img/<cid> /gen/img/<cid> blob-<cid>.png <cid>
65// ★A NOTICE NAMES A URL, NOT AN INODE. If the resolver only understood our internal filename, every
66// real-world notice would have to be hand-translated -- which is where a 48h clock quietly dies.
67// Fail-closed: an unrecognisable locator yields 0, never a guessed path.
68func qt_resolve_cid(loc: *u8, out: *u8) -> i64 {
69 let n: i64 = qt_len(loc)
70 if n <= 0 { return 0 }
71 // take the segment after the last '/'
72 var s: i64 = 0
73 var i: i64 = 0
74 while i < n { if loc[i] == (47 as u8) { s = i + 1 } i = i + 1 }
75 // drop a leading "blob-"
76 if n - s > 5 {
77 if loc[s] == (98 as u8) { if loc[s+1] == (108 as u8) { if loc[s+2] == (111 as u8) {
78 if loc[s+3] == (98 as u8) { if loc[s+4] == (45 as u8) { s = s + 5 } } } } }
79 }
80 var e: i64 = n
81 // drop a trailing ".png"
82 if e - s > 4 {
83 if loc[e-4] == (46 as u8) { if loc[e-3] == (112 as u8) { if loc[e-2] == (110 as u8) {
84 if loc[e-1] == (103 as u8) { e = e - 4 } } } }
85 }
86 let l: i64 = e - s
87 if l <= 0 { return 0 }
88 if l >= QT_CIDCAP { return 0 }
89 var k: i64 = 0
90 while k < l { out[k] = loc[s + k]; k = k + 1 }
91 out[l] = 0 as u8
92 return l
93}
94
95func qt_live_path(cid: *u8, out: *u8) -> i64 {
96 var o: i64 = qt_cat(out, 0, QT_GENDIR)
97 o = qt_cat(out, o, "blob-" as *u8)
98 o = qt_cat(out, o, cid)
99 o = qt_cat(out, o, ".png" as *u8)
100 out[o] = 0 as u8
101 return o
102}
103func qt_held_path(cid: *u8, out: *u8) -> i64 {
104 var o: i64 = qt_cat(out, 0, QT_QDIR)
105 o = qt_cat(out, o, "blob-" as *u8)
106 o = qt_cat(out, o, cid)
107 o = qt_cat(out, o, ".png" as *u8)
108 out[o] = 0 as u8
109 return o
110}
111func qt_exists(p: *u8) -> i64 {
112 let fd: i64 = sys_openat_rd(p)
113 if fd < 0 { return 0 }
114 sys_close(fd)
115 return 1
116}
117func qt_size(p: *u8) -> i64 {
118 let fd: i64 = sys_openat_rd(p)
119 if fd < 0 { return 0 - 1 }
120 let b: *u8 = sys_mmap(QT_MAGIC_65536)
121 var t: i64 = 0
122 var r: i64 = 1
123 while r > 0 { r = sys_read(fd, b, QT_MAGIC_65536); if r > 0 { t = t + r } }
124 sys_close(fd)
125 return t
126}
127
128// append-only quarantine record. Same discipline as the notice ledger: what happened, never edited.
129func qt_record(action: *u8, cid: *u8, notice_id: i64) -> i64 {
130 var fd: i64 = sys_openat_append("knowledge/status/quarantine.jrnl" as *u8, 420)
131 if fd < 0 { fd = sys_openat_append("/volume1/homes/elderwesto/nishihost/knowledge/status/quarantine.jrnl" as *u8, 420) }
132 if fd < 0 { return 0 - 1 }
133 let ln: *u8 = sys_mmap(QT_MAGIC_1024)
134 var o: i64 = qt_catn(ln, 0, sys_now_realtime_sec())
135 o = qt_cat(ln, o, "\t" as *u8); o = qt_cat(ln, o, action)
136 o = qt_cat(ln, o, "\t" as *u8); o = qt_cat(ln, o, cid)
137 o = qt_cat(ln, o, "\t" as *u8); o = qt_catn(ln, o, notice_id)
138 o = qt_cat(ln, o, "\n" as *u8)
139 var w: i64 = 0
140 while w < o { let r: i64 = sys_write(fd, ((ln as i64) + w) as *u8, o - w); if r <= 0 { w = o } else { w = w + r } }
141 sys_close(fd)
142 return 0
143}
144
145// 1 = held, 0 = already held / nothing to do, -1 = no such asset (fail-closed, never invents one)
146func qt_hold(cid: *u8, notice_id: i64) -> i64 {
147 let lp: *u8 = sys_mmap(512)
148 let hp: *u8 = sys_mmap(512)
149 qt_live_path(cid, lp)
150 qt_held_path(cid, hp)
151 if qt_exists(hp) == 1 { return 0 } // idempotent: already withheld, file intact
152 if qt_exists(lp) == 0 { return 0 - 1 } // nothing to hold; do NOT fabricate
153 sys_mkdir(QT_QDIR, QT_MODE_DIR)
154 if sys_renameat(lp, hp) != 0 { return 0 - 1 }
155 qt_record("HOLD" as *u8, cid, notice_id)
156 return 1
157}
158
159// THE REMEDY PATH. Without this a quarantine is just a delete with extra steps.
160func qt_release(cid: *u8, notice_id: i64) -> i64 {
161 let lp: *u8 = sys_mmap(512)
162 let hp: *u8 = sys_mmap(512)
163 qt_live_path(cid, lp)
164 qt_held_path(cid, hp)
165 if qt_exists(hp) == 0 { return 0 - 1 }
166 if sys_renameat(hp, lp) != 0 { return 0 - 1 }
167 qt_record("RELEASE" as *u8, cid, notice_id)
168 return 1
169}
170
171func qt_selftest() -> i64 {
172 var pass: i64 = 0
173 var total: i64 = 0
174 let cid: *u8 = sys_mmap(QT_CIDCAP)
175
176 // T1-T4 locator forms all resolve to the same cid
177 total = total + 1
178 if qt_resolve_cid("https://nishifamily.com/gen/img/nxc1-deadbeef\x00" as *u8, cid) == 13 { pass = pass + 1; qw("T1 url locator resolves OK\n\x00" as *u8) }
179 total = total + 1
180 if qt_resolve_cid("/gen/img/nxc1-deadbeef\x00" as *u8, cid) == 13 { pass = pass + 1; qw("T2 path locator resolves OK\n\x00" as *u8) }
181 total = total + 1
182 if qt_resolve_cid("blob-nxc1-deadbeef.png\x00" as *u8, cid) == 13 { pass = pass + 1; qw("T3 filename locator resolves OK\n\x00" as *u8) }
183 total = total + 1
184 if qt_resolve_cid("nxc1-deadbeef\x00" as *u8, cid) == 13 { pass = pass + 1; qw("T4 bare cid resolves OK\n\x00" as *u8) }
185 // T5 NEG fail-closed on an empty locator
186 total = total + 1
187 if qt_resolve_cid("\x00" as *u8, cid) == 0 { pass = pass + 1; qw("T5 NEG empty locator refuses OK\n\x00" as *u8) }
188
189 // build a real fixture asset
190 let fx: *u8 = "qtselftest-fixture\x00" as *u8
191 let lp: *u8 = sys_mmap(512)
192 let hp: *u8 = sys_mmap(512)
193 qt_live_path(fx, lp)
194 qt_held_path(fx, hp)
195 let fd: i64 = sys_openat_wr(lp, 420)
196 if fd >= 0 { sys_write(fd, "NISHI-QUARANTINE-FIXTURE-BYTES\x00" as *u8, 30); sys_close(fd) }
197 let before: i64 = qt_size(lp)
198
199 // T6 hold withholds it from the live path
200 total = total + 1
201 if qt_hold(fx, 1) == 1 { if qt_exists(lp) == 0 { pass = pass + 1; qw("T6 hold removes it from the LIVE path OK\n\x00" as *u8) } }
202 // ★T7 THE ONE THAT MATTERS: it still EXISTS, byte-identical. Withheld, not destroyed.
203 total = total + 1
204 if qt_exists(hp) == 1 { if qt_size(hp) == before { pass = pass + 1; qw("T7 asset PRESERVED byte-identical in quarantine OK\n\x00" as *u8) } }
205 // T8 idempotent: a second hold does not lose it
206 total = total + 1
207 if qt_hold(fx, 1) == 0 { if qt_exists(hp) == 1 { pass = pass + 1; qw("T8 hold is idempotent, file intact OK\n\x00" as *u8) } }
208 // ★T9 RELEASE restores it byte-identical -- the misread-performer remedy
209 total = total + 1
210 if qt_release(fx, 1) == 1 { if qt_exists(lp) == 1 { if qt_size(lp) == before { pass = pass + 1; qw("T9 release RESTORES byte-identical OK\n\x00" as *u8) } } }
211 // T10 NEG hold on a non-existent asset fails closed
212 total = total + 1
213 if qt_hold("qtselftest-nosuchasset\x00" as *u8, 1) == 0 - 1 { pass = pass + 1; qw("T10 NEG hold on absent asset fails closed OK\n\x00" as *u8) }
214 // T11 NEG release with nothing held fails closed
215 total = total + 1
216 if qt_release("qtselftest-nosuchasset\x00" as *u8, 1) == 0 - 1 { pass = pass + 1; qw("T11 NEG release with nothing held fails closed OK\n\x00" as *u8) }
217
218 sys_unlinkat(lp) // fixture cleanup only -- never an operational path
219
220 qw("QUARANTINEGATE \x00" as *u8); qn(pass); qw("/\x00" as *u8); qn(total)
221 if pass == total { qw(" verdict=GREEN\n\x00" as *u8); return 0 }
222 qw(" verdict=RED\n\x00" as *u8)
223 return 1
224}
225
226func main(argc: i64, argv: *i64) -> i64 {
227 ep_anchor()
228 if argc < 2 { qw("usage: nx_quarantine resolve|hold|release|status <locator> [notice-id] | selftest\n\x00" as *u8); return 1 }
229 let v: *u8 = argv[1] as *u8
230 if v[0] == (115 as u8) { if v[1] == (101 as u8) { return qt_selftest() } } // selftest
231 if argc < 3 { qw("nx_quarantine: needs a locator\n\x00" as *u8); return 2 }
232 let cid: *u8 = sys_mmap(QT_CIDCAP)
233 if qt_resolve_cid(argv[2] as *u8, cid) <= 0 { qw("QT-RESOLVE REFUSED (unrecognisable locator; give a url, blob-<cid>.png, or a bare cid)\n\x00" as *u8); return 4 }
234 var nid: i64 = 0
235 if argc >= 4 { var t: i64 = 0; var i: i64 = 0; let s: *u8 = argv[3] as *u8
236 while s[i] != (0 as u8) { let c: i64 = s[i] & 0xff; if c >= 48 { if c <= 57 { t = t*10 + (c-48) } } i = i + 1 }
237 nid = t }
238 let lp: *u8 = sys_mmap(512)
239 let hp: *u8 = sys_mmap(512)
240 qt_live_path(cid, lp)
241 qt_held_path(cid, hp)
242
243 if v[0] == (114 as u8) {
244 if v[2] == (115 as u8) { // resolve
245 qw("QT-RESOLVE cid=\x00" as *u8); qw(cid); qw(" live=\x00" as *u8); qw(lp); qw("\n\x00" as *u8)
246 return 0
247 }
248 let r: i64 = qt_release(cid, nid) // release
249 if r == 1 { qw("QT-RELEASED cid=\x00" as *u8); qw(cid); qw(" restored to the live path\n\x00" as *u8); return 0 }
250 qw("QT-RELEASE FAILED (nothing held under that cid)\n\x00" as *u8); return 5
251 }
252 if v[0] == (104 as u8) { // hold
253 let r: i64 = qt_hold(cid, nid)
254 if r == 1 { qw("QT-HELD cid=\x00" as *u8); qw(cid); qw(" withheld, PRESERVED (reversible: nx_quarantine release)\n\x00" as *u8); return 0 }
255 if r == 0 { qw("QT-ALREADY-HELD cid=\x00" as *u8); qw(cid); qw("\n\x00" as *u8); return 0 }
256 qw("QT-HOLD FAILED (no such live asset)\n\x00" as *u8); return 5
257 }
258 if v[0] == (115 as u8) { // status
259 if qt_exists(hp) == 1 { qw("QT-STATUS HELD cid=\x00" as *u8); qw(cid); qw("\n\x00" as *u8); return 0 }
260 if qt_exists(lp) == 1 { qw("QT-STATUS LIVE cid=\x00" as *u8); qw(cid); qw("\n\x00" as *u8); return 0 }
261 qw("QT-STATUS ABSENT cid=\x00" as *u8); qw(cid); qw("\n\x00" as *u8); return 0
262 }
263 qw("nx_quarantine: unknown verb\n\x00" as *u8)
264 return 1
265}