code wiki / _hdl_build / nx_opaque_login_daemon.nx
nx_opaque_login_daemon.nx source
↩ module page · 567 lines · 47144 B
1// nx_opaque_login_daemon.nx -- R2: thin HTTP daemon wrapping the gated no-cookie OPAQUE login (nx_opaque_login).
2// Serves the wiki's auth model for any site: full OPAQUE (modauth, RFC 9807) + no-cookie X-Nishi-Session token.
3// argv: [1]=port [2]=server-keys-path [3]=account-store-path [4]=request-budget [5]=argon2id_m_cost (opt; default
4// 65536=64MiB production; smoke passes a small value). Plain HTTP (loopback/LAN); TLS termination = the R3 rung
5// (route on the wiki HTTPS daemon). Routes: GET /login (page) | POST /login | GET /whoami | POST /register.
6// No shortcuts: production KSF by default, every reject path returns a clean status, no token leak on failure.
7import "nx_opaque_login.nx" // olg_ctx_setup / olg_register / olg_login / olg_whoami + NxAuthContext + NX_MAUTH_*
8import "nx_http_form.nx" // nx_http_form_get_field
9import "nx_audio_web.nx" // nx_audio_web_page -- the PROTECTED sovereign-audio page (served only on a valid session)
10import "nx_framed_append.nx" // fa_appendz -- the atomic op-RECEIPT ledger (military acceptance/receipt for the workstream)
11import "nx_lan_signup.nx" // ls_signup_allowed / ls_ip4_from_sockaddr -- THE SIGNUP GATE (LAN AND invited)
12
13const OLGD_REALM_N: i64 = 16 // "nishi_site_admin" -- the same realm this daemon inits its auth context with
14
15// ---- PER-REQUEST BUFFER DISCIPLINE (2026-08-18, lane F box health) -------------------------------------------
16// MEASURED on the live daemon (pid 29279): VmSize 177,867,972 kB == VmData (all ANONYMOUS), VmPTE 347,408 kB,
17// against RSS+Swap 2.4 GB -- and growing in EXACT 2 MiB steps every ~15 s (nx_leak_check series: 8,383 kB/min).
18// The step was olgd_send's per-RESPONSE sys_mmap(2 MiB) compose buffer: one fresh, never-unmapped mapping per
19// response, of which only the header+body pages were ever touched -- plus req/resp/pg pages, ~26 KB touched per
20// request; ~84,000 requests over ~15 days = the 177 GB, and 347 MB of unswappable PAGE TABLES for address space
21// nothing used. A BUFFER ALLOCATED PER RESPONSE IN A DAEMON IS A LEAK WHOSE SIZE IS THE BUFFER, NOT THE RESPONSE.
22// Every per-request buffer is now allocated ONCE, above the accept loop, and WIPED at the end of each request, so
23// passphrases (pbuf, the POST body in req), session tokens (b64/rnb/tb/tbw, response JSON) and mnemonics
24// (rmn/nm/mn) no longer persist in leaked pages -- the live daemon had 2 GB of exactly those pages SWAPPED TO
25// DISK. Wiping restores the mmap-returns-zeroed contract for the next request BY CONSTRUCTION, so no branch code
26// had to change. Response-body wipes live in olgd_send; the /wiki compose buffer is wiped to its used length.
27// sys_arena_reset (nx_syscalls) was deliberately NOT adopted here: nx_seg_store.ss_hget lazily allocates its
28// static scratch (ssh_vo/ssh_vl, 16 B = arena-class) on FIRST CALL -- i.e. inside a request -- and a reset would
29// zero-and-recycle a live static: silent aliasing inside an auth daemon. Hoisting needs no such contract.
30static olgd_sendbuf: *u8 // olgd_send compose buffer (OLGD_MAGIC_2097152, mapped once, wiped per send)
31static olgd_pgbuf: *u8 // login page compose buffer (OLGD_MAGIC_16384, mapped once; content is constant)
32static olgd_recbuf: *u8 // op-receipt line (OLGD_RECBUF, mapped once, wiped per receipt)
33static olgd_scr: *u8 // small scratch page: [0..27] olgd_itoa digits (own page, so arena-immune)
34const OLGD_SCR_BYTES: i64 = 4096
35const OLGD_RECBUF: i64 = 512
36func olgd_wipe(p: *u8, n: i64) -> i64 { var i: i64 = 0; while i < n { p[i] = 0 as u8; i = i + 1 } return 0 }
37
38// THE SIGNUP DECISION, ADOPTED (2026-07-30). This daemon used to gate POST /register on ONE global boolean,
39// which is precisely the "old single-flag world" that nx_account_admin_exceed_gate scores as the NAIVE
40// BASELINE our own ls_signup_allowed beats -- and the flag was live-MEASURED as '1', so anyone on the
41// internet could mint an account on the family domain. Now BOTH must hold: the ops kill-switch AND the
42// by-construction decision (peer IP is LAN **and** the handle is INVITED in this realm's HR roster).
43// FAIL-CLOSED ON EVERY UNKNOWN: no peer address, or no HR roster configured, means DENY -- an unknown
44// origin must never be able to register, and a daemon started without a roster must not fall open.
45func olgd_signup_ok(ip4: *u8, hrstore: *u8, handle: *u8, hn: i64) -> i64 {
46 if olg_registration_open() == 0 { return 0 }
47 if (ip4 as i64) == 0 { return 0 }
48 if (hrstore as i64) == 0 { return 0 }
49 if hrstore[0] == (0 as u8) { return 0 }
50 return ls_signup_allowed(ip4, hrstore, "nishi_site_admin" as *u8, OLGD_REALM_N, handle, hn)
51}
52
53// Emit a structured op RECEIPT (the "acceptance" teammates/workstreams read to validate orchestration).
54// Schema: ts<TAB>actor<TAB>op<TAB>target<TAB>verdict<TAB>detail. NO secrets ever. Atomic. detail carries measured ms.
55func olgd_receipt(op: *u8, target: *u8, tn: i64, ok: i64, detail: *u8, now_s: i64) -> i64 {
56 if (olgd_recbuf as i64) == 0 { olgd_recbuf = sys_mmap(OLGD_RECBUF) }
57 let rec: *u8 = olgd_recbuf; var o: i64 = 0
58 o = fa_catn(rec, o, now_s); rec[o] = 9 as u8; o = o + 1
59 o = fa_cat(rec, o, "nx_opaque_login" as *u8); rec[o] = 9 as u8; o = o + 1
60 o = fa_cat(rec, o, op); rec[o] = 9 as u8; o = o + 1
61 var i: i64 = 0; while i < tn { rec[o] = target[i]; o = o + 1; i = i + 1 } rec[o] = 9 as u8; o = o + 1
62 if ok == 1 { o = fa_cat(rec, o, "ACCEPTED" as *u8) } else { o = fa_cat(rec, o, "REJECTED" as *u8) }
63 rec[o] = 9 as u8; o = o + 1
64 o = fa_cat(rec, o, detail)
65 rec[o] = 0 as u8
66 let frc: i64 = fa_appendz("/volume1/homes/elderwesto/nishihost/op_receipts.tsv" as *u8, rec, OLGD_RECBUF)
67 olgd_wipe(rec, OLGD_RECBUF)
68 return frc
69}
70// build "ms=<n>" into buf (>=32B) -> the MEASURED-latency detail for a receipt ("don't guess, measure").
71func olgd_ms_detail(buf: *u8, ms: i64) -> *u8 {
72 var o: i64 = fa_cat(buf, 0, "ms=" as *u8); o = fa_catn(buf, o, ms); buf[o] = 0 as u8; return buf
73}
74import "nx_hr_entitle.nx" // he_emit_super (superuser-by-construction entitlements) + hra_is_superadmin (HR SSOT)
75const OLGD_MAGIC_2097152: i64 = 2097152
76const OLGD_MAGIC_16384: i64 = 16384
77const OLGD_MAGIC_86400: i64 = 86400
78const OLGD_MAGIC_16383: i64 = 16383
79const OLGD_MAGIC_8192: i64 = 8192
80const OLGD_MAGIC_2048: i64 = 2048
81
82const OLGD_PROD_M: i64 = 65536 // production argon2id m_cost (64 MiB, OWASP 2026); argv[5] overrides for smoke
83const OLGD_WIKI_DIR: *u8 = "/mnt/c/Users/elder/nishi-core/nxc2/knowledge/wiki_pages"
84// no-cookie browse layer: appended to every gated page so link-clicks fetch the next page WITH the
85// X-Nishi-Session header (no cookie, true to the no-cookie cardinal). document.write keeps it SPA-like.
86const OLGD_SPA: *u8 = "<script>(function(){var t=sessionStorage.getItem('nsess');document.addEventListener('click',function(e){var a=e.target.closest&&e.target.closest('a');if(!a)return;var h=a.getAttribute('href');if(!h)return;if(h.slice(-5)!='.html')return;e.preventDefault();fetch('/wiki/'+h,{headers:{'X-Nishi-Session':t}}).then(function(r){return r.ok?r.text():null}).then(function(x){if(x){document.open();document.write(x);document.close()}else{location='/'}})})})();</script>"
87
88func olgd_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
89func olgd_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{return v} if c>57{return v} v=v*10+(c-48); i=i+1 } return v }
90func olgd_starts(buf: *u8, n: i64, pre: *u8) -> i64 { var i: i64=0; while pre[i]!=(0 as u8){ if i>=n {return 0} if buf[i]!=pre[i]{return 0} i=i+1 } return 1 }
91func olgd_find(buf: *u8, n: i64, needle: *u8, nl: i64) -> i64 {
92 if nl==0 { return 0 }
93 var i: i64=0
94 while i+nl<=n { var j: i64=0; var ok: i64=1; while j<nl { if buf[i+j]!=needle[j]{ok=0; j=nl} else {j=j+1} } if ok==1 {return i} i=i+1 }
95 return 0-1
96}
97func olgd_cat(dst: *u8, off: i64, s: *u8) -> i64 { var o: i64=off; var i: i64=0; while s[i]!=(0 as u8){dst[o]=s[i]; o=o+1; i=i+1} return o }
98func olgd_catb(dst: *u8, off: i64, src: *u8, n: i64) -> i64 { var o: i64=off; var i: i64=0; while i<n {dst[o]=src[i]; o=o+1; i=i+1} return o }
99func olgd_itoa(dst: *u8, off: i64, v: i64) -> i64 { if (olgd_scr as i64) == 0 { olgd_scr = sys_mmap(OLGD_SCR_BYTES) } let t: *u8=olgd_scr; 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 o: i64=off; var q: i64=k-1; while q>=0{dst[o]=t[q];o=o+1;q=q-1} return o }
100func olgd_hex(dst: *u8, off: i64, src: *u8, n: i64) -> i64 { let hx: *u8="0123456789abcdef" as *u8; var o: i64=off; var i: i64=0; while i<n { let c: i64=(src[i] as i64)&0xff; dst[o]=hx[(c>>4)&15]; dst[o+1]=hx[c&15]; o=o+2; i=i+1 } return o }
101
102// ---- AUTHZ: data-driven entitlements (no hardcoded policy) ----
103// CUTOVER (no-TSV): entitlements are now a sovereign nx_seg_store (he_ent_put records key/label/url, enumerated by
104// nx_hr_entitle); OLGD_ENT_PATH is the seg_store PREFIX. The live read is he_emit_super (below). The legacy
105// olgd_emit_access / olgd_grant_append TSV funcs are DEAD (no call sites) -- superseded by he_emit_super.
106// nishi_uid_handle.tsv : lines uidhex<TAB>handle (built at login so /access resolves by handle)
107const OLGD_ENT_PATH: *u8 = "/volume1/homes/elderwesto/nishihost/nishi_entitlements-" as *u8
108const OLGD_IDX_PATH: *u8 = "/volume1/homes/elderwesto/nishihost/nishi_uid_handle.tsv" as *u8
109// NISHI HR SSOT: the superadmin (owner, lvl 3) lookup -- an ACTIVE owner here gets EVERY entitlement by construction.
110const OLGD_HR_PATH: *u8 = "/volume1/homes/elderwesto/nishihost/nishi_hr-" as *u8 // CUTOVER: seg_store prefix (was .log)
111
112// uidhex -> handle from the index. writes handle to out_h, returns its length (0 if not found). flag-style
113// loops throughout (break/continue in nested-ifs miscompile on this toolchain -- same landmine as the sites daemon).
114func olgd_idx_lookup(uidhex: *u8, uxn: i64, out_h: *u8, cap: i64) -> i64 {
115 let lb: *i64 = sys_mmap(16) as *i64; lb[0] = 0
116 let data: *u8 = sys_read_file(OLGD_IDX_PATH, lb)
117 if (data as i64) == 0 { return 0 }
118 let n: i64 = lb[0]
119 var i: i64 = 0
120 var found: i64 = 0
121 var run: i64 = 1
122 while run == 1 {
123 if i >= n { run = 0 }
124 else {
125 var e: i64 = i; var g: i64 = 1
126 while g == 1 { if e >= n { g = 0 } else { if data[e] == (10 as u8) { g = 0 } else { e = e + 1 } } }
127 var m: i64 = 1
128 if i + uxn > e { m = 0 }
129 if m == 1 { var j: i64 = 0; while j < uxn { if data[i+j] != uidhex[j] { m = 0; j = uxn } else { j = j + 1 } } }
130 if m == 1 { if i + uxn < e { if data[i+uxn] == (9 as u8) {
131 var s: i64 = i + uxn + 1; var en: i64 = e
132 if en > s { if data[en-1] == (13 as u8) { en = en - 1 } }
133 var o: i64 = 0
134 while s < en { if o < cap - 1 { out_h[o] = data[s]; o = o + 1 } s = s + 1 }
135 found = o; run = 0
136 } } }
137 i = e + 1
138 }
139 }
140 sys_free_file(data, n) // one index read per /whoami and /access; was never released
141 return found
142}
143
144// append "uidhex<TAB>handle\n" to the index IF not already present (idempotent; reuses the lookup).
145func olgd_idx_record(uidhex: *u8, uxn: i64, handle: *u8, hn: i64) -> i64 {
146 let tmp: *u8 = sys_mmap(160)
147 if olgd_idx_lookup(uidhex, uxn, tmp, 160) > 0 { return 0 }
148 let fd: i64 = sys_openat_append(OLGD_IDX_PATH, 420)
149 if fd >= 0 {
150 let line: *u8 = sys_mmap(320); var o: i64 = 0
151 o = olgd_catb(line, o, uidhex, uxn); line[o] = 9 as u8; o = o + 1
152 o = olgd_catb(line, o, handle, hn); line[o] = 10 as u8; o = o + 1
153 sys_write(fd, line, o); sys_close(fd)
154 }
155 return 0
156}
157
158// emit {"handle":"<h>","links":[{"label":..,"url":..},...]} for every entitlement line whose key is "*" or `handle`.
159func olgd_emit_access(handle: *u8, hn: i64, resp: *u8, cap: i64) -> i64 {
160 var o: i64 = 0
161 o = olgd_cat(resp, o, "{\"handle\":\"" as *u8)
162 o = olgd_catb(resp, o, handle, hn)
163 o = olgd_cat(resp, o, "\",\"links\":[" as *u8)
164 let lb: *i64 = sys_mmap(16) as *i64; lb[0] = 0
165 let data: *u8 = sys_read_file(OLGD_ENT_PATH, lb)
166 var first: i64 = 1
167 if (data as i64) != 0 {
168 let n: i64 = lb[0]
169 var i: i64 = 0
170 var run: i64 = 1
171 while run == 1 {
172 if i >= n { run = 0 }
173 else {
174 var e: i64 = i; var g0: i64 = 1
175 while g0 == 1 { if e >= n { g0 = 0 } else { if data[e] == (10 as u8) { g0 = 0 } else { e = e + 1 } } }
176 var k1: i64 = i; var g1: i64 = 1
177 while g1 == 1 { if k1 >= e { g1 = 0 } else { if data[k1] == (9 as u8) { g1 = 0 } else { k1 = k1 + 1 } } }
178 var k2: i64 = k1 + 1; var g2: i64 = 1
179 while g2 == 1 { if k2 >= e { g2 = 0 } else { if data[k2] == (9 as u8) { g2 = 0 } else { k2 = k2 + 1 } } }
180 var ue: i64 = e
181 if ue > k2 { if data[ue-1] == (13 as u8) { ue = ue - 1 } }
182 let keylen: i64 = k1 - i
183 var keyhit: i64 = 0
184 if keylen == 1 { if data[i] == (42 as u8) { keyhit = 1 } }
185 if keylen == hn { var j: i64 = 0; var mm: i64 = 1; while j < hn { if data[i+j] != handle[j] { mm = 0; j = hn } else { j = j + 1 } } if mm == 1 { keyhit = 1 } }
186 if keyhit == 1 { if k1 < e { if k2 < e {
187 if first == 0 { resp[o] = 44; o = o + 1 }
188 o = olgd_cat(resp, o, "{\"label\":\"" as *u8)
189 o = olgd_catb(resp, o, ((data as i64) + k1 + 1) as *u8, k2 - k1 - 1)
190 o = olgd_cat(resp, o, "\",\"url\":\"" as *u8)
191 o = olgd_catb(resp, o, ((data as i64) + k2 + 1) as *u8, ue - k2 - 1)
192 o = olgd_cat(resp, o, "\"}" as *u8)
193 first = 0
194 } } }
195 i = e + 1
196 }
197 }
198 }
199 o = olgd_cat(resp, o, "]}" as *u8)
200 return o
201}
202
203const OLGD_ADMINS_PATH: *u8 = "/volume1/homes/elderwesto/nishihost/nishi_admins.tsv" as *u8
204
205// 1 if `handle` is listed (one handle per line) in nishi_admins.tsv. Only admins may grant entitlements.
206func olgd_is_admin(handle: *u8, hn: i64) -> i64 {
207 if hn <= 0 { return 0 }
208 let lb: *i64 = sys_mmap(16) as *i64; lb[0] = 0
209 let data: *u8 = sys_read_file(OLGD_ADMINS_PATH, lb)
210 if (data as i64) == 0 { return 0 }
211 let n: i64 = lb[0]
212 var i: i64 = 0; var found: i64 = 0; var run: i64 = 1
213 while run == 1 {
214 if i >= n { run = 0 }
215 else {
216 var e: i64 = i; var g: i64 = 1
217 while g == 1 { if e >= n { g = 0 } else { if data[e] == (10 as u8) { g = 0 } else { e = e + 1 } } }
218 var en: i64 = e
219 if en > i { if data[en-1] == (13 as u8) { en = en - 1 } }
220 if en - i == hn {
221 var j: i64 = 0; var m: i64 = 1
222 while j < hn { if data[i+j] != handle[j] { m = 0; j = hn } else { j = j + 1 } }
223 if m == 1 { found = 1; run = 0 }
224 }
225 i = e + 1
226 }
227 }
228 return found
229}
230
231// field must be 1..maxn printable-ASCII bytes -- rejects TAB/newline/control (TSV-injection) + length abuse.
232func olgd_field_ok(s: *u8, n: i64, maxn: i64) -> i64 {
233 if n <= 0 { return 0 }
234 if n > maxn { return 0 }
235 var i: i64 = 0; var ok: i64 = 1
236 while i < n { let c: i64 = s[i] as i64; if c < 32 { ok = 0; i = n } else { if c > 126 { ok = 0; i = n } else { i = i + 1 } } }
237 return ok
238}
239
240// append "who<TAB>label<TAB>url\n" to the entitlements TSV if that exact triple is not already present.
241func olgd_grant_append(who: *u8, wn: i64, label: *u8, ln: i64, url: *u8, un: i64) -> i64 {
242 let line: *u8 = sys_mmap(512); var o: i64 = 0
243 o = olgd_catb(line, o, who, wn); line[o] = 9 as u8; o = o + 1
244 o = olgd_catb(line, o, label, ln); line[o] = 9 as u8; o = o + 1
245 o = olgd_catb(line, o, url, un)
246 let lb: *i64 = sys_mmap(16) as *i64; lb[0] = 0
247 let data: *u8 = sys_read_file(OLGD_ENT_PATH, lb)
248 var present: i64 = 0
249 if (data as i64) != 0 { if olgd_find(data, lb[0], line, o) >= 0 { present = 1 } }
250 if present == 0 {
251 let fd: i64 = sys_openat_append(OLGD_ENT_PATH, 420)
252 if fd >= 0 { line[o] = 10 as u8; sys_write(fd, line, o + 1); sys_close(fd) }
253 }
254 return 0
255}
256
257// send a full HTTP/1.1 response with Content-Length (single write). status e.g. "200 OK", ctype e.g. "application/json".
258func olgd_send(cfd: i64, status: *u8, ctype: *u8, body: *u8, blen: i64) -> i64 {
259 // THE 2 MiB STEP (see the discipline note at the top): this buffer was a fresh mapping per response.
260 if (olgd_sendbuf as i64) == 0 { olgd_sendbuf = sys_mmap(OLGD_MAGIC_2097152) }
261 let buf: *u8 = olgd_sendbuf; var o: i64 = 0
262 o = olgd_cat(buf, o, "HTTP/1.1 " as *u8); o = olgd_cat(buf, o, status)
263 o = olgd_cat(buf, o, "\r\nContent-Type: " as *u8); o = olgd_cat(buf, o, ctype)
264 o = olgd_cat(buf, o, "\r\nContent-Length: " as *u8); o = olgd_itoa(buf, o, blen)
265 o = olgd_cat(buf, o, "\r\nConnection: close\r\nCache-Control: no-store\r\n\r\n" as *u8)
266 if o + blen <= OLGD_MAGIC_2097152 {
267 o = olgd_catb(buf, o, body, blen)
268 sys_write(cfd, buf, o)
269 } else {
270 // A body larger than the compose buffer goes out as header + body in two writes: still ONE
271 // Content-Length-framed response. The old code copied it in unbounded and would have written past the mapping.
272 sys_write(cfd, buf, o); sys_write(cfd, body, blen)
273 }
274 olgd_wipe(buf, o) // tokens and mnemonics ride in response bodies: never leave them in a long-lived buffer
275 return 0
276}
277// extract a request header value (req[0..hend]) for `name` (incl trailing ':'), trimming a leading space. -> len
278func olgd_hdr_val(req: *u8, hend: i64, name: *u8, nl: i64, out: *u8, cap: i64) -> i64 {
279 let p: i64 = olgd_find(req, hend, name, nl)
280 if p < 0 { return 0 }
281 var i: i64 = p + nl
282 if i < hend { if req[i]==(32 as u8) { i=i+1 } } // skip one space after ':'
283 var o: i64 = 0
284 while i < hend { let c: u8 = req[i]; if c==(13 as u8){i=hend} else { if c==(10 as u8){i=hend} else { if o<cap-1 {out[o]=c; o=o+1} i=i+1 } } }
285 out[o]=0 as u8; return o
286}
287
288const OLGD_HTML: *u8 = "<!doctype html><html><head><meta charset=utf-8><meta name=viewport content=\"width=device-width,initial-scale=1\"><title>Nishi OPAQUE login</title><style>body{font-family:system-ui,sans-serif;max-width:480px;margin:5vh auto;padding:0 18px;color:#16202e}h1{font-size:1.2rem}h3{margin:18px 0 4px}input{width:100%;padding:9px;margin:5px 0;box-sizing:border-box;border:1px solid #b9c2d6;border-radius:5px}button{padding:9px 16px;margin:6px 6px 6px 0;background:#0b2545;color:#fff;border:0;border-radius:5px;cursor:pointer;font-size:.95rem}#msg{margin:14px 0;padding:12px;background:#f1f4fa;border-left:3px solid #0b2545;word-break:break-all;min-height:1.2em}</style></head><body><h1>Nishi — no-cookie OPAQUE login</h1><p style=\"color:#667;font-size:.9rem\">Full OPAQUE aPAKE (RFC 9807). The server never stores your passphrase; sessions are Ed25519 no-cookie tokens (sessionStorage + X-Nishi-Session header), never cookies.</p><div id=msg>Register a handle + passphrase, then log in.</div><h3>1. Register</h3><input id=rh placeholder=\"handle (e.g. elder)\"><input id=rp type=password placeholder=\"passphrase\"><button onclick=reg()>Register</button><h3>2. Login</h3><input id=lh placeholder=handle><input id=lp type=password placeholder=passphrase><button onclick=login()>Login</button> <button onclick=who()>Who am I?</button> <button onclick=board()>Open Hub</button> <button onclick=out()>Sign out</button><script>function $(i){return document.getElementById(i)}function M(t){$('msg').textContent=t}async function G(p,b){try{var o={method:b?'POST':'GET',headers:{}};if(b){o.headers['Content-Type']='application/x-www-form-urlencoded';o.body=b}else{o.headers['X-Nishi-Session']=sessionStorage.getItem('nsess')||''}var r=await fetch(p,o);var j={};try{j=await r.json()}catch(e){}return{ok:r.ok,s:r.status,j:j}}catch(e){return{ok:false,s:0,j:{error:'network: '+e}}}}async function reg(){M('Registering\\u2026 (memory-hard OPAQUE step \\u2014 a moment)');var r=await G('/register','handle='+encodeURIComponent($('rh').value)+'&pw='+encodeURIComponent($('rp').value));M(r.ok?('Registered! SAVE THIS RECOVERY MNEMONIC: '+r.j.mnemonic):('Register failed ('+r.s+'): '+(r.j.error||'unknown')))}async function login(){M('Logging in\\u2026 (a moment)');var r=await G('/login','handle='+encodeURIComponent($('lh').value)+'&pw='+encodeURIComponent($('lp').value));if(r.ok){sessionStorage.setItem('nsess',r.j.token);var ret=new URLSearchParams(location.search).get('return');if(ret&&ret.charAt(0)=='/'&&ret.charAt(1)!='/'){location=ret}else if(location.pathname!='/login'){location.reload()}else{board()}}else{M('Login failed ('+r.s+'): '+(r.j.error||'unknown'))}}async function showSpace(){M('Loading your space\\u2026');var r=await G('/whoami');if(!r.ok){M('Session error ('+r.s+')');return}var j=r.j;var h='<h1>Welcome, '+(j.handle||'member')+'</h1><p style=color:#667>Signed in with a no-cookie OPAQUE session. Your access:</p>';if(j.links&&j.links.length){for(var i=0;i<j.links.length;i++){h+='<p><a href='+JSON.stringify(j.links[i].url)+'>'+j.links[i].label+' \\u2192</a></p>'}}else{h+='<p>No resources assigned to you yet.</p>'}h+='<p style=margin-top:20px><button onclick=out()>Sign out</button></p>';document.body.innerHTML=h}async function who(){if(!sessionStorage.getItem('nsess')){M('No session \\u2014 log in first.');return}M('Validating\\u2026');var r=await G('/whoami');M(r.ok?('Valid no-cookie session. user-id hash: '+r.j.uid):('Session rejected ('+r.s+'): '+(r.j.error||'unknown')))}async function board(){if(!sessionStorage.getItem('nsess')){M('Log in first.');return}M('Opening hub...');try{var r=await fetch('/wiki/hub.html',{headers:{'X-Nishi-Session':sessionStorage.getItem('nsess')}});if(r.ok){document.open();document.write(await r.text());document.close()}else{M('Hub denied ('+r.status+')')}}catch(e){M('hub error: '+e)}}function out(){sessionStorage.removeItem('nsess');location='/login'}</script></body></html>" as *u8
289
290// The post-login landing: fetches /access with the no-cookie token and renders ONLY the links this user is
291// entitled to (dynamic per-user). No session -> bounce to /login. The links come from the server (authorized),
292// never from client-claimed identity.
293const OLGD_WELCOME: *u8 = "<!doctype html><html><head><meta charset=utf-8><meta name=viewport content=\"width=device-width,initial-scale=1\"><title>Nishi — your space</title><style>body{font-family:system-ui,sans-serif;max-width:520px;margin:6vh auto;padding:0 18px;color:#16202e}h1{font-size:1.3rem}ul{list-style:none;padding:0}li{margin:10px 0}a.card{display:block;padding:14px 16px;background:#0b2545;color:#fff;text-decoration:none;border-radius:8px;font-size:1.05rem}a.card:hover{background:#13386b}.sub{color:#667;font-size:.9rem}button{margin-top:18px;padding:8px 14px;background:#eef;border:1px solid #b9c2d6;border-radius:6px;cursor:pointer}</style></head><body><h1 id=h>Your space</h1><p class=sub>Signed in with a no-cookie OPAQUE session. These are the resources you have access to.</p><div id=links>Loading\\u2026</div><button onclick=out()>Sign out</button><script>var t=sessionStorage.getItem('nsess');function out(){sessionStorage.removeItem('nsess');location='/login'}if(!t){location='/login'}else{fetch('/access',{headers:{'X-Nishi-Session':t}}).then(function(r){if(!r.ok){location='/login';return null}return r.json()}).then(function(j){if(!j)return;if(j.handle)document.getElementById('h').textContent='Welcome, '+j.handle;var d=document.getElementById('links');if(j.links&&j.links.length){var u='<ul>';for(var i=0;i<j.links.length;i++){u+='<li><a class=card href=\"'+j.links[i].url+'\">'+j.links[i].label+' →</a></li>'}u+='</ul>';d.innerHTML=u}else{d.textContent='No resources are assigned to you yet.'}}).catch(function(e){document.getElementById('links').textContent='error: '+e})}</script></body></html>" as *u8
294
295// Recover fragment appended to the login page (kept a SEPARATE const because nx_cc caps single string literals;
296// OLGD_HTML is already near that cap). Self-contained: own nrec() + own ids, posts to /register with a mnemonic
297// field (the daemon routes that to olg_recover). The NEW rotated mnemonic is shown ONLY in the user's browser.
298const OLGD_RECOVER: *u8 = "<div style=\"max-width:480px;margin:20px auto;padding:0 18px;font-family:system-ui,sans-serif;color:#16202e\"><h3>Recover access (forgot passphrase)</h3><input id=ch placeholder=handle style=\"width:100%;padding:9px;margin:4px 0;box-sizing:border-box\"><input id=cm placeholder=\"24-word recovery mnemonic\" style=\"width:100%;padding:9px;margin:4px 0;box-sizing:border-box\"><input id=cp type=password placeholder=\"new passphrase\" style=\"width:100%;padding:9px;margin:4px 0;box-sizing:border-box\"><button onclick=nrec() style=\"padding:9px 16px;background:#0b2545;color:#fff;border:0;border-radius:5px;cursor:pointer\">Recover</button><div id=rmsg style=\"margin:10px 0;word-break:break-all\"></div></div><script>function nrec(){var m=document.getElementById('rmsg');m.textContent='Recovering...';fetch('/register',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:'handle='+encodeURIComponent(document.getElementById('ch').value)+'&mnemonic='+encodeURIComponent(document.getElementById('cm').value.trim())+'&pw='+encodeURIComponent(document.getElementById('cp').value)}).then(function(r){return r.json().then(function(j){return{ok:r.ok,j:j}})}).then(function(x){m.textContent=x.ok?('Recovered! SAVE THIS NEW MNEMONIC: '+x.j.mnemonic+' -- now log in above with your new passphrase.'):('Recovery failed: '+(x.j.error||'check handle and mnemonic'))})}</script>" as *u8
299
300// AUTO-RECOVER: the missing twin of OLGD_SPA. OLGD_SPA carries the token on link CLICKS; this carries it on the
301// INITIAL navigation. When the login page is served at a NON-/login URL (a top-level nav to a gated path with no
302// X-Nishi-Session header = the "logs in but isn't let in" bounce), it re-fetches the SAME path WITH the token from
303// sessionStorage and renders the real content. sessionStorage, NOT a cookie -- true to the no-cookie cardinal.
304const OLGD_AUTORECOVER: *u8 = "<script>(function(){var t=sessionStorage.getItem('nsess');if(t&&location.pathname!='/login'){fetch(location.pathname,{headers:{'X-Nishi-Session':t}}).then(function(r){return r.ok?r.text():null}).then(function(h){if(h){document.open();document.write(h);document.close()}})}})();</script>"
305
306// serve the login page + the recover fragment + the auto-recover (three consts concatenated -- under the cap).
307func olgd_send_loginpage(cfd: i64) -> i64 {
308 if (olgd_pgbuf as i64) == 0 { olgd_pgbuf = sys_mmap(OLGD_MAGIC_16384) }
309 let pg: *u8 = olgd_pgbuf; var po: i64 = 0 // constant content: recomposed in place, nothing to wipe
310 po = olgd_cat(pg, 0, OLGD_HTML)
311 po = olgd_cat(pg, po, OLGD_RECOVER)
312 po = olgd_cat(pg, po, OLGD_AUTORECOVER) // nav-bounce fix: a gated-path nav re-fetches itself WITH the token
313 olgd_send(cfd, "200 OK" as *u8, "text/html; charset=utf-8" as *u8, pg, po)
314 return 0
315}
316
317func main(argc: i64, argv: *i64) -> i64 {
318 if argc < 5 {
319 sys_write(1, "usage: nx_opaque_login_daemon <port> <keys-path> <store-path> <budget> [argon2_m_cost]\n" as *u8, 86)
320 sys_exit(2); return 2
321 }
322 let port: i64 = olgd_atoi(argv[1] as *u8)
323 let keys_path: *u8 = argv[2] as *u8
324 let store_path: *u8 = argv[3] as *u8
325 let budget: i64 = olgd_atoi(argv[4] as *u8)
326 var m_cost: i64 = OLGD_PROD_M
327 if argc > 5 { m_cost = olgd_atoi(argv[5] as *u8) }
328 var t_cost: i64 = 3
329 if argc > 6 { t_cost = olgd_atoi(argv[6] as *u8) }
330 var p_cost: i64 = 4
331 if argc > 7 { p_cost = olgd_atoi(argv[7] as *u8) }
332 var wikidir: *u8 = OLGD_WIKI_DIR
333 if argc > 8 { wikidir = argv[8] as *u8 } // serving dir (NAS path in prod, /mnt/c local)
334 // [9] = this realm's HR ROSTER (the invite list ls_signup_allowed consults). ABSENT -> signup stays
335 // DENIED, which is exactly today's state, so adding the gate cannot silently re-open registration.
336 // It makes reopening POSSIBLE and SAFE (LAN + invited) instead of a global boolean.
337 var hrstore: *u8 = 0 as *u8
338 if argc > 9 { hrstore = argv[9] as *u8 }
339
340 let ctx: *NxAuthContext = sys_mmap(256) as *NxAuthContext
341 // SESSION LIFETIME = 24h (86400s, the charter hard cap), up from the 900s/15-min default that caused
342 // re-login-every-15-min. The TTL is baked into each minted token; validators read its embedded expiry.
343 // Active sessions stay logged in for a day; sliding-refresh (nx_modern_auth_refresh_session) is the follow-on.
344 if olg_ctx_setup_ttl(ctx, keys_path, store_path, "nishi_site_admin" as *u8, 16, "Nishi site admin" as *u8, 16, OLGD_MAGIC_86400, m_cost, t_cost, p_cost) != 0 {
345 sys_write(1, "CTX-INIT-FAIL\n" as *u8, 14); sys_exit(1); return 1
346 }
347
348 // bind 0.0.0.0:port (LAN + loopback reachable for smoke/test; TLS-fronted in R3)
349 let addr: *u8 = sys_mmap(16)
350 addr[0]=2 as u8; addr[1]=0 as u8
351 addr[2]=((port>>8)&0xff) as u8; addr[3]=(port&0xff) as u8
352 addr[4]=0 as u8; addr[5]=0 as u8; addr[6]=0 as u8; addr[7]=0 as u8
353 var zi: i64=8; while zi<16 { addr[zi]=0 as u8; zi=zi+1 }
354 let lfd: i64 = sys_socket(2, 1, 0)
355 if lfd < 0 { sys_write(1, "SOCKET-FAIL\n" as *u8, 12); sys_exit(1); return 1 }
356 let optv: *u8 = sys_mmap(4); optv[0]=1 as u8
357 sys_setsockopt(lfd, 1, 2, optv, 4)
358 if sys_bind(lfd, addr, 16) < 0 { sys_write(1, "BIND-FAIL\n" as *u8, 10); sys_exit(1); return 1 }
359 if sys_listen(lfd, 16) < 0 { sys_write(1, "LISTEN-FAIL\n" as *u8, 12); sys_exit(1); return 1 }
360 sys_write(1, "OPAQUE-LOGIN-UP\n" as *u8, 16)
361
362 var served: i64 = 0
363 // Peer-address buffers HOISTED above the accept loop deliberately: a sys_mmap per connection is the
364 // per-call-alloc-in-a-loop class that leaked 8KB/call through ss_hget across 205 call sites.
365 let sa: *u8 = sys_mmap(64)
366 let sal: *i64 = sys_mmap(16) as *i64
367 let ip4: *u8 = sys_mmap(4)
368 // PER-REQUEST BUFFERS, ALLOCATED ONCE (see the discipline note at the top). Every one of these used to be a
369 // fresh sys_mmap inside the accept loop, never unmapped -- 24 KB+ of address space per request before any
370 // route ran, and the passphrase/token/mnemonic ones outlived the request. All are wiped before sys_close(cfd).
371 let req: *u8 = sys_mmap(OLGD_MAGIC_16384)
372 let resp: *u8 = sys_mmap(OLGD_MAGIC_8192)
373 let hbuf: *u8 = sys_mmap(128); let hl: *i64 = sys_mmap(16) as *i64
374 let pbuf: *u8 = sys_mmap(320); let pl: *i64 = sys_mmap(16) as *i64
375 let b64: *u8 = sys_mmap(512); let b64n: *i64 = sys_mmap(16) as *i64
376 let ldet: *u8 = sys_mmap(32); let cdet: *u8 = sys_mmap(32); let gdet: *u8 = sys_mmap(32)
377 let ruh: *u8 = sys_mmap(64); let ruhn: *i64 = sys_mmap(16) as *i64; let rux: *u8 = sys_mmap(160)
378 let tb: *u8 = sys_mmap(512); let uh: *u8 = sys_mmap(64); let uhn: *i64 = sys_mmap(16) as *i64
379 let wux: *u8 = sys_mmap(160); let whh: *u8 = sys_mmap(128); let wbig: *u8 = sys_mmap(OLGD_MAGIC_16384)
380 let rnb: *u8 = sys_mmap(512); let rnbn: *i64 = sys_mmap(16) as *i64; let wacc: *u8 = sys_mmap(OLGD_MAGIC_16384)
381 let lbd: *i64 = sys_mmap(16) as *i64
382 let rmn: *u8 = sys_mmap(512); let rml: *i64 = sys_mmap(16) as *i64
383 let nm: *u8 = sys_mmap(512); let nmn: *i64 = sys_mmap(16) as *i64
384 let mn: *u8 = sys_mmap(512); let mnn: *i64 = sys_mmap(16) as *i64
385 let ux: *u8 = sys_mmap(160); let hh: *u8 = sys_mmap(128); let big: *u8 = sys_mmap(OLGD_MAGIC_16384)
386 let tbw: *u8 = sys_mmap(512); let uhw: *u8 = sys_mmap(64); let uhnw: *i64 = sys_mmap(16) as *i64
387 let wpage: *u8 = sys_mmap(512); let wpath: *u8 = sys_mmap(OLGD_MAGIC_2048); let wlb: *i64 = sys_mmap(16) as *i64
388 let comb: *u8 = sys_mmap(OLGD_MAGIC_2097152) // /wiki page + SPA shim compose buffer; wiped to its used length after each send
389 while served < budget {
390 sal[0] = 16
391 // zero the octets EVERY accept: a stale address from the previous connection would be a
392 // LAN verdict inherited by a WAN caller -- the gate must never read last request's peer.
393 ip4[0] = 0 as u8; ip4[1] = 0 as u8; ip4[2] = 0 as u8; ip4[3] = 0 as u8
394 let cfd: i64 = sys_accept_with_addr(lfd, sa, sal)
395 if cfd >= 0 { ls_ip4_from_sockaddr(sa, ip4) }
396 if cfd >= 0 {
397 sys_set_socket_timeout(cfd, 5)
398 let rn: i64 = sys_read(cfd, req, OLGD_MAGIC_16383)
399 if rn > 0 {
400 let he: i64 = olgd_find(req, rn, "\r\n\r\n" as *u8, 4)
401 var body: *u8 = req; var bn: i64 = 0
402 if he >= 0 { body = ((req as i64) + he + 4) as *u8; bn = rn - he - 4 }
403 let now: i64 = sys_now_realtime_sec()
404
405 if olgd_starts(req, rn, "POST /login" as *u8) == 1 {
406 nx_http_form_get_field(body, bn, "handle" as *u8, 6, hbuf, 127, hl)
407 nx_http_form_get_field(body, bn, "pw" as *u8, 2, pbuf, 319, pl)
408 let lt0: i64 = sys_now_ms()
409 var lok: i64 = 0
410 if olg_login(ctx, hbuf, hl[0], pbuf, pl[0], b64, 512, b64n) == NX_MAUTH_OK { lok = 1 }
411 olgd_receipt("login" as *u8, hbuf, hl[0], lok, olgd_ms_detail(ldet, sys_now_ms() - lt0), now)
412 if lok == 1 {
413 // record uid->handle so /access resolves this user's entitlements by handle
414 if olg_whoami(ctx, b64, b64n[0], now, ruh, 64, ruhn) == NX_MAUTH_OK {
415 let ruxn: i64 = olgd_hex(rux, 0, ruh, ruhn[0])
416 olgd_idx_record(rux, ruxn, hbuf, hl[0])
417 }
418 var o: i64 = olgd_cat(resp, 0, "{\"token\":\"" as *u8); o = olgd_catb(resp, o, b64, b64n[0]); o = olgd_cat(resp, o, "\"}" as *u8)
419 olgd_send(cfd, "200 OK" as *u8, "application/json" as *u8, resp, o)
420 } else {
421 let o: i64 = olgd_cat(resp, 0, "{\"error\":\"invalid credentials\"}" as *u8)
422 olgd_send(cfd, "401 Unauthorized" as *u8, "application/json" as *u8, resp, o)
423 }
424 } else { if olgd_starts(req, rn, "GET /whoami" as *u8) == 1 {
425 let tl: i64 = olgd_hdr_val(req, he, "X-Nishi-Session:" as *u8, 16, tb, 512)
426 if olg_whoami(ctx, tb, tl, now, uh, 64, uhn) == NX_MAUTH_OK {
427 // uid + handle + entitled links in ONE response so the login page can render the landing
428 // INLINE via /whoami -- which the root :8443 front already proxies (no need to update that
429 // root front for the new /welcome+/access routes). Additive fields (uid still present).
430 let wuxn: i64 = olgd_hex(wux, 0, uh, uhn[0])
431 let whhn: i64 = olgd_idx_lookup(wux, wuxn, whh, 128)
432 var o: i64 = olgd_cat(wbig, 0, "{\"uid\":\"" as *u8); o = olgd_catb(wbig, o, wux, wuxn); o = olgd_cat(wbig, o, "\"," as *u8)
433 // SLIDING REFRESH: re-mint the session (bumped 24h expiry) + return it as "nsess"; a client
434 // heartbeat stores it so an active session never lapses. Additive field; old clients ignore it.
435 if olg_refresh(ctx, tb, tl, now, rnb, 512, rnbn) == NX_MAUTH_OK { o = olgd_cat(wbig, o, "\"nsess\":\"" as *u8); o = olgd_catb(wbig, o, rnb, rnbn[0]); o = olgd_cat(wbig, o, "\"," as *u8) }
436 let wsup: i64 = hra_is_superadmin(OLGD_HR_PATH, wux, wuxn) // owner -> auto-everything
437 let wan: i64 = he_emit_super(wsup, whh, whhn, OLGD_ENT_PATH, wacc, OLGD_MAGIC_16384)
438 o = olgd_catb(wbig, o, ((wacc as i64) + 1) as *u8, wan - 1)
439 olgd_send(cfd, "200 OK" as *u8, "application/json" as *u8, wbig, o)
440 } else {
441 let o: i64 = olgd_cat(resp, 0, "{\"error\":\"session rejected\"}" as *u8)
442 olgd_send(cfd, "401 Unauthorized" as *u8, "application/json" as *u8, resp, o)
443 }
444 } else { if olgd_starts(req, rn, "GET /board" as *u8) == 1 {
445 // PROTECTED: the Work Board is served ONLY to a valid OPAQUE session (Nishi Family).
446 // No valid X-Nishi-Session token -> 401 (never a public file).
447 let tl: i64 = olgd_hdr_val(req, he, "X-Nishi-Session:" as *u8, 16, tb, 512)
448 if olg_whoami(ctx, tb, tl, now, uh, 64, uhn) == NX_MAUTH_OK {
449 lbd[0] = 0
450 let page: *u8 = sys_read_file("/tmp/nx_work_board.html" as *u8, lbd)
451 if (page as i64) != 0 { olgd_send(cfd, "200 OK" as *u8, "text/html; charset=utf-8" as *u8, page, lbd[0]); sys_free_file(page, lbd[0]) }
452 else { let o2: i64 = olgd_cat(resp, 0, "board not generated yet" as *u8); olgd_send(cfd, "503 Service Unavailable" as *u8, "text/plain" as *u8, resp, o2) }
453 } else {
454 let o: i64 = olgd_cat(resp, 0, "{\"error\":\"login required\"}" as *u8)
455 olgd_send(cfd, "401 Unauthorized" as *u8, "application/json" as *u8, resp, o)
456 }
457 } else { if olgd_starts(req, rn, "POST /register" as *u8) == 1 {
458 nx_http_form_get_field(body, bn, "handle" as *u8, 6, hbuf, 127, hl)
459 nx_http_form_get_field(body, bn, "pw" as *u8, 2, pbuf, 319, pl)
460 nx_http_form_get_field(body, bn, "mnemonic" as *u8, 8, rmn, 511, rml)
461 if rml[0] > 0 {
462 // RECOVERY (ungated): the 24-word mnemonic credential uses IDENTITY-KSF (no argon), so it
463 // verifies regardless of the daemon's m_cost; on success it RESETS the passphrase under the
464 // CURRENT daemon params + ROTATES the mnemonic (the new one returns to the user's browser only).
465 var rok: i64 = 0
466 let ct0: i64 = sys_now_ms()
467 if olg_recover(ctx, hbuf, hl[0], rmn, rml[0], pbuf, pl[0], nm, 512, nmn) == NX_MAUTH_OK { rok = 1 }
468 olgd_receipt("recover" as *u8, hbuf, hl[0], rok, olgd_ms_detail(cdet, sys_now_ms() - ct0), now)
469 if rok == 1 {
470 var o: i64 = olgd_cat(resp, 0, "{\"ok\":true,\"mnemonic\":\"" as *u8); o = olgd_catb(resp, o, nm, nmn[0]); o = olgd_cat(resp, o, "\"}" as *u8)
471 olgd_send(cfd, "200 OK" as *u8, "application/json" as *u8, resp, o)
472 } else {
473 let o: i64 = olgd_cat(resp, 0, "{\"error\":\"recovery failed -- check handle + 24-word mnemonic\"}" as *u8)
474 olgd_send(cfd, "400 Bad Request" as *u8, "application/json" as *u8, resp, o)
475 }
476 } else { if olgd_signup_ok(ip4, hrstore, hbuf, hl[0]) == 0 {
477 // ONE refusal for every denial reason ON PURPOSE: telling a caller WHICH condition
478 // failed would leak whether a handle is invited (an enumeration oracle on the family roster).
479 let orc: i64 = olgd_cat(resp, 0, "{\"error\":\"registration closed -- signup requires the home network and an invite\"}" as *u8)
480 olgd_send(cfd, "403 Forbidden" as *u8, "application/json" as *u8, resp, orc)
481 } else {
482 var rgok: i64 = 0
483 let gt0: i64 = sys_now_ms()
484 if olg_register(ctx, hbuf, hl[0], pbuf, pl[0], mn, 512, mnn) == NX_MAUTH_OK { rgok = 1 }
485 olgd_receipt("register" as *u8, hbuf, hl[0], rgok, olgd_ms_detail(gdet, sys_now_ms() - gt0), now)
486 if rgok == 1 {
487 var o: i64 = olgd_cat(resp, 0, "{\"mnemonic\":\"" as *u8); o = olgd_catb(resp, o, mn, mnn[0]); o = olgd_cat(resp, o, "\"}" as *u8)
488 olgd_send(cfd, "200 OK" as *u8, "application/json" as *u8, resp, o)
489 } else {
490 let o: i64 = olgd_cat(resp, 0, "{\"error\":\"register failed\"}" as *u8)
491 olgd_send(cfd, "400 Bad Request" as *u8, "application/json" as *u8, resp, o)
492 }
493 } }
494 } else { if olgd_starts(req, rn, "GET /access" as *u8) == 1 {
495 // AUTHZ: validate session -> uid -> handle (index) -> this user's entitled links as JSON
496 let tl: i64 = olgd_hdr_val(req, he, "X-Nishi-Session:" as *u8, 16, tb, 512)
497 if olg_whoami(ctx, tb, tl, now, uh, 64, uhn) == NX_MAUTH_OK {
498 let uxn: i64 = olgd_hex(ux, 0, uh, uhn[0])
499 let hhn: i64 = olgd_idx_lookup(ux, uxn, hh, 128)
500 let asup: i64 = hra_is_superadmin(OLGD_HR_PATH, ux, uxn) // owner -> auto-everything
501 let bn: i64 = he_emit_super(asup, hh, hhn, OLGD_ENT_PATH, big, OLGD_MAGIC_16384)
502 olgd_send(cfd, "200 OK" as *u8, "application/json" as *u8, big, bn)
503 } else {
504 let oa: i64 = olgd_cat(resp, 0, "{\"error\":\"session rejected\"}" as *u8)
505 olgd_send(cfd, "401 Unauthorized" as *u8, "application/json" as *u8, resp, oa)
506 }
507 } else { if olgd_starts(req, rn, "GET /welcome" as *u8) == 1 {
508 olgd_send(cfd, "200 OK" as *u8, "text/html; charset=utf-8" as *u8, OLGD_WELCOME, olgd_slen(OLGD_WELCOME))
509 } else { if olgd_starts(req, rn, "GET /wiki" as *u8) == 1 {
510 // PROTECTED: serve any wiki_pages/<page> ONLY to a valid OPAQUE session (path-sanitized).
511 let tlw: i64 = olgd_hdr_val(req, he, "X-Nishi-Session:" as *u8, 16, tbw, 512)
512 if olg_whoami(ctx, tbw, tlw, now, uhw, 64, uhnw) == NX_MAUTH_OK {
513 var ps: i64 = 9
514 if ps < rn { if req[ps] == (47 as u8) { ps = ps + 1 } }
515 var po: i64 = 0; var rg: i64 = 1
516 while rg == 1 { if ps >= rn { rg = 0 } else { let cc: i64 = req[ps] as i64; if cc == 32 { rg = 0 } else { if cc == 63 { rg = 0 } else { if cc == 13 { rg = 0 } else { if po < 500 { wpage[po] = cc as u8; po = po + 1 } ps = ps + 1 } } } } }
517 if po == 0 { let idx: *u8 = "index.html" as *u8; var ii: i64 = 0; while idx[ii] != (0 as u8) { wpage[ii] = idx[ii]; po = po + 1; ii = ii + 1 } }
518 wpage[po] = 0 as u8
519 var safe: i64 = 1; var si: i64 = 0
520 while si < po { let cc: i64 = wpage[si] as i64; var ok: i64 = 0; if cc >= 97 { if cc <= 122 { ok = 1 } } if cc >= 65 { if cc <= 90 { ok = 1 } } if cc >= 48 { if cc <= 57 { ok = 1 } } if cc == 45 { ok = 1 } if cc == 95 { ok = 1 } if cc == 46 { ok = 1 } if ok == 0 { safe = 0; si = po } si = si + 1 }
521 var di: i64 = 0; while di + 1 < po { if wpage[di] == (46 as u8) { if wpage[di + 1] == (46 as u8) { safe = 0; di = po } } di = di + 1 }
522 if safe == 1 {
523 var wo: i64 = olgd_cat(wpath, 0, wikidir); wpath[wo] = 47 as u8; wo = wo + 1
524 var wi: i64 = 0; while wpage[wi] != (0 as u8) { wpath[wo] = wpage[wi]; wo = wo + 1; wi = wi + 1 } wpath[wo] = 0 as u8
525 wlb[0] = 0
526 let wdata: *u8 = sys_read_file(wpath, wlb)
527 if (wdata as i64) != 0 {
528 // BOUNDED (was an unchecked copy into a 2 MiB buffer): a page that cannot fit with the SPA shim is
529 // refused BY NAME, never truncated or overrun. The file buffer is released either way.
530 if wlb[0] + olgd_slen(OLGD_SPA) < OLGD_MAGIC_2097152 {
531 var co: i64 = 0; while co < wlb[0] { comb[co] = wdata[co]; co = co + 1 } co = olgd_cat(comb, co, OLGD_SPA)
532 olgd_send(cfd, "200 OK" as *u8, "text/html; charset=utf-8" as *u8, comb, co)
533 olgd_wipe(comb, co)
534 } else { let o6: i64 = olgd_cat(resp, 0, "page exceeds the 2 MiB serve cap" as *u8); olgd_send(cfd, "500 Internal Server Error" as *u8, "text/plain" as *u8, resp, o6) }
535 sys_free_file(wdata, wlb[0])
536 }
537 else { let o4: i64 = olgd_cat(resp, 0, "page not found" as *u8); olgd_send(cfd, "404 Not Found" as *u8, "text/plain" as *u8, resp, o4) }
538 } else { let o5: i64 = olgd_cat(resp, 0, "bad page name" as *u8); olgd_send(cfd, "400 Bad Request" as *u8, "text/plain" as *u8, resp, o5) }
539 } else { olgd_send_loginpage(cfd) }
540 } else {
541 // GET / or GET /login -> the login page + recover fragment
542 olgd_send_loginpage(cfd)
543 } } } } } } }
544 }
545 // WIPE EVERY PER-REQUEST BUFFER before the socket closes: the request body (passphrase), every token and
546 // mnemonic buffer, every response scratch. This is what makes the hoisted buffers equivalent to fresh
547 // zeroed mappings for the next request -- and what keeps secrets out of a long-lived, swappable process.
548 if rn > 0 { olgd_wipe(req, rn) }
549 olgd_wipe(resp, OLGD_MAGIC_8192)
550 olgd_wipe(hbuf, 128); olgd_wipe(hl as *u8, 16); olgd_wipe(pbuf, 320); olgd_wipe(pl as *u8, 16)
551 olgd_wipe(b64, 512); olgd_wipe(b64n as *u8, 16); olgd_wipe(ldet, 32); olgd_wipe(cdet, 32); olgd_wipe(gdet, 32)
552 olgd_wipe(ruh, 64); olgd_wipe(ruhn as *u8, 16); olgd_wipe(rux, 160)
553 olgd_wipe(tb, 512); olgd_wipe(uh, 64); olgd_wipe(uhn as *u8, 16)
554 olgd_wipe(wux, 160); olgd_wipe(whh, 128); olgd_wipe(wbig, OLGD_MAGIC_16384)
555 olgd_wipe(rnb, 512); olgd_wipe(rnbn as *u8, 16); olgd_wipe(wacc, OLGD_MAGIC_16384)
556 olgd_wipe(lbd as *u8, 16)
557 olgd_wipe(rmn, 512); olgd_wipe(rml as *u8, 16); olgd_wipe(nm, 512); olgd_wipe(nmn as *u8, 16)
558 olgd_wipe(mn, 512); olgd_wipe(mnn as *u8, 16)
559 olgd_wipe(ux, 160); olgd_wipe(hh, 128); olgd_wipe(big, OLGD_MAGIC_16384)
560 olgd_wipe(tbw, 512); olgd_wipe(uhw, 64); olgd_wipe(uhnw as *u8, 16)
561 olgd_wipe(wpage, 512); olgd_wipe(wpath, OLGD_MAGIC_2048); olgd_wipe(wlb as *u8, 16)
562 sys_close(cfd)
563 }
564 served = served + 1
565 }
566 sys_close(lfd); sys_exit(0); return 0
567}