code wiki / _hdl_build / nx_aid_federation.nx

nx_aid_federation.nx source

↩ module page · 209 lines · 10955 B

1// nx_aid_federation.nx -- LIB: CROSS-ORG charity federation (R-AID-R1). Catches a double-dipper claiming across 2// DIFFERENT food banks WITHOUT any org sharing PII. Mechanism: each org locally computes a SALTED TOKEN = 3// hash(federation_salt || recipient_stable_id) and submits ONLY the token (never the name/PII). The same person 4// yields the same token at every org, so a token appearing at >=2 orgs in the period = a cross-org double-claim. 5// 6// HONEST SCOPE (load-bearing): this proves the ARCHITECTURE -- (A) no PII crosses an org boundary (af_submit 7// takes a TOKEN, by signature; the stored record holds only digits + a day), (B) same person -> same token -> 8// overlap detected, (C) FAIL-OPEN preserved (there is NO auto-deny here -- detection only RAISES a review flag; 9// a human decides, mirroring nx_aid). The token DIGEST is a deliberately-swappable primitive: this uses a 10// polynomial hash for the demo; PRODUCTION MUST swap in a cryptographic hash (sha256) + a coordinator-held 11// high-entropy salt (ideally a real private-set-intersection protocol). Tokens are linkable BY DESIGN (that is 12// how overlap is found) -- harden the primitive + salt management before real deployment. 13// Sovereign seg-store (knowledge/store/aidfed-*, NO TSV), integer-only. license_tier: ORIGINAL 14import "nx_food_science.nx" 15import "nx_seg_store.nx" 16import "nx_syscalls.nx" 17const AF_MAGIC_4096: i64 = 4096 18 19const AF_MOD: i64 = 1000000007 20 21func af_put(prefix: *u8, key: *u8, val: *u8) -> i64 { 22 let vl: i64 = as_len(val) 23 if fd_streq_store(prefix, key, val, vl) == 1 { return 0 } 24 let w: *i64 = ss_begin() 25 ss_add(w, 1, key, val, vl) 26 let seg: i64 = fd_seg_next(prefix) 27 ss_commit(prefix, w, seg) 28 return 1 29} 30 31// the shared federation salt (production: from a coordinator / vault, high-entropy). Seeded here for the demo. 32func af_seed_salt(prefix: *u8, salt: *u8) -> i64 { return af_put(prefix, "aidfed:salt" as *u8, salt) } 33func af_salt(prefix: *u8, out: *u8) -> i64 { 34 let pq: *i64 = sys_mmap(16) as *i64 35 let lq: *i64 = sys_mmap(16) as *i64 36 if ss_get(prefix, "aidfed:salt" as *u8, pq, lq) != 1 { out[0] = 0 as u8; return 0 } 37 let b: *u8 = pq[0] as *u8; let n: i64 = lq[0] 38 var i: i64 = 0; while i < n { out[i] = b[i]; i = i + 1 } out[n] = 0 as u8 39 return n 40} 41 42// polynomial rolling hash (bounded, positive). DEMO PRIMITIVE -- swap for sha256 in production. 43func af_hash2(salt: *u8, s: *u8) -> i64 { 44 var h: i64 = 0 45 var i: i64 = 0 46 while salt[i] != (0 as u8) { h = (h * 131 + (salt[i] as i64)) % AF_MOD; i = i + 1 } 47 h = (h * 131 + 255) % AF_MOD // domain separator between salt and id 48 i = 0 49 while s[i] != (0 as u8) { h = (h * 131 + (s[i] as i64)) % AF_MOD; i = i + 1 } 50 return h 51} 52// compute the salted token for a recipient stable_id, as a DECIMAL string into out. Done LOCALLY at the org; 53// only the result (a number) is ever submitted -- the stable_id (PII) never leaves. 54func af_make_token(prefix: *u8, stable_id: *u8, out: *u8) -> i64 { 55 let salt: *u8 = sys_mmap(128); af_salt(prefix, salt) 56 let h: i64 = af_hash2(salt, stable_id) 57 let o: i64 = fd_apnum(out, 0, h) 58 out[o] = 0 as u8 59 return o 60} 61 62// ---- org token submissions (additive; the input is a TOKEN, never PII) ---- 63func af_orgs_key(out: *u8) -> i64 { var o: i64 = 0; o = as_append(out, o, "aidfed:orgs" as *u8); out[o] = 0 as u8; return o } 64func af_orgn_key(org: *u8, out: *u8) -> i64 { var o: i64 = 0; o = as_append(out, o, "aidfed:orgn:" as *u8); o = as_append(out, o, org); out[o] = 0 as u8; return o } 65func af_sub_key(org: *u8, seq: i64, out: *u8) -> i64 { var o: i64 = 0; o = as_append(out, o, "aidfed:sub:" as *u8); o = as_append(out, o, org); out[o] = 58 as u8; o = o + 1; o = fd_apnum(out, o, seq); out[o] = 0 as u8; return o } 66 67func af_list_append(prefix: *u8, key: *u8, token: *u8) -> i64 { 68 let pq: *i64 = sys_mmap(16) as *i64 69 let lq: *i64 = sys_mmap(16) as *i64 70 let cur: *u8 = sys_mmap(AF_MAGIC_4096) 71 var o: i64 = 0 72 if ss_get(prefix, key, pq, lq) == 1 { 73 let b: *u8 = pq[0] as *u8; let n: i64 = lq[0] 74 var i: i64 = 0; var ls: i64 = 0 75 while i <= n { 76 var sep: i64 = 0 77 if i == n { sep = 1 } else { if b[i] == (9 as u8) { sep = 1 } } 78 if sep == 1 { 79 let tl: i64 = i - ls 80 if tl > 0 { if as_len(token) == tl { var m: i64 = 0; var eq: i64 = 1; while m < tl { if b[ls + m] != token[m] { eq = 0 } m = m + 1 } if eq == 1 { return 0 } } } 81 ls = i + 1 82 } 83 i = i + 1 84 } 85 var u: i64 = 0; while u < n { cur[u] = b[u]; u = u + 1 } o = n 86 cur[o] = 9 as u8; o = o + 1 87 } 88 o = as_append(cur, o, token); cur[o] = 0 as u8 89 return af_put(prefix, key, cur) 90} 91func af_orgn(prefix: *u8, org: *u8) -> i64 { 92 let key: *u8 = sys_mmap(96); af_orgn_key(org, key) 93 let pq: *i64 = sys_mmap(16) as *i64 94 let lq: *i64 = sys_mmap(16) as *i64 95 if ss_get(prefix, key, pq, lq) != 1 { return 0 } 96 return fd_atoi(pq[0] as *u8, lq[0]) 97} 98// an org submits a recipient's TOKEN (not PII) for the period. Additive. 99func af_submit(prefix: *u8, org: *u8, token: *u8, day: i64) -> i64 { 100 let ok: *u8 = sys_mmap(96); af_orgs_key(ok); af_list_append(prefix, ok, org) 101 let tk: *u8 = sys_mmap(64); af_list_append(prefix, "aidfed:tokens" as *u8, token) 102 let seq: i64 = af_orgn(prefix, org) 103 let sk: *u8 = sys_mmap(96); af_sub_key(org, seq, sk) 104 let val: *u8 = sys_mmap(96); var v: i64 = 0 105 v = as_append(val, v, token); val[v] = 9 as u8; v = v + 1 106 v = fd_apnum(val, v, day); val[v] = 0 as u8 107 af_put(prefix, sk, val) 108 let nk: *u8 = sys_mmap(96); af_orgn_key(org, nk) 109 let nv: *u8 = sys_mmap(16); var no: i64 = fd_apnum(nv, 0, seq + 1); nv[no] = 0 as u8 110 af_put(prefix, nk, nv) 111 return seq 112} 113func af_sub_str(prefix: *u8, org: *u8, seq: i64, f: i64, out: *u8) -> i64 { 114 let sk: *u8 = sys_mmap(96); af_sub_key(org, seq, sk) 115 let pq: *i64 = sys_mmap(16) as *i64 116 let lq: *i64 = sys_mmap(16) as *i64 117 if ss_get(prefix, sk, pq, lq) != 1 { out[0] = 0 as u8; return 0 } 118 return fd_field(pq[0] as *u8, lq[0], f, out) 119} 120 121// 1 if org submitted `token` within the period ending at now. 122func af_org_has_token(prefix: *u8, org: *u8, token: *u8, now: i64, period: i64) -> i64 { 123 let n: i64 = af_orgn(prefix, org) 124 var i: i64 = 0 125 while i < n { 126 let tb: *u8 = sys_mmap(64); af_sub_str(prefix, org, i, 0, tb) 127 if fd_streq(tb, token) == 1 { 128 let day: i64 = af_sub_day(prefix, org, i) 129 if day <= now { if (now - day) <= period { return 1 } } 130 } 131 i = i + 1 132 } 133 return 0 134} 135func af_sub_day(prefix: *u8, org: *u8, seq: i64) -> i64 { 136 let db: *u8 = sys_mmap(24); let dl: i64 = af_sub_str(prefix, org, seq, 1, db) 137 if dl == 0 { return 0 } 138 return fd_atoi(db, dl) 139} 140// how many DISTINCT orgs submitted this token within the period. 141func af_token_org_count(prefix: *u8, token: *u8, now: i64, period: i64) -> i64 { 142 let pq: *i64 = sys_mmap(16) as *i64 143 let lq: *i64 = sys_mmap(16) as *i64 144 let ok: *u8 = sys_mmap(96); af_orgs_key(ok) 145 if ss_get(prefix, ok, pq, lq) != 1 { return 0 } 146 let b: *u8 = pq[0] as *u8 147 let n: i64 = lq[0] 148 var c: i64 = 0 149 var i: i64 = 0 150 var ls: i64 = 0 151 while i <= n { 152 var sep: i64 = 0 153 if i == n { sep = 1 } else { if b[i] == (9 as u8) { sep = 1 } } 154 if sep == 1 { 155 let tl: i64 = i - ls 156 if tl > 0 { 157 let org: *u8 = sys_mmap(48) 158 var t: i64 = 0 159 while t < tl { org[t] = b[ls + t]; t = t + 1 } org[tl] = 0 as u8 160 if af_org_has_token(prefix, org, token, now, period) == 1 { c = c + 1 } 161 } 162 ls = i + 1 163 } 164 i = i + 1 165 } 166 return c 167} 168// 1 if this token is a cross-org double-claim (appears at >=2 orgs in the period). A REVIEW flag, never a deny. 169func af_is_cross_dipper(prefix: *u8, token: *u8, now: i64, period: i64) -> i64 { 170 if af_token_org_count(prefix, token, now, period) >= 2 { return 1 } 171 return 0 172} 173 174// render the coordinator review: cross-org tokens flagged (NO names -- only tokens + org counts). Sovereign. 175func af_render_review(prefix: *u8, now: i64, period: i64, out: *u8) -> i64 { 176 var o: i64 = 0 177 o = as_append(out, o, "<!doctype html><html lang='en'><head><meta charset='utf-8'><meta name='viewport' content='width=device-width,initial-scale=1'><title>Cross-org review</title><style>body{margin:0;font-family:system-ui,sans-serif;color:#241c2a;background:#f5f1f7;line-height:1.5}header{background:#4a2f5f;color:#fff;padding:22px}header h1{margin:0;font-size:1.4rem}header p{margin:4px 0 0;color:#e3d5ef}main{max-width:700px;margin:0 auto;padding:16px}table{border-collapse:collapse;width:100%;background:#fff;border-radius:12px;overflow:hidden}th,td{padding:9px 12px;text-align:left;border-top:1px solid #efe9f3}th{background:#ece2f3;color:#42305a}td.tok{font-family:ui-monospace,monospace;color:#6a5b78}.hi{color:#a23a2a;font-weight:700}.muted{color:#6a5b78;font-size:.86rem}</style></head><body><header><h1>Cross-org claims &mdash; coordinator review</h1><p>Tokens that appear at more than one food bank. No names ever leave an org &mdash; only tokens.</p></header><main><table><tr><th>Token (not a name)</th><th>Food banks</th><th>Action</th></tr>" as *u8) 178 let pq: *i64 = sys_mmap(16) as *i64 179 let lq: *i64 = sys_mmap(16) as *i64 180 if ss_get(prefix, "aidfed:tokens" as *u8, pq, lq) == 1 { 181 let b: *u8 = pq[0] as *u8 182 let n: i64 = lq[0] 183 var i: i64 = 0 184 var ls: i64 = 0 185 while i <= n { 186 var sep: i64 = 0 187 if i == n { sep = 1 } else { if b[i] == (9 as u8) { sep = 1 } } 188 if sep == 1 { 189 let tl: i64 = i - ls 190 if tl > 0 { 191 let tok: *u8 = sys_mmap(64) 192 var t: i64 = 0 193 while t < tl { tok[t] = b[ls + t]; t = t + 1 } tok[tl] = 0 as u8 194 let cnt: i64 = af_token_org_count(prefix, tok, now, period) 195 if cnt >= 2 { 196 o = as_append(out, o, "<tr><td class='tok'>" as *u8); o = as_append_escaped(out, o, tok, as_len(tok)) 197 o = as_append(out, o, "</td><td class='hi'>" as *u8); o = fd_apnum(out, o, cnt) 198 o = as_append(out, o, "</td><td>flag for review</td></tr>" as *u8) 199 } 200 } 201 ls = i + 1 202 } 203 i = i + 1 204 } 205 } 206 o = as_append(out, o, "</table><p class='muted'>Privacy-first: orgs exchange one-way tokens, never personal data. Detection only flags for a human &mdash; nobody is auto-denied (fail-open). Token digest is a demo primitive; production uses a cryptographic hash + coordinator-held salt.</p></main></body></html>" as *u8) 207 out[o] = 0 as u8 208 return o 209}