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 — coordinator review</h1><p>Tokens that appear at more than one food bank. No names ever leave an org — 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 — 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}