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