code wiki / _hdl_build / nx_dp_reclassify.nx

nx_dp_reclassify.nx source

↩ module page · 127 lines · 7028 B

1// nx_dp_reclassify.nx -- SOVEREIGN per-doc reclassification (operator: "full secret isolation -- a de-listed 2// doc's bytes must also leave the public shard"). Given (domain, cid): copy the doc [+ its url row] into a 3// PRIVATE classified shard dp-<domain>-secret- (a DIFFERENT physical prefix => the public search's 4// ss_open(dp-<domain>-pub-) can NEVER read it -- isolation by construction, the same law as -pub-/-prv-), 5// then TOMBSTONE it in the public shard so dss_search + /doc + dp_read all miss it AT ONCE. 6// 7// ADDITIVE BY CONSTRUCTION (Rule 13): the tombstone is a NEW segment (ss_next_segid); the immutable public 8// segment is never mutated and full history survives (ss_scan_all). This is LOGICAL eviction from every live 9// surface -- it does NOT physically erase the bytes from the public segment file on disk (that would require a 10// destructive segment rewrite, which the immutable-store law forbids; see the review notes). 11// license_tier: ORIGINAL 12import "nx_docportal_lib.nx" // dp_read / dp_prefix / dp_set_policy / DP_VIS_PUBLIC (+ ss_*/sys_* transitively) 13import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc) 14import "nx_docportal_search_seg.nx" // dss_mkkey / dss_mkurlkey (byte-match the search + corpus doc:/url: keys) 15const K_MAGIC_2048: i64 = 2048 16 17func dpr_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 18// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer 19// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the 20// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls). 21// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign. 22func dpr_num(v: i64) -> i64 { nxi_out(v); return 0 } 23func dpr_cat(out: *u8, o: i64, s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { out[o] = s[i]; o = o + 1; i = i + 1 } return o } 24func dpr_atoi(s: *u8) -> i64 { 25 var v: i64 = 0; var i: i64 = 0 26 while s[i] != (0 as u8) { let c: i64 = s[i] as i64; if c >= 48 { if c <= 57 { v = v * 10 + (c - 48) } } i = i + 1 } 27 return v 28} 29// "knowledge/store/dp-<domain>-secret-" (a third shard class alongside -pub-/-prv-, never opened by search) 30func dpr_secret_prefix(domain: *u8, out: *u8) -> i64 { 31 var o: i64 = dpr_cat(out, 0, "knowledge/store/dp-" as *u8) 32 o = dpr_cat(out, o, domain) 33 o = dpr_cat(out, o, "-secret-" as *u8) 34 out[o] = 0 as u8 35 return o 36} 37// "class:<cid>" classification-marker key 38func dpr_classkey(cid: i64, out: *u8) -> i64 { 39 var o: i64 = dpr_cat(out, 0, "class:" as *u8) 40 if cid == 0 { out[o] = 48 as u8; o = o + 1; out[o] = 0 as u8; return o } 41 let t: *u8 = sys_mmap(28); var k: i64 = 0; var m: i64 = cid 42 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 43 var i: i64 = 0; while i < k { out[o] = t[k - 1 - i]; o = o + 1; i = i + 1 } 44 out[o] = 0 as u8; return o 45} 46 47// THE RECLASSIFY. (domain, cid) -> 0 ok; <0 error (-1 not-present, -2 secret add-fail, -3 secret commit-fail, 48// -4 tombstone add-fail, -5 tombstone commit-fail). Ordering is load-bearing: the belt-and-suspenders 49// dp_set_policy (step 4) MUST precede the tombstone (step 5) because dp_set_policy re-reads the live doc. 50func dpr_run(domain: *u8, cid: i64) -> i64 { 51 // 1. the doc MUST currently exist in the PUBLIC shard; copy its bytes into memory we own. 52 let pq: *i64 = sys_mmap(16) as *i64 53 let lq: *i64 = sys_mmap(16) as *i64 54 if dp_read(domain, DP_VIS_PUBLIC, cid, pq, lq) == 0 { return 0 - 1 } 55 let dlen: i64 = lq[0] 56 let docsave: *u8 = sys_mmap(dlen + 8) 57 let dsrc: *u8 = pq[0] as *u8 58 var i: i64 = 0 59 while i < dlen { docsave[i] = dsrc[i]; i = i + 1 } 60 61 // 2. read the optional url:<cid> row from the PUBLIC shard (corpus/site docs carry it; library docs do not). 62 let pub_prefix: *u8 = sys_mmap(512) 63 dp_prefix(domain, DP_VIS_PUBLIC, pub_prefix) 64 let h: *i64 = ss_open(pub_prefix) 65 let urlkey: *u8 = sys_mmap(64) 66 dss_mkurlkey(cid, urlkey) 67 var have_url: i64 = 0 68 var ulen: i64 = 0 69 let urlsave: *u8 = sys_mmap(K_MAGIC_2048) 70 if (h as i64) != 0 { 71 let uq: *i64 = sys_mmap(16) as *i64 72 let ul: *i64 = sys_mmap(16) as *i64 73 if ss_hget(h, urlkey, uq, ul) == 1 { 74 ulen = ul[0] 75 if ulen > K_MAGIC_2048 { ulen = K_MAGIC_2048 } 76 let usrc: *u8 = uq[0] as *u8 77 var j: i64 = 0 78 while j < ulen { urlsave[j] = usrc[j]; j = j + 1 } 79 have_url = 1 80 } 81 } 82 83 // 3. WRITE the classified copy into the SECRET shard: doc:<cid> [+ url:<cid>] + class:<cid>=SECRET. 84 let sec_prefix: *u8 = sys_mmap(512) 85 dpr_secret_prefix(domain, sec_prefix) 86 let dkey: *u8 = sys_mmap(64) 87 dss_mkkey(cid, dkey) 88 let classkey: *u8 = sys_mmap(64) 89 dpr_classkey(cid, classkey) 90 let sw: *i64 = ss_begin() 91 if ss_add(sw, 1, dkey, docsave, dlen) < 0 { return 0 - 2 } 92 if have_url == 1 { if ss_add(sw, 1, urlkey, urlsave, ulen) < 0 { return 0 - 2 } } 93 if ss_add(sw, 1, classkey, "SECRET" as *u8, 6) < 0 { return 0 - 2 } 94 let ssegid: i64 = ss_next_segid(sec_prefix) 95 if ss_commit(sec_prefix, sw, ssegid) != 0 { return 0 - 3 } 96 97 // 4. belt-and-suspenders: clear pub_search in the PUBLIC shard while the doc is still live. Redundant given 98 // the tombstone below (which supersedes it), fire-and-forget, but REQUIRED to precede step 5: dp_set_policy 99 // calls dp_read internally and fails once the doc is tombstoned. 100 dp_set_policy(domain, DP_VIS_PUBLIC, cid, 0) 101 102 // 5. TOMBSTONE doc:<cid> [+ url:<cid>] in the PUBLIC shard as a NEW segment (ss_next_segid): the immutable 103 // live segment is never mutated (Rule 13) and, being newest in manifest order, it shadows the live entry 104 // so ss_hget / ss_term / dss_search all miss it. kind=2 = tombstone; vlen=0 (value ignored on read). 105 let tw: *i64 = ss_begin() 106 if ss_add(tw, 2, dkey, docsave, 0) < 0 { return 0 - 4 } 107 if have_url == 1 { if ss_add(tw, 2, urlkey, urlsave, 0) < 0 { return 0 - 4 } } 108 let tsegid: i64 = ss_next_segid(pub_prefix) 109 if ss_commit(pub_prefix, tw, tsegid) != 0 { return 0 - 5 } 110 return 0 111} 112 113// CLI: nx_dp_reclassify <domain> <cid> (argv idiom modeled on nx_corpus_ingest.main -- argv[k] as *u8) 114func main(argc: i64, argv: *i64) -> i64 { 115 if argc < 3 { dpr_puts("usage: nx_dp_reclassify <domain> <cid>\n" as *u8); return 1 } 116 let domain: *u8 = argv[1] as *u8 117 let cid: i64 = dpr_atoi(argv[2] as *u8) 118 let rc: i64 = dpr_run(domain, cid) 119 if rc == 0 { 120 dpr_puts("reclassify: domain=" as *u8); dpr_puts(domain) 121 dpr_puts(" cid=" as *u8); dpr_num(cid) 122 dpr_puts(" -> SECRET (public tombstoned; bytes preserved in dp-" as *u8); dpr_puts(domain); dpr_puts("-secret-)\n" as *u8) 123 return 0 124 } 125 dpr_puts("reclassify: FAILED rc=" as *u8); dpr_num(rc); dpr_puts(" domain=" as *u8); dpr_puts(domain); dpr_puts("\n" as *u8) 126 return 2 127}