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}