code wiki / _hdl_build / nx_connect_msgx.nx
nx_connect_msgx.nx source
↩ module page · 151 lines · 8008 B
1// nx_connect_msgx.nx -- FUNCTIONALITY rung: message BREADTH. Extends the messaging core with the
2// everyday chat features: reactions (toggle), reply/threads, forward (with provenance), @mentions,
3// pinned (admin-gated), full-text search, and disappearing (TTL) messages.
4//
5// Honest design note: full-text search is CLIENT-SIDE here BECAUSE the store is content-blind --
6// the server holds ciphertext and cannot search plaintext (that is the privacy tradeoff vs WeChat /
7// Telegram-cloud, which search server-side because they read everything). The gate proves the client
8// (with the key) finds matches while the server finds ZERO. 7 checks incl. negative controls.
9// 100% sovereign. license_tier: ORIGINAL expect_exit: 0
10import "nx_syscalls.nx"
11const M_MAGIC_9001: i64 = 9001
12const M_MAGIC_9002: i64 = 9002
13const M_MAGIC_9003: i64 = 9003
14const M_MAGIC_9005: i64 = 9005
15
16const M_STRIDE: i64 = 8
17const F_ID: i64 = 0
18const F_SENDER: i64 = 1
19const F_CIPHER: i64 = 2
20const F_REPLY: i64 = 3
21const F_FWD: i64 = 4
22const F_MENT: i64 = 5 // mention bitmask (uid i -> bit i)
23const F_EXP: i64 = 6 // expiry time (0 = no TTL)
24const F_DEL: i64 = 7
25const ROLE_MEMBER: i64 = 1
26const ROLE_ADMIN: i64 = 2
27const EMOJI_HEART: i64 = 1
28
29func sw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
30func 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 }
31
32func bit_set(mask: i64, pos: i64) -> i64 { var m: i64=mask; var i: i64=0; while i<pos { m=m/2; i=i+1 } return m - (m/2)*2 }
33
34// reactions: store stride 3 [msg,user,emoji]; toggle returns 1 if added, 0 if removed
35func react_toggle(rs: *i64, rc: *i64, msg: i64, user: i64, emoji: i64) -> i64 {
36 var i: i64=0
37 while i<rc[0] {
38 if rs[i*3]==msg { if rs[i*3+1]==user { if rs[i*3+2]==emoji {
39 let last: i64=rc[0]-1
40 rs[i*3]=rs[last*3]; rs[i*3+1]=rs[last*3+1]; rs[i*3+2]=rs[last*3+2]
41 rc[0]=last
42 return 0
43 } } }
44 i=i+1
45 }
46 let c: i64=rc[0]; rs[c*3]=msg; rs[c*3+1]=user; rs[c*3+2]=emoji; rc[0]=c+1
47 return 1
48}
49func react_count(rs: *i64, rc: i64, msg: i64, emoji: i64) -> i64 {
50 var n: i64=0; var i: i64=0
51 while i<rc { if rs[i*3]==msg { if rs[i*3+2]==emoji { n=n+1 } } i=i+1 }
52 return n
53}
54func thread_count(M: *i64, n: i64, root: i64) -> i64 {
55 var c: i64=0; var i: i64=0
56 while i<n { if M[i*M_STRIDE+F_REPLY]==root { c=c+1 } i=i+1 }
57 return c
58}
59func pin_add(pset: *i64, pc: *i64, role: i64, msg: i64) -> i64 {
60 if role<ROLE_ADMIN { return 0 } // only admin+ may pin (groups)
61 let c: i64=pc[0]; pset[c]=msg; pc[0]=c+1; return 1
62}
63func is_pinned(pset: *i64, pc: i64, msg: i64) -> i64 {
64 var i: i64=0; while i<pc { if pset[i]==msg { return 1 } i=i+1 } return 0
65}
66func client_search(M: *i64, n: i64, key: i64, term: i64) -> i64 { // client has the key -> can match
67 var c: i64=0; var i: i64=0
68 while i<n { if M[i*M_STRIDE+F_DEL]==0 { if (M[i*M_STRIDE+F_CIPHER]-key)==term { c=c+1 } } i=i+1 }
69 return c
70}
71func server_search(M: *i64, n: i64, term: i64) -> i64 { // server has only ciphertext
72 var c: i64=0; var i: i64=0
73 while i<n { if M[i*M_STRIDE+F_CIPHER]==term { c=c+1 } i=i+1 }
74 return c
75}
76func visible(expiry: i64, now: i64) -> i64 { // disappearing TTL
77 if expiry==0 { return 1 }
78 if now<expiry { return 1 }
79 return 0
80}
81
82func tcheck(pass: i64, label: *u8, fails: *i64) -> i64 {
83 sw(" " as *u8); sw(label); sw(": " as *u8)
84 if pass==1 { sw("PASS\n" as *u8) } else { sw("FAIL\n" as *u8); fails[0]=fails[0]+1 }
85 return 0
86}
87
88func main() -> i64 {
89 let fails: *i64 = sys_mmap(16) as *i64
90 fails[0]=0
91 let key: i64 = 50
92 let M: *i64 = sys_mmap(512) as *i64
93 // [id, sender, cipher=content+key, reply_to, fwd_from, mention_mask, expiry, deleted]
94 M[0*M_STRIDE+F_ID]=1; M[0*M_STRIDE+F_SENDER]=1; M[0*M_STRIDE+F_CIPHER]=M_MAGIC_9001+key; M[0*M_STRIDE+F_REPLY]=0; M[0*M_STRIDE+F_FWD]=0; M[0*M_STRIDE+F_MENT]=4; M[0*M_STRIDE+F_EXP]=0; M[0*M_STRIDE+F_DEL]=0 // mentions uid2 (bit2=4)
95 M[1*M_STRIDE+F_ID]=2; M[1*M_STRIDE+F_SENDER]=2; M[1*M_STRIDE+F_CIPHER]=M_MAGIC_9002+key; M[1*M_STRIDE+F_REPLY]=1; M[1*M_STRIDE+F_FWD]=0; M[1*M_STRIDE+F_MENT]=0; M[1*M_STRIDE+F_EXP]=0; M[1*M_STRIDE+F_DEL]=0 // reply to id1
96 M[2*M_STRIDE+F_ID]=3; M[2*M_STRIDE+F_SENDER]=1; M[2*M_STRIDE+F_CIPHER]=M_MAGIC_9003+key; M[2*M_STRIDE+F_REPLY]=1; M[2*M_STRIDE+F_FWD]=0; M[2*M_STRIDE+F_MENT]=0; M[2*M_STRIDE+F_EXP]=0; M[2*M_STRIDE+F_DEL]=0 // reply to id1
97 M[3*M_STRIDE+F_ID]=4; M[3*M_STRIDE+F_SENDER]=3; M[3*M_STRIDE+F_CIPHER]=M_MAGIC_9001+key; M[3*M_STRIDE+F_REPLY]=0; M[3*M_STRIDE+F_FWD]=1; M[3*M_STRIDE+F_MENT]=0; M[3*M_STRIDE+F_EXP]=0; M[3*M_STRIDE+F_DEL]=0 // forwarded from sender1
98 M[4*M_STRIDE+F_ID]=5; M[4*M_STRIDE+F_SENDER]=2; M[4*M_STRIDE+F_CIPHER]=M_MAGIC_9005+key; M[4*M_STRIDE+F_REPLY]=0; M[4*M_STRIDE+F_FWD]=0; M[4*M_STRIDE+F_MENT]=0; M[4*M_STRIDE+F_EXP]=100; M[4*M_STRIDE+F_DEL]=0 // disappearing, expiry=100
99 let n: i64 = 5
100
101 let rs: *i64 = sys_mmap(512) as *i64
102 let rc: *i64 = sys_mmap(16) as *i64; rc[0]=0
103
104 sw("=== nx_connect_msgx -- message breadth (reactions, threads, forward, mentions, pin, search, TTL) ===\n" as *u8)
105 sw("-- gate checks --\n" as *u8)
106
107 // T1 reactions toggle
108 let r_add1: i64 = react_toggle(rs,rc, 1, 2, EMOJI_HEART) // user2 hearts msg1
109 let r_add2: i64 = react_toggle(rs,rc, 1, 3, EMOJI_HEART) // user3 hearts msg1
110 let cnt2: i64 = react_count(rs, rc[0], 1, EMOJI_HEART)
111 let r_off: i64 = react_toggle(rs,rc, 1, 2, EMOJI_HEART) // user2 un-hearts (toggle)
112 let cnt1: i64 = react_count(rs, rc[0], 1, EMOJI_HEART)
113 var t1: i64=0; if r_add1==1 { if r_add2==1 { if cnt2==2 { if r_off==0 { if cnt1==1 { t1=1 } } } } }
114 tcheck(t1, "T1 reactions (toggle add/remove, per-user count)" as *u8, fails)
115
116 // T2 reply/threads
117 var t2: i64=0; if thread_count(M,n,1)==2 { t2=1 }
118 tcheck(t2, "T2 reply/threads (2 replies to root)" as *u8, fails)
119
120 // T3 forward provenance + content preserved
121 var t3: i64=0; if M[3*M_STRIDE+F_FWD]==1 { if (M[3*M_STRIDE+F_CIPHER]-key)==M_MAGIC_9001 { t3=1 } }
122 tcheck(t3, "T3 forward (provenance kept, content preserved)" as *u8, fails)
123
124 // T4 mentions
125 var t4: i64=0; if bit_set(M[0*M_STRIDE+F_MENT],2)==1 { if bit_set(M[0*M_STRIDE+F_MENT],3)==0 { t4=1 } }
126 tcheck(t4, "T4 @mentions (uid2 mentioned, uid3 not)" as *u8, fails)
127
128 // T5 pinned (admin-gated)
129 let pset: *i64 = sys_mmap(64) as *i64
130 let pc: *i64 = sys_mmap(16) as *i64; pc[0]=0
131 let pin_mem: i64 = pin_add(pset,pc, ROLE_MEMBER, 1) // member cannot pin
132 let pin_adm: i64 = pin_add(pset,pc, ROLE_ADMIN, 1) // admin pins
133 var t5: i64=0; if pin_mem==0 { if pin_adm==1 { if is_pinned(pset,pc[0],1)==1 { if is_pinned(pset,pc[0],3)==0 { t5=1 } } } }
134 tcheck(t5, "T5 pinned (admin-gated; pin/unpin state)" as *u8, fails)
135
136 // T6 full-text search: client finds, server (content-blind) finds nothing
137 let cli: i64 = client_search(M, n, key, M_MAGIC_9003)
138 let srv: i64 = server_search(M, n, M_MAGIC_9003)
139 var t6: i64=0; if cli==1 { if srv==0 { t6=1 } }
140 tcheck(t6, "T6 search is client-side by content-blind design (client=1, server=0)" as *u8, fails)
141
142 // T7 disappearing TTL
143 var t7: i64=0; if visible(M[4*M_STRIDE+F_EXP],50)==1 { if visible(M[4*M_STRIDE+F_EXP],150)==0 { t7=1 } }
144 tcheck(t7, "T7 disappearing messages (visible before TTL, gone after)" as *u8, fails)
145
146 sw(" fails=" as *u8); sn(fails[0]); sw("\n" as *u8)
147 if fails[0]==0 { sw("VERDICT: GREEN (message breadth: reactions/threads/forward/mentions/pin/search/TTL all correct)\n" as *u8); sys_exit(0) }
148 sw("VERDICT: RED\n" as *u8)
149 sys_exit(1)
150 return 1
151}