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}