code wiki / _hdl_build / nx_hr_audit.nx
nx_hr_audit.nx source
↩ module page · 67 lines · 4842 B
1// nx_hr_audit.nx -- the owner-gated /hr/audit AUDIT-TRAIL view. The HR store is append-only (#13, history sacred):
2// every lifecycle event (invite / claim / suspend / role-change) is a preserved record with a timestamp + actor.
3// The /hr console shows the CURRENT roster; this surfaces the FULL HISTORY for accountability -- who did what, to
4// whom, when -- including SUPERSEDED records (e.g. a member shows invited -> active -> suspended as three rows).
5// Owner-gated like the console (a non-owner gets the stub, no history leak). Server-rendered, zero 3rd-party JS.
6import "nx_hr.nx" // hr_read / hr_line_end / hr_field_end / hr_intrange / hr_puts
7import "nx_hr_sov.nx" // CUTOVER: history read from the seg_store (hrs_index_count / hrs_index_get_cred / hrs_field)
8import "nx_seg_store.nx" // ss_scan_all (ALL versions of a key = the audit history)
9import "nx_uxf_decode.nx" // canon_decode
10import "nx_syscalls.nx"
11
12const HAU_BUF: i64 = 1048576
13func hau_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 }
14
15func hau_putrange(out: *u8, o: i64, src: *u8, s: i64, e: i64) -> i64 { var oo: i64=o; var i: i64=s; while i<e { out[oo]=src[i]; oo=oo+1; i=i+1 } return oo }
16func hau_role(out: *u8, o: i64, lvl: i64) -> i64 {
17 if lvl >= 3 { return hr_puts(out, o, "owner" as *u8) }
18 if lvl == 1 { return hr_puts(out, o, "member" as *u8) }
19 return hr_puts(out, o, "none" as *u8)
20}
21
22const HAU_HEAD: *u8 = "<!doctype html><html><head><meta charset=utf-8><meta name=viewport content=\"width=device-width,initial-scale=1\"><title>Nishi HR — audit</title><style>body{font-family:system-ui,sans-serif;max-width:760px;margin:5vh auto;padding:0 18px;color:#16202e}h1{font-size:1.2rem}table{border-collapse:collapse;width:100%;margin:12px 0;font-size:.92rem}th,td{text-align:left;padding:6px 9px;border-bottom:1px solid #e1e6f0}th{color:#667;font-size:.82rem}.s-active{color:#137333}.s-invited{color:#a56300}.s-suspended{color:#a50e0e}</style></head><body>" as *u8
23
24// emit the audit trail into out; returns length. viewer_is_super gates the whole history.
25func hau_emit(hr_store: *u8, viewer_is_super: i64, out: *u8, cap: i64) -> i64 {
26 var o: i64 = 0
27 o = hr_puts(out, o, HAU_HEAD)
28 if viewer_is_super == 0 {
29 o = hr_puts(out, o, "<h1>Nishi HR — audit</h1><p>This log is owner-only.</p></body></html>" as *u8)
30 return o
31 }
32 o = hr_puts(out, o, "<h1>Nishi HR — audit trail</h1><p style=\"color:#667;font-size:.9rem\">Every membership event, oldest first (append-only history). ts = epoch seconds.</p>" as *u8)
33 o = hr_puts(out, o, "<table><tr><th>ts</th><th>actor</th><th>handle</th><th>role</th><th>event</th></tr>" as *u8)
34 // iterate the roster index; for each cred, ss_scan_all returns ALL its preserved versions (the append-only history)
35 let ncreds: i64 = hrs_index_count(hr_store); let credbuf: *u8 = sys_mmap(72)
36 let kinds: *i64 = sys_mmap(256*8) as *i64; let ptrs: *i64 = sys_mmap(256*8) as *i64
37 let lens: *i64 = sys_mmap(256*8) as *i64; let srcs: *i64 = sys_mmap(256*8) as *i64
38 let ok: *i64 = sys_mmap(64*8) as *i64; let ov: *i64 = sys_mmap(64*8) as *i64
39 var ci: i64 = 0
40 while ci < ncreds {
41 if hrs_index_get_cred(hr_store, ci, credbuf) == 1 {
42 let nv: i64 = ss_scan_all(hr_store, credbuf, kinds, ptrs, lens, srcs)
43 var v: i64 = 0
44 while v < nv {
45 let nf: i64 = canon_decode(ptrs[v] as *u8, lens[v], ok, ov, 64)
46 if nf > 0 {
47 let hf: *u8 = hrs_field(ok, ov, nf, "handle" as *u8)
48 let lf: *u8 = hrs_field(ok, ov, nf, "level" as *u8)
49 let sf: *u8 = hrs_field(ok, ov, nf, "status" as *u8)
50 let tf: *u8 = hrs_field(ok, ov, nf, "ts" as *u8)
51 let af: *u8 = hrs_field(ok, ov, nf, "actor" as *u8)
52 o = hr_puts(out, o, "<tr><td>" as *u8); if (tf as i64)!=0 { o=hr_puts(out, o, tf) }
53 o = hr_puts(out, o, "</td><td>" as *u8); if (af as i64)!=0 { o=hr_puts(out, o, af) }
54 o = hr_puts(out, o, "</td><td>" as *u8); if (hf as i64)!=0 { o=hr_puts(out, o, hf) }
55 o = hr_puts(out, o, "</td><td>" as *u8); var lvv: i64=0; if (lf as i64)!=0 { lvv=hau_atoi(lf) } o=hau_role(out, o, lvv)
56 o = hr_puts(out, o, "</td><td class=\"s-" as *u8); if (sf as i64)!=0 { o=hr_puts(out, o, sf) }
57 o = hr_puts(out, o, "\">" as *u8); if (sf as i64)!=0 { o=hr_puts(out, o, sf) }
58 o = hr_puts(out, o, "</td></tr>" as *u8)
59 }
60 v = v + 1
61 }
62 }
63 ci = ci + 1
64 }
65 o = hr_puts(out, o, "</table></body></html>" as *u8)
66 return o
67}