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}