code wiki / _hdl_build / nx_notice.nx

nx_notice.nx source

↩ module page · 422 lines · 17799 B

1// nx_notice.nx -- W-TD-2: THE NOTICE LEDGER. The state half of the safe-harbour posture. 2// 3// nx_takedown decides WHAT SHOULD HAPPEN to a notice; it is pure and holds nothing. That made it a 4// document rather than a control (debt 1785893553): the operator's posture -- "like google or bing 5// or ai companies i use what is available till its requested to be removed, and i inherit that 6// different set of requirements" -- only exists if the REQUEST can be received, TIMED, and ANSWERED 7// with evidence. ★★★★★AN INSTRUMENT THAT NOTHING IS REQUIRED TO CONSULT IS A REPORT, NOT A CONTROL 8// (the estate's own publishing-house law, applied here). 9// 10// WHAT COMPLIANCE ACTUALLY REQUIRES OF THE RECORD, and why each is mechanical here: 11// * EXPEDITIOUS RESPONSE is a claim about TIME, so the clock must start at receipt and be 12// queryable -- `overdue` answers "what am I late on" without anyone remembering to look. 13// * The record must be ANSWERABLE LATER, so it is APPEND-ONLY (rule 13, history is sacred). 14// Acting on a notice APPENDS an action row referencing the id; it never edits the original. 15// ⇒ a ledger you can rewrite is not evidence of anything, and rewriting it to look compliant is 16// the failure mode that turns a paperwork problem into a credibility problem. 17// * 512(i) repeat-infringer policy needs a COUNT, which needs persistence. 18// * QUARANTINE (nx_realperson's RP_QUARANTINE, for a SUSPECTED-minor call that may have misread a 19// lawful adult) must WITHHOLD WITHOUT DESTROYING -- so it renames into a quarantine prefix and 20// records the move. Never unlink: the misread performer's evidence has to survive. 21// 22// ⚠NOT LEGAL ADVICE. This is bookkeeping built to the shape of the regime, so a human can answer 23// quickly and prove what they did. 24// 25// verbs: 26// file <class> <locator> <complainant> <w><l><c><g><s> -- record a notice; prints its id 27// act <id> <action> [note] -- append what was done (never edits) 28// list [n] -- recent rows 29// overdue <now_epoch> -- notices past their statutory clock 30// strikes <target> -- 512(i) count for a target 31// license_tier: ORIGINAL expect_exit: 0 32// module: nishi-core.hosting.notice 33import "nx_syscalls.nx" 34import "nx_estate_path.nx" 35import "nx_takedown.nx" 36const NL_MAGIC_4096: i64 = 4096 37 38const NL_CAP: i64 = 1048576 39const NL_SECS_PER_HOUR: i64 = 3600 40 41func nw(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 42func nn(v: i64) -> i64 { 43 let t: *u8 = sys_mmap(32); let b: *u8 = sys_mmap(32) 44 var m: i64 = v; var k: i64 = 0 45 if m < 0 { nw("-\x00" as *u8); m = 0 - m } 46 if m == 0 { t[0] = 48 as u8; k = 1 } 47 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 48 var i: i64 = 0 49 while i < k { b[i] = t[k - 1 - i]; i = i + 1 } 50 sys_write(1, b, k); return 0 51} 52func nl_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 53func nl_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 } 54func nl_catn(d: *u8, o: i64, v: i64) -> i64 { 55 if v < 0 { d[o] = 45 as u8; return nl_catn(d, o + 1, 0 - v) } 56 let t: *u8 = sys_mmap(32) 57 var m: i64 = v; var k: i64 = 0 58 if m == 0 { t[0] = 48 as u8; k = 1 } 59 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 60 var a: i64 = o; var j: i64 = k - 1 61 while j >= 0 { d[a] = t[j]; a = a + 1; j = j - 1 } 62 return a 63} 64func nl_atoi(s: *u8) -> i64 { 65 var v: i64 = 0; var i: i64 = 0; var seen: i64 = 0 66 while s[i] != (0 as u8) { 67 let c: i64 = s[i] & 0xff 68 if c >= 48 { if c <= 57 { v = v * 10 + (c - 48); seen = 1 } } 69 i = i + 1 70 } 71 if seen == 0 { return 0 - 1 } 72 return v 73} 74 75// APPEND ONE ROW. The only write this organ performs -- there is deliberately no update path. 76func nl_append(line: *u8, n: i64) -> i64 { 77 var fd: i64 = sys_openat_append("knowledge/status/notices.jrnl" as *u8, 420) 78 if fd < 0 { fd = sys_openat_append("/volume1/homes/elderwesto/nishihost/knowledge/status/notices.jrnl" as *u8, 420) } 79 if fd < 0 { return 0 - 1 } 80 var w: i64 = 0 81 while w < n { 82 let r: i64 = sys_write(fd, ((line as i64) + w) as *u8, n - w) 83 if r <= 0 { w = n } else { w = w + r } 84 } 85 sys_close(fd) 86 return 0 87} 88 89func nl_slurp(buf: *u8) -> i64 { 90 let fd: i64 = ep_open_rd("knowledge/status/notices.jrnl" as *u8) 91 if fd < 0 { return 0 } 92 var t: i64 = 0 93 var r: i64 = 1 94 while r > 0 { 95 if t >= NL_CAP { r = 0 } else { 96 r = sys_read(fd, ((buf as i64) + t) as *u8, NL_CAP - t) 97 if r > 0 { t = t + r } 98 } 99 } 100 sys_close(fd) 101 return t 102} 103 104// field <idx> of the tab-separated row [s,e) -> box[0]=off box[1]=len. 0 when absent. 105// ⚠the terminator search keeps its position (the banked law, obeyed on purpose this time). 106func nl_field(buf: *u8, s: i64, e: i64, idx: i64, box: *i64) -> i64 { 107 var p: i64 = s 108 var f: i64 = 0 109 while f < idx { 110 var go: i64 = 1 111 while go == 1 { 112 if p >= e { go = 0 } else { 113 if buf[p] == (9 as u8) { p = p + 1; go = 0 } else { p = p + 1 } 114 } 115 } 116 f = f + 1 117 } 118 if p >= e { box[0] = 0; box[1] = 0; return 0 } 119 var q: i64 = p 120 var g: i64 = 1 121 while g == 1 { 122 if q >= e { g = 0 } else { 123 if buf[q] == (9 as u8) { g = 0 } else { q = q + 1 } 124 } 125 } 126 box[0] = p; box[1] = q - p 127 return 1 128} 129 130func nl_fieldnum(buf: *u8, s: i64, e: i64, idx: i64, box: *i64) -> i64 { 131 if nl_field(buf, s, e, idx, box) == 0 { return 0 - 1 } 132 var v: i64 = 0 133 var i: i64 = 0 134 var seen: i64 = 0 135 while i < box[1] { 136 let c: i64 = buf[box[0] + i] & 0xff 137 if c >= 48 { if c <= 57 { v = v * 10 + (c - 48); seen = 1 } } 138 i = i + 1 139 } 140 if seen == 0 { return 0 - 1 } 141 return v 142} 143 144// row layout: epoch \t id \t kind \t class \t valid \t target \t action \t note 145// kind: N = notice filed, A = action taken. An A row REFERENCES an id; it never replaces the N row. 146// ⚠MEASURED DEFECT ON THE FIRST RUN: the id was simply the epoch, so three notices filed in the 147// SAME SECOND all received id 1785893887. In a compliance ledger that is not cosmetic -- `act <id>` 148// would answer all of them at once, and nl_answered would report a sibling as handled while it sat 149// untouched past its clock. A ledger whose keys collide cannot evidence anything. 150// FIX: monotonic id = max(now, highest_existing_id + 1). Scans the ledger it is about to append to, 151// so uniqueness holds across restarts and across same-second bursts. 152// ★AN IDENTIFIER MINTED FROM A CLOCK COLLIDES AT THE CLOCK'S RESOLUTION -- AND A BURST IS EXACTLY 153// WHEN A TAKEDOWN QUEUE FILLS UP. 154func nl_next_id(now: i64) -> i64 { 155 let buf: *u8 = sys_mmap(NL_CAP) 156 let n: i64 = nl_slurp(buf) 157 if n <= 0 { return now } 158 let box: *i64 = sys_mmap(32) as *i64 159 var maxid: i64 = 0 160 var p: i64 = 0 161 while p < n { 162 var e: i64 = n 163 var q: i64 = p 164 var go: i64 = 1 165 while go == 1 { 166 if q >= n { e = n; go = 0 } else { 167 if buf[q] == (10 as u8) { e = q; go = 0 } else { q = q + 1 } 168 } 169 } 170 if e > p { 171 let id: i64 = nl_fieldnum(buf, p, e, 1, box) 172 if id > maxid { maxid = id } 173 } 174 p = e + 1 175 } 176 if maxid >= now { return maxid + 1 } 177 return now 178} 179 180func nl_file_notice(class: i64, locator: *u8, complainant: *u8, valid: i64) -> i64 { 181 let now: i64 = sys_now_realtime_sec() 182 let id: i64 = nl_next_id(now) 183 let ln: *u8 = sys_mmap(NL_MAGIC_4096) 184 var o: i64 = nl_catn(ln, 0, now) 185 o = nl_cat(ln, o, "\t" as *u8); o = nl_catn(ln, o, id) 186 o = nl_cat(ln, o, "\tN\t" as *u8); o = nl_catn(ln, o, class) 187 o = nl_cat(ln, o, "\t" as *u8); o = nl_catn(ln, o, valid) 188 o = nl_cat(ln, o, "\t" as *u8); o = nl_cat(ln, o, locator) 189 o = nl_cat(ln, o, "\t" as *u8); o = nl_catn(ln, o, td_action(class, valid)) 190 o = nl_cat(ln, o, "\t" as *u8); o = nl_cat(ln, o, complainant) 191 o = nl_cat(ln, o, "\n" as *u8) 192 if nl_append(ln, o) < 0 { return 0 - 1 } 193 return id 194} 195 196// Does an N (notice) row with this id exist? ⚠MEASURED: `act` originally accepted ANY id, so a 197// typo or a bad extraction produced ORPHAN action rows -- "we answered notice 1" with no notice 1 198// anywhere. That is corrosive in both directions: the ledger can read as though something was 199// handled when it was not, and an orphan whose id later collides with a real notice would mark that 200// notice answered, silencing its overdue alarm. A compliance record must not be able to claim work 201// that has no subject. ★★★★★AN ACTION ROW THAT CANNOT NAME A REAL SUBJECT IS NOT EVIDENCE, IT IS 202// NOISE THAT LOOKS LIKE EVIDENCE. 203func nl_notice_exists(id: i64) -> i64 { 204 let buf: *u8 = sys_mmap(NL_CAP) 205 let n: i64 = nl_slurp(buf) 206 if n <= 0 { return 0 } 207 let box: *i64 = sys_mmap(32) as *i64 208 var p: i64 = 0 209 while p < n { 210 var e: i64 = n 211 var q: i64 = p 212 var go: i64 = 1 213 while go == 1 { 214 if q >= n { e = n; go = 0 } else { 215 if buf[q] == (10 as u8) { e = q; go = 0 } else { q = q + 1 } 216 } 217 } 218 if e > p { 219 if nl_field(buf, p, e, 2, box) == 1 { 220 if box[1] == 1 { if buf[box[0]] == (78 as u8) { 221 if nl_fieldnum(buf, p, e, 1, box) == id { return 1 } 222 } } 223 } 224 } 225 p = e + 1 226 } 227 return 0 228} 229 230func nl_record_action(id: i64, action: i64, note: *u8) -> i64 { 231 let now: i64 = sys_now_realtime_sec() 232 let ln: *u8 = sys_mmap(NL_MAGIC_4096) 233 var o: i64 = nl_catn(ln, 0, now) 234 o = nl_cat(ln, o, "\t" as *u8); o = nl_catn(ln, o, id) 235 o = nl_cat(ln, o, "\tA\t-\t-\t-\t" as *u8); o = nl_catn(ln, o, action) 236 o = nl_cat(ln, o, "\t" as *u8); o = nl_cat(ln, o, note) 237 o = nl_cat(ln, o, "\n" as *u8) 238 return nl_append(ln, o) 239} 240 241// Has this notice id been answered? Scans for an A row referencing it. 242func nl_answered(buf: *u8, n: i64, id: i64) -> i64 { 243 let box: *i64 = sys_mmap(32) as *i64 244 var p: i64 = 0 245 while p < n { 246 var e: i64 = n 247 var q: i64 = p 248 var go: i64 = 1 249 while go == 1 { 250 if q >= n { e = n; go = 0 } else { 251 if buf[q] == (10 as u8) { e = q; go = 0 } else { q = q + 1 } 252 } 253 } 254 if e > p { 255 if nl_field(buf, p, e, 2, box) == 1 { 256 if box[1] == 1 { if buf[box[0]] == (65 as u8) { 257 if nl_fieldnum(buf, p, e, 1, box) == id { return 1 } 258 } } 259 } 260 } 261 p = e + 1 262 } 263 return 0 264} 265 266// UNANSWERED notices past their statutory clock. This is the query that makes "expeditious" real. 267func nl_overdue(now: i64) -> i64 { 268 let buf: *u8 = sys_mmap(NL_CAP) 269 let n: i64 = nl_slurp(buf) 270 if n <= 0 { nw("NOTICE-OVERDUE none (ledger empty)\n\x00" as *u8); return 0 } 271 let box: *i64 = sys_mmap(32) as *i64 272 var late: i64 = 0 273 var seen: i64 = 0 274 var p: i64 = 0 275 while p < n { 276 var e: i64 = n 277 var q: i64 = p 278 var go: i64 = 1 279 while go == 1 { 280 if q >= n { e = n; go = 0 } else { 281 if buf[q] == (10 as u8) { e = q; go = 0 } else { q = q + 1 } 282 } 283 } 284 if e > p { 285 if nl_field(buf, p, e, 2, box) == 1 { 286 if box[1] == 1 { if buf[box[0]] == (78 as u8) { 287 seen = seen + 1 288 let ep: i64 = nl_fieldnum(buf, p, e, 0, box) 289 let id: i64 = nl_fieldnum(buf, p, e, 1, box) 290 let cl: i64 = nl_fieldnum(buf, p, e, 3, box) 291 if ep >= 0 { 292 let age_h: i64 = (now - ep) / NL_SECS_PER_HOUR 293 if td_overdue(cl, age_h) == 1 { 294 if nl_answered(buf, n, id) == 0 { 295 late = late + 1 296 nw(" OVERDUE id=\x00" as *u8); nn(id) 297 nw(" class=\x00" as *u8); nn(cl) 298 nw(" age_h=\x00" as *u8); nn(age_h) 299 nw(" deadline_h=\x00" as *u8); nn(td_deadline_hours(cl)) 300 nw("\n\x00" as *u8) 301 } 302 } 303 } 304 } } 305 } 306 } 307 p = e + 1 308 } 309 nw("NOTICE-OVERDUE notices=\x00" as *u8); nn(seen) 310 nw(" overdue=\x00" as *u8); nn(late) 311 if late == 0 { nw(" verdict=GREEN\n\x00" as *u8); return 0 } 312 nw(" verdict=LATE\n\x00" as *u8) 313 return 0 314} 315 316// Count notices filed against a LOCATOR (field 5 = the asset), not against an account. 317// ⚠SAY WHAT THIS ACTUALLY IS: 512(i) repeat-infringer termination is per-SUBSCRIBER, and this 318// estate has no account/uploader identity to count against, so a per-asset tally is NOT yet that 319// policy. It is a useful signal (an asset drawing repeated valid notices) and it is the honest 320// maximum this schema supports. My first output labelled it "target=<complainant>", which was wrong 321// twice over -- wrong field, and wrong noun for the legal concept. 322// ★★★★★A COUNTER THAT REPORTS A NUMBER UNDER THE WRONG NOUN IS WORSE THAN NO COUNTER: it looks 323// like compliance evidence and is not. Gap filed rather than papered over. 324func nl_strikes(target: *u8) -> i64 { 325 let buf: *u8 = sys_mmap(NL_CAP) 326 let n: i64 = nl_slurp(buf) 327 if n <= 0 { return 0 } 328 let box: *i64 = sys_mmap(32) as *i64 329 let tl: i64 = nl_len(target) 330 var c: i64 = 0 331 var p: i64 = 0 332 while p < n { 333 var e: i64 = n 334 var q: i64 = p 335 var go: i64 = 1 336 while go == 1 { 337 if q >= n { e = n; go = 0 } else { 338 if buf[q] == (10 as u8) { e = q; go = 0 } else { q = q + 1 } 339 } 340 } 341 if e > p { 342 if nl_field(buf, p, e, 2, box) == 1 { 343 if box[1] == 1 { if buf[box[0]] == (78 as u8) { 344 if nl_field(buf, p, e, 5, box) == 1 { 345 if box[1] == tl { 346 var k: i64 = 0 347 var same: i64 = 1 348 while k < tl { if buf[box[0] + k] != target[k] { same = 0; k = tl } else { k = k + 1 } } 349 if same == 1 { c = c + 1 } 350 } 351 } 352 } } 353 } 354 } 355 p = e + 1 356 } 357 return c 358} 359 360func main(argc: i64, argv: *i64) -> i64 { 361 ep_anchor() 362 if argc < 2 { 363 nw("usage: nx_notice file <class> <locator> <complainant> <w><l><c><g><s> | act <id> <action> [note] | overdue <now> | strikes <target> | list [n]\n\x00" as *u8) 364 return 1 365 } 366 let v: *u8 = argv[1] as *u8 367 if v[0] == (102 as u8) { // file 368 if argc < 6 { nw("nx_notice file needs <class> <locator> <complainant> <flags5>\n\x00" as *u8); return 2 } 369 let class: i64 = nl_atoi(argv[2] as *u8) 370 let flags: *u8 = argv[5] as *u8 371 var ok: i64 = 1 372 var i: i64 = 0 373 while i < 5 { if flags[i] != (49 as u8) { ok = 0 } i = i + 1 } 374 let id: i64 = nl_file_notice(class, argv[3] as *u8, argv[4] as *u8, ok) 375 if id < 0 { nw("NOTICE-FILE FAILED (ledger unwritable)\n\x00" as *u8); return 3 } 376 nw("NOTICE-FILED id=\x00" as *u8); nn(id) 377 nw(" class=\x00" as *u8); nn(class) 378 nw(" valid=\x00" as *u8); nn(ok) 379 nw(" required_action=\x00" as *u8); nn(td_action(class, ok)) 380 nw(" deadline_h=\x00" as *u8); nn(td_deadline_hours(class)) 381 nw("\n\x00" as *u8) 382 return 0 383 } 384 if v[0] == (97 as u8) { // act 385 if argc < 4 { nw("nx_notice act needs <id> <action>\n\x00" as *u8); return 2 } 386 var note: *u8 = "-\x00" as *u8 387 if argc >= 5 { note = argv[4] as *u8 } 388 let aid: i64 = nl_atoi(argv[2] as *u8) 389 // FAIL-CLOSED: refuse to record work against a notice that does not exist. 390 if nl_notice_exists(aid) == 0 { 391 nw("NOTICE-ACTION REFUSED: no notice with id=\x00" as *u8); nn(aid) 392 nw(" (an action row with no subject is not evidence -- check the id with `list`)\n\x00" as *u8) 393 return 4 394 } 395 if nl_record_action(aid, nl_atoi(argv[3] as *u8), note) < 0 { return 3 } 396 nw("NOTICE-ACTION recorded (appended, original row untouched)\n\x00" as *u8) 397 return 0 398 } 399 if v[0] == (111 as u8) { // overdue 400 var now: i64 = sys_now_realtime_sec() 401 if argc >= 3 { let t: i64 = nl_atoi(argv[2] as *u8); if t > 0 { now = t } } 402 return nl_overdue(now) 403 } 404 if v[0] == (115 as u8) { // strikes 405 if argc < 3 { nw("nx_notice strikes needs <target>\n\x00" as *u8); return 2 } 406 let c: i64 = nl_strikes(argv[2] as *u8) 407 nw("NOTICE-STRIKES locator=\x00" as *u8); nw(argv[2] as *u8) 408 nw(" notices=\x00" as *u8); nn(c) 409 nw(" over_threshold=\x00" as *u8); nn(td_repeat_action(c, TD_REPEAT_DEFAULT)) 410 nw(" envelope=PER-ASSET-NOT-PER-SUBSCRIBER (512i needs an account model this estate lacks)\n\x00" as *u8) 411 return 0 412 } 413 if v[0] == (108 as u8) { // list 414 let buf: *u8 = sys_mmap(NL_CAP) 415 let n: i64 = nl_slurp(buf) 416 if n <= 0 { nw("NOTICE-LIST empty\n\x00" as *u8); return 0 } 417 sys_write(1, buf, n) 418 return 0 419 } 420 nw("nx_notice: unknown verb\n\x00" as *u8) 421 return 1 422}