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 &mdash; 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 &mdash; 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 &mdash; 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}