code wiki / _hdl_build / nx_connect_group_lib.nx
nx_connect_group_lib.nx source
↩ module page · 70 lines · 3266 B
1// nx_connect_group_lib.nx -- group + broadcast-channel ROLES and PERMISSIONS, extracted as a shared library
2// so the verifying gate (nx_connect_group) and the LIVE app (nx_connect_serve /community) bind ONE
3// implementation. Until this split the permission model had NO user-facing surface at all.
4//
5// THE MODEL, by construction rather than by policy: OWNER > ADMIN > MEMBER. Only admins add or remove; the
6// OWNER can never be removed; an admin cannot remove a peer or anyone above them, so there is no admin coup;
7// only the owner promotes. A CHANNEL is admin-post / subscriber-read, a GROUP is members-post. The
8// minor-to-romance wall extends to membership, so a minor is never admitted to a DATE-lane group.
9// Symbols moved VERBATIM (no renames): renaming is what de-grounded compare rows on the earlier splits.
10// license_tier: ORIGINAL
11const ROLE_NONE: i64 = 0
12const ROLE_MEMBER: i64 = 1
13const ROLE_ADMIN: i64 = 2
14const ROLE_OWNER: i64 = 3
15const MODE_GROUP: i64 = 0
16const MODE_CHANNEL:i64 = 1
17const LANE_LANG: i64 = 0
18const LANE_DATE: i64 = 3
19
20func find_mi(meta: *i64, mem: *i64, uid: i64) -> i64 {
21 var i: i64=0
22 while i<meta[3] { if mem[i*2]==uid { return i } i=i+1 }
23 return 0-1
24}
25func role_of(meta: *i64, mem: *i64, uid: i64) -> i64 {
26 let idx: i64 = find_mi(meta,mem,uid)
27 if idx<0 { return ROLE_NONE }
28 return mem[idx*2+1]
29}
30func grp_create(meta: *i64, mem: *i64, owner: i64, mode: i64, lane: i64) -> i64 {
31 meta[0]=owner; meta[1]=mode; meta[2]=lane; meta[3]=1
32 mem[0]=owner; mem[1]=ROLE_OWNER
33 return 0
34}
35func grp_add(meta: *i64, mem: *i64, actor: i64, uid: i64, is_minor: i64) -> i64 {
36 if role_of(meta,mem,actor) < ROLE_ADMIN { return 0 } // only admin+ add
37 if meta[2]==LANE_DATE { if is_minor==1 { return 0 } } // SAFETY: no minor in romance group
38 if role_of(meta,mem,uid) > ROLE_NONE { return 1 } // already a member (idempotent)
39 let c: i64 = meta[3]
40 mem[c*2]=uid; mem[c*2+1]=ROLE_MEMBER; meta[3]=c+1
41 return 1
42}
43func grp_remove(meta: *i64, mem: *i64, actor: i64, uid: i64) -> i64 {
44 let ar: i64 = role_of(meta,mem,actor)
45 let tr: i64 = role_of(meta,mem,uid)
46 if ar < ROLE_ADMIN { return 0 } // only admin+ remove
47 if uid == meta[0] { return 0 } // OWNER can never be removed
48 if tr == ROLE_NONE { return 0 } // not a member
49 if tr >= ar { return 0 } // cannot remove a peer/higher (no coup)
50 let idx: i64 = find_mi(meta,mem,uid)
51 let c: i64 = meta[3]
52 mem[idx*2] = mem[(c-1)*2] // swap-remove
53 mem[idx*2+1] = mem[(c-1)*2+1]
54 meta[3]=c-1
55 return 1
56}
57func grp_promote(meta: *i64, mem: *i64, actor: i64, uid: i64, newrole: i64) -> i64 {
58 if role_of(meta,mem,actor) != ROLE_OWNER { return 0 } // only owner promotes/demotes
59 let idx: i64 = find_mi(meta,mem,uid)
60 if idx<0 { return 0 }
61 mem[idx*2+1]=newrole
62 return 1
63}
64func grp_can_post(meta: *i64, mem: *i64, uid: i64) -> i64 {
65 let r: i64 = role_of(meta,mem,uid)
66 if r==ROLE_NONE { return 0 }
67 if meta[1]==MODE_CHANNEL { if r>=ROLE_ADMIN { return 1 } return 0 } // channel: admins only
68 return 1 // group: any member
69}
70