code wiki / (root) / nx_hr_sov.nx

nx_hr_sov.nx source

↩ module page · 197 lines · 13688 B

1// nx_hr_sov.nx -- the SOVEREIGN-STORED HR directory: the same access-SSOT semantics as nx_hr (cred_id-keyed, 2// invited/active/suspended lifecycle, deny-by-default, append-only history) but persisted on nx_seg_store + canon 3// records instead of a TAB-delimited log (operator NO-TSV doctrine [[feedback-no-3rd-party-tsv-sovereign-store]]). 4// This is the ADOPTABLE FOUNDATION for the HR team's migration (filed via the coord queue): a drop-in proven core, 5// so the cutover is "point nx_hr at this + switch callers' store path together" -- NOT a risky unilateral rewrite of 6// the shared organ (torrent/gallery/hub depend on nx_hr's path). seg_store gives latest-wins (resolve), additive 7// history (audit), crash-safe commit -- for free. cred_id = SHA-256(realm|||handle) (same derivation as nx_hr/whoami). 8// record (keyed by cred_id): handle, level, family, status, ts, actor status: invited|active|suspended 9import "hub/nx_no_cookie_session.nx" // nx_ncs_derive_user_id_hash + NX_NCS_OK 10import "nx_seg_store.nx" // ss_begin / ss_add / ss_commit / ss_open / ss_hget 11import "nx_canon_cid.nx" // canon_encode 12import "nx_uxf_decode.nx" // canon_decode 13import "nx_sha256.nx" // sha256_digest (auto-segid) 14import "nx_syscalls.nx" 15const HRS_MAGIC_4096: i64 = 4096 16 17const HRS_OWNER: i64 = 3 18const HRS_MEMBER: i64 = 1 19const HRS_NONE: i64 = 0 20const HRS_ST_NONE: i64 = 0 21const HRS_ST_ACTIVE: i64 = 1 22const HRS_ST_SUSPENDED: i64 = 2 23const HRS_ST_INVITED: i64 = 3 24 25func hrs_streq(a: *u8, b: *u8) -> i64 { var i: i64=0; while 1==1 { let ca: i64=a[i] as i64; let cb: i64=b[i] as i64; if ca!=cb {return 0} if ca==0 {return 1} i=i+1 } return 1 } 26func hrs_hexenc(inp: *u8, n: i64, out: *u8) -> i64 { let hx: *u8="0123456789abcdef" as *u8; var i: i64=0; while i<n { out[i*2]=hx[((inp[i] as i64)>>4)&15]; out[i*2+1]=hx[(inp[i] as i64)&15]; i=i+1 } out[n*2]=0 as u8; return n*2 } 27func hrs_itoa(v: i64, out: *u8) -> i64 { let t: *u8=sys_mmap(28); var m: i64=v; 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{out[i]=t[k-1-i];i=i+1} out[k]=0 as u8; return k } 28func hrs_atoi(s: *u8) -> i64 { var v: i64=0; var i: i64=0; while s[i]!=(0 as u8){ let c: i64=s[i] as i64; if c>=48 { if c<=57 { v=v*10+(c-48) } } i=i+1 } return v } 29 30// the stable cred_id (64-hex) for (realm, handle) -- the directory key, derivable WITHOUT a login (== whoami uid). 31func hrs_cred_id(realm: *u8, realm_n: i64, handle: *u8, hn: i64, out_hex: *u8) -> i64 { 32 let uid: *u8=sys_mmap(32) 33 if nx_ncs_derive_user_id_hash(realm, realm_n, handle, hn, uid) != NX_NCS_OK { out_hex[0]=0 as u8; return 0 } 34 return hrs_hexenc(uid, 32, out_hex) 35} 36 37func hrs_atoi_n(s: *u8, n: i64) -> i64 { var v: i64=0; var i: i64=0; while i<n { let c: i64=s[i] as i64; if c>=48 { if c<=57 { v=v*10+(c-48) } } i=i+1 } return v } 38// AUTO-SEGID: a UNIQUE seg_store commit id derived from the record content (sha256(cred|status|ts), 7 bytes -> 39// always-positive i64). Distinct (cred,status,ts) -> distinct segid (no data loss); an idempotent re-emit of the 40// IDENTICAL record -> same segid -> harmless overwrite. So the HR API needs NO segid param (true drop-in for nx_hr). 41func hrs_segid(cred_id: *u8, status: *u8, ts: i64) -> i64 { 42 let b: *u8=sys_mmap(256); var o: i64=0 43 var i: i64=0; while cred_id[i]!=(0 as u8){ b[o]=cred_id[i]; o=o+1; i=i+1 } 44 b[o]=124 as u8; o=o+1 45 var j: i64=0; while status[j]!=(0 as u8){ b[o]=status[j]; o=o+1; j=j+1 } 46 b[o]=124 as u8; o=o+1 47 let tss: *u8=sys_mmap(28); let tl: i64=hrs_itoa(ts, tss); var k: i64=0; while k<tl{ b[o]=tss[k]; o=o+1; k=k+1 } 48 let dig: *u8=sys_mmap(32); sha256_digest(b, o, dig) 49 var v: i64=0; var m: i64=0; while m<7 { v=(v<<8)|((dig[m] as i64)&255); m=m+1 } // 56-bit -> positive 50 return v 51} 52// canon-encode the 6-field record into out; returns blen. 53func hrs_encode_rec(handle: *u8, level: i64, family: *u8, status: *u8, ts: i64, actor: *u8, out: *u8) -> i64 { 54 let lv: *u8=sys_mmap(28); hrs_itoa(level, lv) 55 let tss: *u8=sys_mmap(28); hrs_itoa(ts, tss) 56 let keys: *i64=sys_mmap(8*7) as *i64; let vals: *i64=sys_mmap(8*7) as *i64 57 keys[0]="handle" as *u8 as i64; vals[0]=handle as i64 58 keys[1]="level" as *u8 as i64; vals[1]=lv as i64 59 keys[2]="family" as *u8 as i64; vals[2]=family as i64 60 keys[3]="status" as *u8 as i64; vals[3]=status as i64 61 keys[4]="ts" as *u8 as i64; vals[4]=tss as i64 62 keys[5]="actor" as *u8 as i64; vals[5]=actor as i64 63 return canon_encode(keys, vals, 6, out) 64} 65// 1 iff a segment with this segid is ALREADY in the manifest ("seg-<segid>"). Re-committing a deterministic segid would 66// DUPLICATE the manifest entry (seg_store's contract is unique-segid-per-commit, which corrupts reads) -> dedup here 67// makes every put IDEMPOTENT (#10): re-migrating / re-provisioning the identical record is a true no-op. 68func hrs_seg_committed(prefix: *u8, segid: i64) -> i64 { 69 let segs: *i64=sys_mmap(8*260) as *i64; let ns: i64=ss_manifest(prefix, segs) 70 if ns<=0 { return 0 } 71 let want: *u8=sys_mmap(64); let p: *u8="seg-" as *u8; var o: i64=0; while p[o]!=(0 as u8){want[o]=p[o];o=o+1} 72 hrs_itoa(segid, (want as i64 + o) as *u8) 73 var i: i64=0; while i<ns { if hrs_streq(segs[i] as *u8, want)==1 { return 1 } i=i+1 } return 0 74} 75// PUT one directory record keyed by cred_id (no index touch) -- for suspend/set_role on an EXISTING cred. UNIQUE segid. 76func hrs_put(prefix: *u8, cred_id: *u8, handle: *u8, level: i64, family: *u8, status: *u8, ts: i64, actor: *u8, segid: i64) -> i64 { 77 if hrs_seg_committed(prefix, segid)==1 { return 0 } 78 let buf: *u8=sys_mmap(HRS_MAGIC_4096); let blen: i64=hrs_encode_rec(handle, level, family, status, ts, actor, buf) 79 let w: *i64=ss_begin(); ss_add(w, 1, cred_id, buf, blen); ss_commit(prefix, w, segid) 80 return 0 81} 82// ---- the roster INDEX (so seg_store can ENUMERATE distinct creds, like nx_native_config's count+NNNN) ---- 83// key "idx:count" -> N ; key "idx:<i>" -> the i-th distinct cred_id (64 hex). 84func hrs_idx_key(i: i64, out: *u8) -> i64 { let p: *u8="idx:" as *u8; var o: i64=0; while p[o]!=(0 as u8){out[o]=p[o];o=o+1} let k: i64=hrs_itoa(i, (out as i64 + o) as *u8); return o+k } 85func hrs_index_count(prefix: *u8) -> i64 { 86 let h: *i64=ss_open(prefix); if (h as i64)==0 { return 0 } 87 let pp: *i64=sys_mmap(16) as *i64; let ll: *i64=sys_mmap(16) as *i64 88 if ss_hget(h, "idx:count" as *u8, pp, ll) != 1 { return 0 } 89 return hrs_atoi_n(pp[0] as *u8, ll[0]) 90} 91func hrs_index_get_cred(prefix: *u8, i: i64, out64: *u8) -> i64 { 92 let ik: *u8=sys_mmap(32); hrs_idx_key(i, ik); ik[hrs_idx_key(i, ik)]=0 as u8 93 let h: *i64=ss_open(prefix); if (h as i64)==0 { return 0 } 94 let pp: *i64=sys_mmap(16) as *i64; let ll: *i64=sys_mmap(16) as *i64 95 if ss_hget(h, ik, pp, ll) != 1 { return 0 } 96 let src: *u8=pp[0] as *u8; var j: i64=0; while j<ll[0] { out64[j]=src[j]; j=j+1 } out64[ll[0]]=0 as u8 97 return 1 98} 99// PUT a record AND (if cred is new) append it to the roster index -- ONE atomic commit. for enroll/invite. 100func hrs_put_indexed(prefix: *u8, cred_id: *u8, handle: *u8, level: i64, family: *u8, status: *u8, ts: i64, actor: *u8, segid: i64) -> i64 { 101 if hrs_seg_committed(prefix, segid)==1 { return 0 } // idempotent: this exact record already committed (no manifest dup) 102 var isnew: i64 = 0; if hrs_status_code(prefix, cred_id)==HRS_ST_NONE { isnew=1 } 103 var n: i64 = 0; if isnew==1 { n=hrs_index_count(prefix) } 104 let buf: *u8=sys_mmap(HRS_MAGIC_4096); let blen: i64=hrs_encode_rec(handle, level, family, status, ts, actor, buf) 105 let w: *i64=ss_begin() 106 ss_add(w, 1, cred_id, buf, blen) 107 if isnew==1 { 108 let ik: *u8=sys_mmap(32); let ikl: i64=hrs_idx_key(n, ik); ik[ikl]=0 as u8 109 ss_add(w, 1, ik, cred_id, 64) 110 let cv: *u8=sys_mmap(28); let cl: i64=hrs_itoa(n+1, cv) 111 ss_add(w, 1, "idx:count" as *u8, cv, cl) 112 } 113 ss_commit(prefix, w, segid) 114 return 0 115} 116// read the LATEST record for cred_id -> decode into out_keys/out_vals. returns nf (>0) or <=0 if absent. 117func hrs_get(prefix: *u8, cred_id: *u8, out_keys: *i64, out_vals: *i64, maxf: i64) -> i64 { 118 let h: *i64=ss_open(prefix); if (h as i64)==0 { return 0 } 119 let pp: *i64=sys_mmap(16) as *i64; let ll: *i64=sys_mmap(16) as *i64 120 if ss_hget(h, cred_id, pp, ll) != 1 { return 0 } 121 return canon_decode(pp[0] as *u8, ll[0], out_keys, out_vals, maxf) 122} 123func hrs_field(ok: *i64, ov: *i64, nf: i64, name: *u8) -> *u8 { var i: i64=0; while i<nf { if hrs_streq(ok[i] as *u8, name)==1 { return ov[i] as *u8 } i=i+1 } return 0 as *u8 } 124 125// ENROLL active at a level (derives + returns cred_id). INVITE = enroll status=invited. SUSPEND / SET_ROLE = a new put. 126func hrs_enroll(prefix: *u8, realm: *u8, realm_n: i64, handle: *u8, hn: i64, level: i64, family: *u8, ts: i64, actor: *u8, out_cid: *u8) -> i64 { 127 if hrs_cred_id(realm, realm_n, handle, hn, out_cid) <= 0 { return 1 } 128 return hrs_put_indexed(prefix, out_cid, handle, level, family, "active" as *u8, ts, actor, hrs_segid(out_cid, "active" as *u8, ts)) 129} 130func hrs_invite(prefix: *u8, realm: *u8, realm_n: i64, handle: *u8, hn: i64, level: i64, family: *u8, ts: i64, actor: *u8, out_cid: *u8) -> i64 { 131 if hrs_cred_id(realm, realm_n, handle, hn, out_cid) <= 0 { return 1 } 132 return hrs_put_indexed(prefix, out_cid, handle, level, family, "invited" as *u8, ts, actor, hrs_segid(out_cid, "invited" as *u8, ts)) 133} 134func hrs_suspend(prefix: *u8, cred_id: *u8, handle: *u8, family: *u8, ts: i64, actor: *u8) -> i64 { return hrs_put(prefix, cred_id, handle, 0, family, "suspended" as *u8, ts, actor, hrs_segid(cred_id, "suspended" as *u8, ts)) } 135func hrs_set_role(prefix: *u8, cred_id: *u8, handle: *u8, level: i64, family: *u8, ts: i64, actor: *u8) -> i64 { return hrs_put(prefix, cred_id, handle, level, family, "active" as *u8, ts, actor, hrs_segid(cred_id, "active" as *u8, ts)) } 136// MIGRATE one historical record verbatim, replayed by cred_id (preserving status/ts/actor): auto-segid + index on the 137// FIRST occurrence of a cred. The TAB-log -> seg_store migrator calls this per line so ALL versions (history) survive 138// and latest-wins is preserved. Idempotent: re-migrating the identical (cred,status,ts) -> same segid -> no dup. 139func hrs_migrate(prefix: *u8, cred_id: *u8, handle: *u8, level: i64, family: *u8, status: *u8, ts: i64, actor: *u8) -> i64 { 140 return hrs_put_indexed(prefix, cred_id, handle, level, family, status, ts, actor, hrs_segid(cred_id, status, ts)) 141} 142// CLAIM an invite: invited -> active at the SAME intended level (the LAN-signup completing). returns the claimed 143// level (>0), or 0 if the handle was not invited (idempotent: a re-claim of an active user returns 0). 144func hrs_claim(prefix: *u8, realm: *u8, realm_n: i64, handle: *u8, hn: i64, family: *u8, ts: i64, actor: *u8) -> i64 { 145 let cid: *u8=sys_mmap(96); if hrs_cred_id(realm, realm_n, handle, hn, cid) <= 0 { return 0 } 146 let ok: *i64=sys_mmap(64*8) as *i64; let ov: *i64=sys_mmap(64*8) as *i64 147 let nf: i64=hrs_get(prefix, cid, ok, ov, 64) 148 if nf<=0 { return 0 } 149 let st: *u8=hrs_field(ok, ov, nf, "status" as *u8) 150 if (st as i64)==0 { return 0 } 151 if hrs_streq(st, "invited" as *u8)==0 { return 0 } 152 let lvf: *u8=hrs_field(ok, ov, nf, "level" as *u8) 153 var want: i64=0; if (lvf as i64)!=0 { want=hrs_atoi(lvf) } 154 hrs_set_role(prefix, cid, handle, want, family, ts, actor) 155 return want 156} 157 158// status code of the latest record (deny-by-default: unknown -> NONE). 159func hrs_status_code(prefix: *u8, cred_id: *u8) -> i64 { 160 let ok: *i64=sys_mmap(64*8) as *i64; let ov: *i64=sys_mmap(64*8) as *i64 161 let nf: i64=hrs_get(prefix, cred_id, ok, ov, 64) 162 if nf<=0 { return HRS_ST_NONE } 163 let st: *u8=hrs_field(ok, ov, nf, "status" as *u8) 164 if (st as i64)==0 { return HRS_ST_NONE } 165 if hrs_streq(st, "active" as *u8)==1 { return HRS_ST_ACTIVE } 166 if hrs_streq(st, "invited" as *u8)==1 { return HRS_ST_INVITED } 167 if hrs_streq(st, "suspended" as *u8)==1 { return HRS_ST_SUSPENDED } 168 return HRS_ST_NONE 169} 170// THE RESOLVER (access SSOT): latest level if status=active, else 0. Unknown/suspended/invited -> 0 (deny-by-default). 171func hrs_resolve_level(prefix: *u8, cred_id: *u8) -> i64 { 172 let ok: *i64=sys_mmap(64*8) as *i64; let ov: *i64=sys_mmap(64*8) as *i64 173 let nf: i64=hrs_get(prefix, cred_id, ok, ov, 64) 174 if nf<=0 { return 0 } 175 let st: *u8=hrs_field(ok, ov, nf, "status" as *u8) 176 if (st as i64)==0 { return 0 } 177 if hrs_streq(st, "active" as *u8)==0 { return 0 } 178 let lv: *u8=hrs_field(ok, ov, nf, "level" as *u8) 179 if (lv as i64)==0 { return 0 } 180 return hrs_atoi(lv) 181} 182func hrs_is_superadmin(prefix: *u8, cred_id: *u8) -> i64 { if hrs_resolve_level(prefix, cred_id) >= HRS_OWNER { return 1 } return 0 } 183func hrs_is_invited(prefix: *u8, realm: *u8, realm_n: i64, handle: *u8, hn: i64) -> i64 { 184 let cid: *u8=sys_mmap(96); if hrs_cred_id(realm, realm_n, handle, hn, cid) <= 0 { return 0 } 185 if hrs_status_code(prefix, cid)==HRS_ST_INVITED { return 1 } return 0 186} 187// ROSTER ENUMERATION (the "logins you see"): count distinct creds whose LATEST status == want_status. Iterates the 188// index, so seg_store enumerates without a foreign format. (store, HRS_ST_ACTIVE)=active logins; (.,INVITED)=pending. 189func hrs_roster_count(prefix: *u8, want_status: i64) -> i64 { 190 let n: i64=hrs_index_count(prefix) 191 var cnt: i64=0; var i: i64=0; let credbuf: *u8=sys_mmap(72) 192 while i<n { 193 if hrs_index_get_cred(prefix, i, credbuf)==1 { if hrs_status_code(prefix, credbuf)==want_status { cnt=cnt+1 } } 194 i=i+1 195 } 196 return cnt 197}