nx_hr.nx source
↩ module page · 96 lines · 6822 B
1// nx_hr.nx -- NISHI HR: the S-class sovereign USER-MANAGEMENT structure (the access SSOT). Built ON the OPAQUE
2// identity: a user's primary key is the cred_id = lowercase-hex of SHA-256(realm|||handle) -- the STABLE per-realm
3// id, derivable from a handle WITHOUT a login (so the operator provisions people by handle). The directory is
4// APPEND-ONLY (CLAUDE.md #13 additive-only, DV2.0 satellite/is_current): every lifecycle event appends a record;
5// the LATEST record per cred_id is current. DENY-BY-DEFAULT: an unknown OR suspended user resolves to level 0.
6// History is preserved (audit + rollback). This REPLACES the hand-maintained flat roles TSV as the "granted
7// level" source for every gated surface (torrent, gallery, hub). Composes hub/nx_no_cookie_session (the id hash).
8// record: cred_id <TAB> handle <TAB> level <TAB> family <TAB> status <TAB> ts <TAB> actor \n
9// status: active | suspended level: 3=owner/operator, 1=family/member, 0=none license_tier: ORIGINAL
10import "hub/nx_no_cookie_session.nx" // nx_ncs_derive_user_id_hash + NX_NCS_OK
11import "nx_syscalls.nx"
12const HR_MAGIC_1048576: i64 = 1048576
13
14const HR_TAB: i64 = 9
15const HR_NL: i64 = 10
16
17func hr_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
18func hr_puts(dst: *u8, o: i64, s: *u8) -> i64 { var i: i64=0; while s[i]!=(0 as u8){ dst[o+i]=s[i]; i=i+1 } return o+i }
19func hr_putn(dst: *u8, o: i64, v: i64) -> i64 { if v==0 { dst[o]=48 as u8; return o+1 } var m: i64=v; if m<0{m=0-m} let t:*u8=sys_mmap(24); var k:i64=0; while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} var j:i64=0; while j<k{dst[o+j]=t[k-1-j];j=j+1} return o+k }
20func hr_read(path: *u8, buf: *u8, cap: i64) -> i64 { let fd: i64=sys_openat_rd(path); if fd<0 {return 0} var off: i64=0; var go: i64=1; while go==1 { if off>=cap {go=0} else { let r: i64=sys_read(fd, buf+off, cap-off); if r<=0 {go=0} else {off=off+r} } } sys_close(fd); return off }
21
22// lowercase-hex encode in[0..n) -> out (NUL-term). returns 2n. (local-bound const = no nx_cc const-index crash)
23func hr_hexenc(inp: *u8, n: i64, out: *u8) -> i64 {
24 let hx: *u8 = "0123456789abcdef" as *u8
25 var i: i64=0
26 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 }
27 out[n*2]=0 as u8; return n*2
28}
29// the stable credential-id (64-hex) for (realm, handle) -- the HR primary key, derivable WITHOUT login.
30func hr_cred_id(realm: *u8, realm_n: i64, handle: *u8, hn: i64, out_hex: *u8) -> i64 {
31 let uid: *u8 = sys_mmap(32)
32 if nx_ncs_derive_user_id_hash(realm, realm_n, handle, hn, uid) != NX_NCS_OK { out_hex[0]=0 as u8; return 0 }
33 return hr_hexenc(uid, 32, out_hex)
34}
35
36// append ONE directory record (atomic O_APPEND -- never clobbers history; concurrent appends can't lose a record).
37func hr_append(store: *u8, cred_id: *u8, handle: *u8, level: i64, family: *u8, status_active: i64, ts: i64, actor: *u8) -> i64 {
38 let line: *u8 = sys_mmap(640); var o: i64 = 0
39 o = hr_puts(line, o, cred_id); line[o]=HR_TAB as u8; o=o+1
40 o = hr_puts(line, o, handle); line[o]=HR_TAB as u8; o=o+1
41 o = hr_putn(line, o, level); line[o]=HR_TAB as u8; o=o+1
42 o = hr_puts(line, o, family); line[o]=HR_TAB as u8; o=o+1
43 if status_active==1 { o=hr_puts(line, o, "active" as *u8) } else { o=hr_puts(line, o, "suspended" as *u8) }
44 line[o]=HR_TAB as u8; o=o+1
45 o = hr_putn(line, o, ts); line[o]=HR_TAB as u8; o=o+1
46 o = hr_puts(line, o, actor); line[o]=HR_NL as u8; o=o+1
47 let fd: i64 = __syscall(SYS_OPENAT, AT_FDCWD, store as i64, O_WRONLY_CA, 0x1a4, 0, 0) // O_WRONLY|O_CREAT|O_APPEND
48 if fd < 0 { return 0 - 1 }
49 sys_write(fd, line, o); sys_close(fd)
50 return 0
51}
52
53// ENROLL a user by handle (derives cred_id, writes it to out_hex). returns 0 ok.
54func hr_enroll(store: *u8, realm: *u8, realm_n: i64, handle: *u8, hn: i64, level: i64, family: *u8, ts: i64, actor: *u8, out_hex: *u8) -> i64 {
55 if hr_cred_id(realm, realm_n, handle, hn, out_hex) <= 0 { return 1 }
56 return hr_append(store, out_hex, handle, level, family, 1, ts, actor)
57}
58// lifecycle changes = append an updated record for an existing cred_id (history preserved).
59func hr_set_role(store: *u8, cred_id: *u8, handle: *u8, level: i64, family: *u8, ts: i64, actor: *u8) -> i64 { return hr_append(store, cred_id, handle, level, family, 1, ts, actor) }
60func hr_suspend(store: *u8, cred_id: *u8, handle: *u8, family: *u8, ts: i64, actor: *u8) -> i64 { return hr_append(store, cred_id, handle, 0, family, 0, ts, actor) }
61
62// index of the next TAB at or after `start` (capped at `le`); flat helper to avoid deep nested loops.
63func hr_field_end(buf: *u8, start: i64, le: i64) -> i64 { var e: i64=start; while e<le { if buf[e]==(HR_TAB as u8) { return e } e=e+1 } return le }
64func hr_line_end(buf: *u8, start: i64, n: i64) -> i64 { var e: i64=start; while e<n { if buf[e]==(HR_NL as u8) { return e } e=e+1 } return n }
65func hr_eqrange(buf: *u8, s: i64, e: i64, lit: *u8, litn: i64) -> i64 { if (e-s)!=litn { return 0 } var k: i64=0; while k<litn { if buf[s+k]!=lit[k] { return 0 } k=k+1 } return 1 }
66func hr_intrange(buf: *u8, s: i64, e: i64) -> i64 { var v: i64=0; var q: i64=s; while q<e { let c: i64=buf[q] as i64; if c>=48 { if c<=57 { v=v*10+(c-48); q=q+1 } else { q=e } } else { q=e } } return v }
67
68// THE RESOLVER (the access SSOT): the LATEST record for cred_id; its level if status=active, else 0. Unknown
69// cred_id or suspended -> 0 (DENY-BY-DEFAULT). Scans append-only so the last matching line = current state.
70func hr_resolve_level(store: *u8, cred_id: *u8, cidlen: i64) -> i64 {
71 let buf: *u8 = sys_mmap(HR_MAGIC_1048576); let n: i64 = hr_read(store, buf, HR_MAGIC_1048576)
72 if n <= 0 { return 0 }
73 var level: i64 = 0
74 var i: i64 = 0
75 while i < n {
76 let le: i64 = hr_line_end(buf, i, n)
77 let f0e: i64 = hr_field_end(buf, i, le) // cred_id
78 if hr_eqrange(buf, i, f0e, cred_id, cidlen) == 1 {
79 let f1e: i64 = hr_field_end(buf, f0e+1, le) // handle
80 let f2s: i64 = f1e+1; let f2e: i64 = hr_field_end(buf, f2s, le) // level
81 let f3e: i64 = hr_field_end(buf, f2e+1, le) // family
82 let f4s: i64 = f3e+1; let f4e: i64 = hr_field_end(buf, f4s, le) // status
83 if hr_eqrange(buf, f4s, f4e, "active" as *u8, 6) == 1 { level = hr_intrange(buf, f2s, f2e) } else { level = 0 }
84 }
85 i = le + 1
86 }
87 return level
88}
89
90// count records in the directory (audit: history length, incl. superseded records -- proves append-only).
91func hr_record_count(store: *u8) -> i64 {
92 let buf: *u8 = sys_mmap(HR_MAGIC_1048576); let n: i64 = hr_read(store, buf, HR_MAGIC_1048576)
93 var c: i64 = 0; var i: i64 = 0
94 while i < n { if buf[i]==(HR_NL as u8) { c=c+1 } i=i+1 }
95 return c
96}