code wiki / _hdl_build / nx_meal_source.nx

nx_meal_source.nx source

↩ module page · 239 lines · 10233 B

1// nx_meal_source.nx -- LIB: the LINK-ROT-PROOF provenance spine of the meal planner. Every recipe rendered 2// on a Nishi page carries WHERE IT CAME FROM -- publisher, original URL, license -- together with a PRESERVED 3// COPY of that origin page written as a WARC/1.0 resource record (nx_web_archive, ISO 28500 -- the same format 4// the Internet Archive and Browsertrix emit, so ours interoperates). When the origin rots, the citation still 5// resolves: the page serves the preserved bytes instead of a 404, and the credit line stays TRUE instead of 6// pointing at a dead host. 7// 8// HONESTY BY CONSTRUCTION (rule 23; R0a "a placeholder that reads as a real claim"): ms_is_archived is a 9// MEASUREMENT, never a stored flag -- it re-reads the WARC and confirms the payload is actually retrievable 10// before the page is permitted to render "archived copy". A recorded INTENT to archive can therefore never 11// masquerade as an archive that EXISTS. ms_capture_verify proves the same for a fresh capture, round-trip, 12// byte-for-byte -- binary payloads with embedded CRLF are exactly where a line-based archive corrupts silently. 13// 14// Schema (prefix passed in, e.g. knowledge/store/meal-): 15// meal:srcids -> TAB list of recipe ids 16// meal:src:<rid> -> title <t> publisher <t> origin_url <t> license <t> captured_day <t> origin_bytes 17// 18// This file RECORDS and MEASURES provenance; it renders NOTHING (rule 9) -- nx_meal_page owns all markup. 19// license_tier: ORIGINAL No hw writes (Rule 26). 20import "nx_food_science.nx" 21import "nx_web_archive.nx" 22import "nx_seg_store.nx" 23import "nx_syscalls.nx" 24 25const MS_TAB: i64 = 9 26const MS_KEYCAP: i64 = 160 27const MS_VALCAP: i64 = 2048 28const MS_LISTCAP: i64 = 8192 29const MS_TOKCAP: i64 = 96 30 31// field indices of meal:src:<rid> 32const MS_F_TITLE: i64 = 0 33const MS_F_PUBLISHER: i64 = 1 34const MS_F_URL: i64 = 2 35const MS_F_LICENSE: i64 = 3 36const MS_F_DAY: i64 = 4 37const MS_F_BYTES: i64 = 5 38 39// rot state -- the tri-state the page renders from (never a boolean: "no source" and "source that may rot" 40// are different claims and must not collapse into one) 41const MS_ROT_NONE: i64 = 0 42const MS_ROT_RISK: i64 = 1 43const MS_ROT_PROOF: i64 = 2 44 45// static leaf scratch: each buffer dies inside its call, so there is no reset coordination and no per-call 46// page leak (sys_mmap is page-granular with no allocator behind it -- an unpaired scratch mmap burns 4096B 47// per call even for 16 bytes). Read paths run per-request, so they get statics; the rare seeding paths keep 48// the plain mmap idiom of their nx_price siblings. 49static MS_KB: i64 50static MS_VB: i64 51static MS_PQ: i64 52static MS_LQ: i64 53 54func ms_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 55func ms_keybuf() -> *u8 { if MS_KB == 0 { MS_KB = sys_mmap(MS_KEYCAP) as i64 } return MS_KB as *u8 } 56func ms_valbuf() -> *u8 { if MS_VB == 0 { MS_VB = sys_mmap(MS_VALCAP) as i64 } return MS_VB as *u8 } 57func ms_pq() -> *i64 { if MS_PQ == 0 { MS_PQ = sys_mmap(16) as i64 } return MS_PQ as *i64 } 58func ms_lq() -> *i64 { if MS_LQ == 0 { MS_LQ = sys_mmap(16) as i64 } return MS_LQ as *i64 } 59 60// idempotent seg-store put (the nx_price pr_put shape): an identical value is never re-committed. 61func ms_put(prefix: *u8, key: *u8, val: *u8) -> i64 { 62 let vl: i64 = ms_len(val) 63 if fd_streq_store(prefix, key, val, vl) == 1 { return 0 } 64 let w: *i64 = ss_begin() 65 ss_add(w, 1, key, val, vl) 66 let seg: i64 = fd_seg_next(prefix) 67 ss_commit(prefix, w, seg) 68 return 1 69} 70 71func ms_src_key(rid: *u8, out: *u8) -> i64 { 72 var o: i64 = 0 73 o = as_append(out, o, "meal:src:" as *u8) 74 o = as_append(out, o, rid) 75 out[o] = 0 as u8 76 return o 77} 78 79// split a TAB list into NUL-terminated tokens; returns the count. 80func ms_split(buf: *u8, len: i64, outtoks: *i64) -> i64 { 81 var c: i64 = 0 82 var i: i64 = 0 83 var ls: i64 = 0 84 while i <= len { 85 var sep: i64 = 0 86 if i == len { sep = 1 } else { if buf[i] == (MS_TAB as u8) { sep = 1 } } 87 if sep == 1 { 88 let tl: i64 = i - ls 89 if tl > 0 { 90 let tok: *u8 = sys_mmap(MS_TOKCAP) 91 var t: i64 = 0 92 while t < tl { tok[t] = buf[ls + t]; t = t + 1 } 93 tok[tl] = 0 as u8 94 outtoks[c] = tok as i64 95 c = c + 1 96 } 97 ls = i + 1 98 } 99 i = i + 1 100 } 101 return c 102} 103 104// enumerate recorded recipe ids; returns the count. 105func ms_list(prefix: *u8, outtoks: *i64) -> i64 { 106 let pq: *i64 = ms_pq() 107 let lq: *i64 = ms_lq() 108 if ss_get(prefix, "meal:srcids" as *u8, pq, lq) != 1 { return 0 } 109 return ms_split(pq[0] as *u8, lq[0], outtoks) 110} 111 112// idempotent append of rid to the meal:srcids TAB list (seeding path -- mirrors pr_list_append). 113func ms_list_add(prefix: *u8, rid: *u8) -> i64 { 114 let pq: *i64 = sys_mmap(16) as *i64 115 let lq: *i64 = sys_mmap(16) as *i64 116 let cur: *u8 = sys_mmap(MS_LISTCAP) 117 var o: i64 = 0 118 if ss_get(prefix, "meal:srcids" as *u8, pq, lq) == 1 { 119 let b: *u8 = pq[0] as *u8 120 let n: i64 = lq[0] 121 let toks: *i64 = sys_mmap(8 * 512) as *i64 122 let nt: i64 = ms_split(b, n, toks) 123 var i: i64 = 0 124 while i < nt { if fd_streq(toks[i] as *u8, rid) == 1 { return 0 } i = i + 1 } 125 var u: i64 = 0 126 while u < n { cur[u] = b[u]; u = u + 1 } 127 o = n 128 cur[o] = MS_TAB as u8; o = o + 1 129 } 130 o = as_append(cur, o, rid) 131 cur[o] = 0 as u8 132 return ms_put(prefix, "meal:srcids" as *u8, cur) 133} 134 135// RECORD provenance for a recipe (idempotent). captured_day = integer day number (0 = not captured yet). 136// origin_bytes = the size of the origin page we preserved, so the page can show WHAT was kept. 137func ms_record(prefix: *u8, rid: *u8, title: *u8, publisher: *u8, url: *u8, license: *u8, captured_day: i64, origin_bytes: i64) -> i64 { 138 let key: *u8 = sys_mmap(MS_KEYCAP) 139 ms_src_key(rid, key) 140 let val: *u8 = sys_mmap(MS_VALCAP) 141 var o: i64 = 0 142 o = as_append(val, o, title); val[o] = MS_TAB as u8; o = o + 1 143 o = as_append(val, o, publisher); val[o] = MS_TAB as u8; o = o + 1 144 o = as_append(val, o, url); val[o] = MS_TAB as u8; o = o + 1 145 o = as_append(val, o, license); val[o] = MS_TAB as u8; o = o + 1 146 o = fd_apnum(val, o, captured_day); val[o] = MS_TAB as u8; o = o + 1 147 o = fd_apnum(val, o, origin_bytes) 148 val[o] = 0 as u8 149 let w: i64 = ms_put(prefix, key, val) 150 ms_list_add(prefix, rid) 151 return w 152} 153 154// a string field of the provenance record; returns its length (0 = no record / empty field). 155func ms_field(prefix: *u8, rid: *u8, f: i64, out: *u8) -> i64 { 156 let key: *u8 = ms_keybuf() 157 ms_src_key(rid, key) 158 let pq: *i64 = ms_pq() 159 let lq: *i64 = ms_lq() 160 if ss_get(prefix, key, pq, lq) != 1 { out[0] = 0 as u8; return 0 } 161 return fd_field(pq[0] as *u8, lq[0], f, out) 162} 163 164// an integer field (captured_day / origin_bytes); -1 when there is no record. 165func ms_field_num(prefix: *u8, rid: *u8, f: i64) -> i64 { 166 let b: *u8 = ms_valbuf() 167 let n: i64 = ms_field(prefix, rid, f, b) 168 if n == 0 { return 0 - 1 } 169 return fd_atoi(b, n) 170} 171 172// ---- the ARCHIVE half: capture, then MEASURE that the capture is real ---- 173 174// append a WARC/1.0 resource record preserving the origin page bytes. Thin over wa_write_resource so the 175// planner never hand-rolls the record format. Returns the new WARC offset. 176func ms_capture(warc: *u8, off: i64, url: *u8, date: *u8, ctype: *u8, payload: *u8, plen: i64) -> i64 { 177 return wa_write_resource(warc, off, url, date, ctype, payload, plen) 178} 179 180// MEASURED: 1 only if `url` actually resolves to a retrievable payload in this WARC. The page may render 181// "archived copy" ONLY when this returns 1. 182func ms_is_archived(warc: *u8, warclen: i64, url: *u8) -> i64 { 183 let op: *i64 = ms_pq() 184 let ol: *i64 = ms_lq() 185 if wa_lookup(warc, warclen, url, ms_len(url), op, ol) != 1 { return 0 } 186 if ol[0] <= 0 { return 0 } 187 return 1 188} 189 190// preserved payload length for a url, or -1 when nothing is archived. 191func ms_archive_bytes(warc: *u8, warclen: i64, url: *u8) -> i64 { 192 let op: *i64 = ms_pq() 193 let ol: *i64 = ms_lq() 194 if wa_lookup(warc, warclen, url, ms_len(url), op, ol) != 1 { return 0 - 1 } 195 return ol[0] 196} 197 198// byte offset of the preserved payload inside the WARC (the serve-from-archive primitive), or -1. 199func ms_archive_off(warc: *u8, warclen: i64, url: *u8) -> i64 { 200 let op: *i64 = ms_pq() 201 let ol: *i64 = ms_lq() 202 if wa_lookup(warc, warclen, url, ms_len(url), op, ol) != 1 { return 0 - 1 } 203 return op[0] 204} 205 206// ROUND-TRIP PROOF: the preserved bytes must come back BYTE-IDENTICAL to what went in. "We wrote a record" 207// and "the copy survives" are different claims; this asserts the second one. 208func ms_capture_verify(warc: *u8, warclen: i64, url: *u8, payload: *u8, plen: i64) -> i64 { 209 let op: *i64 = ms_pq() 210 let ol: *i64 = ms_lq() 211 if wa_lookup(warc, warclen, url, ms_len(url), op, ol) != 1 { return 0 } 212 if ol[0] != plen { return 0 } 213 let base: i64 = op[0] 214 var i: i64 = 0 215 while i < plen { 216 if (warc[base + i] & 0xff) != (payload[i] & 0xff) { return 0 } 217 i = i + 1 218 } 219 return 1 220} 221 222// CITABLE = a recorded, non-empty origin URL. A recipe without one may still be rendered, but the page must 223// NOT print a credit line for it -- an empty "brought to you by" is itself a fake. 224func ms_is_citable(prefix: *u8, rid: *u8) -> i64 { 225 let b: *u8 = ms_valbuf() 226 if ms_field(prefix, rid, MS_F_URL, b) == 0 { return 0 } 227 return 1 228} 229 230// THE PAGE'S ONE QUESTION, answered as a tri-state so two different truths cannot collapse into one boolean: 231// MS_ROT_NONE -- no recorded source: render NO credit strip at all 232// MS_ROT_RISK -- a source we cite but have NOT preserved: render the citation, and NO archive link 233// MS_ROT_PROOF -- a source we cite AND hold preserved bytes for: render the citation + "archived copy" 234func ms_rot_state(prefix: *u8, rid: *u8, warc: *u8, warclen: i64) -> i64 { 235 let u: *u8 = ms_valbuf() 236 if ms_field(prefix, rid, MS_F_URL, u) == 0 { return MS_ROT_NONE } 237 if ms_is_archived(warc, warclen, u) == 1 { return MS_ROT_PROOF } 238 return MS_ROT_RISK 239}