code wiki / _hdl_build / nx_hub_gw.nx

nx_hub_gw.nx source

↩ module page · 416 lines · 26440 B

1// nx_hub_gw.nx -- the HUB gateway DAEMON (loopback HTTP), the live shell over the gated maturity router. 2// Sits behind sites_daemon's fail-closed /hub reverse-proxy (mirrors the /wiki -> wiki_gw topology). Per 3// request it: resolves the OPAQUE no-cookie X-Nishi-Session token -> handle (the PROVEN chain olg_whoami -> 4// uid -> hex -> idx_lookup), re-reads the maturity registry + roles (HOT, leak-free -> a flag edit is live 5// with NO restart = the can't-crash-hosting property), calls mr_route, and acts FAIL-CLOSED: 6// SERVE -> 200 (artifact body, path-traversal-checked) DENY/no-session -> 302 /login 7// DENY/under-level -> 403 NOTFOUND / TIER_MISMATCH -> 404 (no leak) 8// ISOLATED from the login daemon ON PURPOSE: a bug here can never regress login/whoami/register. 9// 10// LEAK DISCIPLINE: every per-request read uses hgw_read_file (bounded sys_read into a REUSED buffer), NOT 11// sys_read_file (which reserves 4 GiB/call and never unmaps -- the F-class root). All buffers are mmap'd 12// ONCE before the accept loop and reused -> the daemon's virtual footprint is flat across requests. 13// Sovereign: nx_opaque_login (auth) + nx_maturity_registry (mr_route) + nx_hub_gw_decide (parsers). ORIGINAL. 14import "nx_opaque_login.nx" // olg_ctx_setup / olg_whoami + NxAuthContext + NX_MAUTH_OK 15import "nx_hub_gw_decide.nx" // hgw_path / hgw_hdr_val (reused parsers) 16import "nx_maturity_registry.nx" // mr_route 17import "nx_site_lock_lib.nx" // slk_* (idx parse + byte-eq) 18import "nx_access_lib.nx" // ag_resolve_level (handle -> level for the leveled index) 19import "nx_hub_render.nx" // mr_render_index (the leveled hub index) 20const HGW_MAGIC_65536: i64 = 65536 21const HGW_MAGIC_16400: i64 = 16400 22const HGW_MAGIC_131072: i64 = 131072 23const HGW_MAGIC_32768: i64 = 32768 24const HGW_MAGIC_262144: i64 = 262144 25const HGW_MAGIC_524288: i64 = 524288 26const HGW_MAGIC_1024: i64 = 1024 27const HGW_MAGIC_1280: i64 = 1280 28const HGW_MAGIC_16383: i64 = 16383 29const HGW_MAGIC_2097152: i64 = 2097152 30 31const HGW_REG: *u8 = "/volume1/ai/hub/maturity.tsv" 32const HGW_ROLES: *u8 = "/volume1/ai/hub/roles.tsv" 33const HGW_DOCROOT: *u8 = "/volume1/ai/hub/pages" 34const HGW_IDX: *u8 = "/volume1/homes/elderwesto/nishihost/nishi_uid_handle.tsv" 35// Seg-cache telemetry cadence. Rule 11: named for ONE purpose -- how often the per-process seg-store 36// counters are echoed to the log. 256 accepts is frequent enough to catch a fallback storm in minutes 37// and rare enough that it cannot itself become log noise (this daemon already writes one line/accept). 38const HGW_STAT_EVERY: i64 = 256 39// NO-COOKIE bootstrap (mirrors the wiki's OLGD_SPA): a token-less /hub navigation lands here; the JS reads the 40// X-Nishi-Session token from sessionStorage and RE-FETCHES the same path WITH the header (the only way the 41// no-cookie token reaches the gateway on a plain navigation), then document.write's the gated response. No 42// token in sessionStorage -> /login. This is the irreducible last-mile Web-API binding, emitted by this organ. 43const HGW_BOOTSTRAP: *u8 = "<!DOCTYPE html><html lang='en'><head><meta charset='utf-8'><meta name='viewport' content='width=device-width, initial-scale=1'><title>Nishi Hub</title></head><body><script>(function(){var t=sessionStorage.getItem('nsess');if(!t){location='/login';return}fetch(location.pathname,{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='/login'}})})();</script></body></html>" 44 45func hgw_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 } 46func hgw_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 } 47func hgw_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 } 48// 2026-07-29 seq1232: per-request scratch hoisted to lazy statics -- these helpers ran DOZENS of times 49// per request, each sys_mmap costing a fresh 4 KiB page with no munmap in the runtime (~1 MB/request 50// measured). Safe: this daemon never forks (sys_fork count = 0) and each box is consumed before reuse. 51static hgw_scr_t: *u8 52static hgw_scr_rk8: *i64 53static hgw_scr_rv8: *i64 54static hgw_scr_rk16: *i64 55static hgw_scr_rv16: *i64 56static hgw_scr_fs: *i64 57static hgw_scr_fe: *i64 58func hgw_itoa(dst: *u8, off: i64, v: i64) -> i64 { if (hgw_scr_t as i64)==0 { hgw_scr_t=sys_mmap(28) } let t: *u8=hgw_scr_t; 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 } 59func hgw_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 } 60func hgw_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n } 61 62// leak-free bounded read into a REUSED caller buffer; returns bytes (>=0) or -1. 63func hgw_read_file(path: *u8, out: *u8, cap: i64) -> i64 { 64 let fd: i64 = sys_openat_rd(path) 65 if fd < 0 { return 0 - 1 } 66 var total: i64 = 0; var go: i64 = 1 67 while go == 1 { 68 let base: i64 = out as i64 69 let tail: *u8 = (base + total) as *u8 70 let nr: i64 = sys_read(fd, tail, cap - total) 71 if nr <= 0 { go = 0 } 72 if nr > 0 { total = total + nr } 73 if total >= cap { go = 0 } 74 } 75 sys_close(fd) 76 return total 77} 78 79// resolve a handle's access level from the SOVEREIGN native store (nx_native_config), NOT roles.tsv 80// (operator: no tsv, use nishi data management). roles rows {handle, level}; exact-handle match -> its 81// level; absent/store-missing -> 0 (deny-by-default). Seeded by nx_hub_roles_seed. Read per-request so a 82// role change is live with NO restart (mirrors the old hot-reread). ncfg_* is in-unit via nx_hub_render. 83func hgw_level_native(handle: *u8, hlen: i64) -> i64 { 84 let rh0: *i64 = ncfg_open("knowledge/store/hub-roles-\x00" as *u8) 85 if (rh0 as i64) == 0 { return 0 } 86 let cnt: i64 = ncfg_count(rh0, "role\x00" as *u8) 87 if (hgw_scr_rk8 as i64) == 0 { hgw_scr_rk8 = sys_mmap(8 * 8) as *i64 } 88 if (hgw_scr_rv8 as i64) == 0 { hgw_scr_rv8 = sys_mmap(8 * 8) as *i64 } 89 let rk: *i64 = hgw_scr_rk8 90 let rv: *i64 = hgw_scr_rv8 91 var i: i64 = 0 92 var lvl: i64 = 0 93 while i < cnt { 94 let rf: i64 = ncfg_row(rh0, "role\x00" as *u8, i, rk, rv, 8) 95 if rf > 0 { 96 let rhn: *u8 = ncfg_field(rk, rv, rf, "handle\x00" as *u8) 97 var same: i64 = 1 98 var k: i64 = 0 99 while k < hlen { if rhn[k] != handle[k] { same = 0; k = hlen } else { k = k + 1 } } 100 if same == 1 { if rhn[hlen] == (0 as u8) { 101 let rl: *u8 = ncfg_field(rk, rv, rf, "level\x00" as *u8) 102 lvl = hgw_atoi(rl) 103 } } 104 } 105 i = i + 1 106 } 107 return lvl 108} 109 110// read ALL rows of `tag` from the SOVEREIGN native store at `prefix` and emit them as TAB-delimited lines 111// (fields in `keys` order) into `buf` -- the in-memory shape the legacy slk_*-parsing mr_*/ag_* consume. 112// The DATA lives in the content-addressed store (nx_native_config, NO tsv file); this is only the transient 113// in-memory form. Store absent -> 0 rows (fail-closed). Called per-request so a data edit is live, no restart. 114func hgw_ncfg_to_buf(prefix: *u8, tag: *u8, keys: *i64, nkeys: i64, buf: *u8, cap: i64) -> i64 { 115 let hh: *i64 = ncfg_open(prefix) 116 if (hh as i64) == 0 { return 0 } 117 let cnt: i64 = ncfg_count(hh, tag) 118 if (hgw_scr_rk16 as i64) == 0 { hgw_scr_rk16 = sys_mmap(8 * 16) as *i64 } 119 if (hgw_scr_rv16 as i64) == 0 { hgw_scr_rv16 = sys_mmap(8 * 16) as *i64 } 120 let rk: *i64 = hgw_scr_rk16 121 let rv: *i64 = hgw_scr_rv16 122 var o: i64 = 0 123 var i: i64 = 0 124 while i < cnt { 125 let rf: i64 = ncfg_row(hh, tag, i, rk, rv, 16) 126 if rf > 0 { 127 var f: i64 = 0 128 while f < nkeys { 129 let vp: *u8 = ncfg_field(rk, rv, rf, keys[f] as *u8) 130 if (vp as i64) != 0 { 131 var j: i64 = 0 132 while vp[j] != (0 as u8) { if o < cap - 2 { buf[o] = vp[j]; o = o + 1 } j = j + 1 } 133 } 134 if f + 1 < nkeys { if o < cap - 2 { buf[o] = 9 as u8; o = o + 1 } } 135 f = f + 1 136 } 137 if o < cap - 2 { buf[o] = 10 as u8; o = o + 1 } 138 } 139 i = i + 1 140 } 141 return o 142} 143 144// uidhex -> handle from the shared index buffer; returns handle len (0 if not found). flag-loop (no break). 145func hgw_idx_lookup(idxbuf: *u8, idxlen: i64, uidhex: *u8, uxn: i64, out_h: *u8, cap: i64) -> i64 { 146 if (hgw_scr_fs as i64) == 0 { hgw_scr_fs = sys_mmap(8) as *i64 } 147 if (hgw_scr_fe as i64) == 0 { hgw_scr_fe = sys_mmap(8) as *i64 } 148 let fs: *i64 = hgw_scr_fs; let fe: *i64 = hgw_scr_fe 149 var ls: i64 = 0; var found: i64 = 0 150 while ls < idxlen { 151 let le: i64 = slk_line_end(idxbuf, idxlen, ls) 152 if found == 0 { if le > ls { if idxbuf[ls] != (35 as u8) { 153 if slk_field(idxbuf, ls, le, 0, fs, fe) == 1 { 154 if slk_eq(slk_at(idxbuf, fs[0]), fe[0] - fs[0], uidhex, uxn) == 1 { 155 if slk_field(idxbuf, ls, le, 1, fs, fe) == 1 { 156 var o: i64 = 0; let hl: i64 = fe[0] - fs[0] 157 while o < hl { if o < cap - 1 { out_h[o] = idxbuf[fs[0] + o] } o = o + 1 } 158 out_h[o] = 0 as u8; found = hl 159 } 160 } 161 } 162 } } } 163 ls = le + 1 164 } 165 return found 166} 167 168// content-type by extension (covers what the hub serves; .wasm/.js explicit). 169func hgw_ctype(file: *u8, n: i64) -> *u8 { 170 var dot: i64 = 0 - 1; var i: i64 = 0 171 while i < n { if file[i] == (46 as u8) { dot = i } i = i + 1 } 172 if dot < 0 { return "application/octet-stream" as *u8 } 173 let ext: *u8 = ((file as i64) + dot) as *u8 174 let el: i64 = n - dot 175 if slk_eq(ext, el, ".html" as *u8, 5) == 1 { return "text/html; charset=utf-8" as *u8 } 176 if slk_eq(ext, el, ".js" as *u8, 3) == 1 { return "application/javascript" as *u8 } 177 if slk_eq(ext, el, ".wasm" as *u8, 5) == 1 { return "application/wasm" as *u8 } 178 if slk_eq(ext, el, ".css" as *u8, 4) == 1 { return "text/css" as *u8 } 179 if slk_eq(ext, el, ".json" as *u8, 5) == 1 { return "application/json" as *u8 } 180 if slk_eq(ext, el, ".png" as *u8, 4) == 1 { return "image/png" as *u8 } 181 if slk_eq(ext, el, ".svg" as *u8, 4) == 1 { return "image/svg+xml" as *u8 } 182 return "application/octet-stream" as *u8 183} 184 185// boundary defense (rule #12): doc-root-relative only -- reject ".." and any leading '/'. 186func hgw_path_safe(p: *u8, n: i64) -> i64 { 187 if n <= 0 { return 0 } 188 if p[0] == (47 as u8) { return 0 } 189 var i: i64 = 0 190 while i + 1 < n { if p[i] == (46 as u8) { if p[i + 1] == (46 as u8) { return 0 } } i = i + 1 } 191 return 1 192} 193 194// response emitter into a REUSED scratch buffer (leak-free). returns bytes written. 195func hgw_send(cfd: i64, scratch: *u8, status: *u8, ctype: *u8, body: *u8, blen: i64) -> i64 { 196 var o: i64 = 0 197 o = hgw_cat(scratch, o, "HTTP/1.1 " as *u8); o = hgw_cat(scratch, o, status) 198 o = hgw_cat(scratch, o, "\r\nContent-Type: " as *u8); o = hgw_cat(scratch, o, ctype) 199 o = hgw_cat(scratch, o, "\r\nContent-Length: " as *u8); o = hgw_itoa(scratch, o, blen) 200 o = hgw_cat(scratch, o, "\r\nConnection: close\r\nCache-Control: no-store\r\n\r\n" as *u8) 201 o = hgw_catb(scratch, o, body, blen) 202 sys_write(cfd, scratch, o) 203 return o 204} 205func hgw_redirect(cfd: i64, scratch: *u8, loc: *u8) -> i64 { 206 var o: i64 = 0 207 o = hgw_cat(scratch, o, "HTTP/1.1 302 Found\r\nLocation: " as *u8); o = hgw_cat(scratch, o, loc) 208 o = hgw_cat(scratch, o, "\r\nContent-Length: 0\r\nConnection: close\r\nCache-Control: no-store\r\n\r\n" as *u8) 209 sys_write(cfd, scratch, o) 210 return o 211} 212func hgw_text(cfd: i64, scratch: *u8, status: *u8, msg: *u8) -> i64 { 213 return hgw_send(cfd, scratch, status, "text/plain; charset=utf-8" as *u8, msg, hgw_slen(msg)) 214} 215 216func main(argc: i64, argv: *i64) -> i64 { 217 if argc < 5 { 218 sys_write(1, "usage: nx_hub_gw <port> <keys> <store> <budget> [m] [t] [p]\n" as *u8, 59) 219 sys_exit(2); return 2 220 } 221 let port: i64 = hgw_atoi(argv[1] as *u8) 222 let keys_path: *u8 = argv[2] as *u8 223 let store_path: *u8 = argv[3] as *u8 224 let budget: i64 = hgw_atoi(argv[4] as *u8) 225 var m_cost: i64 = HGW_MAGIC_65536 226 if argc > 5 { m_cost = hgw_atoi(argv[5] as *u8) } 227 var t_cost: i64 = 3 228 if argc > 6 { t_cost = hgw_atoi(argv[6] as *u8) } 229 var p_cost: i64 = 4 230 if argc > 7 { p_cost = hgw_atoi(argv[7] as *u8) } 231 232 // REALM MUST MATCH the /login daemon (:9091) that ISSUES the sessions -- olg_whoami validates the token 233 // against this realm+keys. /login uses "nishi_site_admin" (nx_opaque_login_daemon.nx); a different realm 234 // here would reject every valid token. keys_path/store_path (argv) MUST also be the login daemon's, read-only. 235 let ctx: *NxAuthContext = sys_mmap(256) as *NxAuthContext 236 if olg_ctx_setup(ctx, keys_path, store_path, "nishi_site_admin" as *u8, 16, "Nishi site admin" as *u8, 16, m_cost, t_cost, p_cost) != 0 { 237 sys_write(1, "CTX-INIT-FAIL\n" as *u8, 14); sys_exit(1); return 1 238 } 239 240 let addr: *u8 = sys_mmap(16) 241 addr[0] = 2 as u8; addr[1] = 0 as u8 242 addr[2] = ((port >> 8) & 255) as u8; addr[3] = (port & 255) as u8 243 var zi: i64 = 4; while zi < 16 { addr[zi] = 0 as u8; zi = zi + 1 } 244 let lfd: i64 = sys_socket(2, 1, 0) 245 if lfd < 0 { sys_write(1, "SOCKET-FAIL\n" as *u8, 12); sys_exit(1); return 1 } 246 let optv: *u8 = sys_mmap(4); optv[0] = 1 as u8; optv[1] = 0 as u8; optv[2] = 0 as u8; optv[3] = 0 as u8 247 sys_setsockopt(lfd, 1, 2, optv, 4) 248 if sys_bind(lfd, addr, 16) < 0 { sys_write(1, "BIND-FAIL\n" as *u8, 10); sys_exit(1); return 1 } 249 if sys_listen(lfd, 16) < 0 { sys_write(1, "LISTEN-FAIL\n" as *u8, 12); sys_exit(1); return 1 } 250 // FD_CLOEXEC on the LISTENER (debt 1785529579). This daemon binds by RAW SYSCALL and never calls 251 // nx_http_server_listen, so the helper's CLOEXEC fix cannot reach it -- rebuilding alone changes 252 // nothing. Measured live by nx_livecloexec_gate: this pid held a listener on fd=4 with the bit CLEAR. 253 // WHY IT MATTERS: /api/deploy forks+execs children from inside daemons; a child that inherits this 254 // listening fd holds the port FOREVER, and restarting the victim can never free it -- an outage that 255 // SURVIVES EVERY RESTART. SO_REUSEPORT does not rescue it: the kernel permits co-binding only when 256 // EVERY socket on the port set it, so one inherited legacy socket locks out even a REUSEPORT binder. 257 // 72 = SYS_FCNTL, 2 = F_SETFD, 1 = FD_CLOEXEC. 258 __syscall(72, lfd, 2, 1, 0, 0, 0) 259 sys_write(1, "HUB-GATEWAY-UP\n" as *u8, 15) 260 261 // reused buffers (allocated ONCE -> flat virtual footprint across requests) 262 let req: *u8 = sys_mmap(HGW_MAGIC_16400) 263 let regbuf: *u8 = sys_mmap(HGW_MAGIC_131072) 264 let rolesbuf: *u8 = sys_mmap(HGW_MAGIC_32768) 265 let idxbuf: *u8 = sys_mmap(HGW_MAGIC_262144) 266 // ⚠2026-07-25 OVERFLOW FIX: this was sys_mmap(HGW_MAGIC_262144) = 256 KiB, but ALL SIX writers 267 // (mr_render_index x2, hgw_read_file x3 incl. arbitrary docroot files + games.html) are handed 268 // HGW_MAGIC_2097152 as its capacity. Any hub page or static file over 256 KiB wrote up to 1.75 MiB 269 // PAST the end of this mapping, into the neighbouring reused buffers (scratch is allocated next). 270 // The allocation was simply never raised when the cap constant was. Allocate what the callers claim. 271 let bodybuf: *u8 = sys_mmap(HGW_MAGIC_2097152) 272 let scratch: *u8 = sys_mmap(HGW_MAGIC_524288) 273 let tokbuf: *u8 = sys_mmap(512) 274 let uid: *u8 = sys_mmap(64); let uidn: *i64 = sys_mmap(16) as *i64 275 let uxbuf: *u8 = sys_mmap(160) 276 let handle: *u8 = sys_mmap(128) 277 let path: *u8 = sys_mmap(HGW_MAGIC_1024) 278 let tgt: *u8 = sys_mmap(512) 279 let fullpath: *u8 = sys_mmap(HGW_MAGIC_1280) 280 let reqbox: *i64 = sys_mmap(8); let grantbox: *i64 = sys_mmap(8) 281 // native-store field-key arrays (built ONCE) for the per-request ncfg->buf reconstruction (no tsv files). 282 let rkeys: *i64 = sys_mmap(8 * 2) as *i64 283 rkeys[0] = ("handle\x00") as i64; rkeys[1] = ("level\x00") as i64 284 let mkeys: *i64 = sys_mmap(8 * 8) as *i64 285 mkeys[0] = ("artifact\x00") as i64; mkeys[1] = ("maturity\x00") as i64; mkeys[2] = ("access\x00") as i64 286 mkeys[3] = ("path\x00") as i64; mkeys[4] = ("title\x00") as i64; mkeys[5] = ("load_date\x00") as i64; mkeys[6] = ("is_current\x00") as i64 287 sys_write(1, "HGW-BUFS-OK\n" as *u8, 12) 288 289 var served: i64 = 0 290 while served < budget { 291 // ---- seq1057 RECLAMATION POINT (first consumer of ss_cache_reap) ------------------------ 292 // WHY HERE AND NOWHERE ELSE: ss_cache_reap frees seg-store handles that a cache invalidation 293 // orphaned, and its contract is that the CALLER guarantees no such handle is still in use. 294 // The top of the accept loop is structurally quiescent -- the previous request's frames have 295 // all returned, and the next request has not begun. Both of this daemon's store readers 296 // (hgw_level_native, hgw_ncfg_to_buf) bind their handle to a function-local `let` and never 297 // publish it into a static, a struct or a return value, so no handle can outlive an iteration. 298 // Reaping mid-request, or anywhere a handle is held, would be a use-after-free. 299 // 300 // WHAT IT ACTUALLY RECLAIMS: orphans appear only when a store's manifest CHANGES -- a roles or 301 // maturity edit. In steady state this is a no-op returning 0. After an edit it hands back the 302 // entire previous mapping set instead of leaking it for the life of the daemon, which is the 303 // hot-reread-without-restart property this gateway is built around. 304 ss_cache_reap() 305 let cfd: i64 = sys_accept(lfd) 306 if cfd >= 0 { 307 sys_set_socket_timeout(cfd, 5) 308 sys_write(1, "HGW-ACC\n" as *u8, 8) 309 // SEG-CACHE TELEMETRY READER (2026-08-14). The fallback/drop counters added to nx_seg_store 310 // the same day had NO reader anywhere in the estate -- and a counter nobody prints is exactly 311 // the fabricated-constant defect it was added to expose, so leaving it unread would have 312 // shipped the gap it closes. These are PER-PROCESS statics, so only the daemon can read them. 313 // WHAT TO WATCH: fallback climbing means the 16-slot prefix cache is exhausted and every call 314 // is opening a store per-request (now retired+reaped rather than leaked, but still real work); 315 // drop climbing means the 32-entry retire ring overflowed and pointers were shed -- the 316 // documented bounded leak, finally visible. retired is the live backlog awaiting the next reap. 317 // Uses the already-allocated `scratch` buffer: telemetry must not allocate in the hot loop. 318 if served % HGW_STAT_EVERY == 0 { 319 var so: i64 = 0 320 so = hgw_cat(scratch, so, "HGW-SEGCACHE fallback=" as *u8) 321 so = hgw_itoa(scratch, so, ss_cache_stat(SSC_ST_FALLBACK)) 322 so = hgw_cat(scratch, so, " drop=" as *u8) 323 so = hgw_itoa(scratch, so, ss_cache_stat(SSC_ST_DROP)) 324 so = hgw_cat(scratch, so, " retired=" as *u8) 325 so = hgw_itoa(scratch, so, ss_cache_retired()) 326 so = hgw_cat(scratch, so, "\n" as *u8) 327 sys_write(1, scratch, so) 328 } 329 let rn: i64 = sys_read(cfd, req, HGW_MAGIC_16383) 330 if rn > 0 { 331 let now: i64 = sys_now_realtime_sec() 332 let pl: i64 = hgw_path(req, rn, path, HGW_MAGIC_1024) 333 // ---- resolve session -> handle (empty if no/invalid session) ---- 334 var hl: i64 = 0 335 let tl: i64 = hgw_hdr_val(req, rn, "X-Nishi-Session:" as *u8, 16, tokbuf, 512) 336 if tl > 0 { 337 if olg_whoami(ctx, tokbuf, tl, now, uid, 64, uidn) == NX_MAUTH_OK { 338 let uxn: i64 = hgw_hex(uxbuf, 0, uid, uidn[0]) 339 let il: i64 = hgw_read_file(HGW_IDX, idxbuf, HGW_MAGIC_262144) 340 if il > 0 { hl = hgw_idx_lookup(idxbuf, il, uxbuf, uxn, handle, 128) } 341 } 342 } 343 // ---- HOT re-read registry + roles from the SOVEREIGN native store (nx_native_config, NOT 344 // tsv files); reconstructed into the in-memory buffers the slk_*-parsing mr_*/ag_* read. 345 // Store edit is live with NO restart (same hot property, now content-addressed). ---- 346 let rgl: i64 = hgw_ncfg_to_buf("knowledge/store/hub-maturity-\x00" as *u8, "art\x00" as *u8, mkeys, 7, regbuf, HGW_MAGIC_131072) 347 let rll: i64 = hgw_ncfg_to_buf("knowledge/store/hub-roles-\x00" as *u8, "role\x00" as *u8, rkeys, 2, rolesbuf, HGW_MAGIC_32768) 348 // ---- bare /hub or /hub/ = the LEVELED gated hub index (requires a session) ---- 349 var is_index: i64 = 0 350 if slk_eq(path, pl, "/hub" as *u8, 4) == 1 { is_index = 1 } 351 if slk_eq(path, pl, "/hub/" as *u8, 5) == 1 { is_index = 1 } 352 if is_index == 1 { 353 if hl > 0 { 354 // valid session -> the full leveled hub (with_upgrade=0 -> no re-fetch loop). 355 // level from the SOVEREIGN native store (nx_native_config), NOT roles.tsv. 356 let level: i64 = hgw_level_native(handle, hl) 357 let bn: i64 = mr_render_index(regbuf, rgl, level, 0, bodybuf, HGW_MAGIC_2097152) 358 hgw_send(cfd, scratch, "200 OK" as *u8, "text/html; charset=utf-8" as *u8, bodybuf, bn) 359 } else { 360 // no token on a plain navigation -> the OPEN games hub (level 0) + upgrade shim, so 361 // guests can play games AND a logged-in visitor is re-fetched to their full hub. A 362 // present-but-invalid/expired token -> /login (avoids the upgrade-shim loop). 363 if tl == 0 { 364 let bn2: i64 = mr_render_index(regbuf, rgl, 0, 1, bodybuf, HGW_MAGIC_2097152) 365 hgw_send(cfd, scratch, "200 OK" as *u8, "text/html; charset=utf-8" as *u8, bodybuf, bn2) 366 } 367 else { hgw_redirect(cfd, scratch, "/login" as *u8) } 368 } 369 } else { 370 if slk_eq(path, pl, "/hub/games-manifest.json" as *u8, 24) == 1 { 371 // ---- games update manifest (PUBLIC, no auth): the sovereign signed manifest as JSON. 372 // Steam/itch-style: version+sha256 per game from the native store, served for the 373 // in-browser auto-update client. Read from the docroot (deployed by the manifest organ). ---- 374 let gmn: i64 = hgw_read_file("/volume1/ai/hub/pages/games-manifest.json" as *u8, bodybuf, HGW_MAGIC_2097152) 375 if gmn >= 0 { hgw_send(cfd, scratch, "200 OK" as *u8, "application/json" as *u8, bodybuf, gmn) } 376 else { hgw_text(cfd, scratch, "404 Not Found" as *u8, "no games manifest" as *u8) } 377 } else { 378 if slk_eq(path, pl, "/hub/games" as *u8, 10) == 1 { 379 // ---- UNIFIED games portal (PUBLIC): the registry-driven portal HTML (nx_games_portal) 380 // served from the hub docroot. sites.elf serves the legacy /games; THIS is the one 381 // games space (all games from the sovereign registry), reachable via the hub card. ---- 382 let gpn: i64 = hgw_read_file("/volume1/ai/hub/pages/games.html" as *u8, bodybuf, HGW_MAGIC_2097152) 383 if gpn >= 0 { hgw_send(cfd, scratch, "200 OK" as *u8, "text/html; charset=utf-8" as *u8, bodybuf, gpn) } 384 else { hgw_text(cfd, scratch, "404 Not Found" as *u8, "no games portal" as *u8) } 385 } else { 386 // ---- artifact request: mr_route over the (already-read) registry + roles ---- 387 let act: i64 = mr_route(regbuf, rgl, rolesbuf, rll, path, pl, handle, hl, tgt, 512, reqbox, grantbox) 388 if act == 1 { 389 if hgw_path_safe(tgt, hgw_slen(tgt)) == 1 { 390 var fo: i64 = hgw_cat(fullpath, 0, HGW_DOCROOT); fullpath[fo] = 47 as u8; fo = fo + 1 391 fo = hgw_cat(fullpath, fo, tgt); fullpath[fo] = 0 as u8 392 let bn2: i64 = hgw_read_file(fullpath, bodybuf, HGW_MAGIC_2097152) 393 if bn2 >= 0 { hgw_send(cfd, scratch, "200 OK" as *u8, hgw_ctype(tgt, hgw_slen(tgt)), bodybuf, bn2) } 394 else { hgw_text(cfd, scratch, "404 Not Found" as *u8, "not found" as *u8) } 395 } else { hgw_text(cfd, scratch, "400 Bad Request" as *u8, "bad path" as *u8) } 396 } else { 397 if act == 0 { 398 if grantbox[0] == 0 { 399 if tl == 0 { hgw_send(cfd, scratch, "200 OK" as *u8, "text/html; charset=utf-8" as *u8, HGW_BOOTSTRAP, hgw_slen(HGW_BOOTSTRAP)) } 400 else { hgw_redirect(cfd, scratch, "/login" as *u8) } 401 } 402 else { hgw_text(cfd, scratch, "403 Forbidden" as *u8, "insufficient access level" as *u8) } 403 } else { 404 hgw_text(cfd, scratch, "404 Not Found" as *u8, "not found" as *u8) 405 } 406 } 407 } 408 } 409 } 410 } 411 sys_close(cfd) 412 } 413 served = served + 1 414 } 415 sys_close(lfd); sys_exit(0); return 0 416}