code wiki / _hdl_build / nx_hub_gw.nx

nx_hub_gw.nx source

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