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}