code wiki / (root) / nx_hr_sov.nx

nx_hr_sov.nx source

↩ module page · 206 lines · 14359 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 } 85// CLOSE WHAT YOU OPEN (2026-08-18, lane F): every reader below opened a full store mapping per call and dropped 86// it; hrs_is_superadmin (via hrs_resolve_level -> hrs_get) runs on EVERY /whoami and /access of the OPAQUE login 87// daemon. Each reader copies out before returning (ints, or canon_decode's null-terminated copies), so the handle 88// is closed right after the read. Same class, same remedy as nx_hr_entitle. 89func hrs_index_count(prefix: *u8) -> i64 { 90 let h: *i64=ss_open(prefix); if (h as i64)==0 { return 0 } 91 let pp: *i64=sys_mmap(16) as *i64; let ll: *i64=sys_mmap(16) as *i64 92 var n: i64=0 93 if ss_hget(h, "idx:count" as *u8, pp, ll) == 1 { n=hrs_atoi_n(pp[0] as *u8, ll[0]) } 94 ss_close(h) 95 return n 96} 97func hrs_index_get_cred(prefix: *u8, i: i64, out64: *u8) -> i64 { 98 let ik: *u8=sys_mmap(32); hrs_idx_key(i, ik); ik[hrs_idx_key(i, ik)]=0 as u8 99 let h: *i64=ss_open(prefix); if (h as i64)==0 { return 0 } 100 let pp: *i64=sys_mmap(16) as *i64; let ll: *i64=sys_mmap(16) as *i64 101 if ss_hget(h, ik, pp, ll) != 1 { ss_close(h); return 0 } 102 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 103 ss_close(h) // out64 holds the copy; the mapping is not referenced past here 104 return 1 105} 106// PUT a record AND (if cred is new) append it to the roster index -- ONE atomic commit. for enroll/invite. 107func hrs_put_indexed(prefix: *u8, cred_id: *u8, handle: *u8, level: i64, family: *u8, status: *u8, ts: i64, actor: *u8, segid: i64) -> i64 { 108 if hrs_seg_committed(prefix, segid)==1 { return 0 } // idempotent: this exact record already committed (no manifest dup) 109 var isnew: i64 = 0; if hrs_status_code(prefix, cred_id)==HRS_ST_NONE { isnew=1 } 110 var n: i64 = 0; if isnew==1 { n=hrs_index_count(prefix) } 111 let buf: *u8=sys_mmap(HRS_MAGIC_4096); let blen: i64=hrs_encode_rec(handle, level, family, status, ts, actor, buf) 112 let w: *i64=ss_begin() 113 ss_add(w, 1, cred_id, buf, blen) 114 if isnew==1 { 115 let ik: *u8=sys_mmap(32); let ikl: i64=hrs_idx_key(n, ik); ik[ikl]=0 as u8 116 ss_add(w, 1, ik, cred_id, 64) 117 let cv: *u8=sys_mmap(28); let cl: i64=hrs_itoa(n+1, cv) 118 ss_add(w, 1, "idx:count" as *u8, cv, cl) 119 } 120 ss_commit(prefix, w, segid) 121 return 0 122} 123// read the LATEST record for cred_id -> decode into out_keys/out_vals. returns nf (>0) or <=0 if absent. 124func hrs_get(prefix: *u8, cred_id: *u8, out_keys: *i64, out_vals: *i64, maxf: i64) -> i64 { 125 let h: *i64=ss_open(prefix); if (h as i64)==0 { return 0 } 126 let pp: *i64=sys_mmap(16) as *i64; let ll: *i64=sys_mmap(16) as *i64 127 var nf: i64=0 128 if ss_hget(h, cred_id, pp, ll) == 1 { nf=canon_decode(pp[0] as *u8, ll[0], out_keys, out_vals, maxf) } 129 ss_close(h) // canon_decode COPIED the fields out (nx_uxf_decode); the mapping is not referenced past here 130 return nf 131} 132func 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 } 133 134// ENROLL active at a level (derives + returns cred_id). INVITE = enroll status=invited. SUSPEND / SET_ROLE = a new put. 135func 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 { 136 if hrs_cred_id(realm, realm_n, handle, hn, out_cid) <= 0 { return 1 } 137 return hrs_put_indexed(prefix, out_cid, handle, level, family, "active" as *u8, ts, actor, hrs_segid(out_cid, "active" as *u8, ts)) 138} 139func 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 { 140 if hrs_cred_id(realm, realm_n, handle, hn, out_cid) <= 0 { return 1 } 141 return hrs_put_indexed(prefix, out_cid, handle, level, family, "invited" as *u8, ts, actor, hrs_segid(out_cid, "invited" as *u8, ts)) 142} 143func 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)) } 144func 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)) } 145// MIGRATE one historical record verbatim, replayed by cred_id (preserving status/ts/actor): auto-segid + index on the 146// FIRST occurrence of a cred. The TAB-log -> seg_store migrator calls this per line so ALL versions (history) survive 147// and latest-wins is preserved. Idempotent: re-migrating the identical (cred,status,ts) -> same segid -> no dup. 148func hrs_migrate(prefix: *u8, cred_id: *u8, handle: *u8, level: i64, family: *u8, status: *u8, ts: i64, actor: *u8) -> i64 { 149 return hrs_put_indexed(prefix, cred_id, handle, level, family, status, ts, actor, hrs_segid(cred_id, status, ts)) 150} 151// CLAIM an invite: invited -> active at the SAME intended level (the LAN-signup completing). returns the claimed 152// level (>0), or 0 if the handle was not invited (idempotent: a re-claim of an active user returns 0). 153func hrs_claim(prefix: *u8, realm: *u8, realm_n: i64, handle: *u8, hn: i64, family: *u8, ts: i64, actor: *u8) -> i64 { 154 let cid: *u8=sys_mmap(96); if hrs_cred_id(realm, realm_n, handle, hn, cid) <= 0 { return 0 } 155 let ok: *i64=sys_mmap(64*8) as *i64; let ov: *i64=sys_mmap(64*8) as *i64 156 let nf: i64=hrs_get(prefix, cid, ok, ov, 64) 157 if nf<=0 { return 0 } 158 let st: *u8=hrs_field(ok, ov, nf, "status" as *u8) 159 if (st as i64)==0 { return 0 } 160 if hrs_streq(st, "invited" as *u8)==0 { return 0 } 161 let lvf: *u8=hrs_field(ok, ov, nf, "level" as *u8) 162 var want: i64=0; if (lvf as i64)!=0 { want=hrs_atoi(lvf) } 163 hrs_set_role(prefix, cid, handle, want, family, ts, actor) 164 return want 165} 166 167// status code of the latest record (deny-by-default: unknown -> NONE). 168func hrs_status_code(prefix: *u8, cred_id: *u8) -> i64 { 169 let ok: *i64=sys_mmap(64*8) as *i64; let ov: *i64=sys_mmap(64*8) as *i64 170 let nf: i64=hrs_get(prefix, cred_id, ok, ov, 64) 171 if nf<=0 { return HRS_ST_NONE } 172 let st: *u8=hrs_field(ok, ov, nf, "status" as *u8) 173 if (st as i64)==0 { return HRS_ST_NONE } 174 if hrs_streq(st, "active" as *u8)==1 { return HRS_ST_ACTIVE } 175 if hrs_streq(st, "invited" as *u8)==1 { return HRS_ST_INVITED } 176 if hrs_streq(st, "suspended" as *u8)==1 { return HRS_ST_SUSPENDED } 177 return HRS_ST_NONE 178} 179// THE RESOLVER (access SSOT): latest level if status=active, else 0. Unknown/suspended/invited -> 0 (deny-by-default). 180func hrs_resolve_level(prefix: *u8, cred_id: *u8) -> i64 { 181 let ok: *i64=sys_mmap(64*8) as *i64; let ov: *i64=sys_mmap(64*8) as *i64 182 let nf: i64=hrs_get(prefix, cred_id, ok, ov, 64) 183 if nf<=0 { return 0 } 184 let st: *u8=hrs_field(ok, ov, nf, "status" as *u8) 185 if (st as i64)==0 { return 0 } 186 if hrs_streq(st, "active" as *u8)==0 { return 0 } 187 let lv: *u8=hrs_field(ok, ov, nf, "level" as *u8) 188 if (lv as i64)==0 { return 0 } 189 return hrs_atoi(lv) 190} 191func hrs_is_superadmin(prefix: *u8, cred_id: *u8) -> i64 { if hrs_resolve_level(prefix, cred_id) >= HRS_OWNER { return 1 } return 0 } 192func hrs_is_invited(prefix: *u8, realm: *u8, realm_n: i64, handle: *u8, hn: i64) -> i64 { 193 let cid: *u8=sys_mmap(96); if hrs_cred_id(realm, realm_n, handle, hn, cid) <= 0 { return 0 } 194 if hrs_status_code(prefix, cid)==HRS_ST_INVITED { return 1 } return 0 195} 196// ROSTER ENUMERATION (the "logins you see"): count distinct creds whose LATEST status == want_status. Iterates the 197// index, so seg_store enumerates without a foreign format. (store, HRS_ST_ACTIVE)=active logins; (.,INVITED)=pending. 198func hrs_roster_count(prefix: *u8, want_status: i64) -> i64 { 199 let n: i64=hrs_index_count(prefix) 200 var cnt: i64=0; var i: i64=0; let credbuf: *u8=sys_mmap(72) 201 while i<n { 202 if hrs_index_get_cred(prefix, i, credbuf)==1 { if hrs_status_code(prefix, credbuf)==want_status { cnt=cnt+1 } } 203 i=i+1 204 } 205 return cnt 206}