code wiki / _hdl_build / nx_connect_social.nx
nx_connect_social.nx source
↩ module page · 377 lines · 15476 B
1// nx_connect_social.nx -- CONNECT SOCIAL SURFACE: profile cards + moments feed + stories (TTL) +
2// consent-preserving contact discovery (exact handle + QR-style share token). Closes three SOC cells
3// of nx_connect_func_census (profile+feed, stories, contact discovery + QR add).
4// BY CONSTRUCTION properties (each with a tooth or neg-control):
5// - faith is GDPR Art.9 special-category: the profile CARD omits it unless faith_consent=1.
6// - looks de-emphasized: the profile schema has NO photo-rank field at all; cards are text-first.
7// - delete = TOMBSTONE (additive-only, rule 13): a deleted moment / expired story stops RENDERING
8// but its bytes are never destroyed.
9// - discovery is CONSENT-ARTIFACT-ONLY: you are findable by your chosen handle or a QR token YOU
10// minted -- there is NO phonebook-upload ingestion path, so a non-consenting person structurally
11// cannot be surfaced. NEG-CONTROL: the incumbent phonebook-upload model (FB 'people you may know'
12// style) surfaces exactly that non-consenting user; the same checker proves ours cannot.
13// Times are passed in (deterministic, no clock). 100% sovereign. license_tier: ORIGINAL expect_exit: 0
14import "nx_syscalls.nx"
15const PF_MAGIC_4096: i64 = 4096
16const PF_MAGIC_4095: i64 = 4095
17const PF_MAGIC_7741: i64 = 7741
18const PF_MAGIC_88123: i64 = 88123
19const PF_MAGIC_9999: i64 = 9999
20const PF_MAGIC_31337: i64 = 31337
21
22const PF_MAX: i64 = 16
23const MO_MAX: i64 = 32
24const ST_MAX: i64 = 16
25const FW_MAX: i64 = 32
26const DH_MAX: i64 = 16
27const QR_MAX: i64 = 16
28
29const CX_PFU: i64 = 0
30const CX_PFTEACH: i64 = 1
31const CX_PFLEARN: i64 = 2
32const CX_PFINT: i64 = 3
33const CX_PFFAITH: i64 = 4
34const CX_PFCONS: i64 = 5
35const CX_NPF: i64 = 6
36const CX_MOU: i64 = 7
37const CX_MOT: i64 = 8
38const CX_MOTX: i64 = 9
39const CX_MOTOMB: i64 = 10
40const CX_NMO: i64 = 11
41const CX_FWF: i64 = 12
42const CX_FWT: i64 = 13
43const CX_NFW: i64 = 14
44const CX_STU: i64 = 15
45const CX_STT: i64 = 16
46const CX_STTTL: i64 = 17
47const CX_STTX: i64 = 18
48const CX_STTOMB: i64 = 19
49const CX_NST: i64 = 20
50const CX_DHH: i64 = 21
51const CX_DHU: i64 = 22
52const CX_NDH: i64 = 23
53const CX_QRT: i64 = 24
54const CX_QRU: i64 = 25
55const CX_QRE: i64 = 26
56const CX_NQR: i64 = 27
57
58func sw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
59func sn(v: i64) -> i64 { let bb: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m;sys_write(1,"-" as *u8,1)} let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48 as u8;k=1} while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} var i: i64=0; while i<k{bb[i]=t[k-1-i];i=i+1} sys_write(1,bb,k); return 0 }
60func tcheck(pass: i64, label: *u8, fails: *i64) -> i64 {
61 sw(" " as *u8); sw(label); sw(": " as *u8)
62 if pass==1 { sw("PASS\n" as *u8) } else { sw("FAIL\n" as *u8); fails[0]=fails[0]+1 }
63 return 0
64}
65func cxp(ctx: *i64, i: i64) -> *i64 { return ctx[i] as *i64 }
66func cxb(ctx: *i64, i: i64) -> *u8 { return ctx[i] as *u8 }
67func so_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
68func so_app(dst: *u8, pos: *i64, cap: i64, s: *u8) -> i64 {
69 var i: i64=0
70 while s[i]!=(0 as u8) { let p: i64=*pos; if p<cap { dst[p]=s[i]; *pos=p+1 } i=i+1 }
71 return 0
72}
73func so_find(buf: *u8, n: i64, needle: *u8) -> i64 {
74 let m: i64 = so_slen(needle)
75 if m==0 { return 0 }
76 var i: i64=0
77 while i+m<=n {
78 var j: i64=0
79 var hit: i64=1
80 while j<m { if buf[i+j]!=needle[j] { hit=0; break } j=j+1 }
81 if hit==1 { return 1 }
82 i=i+1
83 }
84 return 0
85}
86func so_copy64(dst: *u8, slot: i64, s: *u8) -> i64 {
87 var w: i64=0
88 while s[w]!=(0 as u8) { if w<63 { dst[slot*64+w]=s[w] } w=w+1 }
89 if w>63 { w=63 }
90 dst[slot*64+w]=0 as u8
91 return w
92}
93
94func sn_into(dst: *u8, pos: *i64, cap: i64, v: i64) -> i64 {
95 let t: *u8=sys_mmap(28)
96 var m: i64=v
97 var k: i64=0
98 if m<0 { m=0-m }
99 if m==0 { t[0]=48 as u8; k=1 }
100 while m>0 { t[k]=(48+(m%10)) as u8; m=m/10; k=k+1 }
101 var i: i64=0
102 while i<k { let p: i64=*pos; if p<cap { dst[p]=t[k-1-i]; *pos=p+1 } i=i+1 }
103 return 0
104}
105// find needle within the 64-byte slot `slot` of a slotted text arena
106func so_find_slot(arena: *u8, slot: i64, needle: *u8) -> i64 {
107 let m: i64 = so_slen(needle)
108 if m==0 { return 0 }
109 var i: i64=0
110 while i+m<=63 {
111 var j: i64=0
112 var hit: i64=1
113 while j<m { if arena[slot*64+i+j]!=needle[j] { hit=0; break } j=j+1 }
114 if hit==1 { return 1 }
115 i=i+1
116 }
117 return 0
118}
119func so_prof_idx(ctx: *i64, user: i64) -> i64 {
120 let pu: *i64 = cxp(ctx, CX_PFU)
121 let np: *i64 = cxp(ctx, CX_NPF)
122 var i: i64=0
123 while i<np[0] { if pu[i]==user { return i } i=i+1 }
124 return 0-1
125}
126// render the profile CARD: languages + interests count; faith ONLY if consented (Art.9)
127func so_card(ctx: *i64, user: i64, out: *u8, cap: i64) -> i64 {
128 let pos: *i64 = sys_mmap(8) as *i64
129 *pos=0
130 let pi: i64 = so_prof_idx(ctx, user)
131 if pi<0 { out[0]=0 as u8; return 0 }
132 let pteach: *i64 = cxp(ctx, CX_PFTEACH)
133 let plearn: *i64 = cxp(ctx, CX_PFLEARN)
134 let pint: *i64 = cxp(ctx, CX_PFINT)
135 let pcons: *i64 = cxp(ctx, CX_PFCONS)
136 let pfaith: *i64 = cxp(ctx, CX_PFFAITH)
137 so_app(out, pos, cap, "teaches:" as *u8)
138 sn_into(out, pos, cap, pteach[pi])
139 so_app(out, pos, cap, " learns:" as *u8)
140 sn_into(out, pos, cap, plearn[pi])
141 so_app(out, pos, cap, " interests:" as *u8)
142 sn_into(out, pos, cap, pint[pi])
143 if pcons[pi]==1 {
144 so_app(out, pos, cap, " faith:" as *u8)
145 sn_into(out, pos, cap, pfaith[pi])
146 }
147 let e: i64=*pos
148 out[e]=0 as u8
149 return e
150}
151func so_follows(ctx: *i64, a: i64, b: i64) -> i64 {
152 let ff: *i64 = cxp(ctx, CX_FWF)
153 let ft: *i64 = cxp(ctx, CX_FWT)
154 let nf: *i64 = cxp(ctx, CX_NFW)
155 var i: i64=0
156 while i<nf[0] { if ff[i]==a { if ft[i]==b { return 1 } } i=i+1 }
157 return 0
158}
159// collect feed for viewer: moments of FOLLOWED users, non-tombstoned, time-DESC. returns count.
160func so_feed(ctx: *i64, viewer: i64, oidx: *i64, maxn: i64) -> i64 {
161 let mu: *i64 = cxp(ctx, CX_MOU)
162 let mt: *i64 = cxp(ctx, CX_MOT)
163 let mb: *i64 = cxp(ctx, CX_MOTOMB)
164 let nm: *i64 = cxp(ctx, CX_NMO)
165 var c: i64=0
166 var i: i64=0
167 while i<nm[0] {
168 if mb[i]==0 {
169 if so_follows(ctx, viewer, mu[i])==1 { if c<maxn { oidx[c]=i; c=c+1 } }
170 }
171 i=i+1
172 }
173 // bubble time-desc (small n)
174 var a: i64=0
175 while a<c {
176 var b: i64=0
177 while b+1<c-a {
178 if mt[oidx[b]] < mt[oidx[b+1]] { let tmp: i64=oidx[b]; oidx[b]=oidx[b+1]; oidx[b+1]=tmp }
179 b=b+1
180 }
181 a=a+1
182 }
183 return c
184}
185// a story renders iff not tombstoned and now < posted+ttl
186func so_story_visible(ctx: *i64, si: i64, now: i64) -> i64 {
187 let st: *i64 = cxp(ctx, CX_STT)
188 let sl: *i64 = cxp(ctx, CX_STTTL)
189 let sb: *i64 = cxp(ctx, CX_STTOMB)
190 if sb[si]==1 { return 0 }
191 if now < st[si]+sl[si] { return 1 }
192 return 0
193}
194// discovery: exact handle -> user, else -1. NO fuzzy, NO phonebook path exists at all.
195func so_discover_handle(ctx: *i64, handle: i64) -> i64 {
196 let dh: *i64 = cxp(ctx, CX_DHH)
197 let du: *i64 = cxp(ctx, CX_DHU)
198 let nd: *i64 = cxp(ctx, CX_NDH)
199 var i: i64=0
200 while i<nd[0] { if dh[i]==handle { return du[i] } i=i+1 }
201 return 0-1
202}
203// QR-style share token: minted BY the user (consent artifact), expiring. -1 forged, -2 expired.
204func so_add_by_token(ctx: *i64, token: i64, now: i64) -> i64 {
205 let qt: *i64 = cxp(ctx, CX_QRT)
206 let qu: *i64 = cxp(ctx, CX_QRU)
207 let qe: *i64 = cxp(ctx, CX_QRE)
208 let nq: *i64 = cxp(ctx, CX_NQR)
209 var i: i64=0
210 while i<nq[0] {
211 if qt[i]==token {
212 if now<qe[i] { return qu[i] }
213 return 0-2
214 }
215 i=i+1
216 }
217 return 0-1
218}
219// NEG-CONTROL incumbent model: PHONEBOOK-UPLOAD discovery -- surfaces anyone whose number appears in
220// an uploaded contact book, consent NEVER consulted. (FB 'people you may know' class.)
221func so_phonebook_discover(pbook: *i64, npb: i64, target_user: i64) -> i64 {
222 var i: i64=0
223 while i<npb { if pbook[i]==target_user { return target_user } i=i+1 }
224 return 0-1
225}
226
227func main() -> i64 {
228 let fails: *i64 = sys_mmap(16) as *i64
229 fails[0]=0
230 sw("=== nx_connect_social -- profile + moments feed + stories(TTL) + consent-only discovery ===\n" as *u8)
231
232 let ctx: *i64 = sys_mmap(32*8) as *i64
233 let pf_user: *i64 = sys_mmap(PF_MAX*8) as *i64
234 let pf_teach: *i64 = sys_mmap(PF_MAX*8) as *i64
235 let pf_learn: *i64 = sys_mmap(PF_MAX*8) as *i64
236 let pf_int: *i64 = sys_mmap(PF_MAX*8) as *i64
237 let pf_faith: *i64 = sys_mmap(PF_MAX*8) as *i64
238 let pf_cons: *i64 = sys_mmap(PF_MAX*8) as *i64
239 let npf: *i64 = sys_mmap(8) as *i64
240 npf[0]=0
241 let mo_user: *i64 = sys_mmap(MO_MAX*8) as *i64
242 let mo_time: *i64 = sys_mmap(MO_MAX*8) as *i64
243 let mo_text: *u8 = sys_mmap(MO_MAX*64)
244 let mo_tomb: *i64 = sys_mmap(MO_MAX*8) as *i64
245 let nmo: *i64 = sys_mmap(8) as *i64
246 nmo[0]=0
247 let fw_from: *i64 = sys_mmap(FW_MAX*8) as *i64
248 let fw_to: *i64 = sys_mmap(FW_MAX*8) as *i64
249 let nfw: *i64 = sys_mmap(8) as *i64
250 nfw[0]=0
251 let st_user: *i64 = sys_mmap(ST_MAX*8) as *i64
252 let st_time: *i64 = sys_mmap(ST_MAX*8) as *i64
253 let st_ttl: *i64 = sys_mmap(ST_MAX*8) as *i64
254 let st_text: *u8 = sys_mmap(ST_MAX*64)
255 let st_tomb: *i64 = sys_mmap(ST_MAX*8) as *i64
256 let nst: *i64 = sys_mmap(8) as *i64
257 nst[0]=0
258 let dh_h: *i64 = sys_mmap(DH_MAX*8) as *i64
259 let dh_u: *i64 = sys_mmap(DH_MAX*8) as *i64
260 let ndh: *i64 = sys_mmap(8) as *i64
261 ndh[0]=0
262 let qr_t: *i64 = sys_mmap(QR_MAX*8) as *i64
263 let qr_u: *i64 = sys_mmap(QR_MAX*8) as *i64
264 let qr_e: *i64 = sys_mmap(QR_MAX*8) as *i64
265 let nqr: *i64 = sys_mmap(8) as *i64
266 nqr[0]=0
267 ctx[CX_PFU]=pf_user as i64; ctx[CX_PFTEACH]=pf_teach as i64; ctx[CX_PFLEARN]=pf_learn as i64
268 ctx[CX_PFINT]=pf_int as i64; ctx[CX_PFFAITH]=pf_faith as i64; ctx[CX_PFCONS]=pf_cons as i64
269 ctx[CX_NPF]=npf as i64
270 ctx[CX_MOU]=mo_user as i64; ctx[CX_MOT]=mo_time as i64; ctx[CX_MOTX]=mo_text as i64
271 ctx[CX_MOTOMB]=mo_tomb as i64; ctx[CX_NMO]=nmo as i64
272 ctx[CX_FWF]=fw_from as i64; ctx[CX_FWT]=fw_to as i64; ctx[CX_NFW]=nfw as i64
273 ctx[CX_STU]=st_user as i64; ctx[CX_STT]=st_time as i64; ctx[CX_STTTL]=st_ttl as i64
274 ctx[CX_STTX]=st_text as i64; ctx[CX_STTOMB]=st_tomb as i64; ctx[CX_NST]=nst as i64
275 ctx[CX_DHH]=dh_h as i64; ctx[CX_DHU]=dh_u as i64; ctx[CX_NDH]=ndh as i64
276 ctx[CX_QRT]=qr_t as i64; ctx[CX_QRU]=qr_u as i64; ctx[CX_QRE]=qr_e as i64; ctx[CX_NQR]=nqr as i64
277
278 // profiles: user 1 (faith consented), user 2 (faith NOT consented -- must stay private)
279 pf_user[0]=1; pf_teach[0]=2; pf_learn[0]=1; pf_int[0]=5; pf_faith[0]=3; pf_cons[0]=1
280 pf_user[1]=2; pf_teach[1]=1; pf_learn[1]=4; pf_int[1]=9; pf_faith[1]=3; pf_cons[1]=0
281 npf[0]=2
282
283 let out: *u8 = sys_mmap(PF_MAGIC_4096)
284
285 // T1: profile card renders languages + interests
286 let n1: i64 = so_card(ctx, 1, out, PF_MAGIC_4095)
287 var t1: i64=0
288 if so_find(out, n1, "teaches:2" as *u8)==1 { if so_find(out, n1, "learns:1" as *u8)==1 { if so_find(out, n1, "interests:5" as *u8)==1 { t1=1 } } }
289 sw(" card u1: " as *u8); sw(out); sw("\n" as *u8)
290 tcheck(t1, "T1 profile card renders teach/learn/interests (text-first, no photo field exists)" as *u8, fails)
291
292 // T2: Art.9 -- faith shown ONLY with consent
293 var t2: i64=0
294 if so_find(out, n1, "faith:3" as *u8)==1 {
295 let n2: i64 = so_card(ctx, 2, out, PF_MAGIC_4095)
296 if so_find(out, n2, "faith" as *u8)==0 { t2=1 }
297 sw(" card u2 (no faith consent): " as *u8); sw(out); sw("\n" as *u8)
298 }
299 tcheck(t2, "T2 faith on the card ONLY with explicit consent (Art.9 default-private)" as *u8, fails)
300
301 // moments: u1 posts at t=10, t=30; u3 posts at t=20 (viewer follows 1 and 3, not 2)
302 mo_user[0]=1; mo_time[0]=10; mo_tomb[0]=0
303 so_copy64(mo_text, 0, "learned 20 words" as *u8)
304 mo_user[1]=3; mo_time[1]=20; mo_tomb[1]=0
305 so_copy64(mo_text, 1, "hiked the ridge" as *u8)
306 mo_user[2]=1; mo_time[2]=30; mo_tomb[2]=0
307 so_copy64(mo_text, 2, "first call in polish" as *u8)
308 mo_user[3]=2; mo_time[3]=25; mo_tomb[3]=0
309 so_copy64(mo_text, 3, "not followed post" as *u8)
310 nmo[0]=4
311 fw_from[0]=9; fw_to[0]=1
312 fw_from[1]=9; fw_to[1]=3
313 nfw[0]=2
314
315 // T3: feed = followed users only, time-DESC
316 let oidx: *i64 = sys_mmap(16*8) as *i64
317 let c3: i64 = so_feed(ctx, 9, oidx, 16)
318 var t3: i64=0
319 if c3==3 { if oidx[0]==2 { if oidx[1]==1 { if oidx[2]==0 { t3=1 } } } }
320 tcheck(t3, "T3 feed merges followed users time-desc; non-followed absent" as *u8, fails)
321
322 // T4: delete = tombstone -- moment leaves the feed, bytes remain (additive-only)
323 mo_tomb[1]=1
324 let c4: i64 = so_feed(ctx, 9, oidx, 16)
325 var t4: i64=0
326 if c4==2 { if so_find_slot(mo_text, 1, "hiked" as *u8)==1 { t4=1 } }
327 tcheck(t4, "T4 tombstoned moment leaves feed; bytes preserved (additive, never destroyed)" as *u8, fails)
328
329 // stories: u1 posts at t=100 ttl=50
330 st_user[0]=1; st_time[0]=100; st_ttl[0]=50; st_tomb[0]=0
331 so_copy64(st_text, 0, "story of the day" as *u8)
332 nst[0]=1
333
334 // T5: story visible before expiry, gone after, bytes remain
335 let v5a: i64 = so_story_visible(ctx, 0, 120)
336 let v5b: i64 = so_story_visible(ctx, 0, 151)
337 var t5: i64=0
338 if v5a==1 { if v5b==0 { if so_find_slot(st_text, 0, "story of" as *u8)==1 { t5=1 } } }
339 tcheck(t5, "T5 story TTL: visible pre-expiry, hidden post-expiry, bytes preserved" as *u8, fails)
340
341 // discovery: u1 registered handle 7741; u2 minted QR token 88123 exp t=500. user 555 = NOTHING.
342 dh_h[0]=PF_MAGIC_7741; dh_u[0]=1
343 ndh[0]=1
344 qr_t[0]=PF_MAGIC_88123; qr_u[0]=2; qr_e[0]=500
345 nqr[0]=1
346
347 // T6: exact-handle discovery; unregistered handle NOT found (no fuzzy leak)
348 let d6a: i64 = so_discover_handle(ctx, PF_MAGIC_7741)
349 let d6b: i64 = so_discover_handle(ctx, PF_MAGIC_9999)
350 var t6: i64=0
351 if d6a==1 { if d6b==(0-1) { t6=1 } }
352 tcheck(t6, "T6 discovery: exact handle hit; unregistered handle not found" as *u8, fails)
353
354 // T7: QR token add -- valid works; forged refused; expired refused
355 let a7: i64 = so_add_by_token(ctx, PF_MAGIC_88123, 400)
356 let f7: i64 = so_add_by_token(ctx, PF_MAGIC_31337, 400)
357 let e7: i64 = so_add_by_token(ctx, PF_MAGIC_88123, 600)
358 var t7: i64=0
359 if a7==2 { if f7==(0-1) { if e7==(0-2) { t7=1 } } }
360 tcheck(t7, "T7 QR share token: valid add, forged refused, expired refused" as *u8, fails)
361
362 // T8: NEG-CONTROL -- phonebook-upload incumbent surfaces non-consenting user 555; ours has NO path
363 let pbook: *i64 = sys_mmap(8*8) as *i64
364 pbook[0]=555; pbook[1]=42
365 let inc8: i64 = so_phonebook_discover(pbook, 2, 555)
366 let ours8: i64 = so_discover_handle(ctx, 555)
367 var t8: i64=0
368 if inc8==555 { if ours8==(0-1) { t8=1 } }
369 sw(" incumbent phonebook model surfaced non-consenting user " as *u8); sn(inc8); sw("; our consent-only discovery: not found\n" as *u8)
370 tcheck(t8, "T8 NEG-CONTROL phonebook-upload surfaces non-consenting user; consent-only cannot" as *u8, fails)
371
372 sw(" fails=" as *u8); sn(fails[0]); sw("\n" as *u8)
373 if fails[0]==0 { sw("VERDICT: GREEN (social surface live: Art.9 faith gate, additive feed/stories, consent-only discovery)\n" as *u8); sys_exit(0) }
374 sw("VERDICT: RED\n" as *u8)
375 sys_exit(1)
376 return 1
377}