code wiki / _hdl_build / nx_notice.nx

nx_notice.nx source

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