code wiki / _hdl_build / nx_docportal_admin_daemon.nx
nx_docportal_admin_daemon.nx source
↩ module page · 1099 lines · 72720 B
1// nx_docportal_admin_daemon.nx -- the admin.<domain> DOCUMENT-PORTAL daemon, on the CANONICAL ecosystem auth
2// (the SAME Modern Auth nishifamily.com/status uses: OPAQUE-3DH + Argon2id + NO-COOKIE Ed25519 session). Operator
3// 2026-06-28: "match it to what we have in the nishi ecosystem and team." So this does NOT invent a token scheme --
4// it COMPOSES nx_status_daemon's proven auth (sd_* request parsing + nx_modern_auth_login + nx_sa_validate) and
5// wraps the proven nx_docportal_admin_lib router da_handle:
6// GET /admin -> the no-cookie login + upload SPA shell (public)
7// POST /admin/login -> handle+passphrase -> nx_modern_auth_login -> 200 {"token":...} | 401
8// * /admin/* (token) -> X-Nishi-Session validates (nx_sa_validate) ? da_handle at STAFF level : 401
9// A loopback HTTP daemon the sites daemon reverse-proxies to (behind the SNI router admin.<d> row); the public
10// TLS + cert are the operator-gated deploy step. Pure router dad_handle = bytes-in/bytes-out (the gate drives it
11// in-process, no socket). license_tier: ORIGINAL
12import "nx_docportal_admin_lib.nx"
13import "nx_status_daemon.nx"
14import "hub/nx_modern_auth_flow.nx"
15import "nx_syscalls.nx"
16import "nx_invite_token.nx" // invite-token store: inv_issue / inv_check / inv_consume
17import "nx_opaque_login.nx" // olg_register (OPAQUE one-shot over nx_modern_auth_register)
18import "nx_proxy_route.nx" // L2: pr_lookup_tmo, the per-read window the edge gives this host's /search row, so the heartbeat cadence is derived from the route table and never guessed
19import "nx_docportal_search_serve.nx" // R2 dss_serve: PUBLIC sovereign seg_store-native /search (no tsv, no derived .idx)
20import "nx_tool_registry.nx" // API-FIRST: the service self-registers nishi_search/nishi_doc at startup
21import "nx_multipart.nx" // RFC 7578 multipart/form-data parser (real <input type=file> uploads)
22import "nx_sysload.nx" // host sensors (sl_ncpu / sl_loadavg_milli): the concurrency ceiling is DERIVED from the live box, never guessed
23
24// Rule 11: the accept-failure backoff is a named constant, not a number buried in the loop.
25// 50ms is short enough that a transient accept error costs nothing perceptible and long enough that
26// a PERSISTENT one cannot spin a core -- 20 retries/sec instead of millions.
27const DAD_ACCEPT_BACKOFF_MS: i64 = 50
28// Rule 11: how stale the web-shard handle may get, in seconds. The refresh reopens a 1.59GB shard
29// on any manifest change, so it must NOT run per request. 10s bounds index staleness while keeping
30// the reopen off the hot path entirely when the crawler is active.
31// RAISED 10 -> 300 (2026-08-08) after measuring the PUBLIC cost of this gate. dss_web_cache_refresh
32// closes and reopens the whole web shard (94 segments, 1.59GB) whenever the manifest signature moved,
33// and that reopen was measured at 3-8s. This daemon is no longer the loopback admin daemon its
34// comments describe: proxy_routes.conf routes PUBLIC nishifamily.com /search and /doc here, so a
35// 10s gate put a multi-second reopen in front of essentially every human-paced click -- measured
36// from a browser vantage as 0.5s to 40s on /search and /doc while the crawler was committing.
37// 300s bounds index staleness at 5 minutes (fine for a doc/search index) and cuts a browsing user's
38// exposure ~30x. HONEST LIMIT: this is a MITIGATION, not the architectural fix. The reopen still
39// lands on the request path, it just lands 30x less often. The real fix is to refresh on a
40// background beat and swap the handle in when it is ready; the rate-limit state cannot simply move
41// into the forked child because the child exits and last_refresh would never advance, making EVERY
42// request refresh -- strictly worse than today.
43const DAD_REFRESH_MIN_SEC: i64 = 300
44// CEILING ON DEFERRAL (2026-08-14). Derived from DAD_REFRESH_MIN_SEC, not guessed: three refresh
45// intervals. It bounds worst-case index staleness at 15 minutes on a daemon that never goes idle,
46// while leaving ample room for the common case (any gap with zero children in flight refreshes at the
47// normal 300s). Named for exactly ONE purpose -- the deferral ceiling -- so it can never drift into
48// doubling as anything else.
49const DAD_REFRESH_MAX_SEC: i64 = 3 * DAD_REFRESH_MIN_SEC
50const DAD_REQCAP: i64 = 65536
51const DAD_OUTCAP: i64 = 524288
52
53// ---- BOUNDED CONCURRENCY (2026-08-14) -------------------------------------------------------
54// Rule 11: the ceiling is DERIVED from the live host (ncpu - reserve - load) via the incumbent
55// sensor layer nx_sysload, not guessed. Only two policy numbers remain, each named for ONE thing.
56// DAD_CPU_RESERVE: cores deliberately left to the rest of the estate (sites.elf, gallery, crawler)
57// so serving /search can never starve the box -- the same polite-worker-budget shape nx_sysload
58// was written to feed (nx_resource_governor: "ncpu - reserve - load").
59const DAD_CPU_RESERVE: i64 = 2
60// DAD_MIN_WORKERS: the floor. 1 reproduces exactly today's behaviour, so this change can only ever
61// ADD concurrency and never remove it -- the fail-safe direction is "behave as before".
62const DAD_MIN_WORKERS: i64 = 1
63
64// Reap every child that has ALREADY exited, without blocking. Returns how many were reaped.
65// Composes the proven idiom rather than inventing a second reaper: nx_initlive_probe verifies
66// fork + wait4(WNOHANG) works on this host (verdict=GREEN) and nx_init_live supervises with it.
67func dad_reap_done(wst: *i64) -> i64 {
68 var n: i64 = 0
69 var go: i64 = 1
70 while go == 1 {
71 let r: i64 = sys_wait4(0 - 1, wst, WNOHANG)
72 if r > 0 { n = n + 1 } else { go = 0 }
73 }
74 return n
75}
76
77// The concurrency ceiling, sensed from the REAL host through nx_sysload's reader.
78// DELIBERATELY NOT "ncpu - reserve - load". That is nx_resource_governor's POLITE BATCH budget, and
79// it is correct for background work that must yield to interactive serving. This daemon IS the
80// interactive serving path, so subtracting instantaneous load is wrong BY CONSTRUCTION: the load it
81// samples INCLUDES the traffic it exists to serve, so it throttles itself hardest exactly when
82// demand is highest. MEASURED 2026-08-14 on this box (AMD Ryzen Embedded V1500B, 4c/8t): ncpu=8,
83// loadavg~6 gave 8 - 2 - 6 = 0 -> clamped to 1 -> the ceiling silently reproduced the very serial
84// behaviour this change exists to remove, and 12 concurrent queries still completed strictly in
85// sequence (1.3s..14.1s).
86// ★A CEILING THAT SUBTRACTS THE DEMAND IT MUST ABSORB IS A SELF-DEFEATING BOUND.
87// CAPACITY is the ceiling; OVERLOAD is handled by the backpressure wait below, which is dynamic and
88// cannot deadlock. sl_ncpu() returns 1 as its safe default on read failure, so ncpu is PRINTED at
89// startup beside the cap -- an unsensed 1 must be visible as such, never mistaken for a measurement.
90func dad_worker_cap() -> i64 {
91 let ncpu: i64 = sl_ncpu()
92 var capw: i64 = ncpu - DAD_CPU_RESERVE
93 if capw < DAD_MIN_WORKERS { capw = DAD_MIN_WORKERS }
94 return capw
95}
96
97// the no-cookie admin SPA: log in -> token in sessionStorage -> upload form posts to /admin/upload with the
98// X-Nishi-Session header (NEVER a cookie). Minimal Web-API binding only (fetch/FileReader are JS-only). (public UI)
99func dad_shell(out: *u8) -> i64 {
100 // Build the HTML body FIRST into a temp buffer so the header can carry Content-Length. sites.elf's BUFFERED
101 // reverse proxy delimits the upstream body by Content-Length; a close-delimited response (no length) makes the
102 // proxy hop fail (-4) even though the daemon serves a direct client fine. Mirrors the JSON paths + the mgmt API.
103 let body: *u8 = sys_mmap(DAD_OUTCAP)
104 var b: i64 = 0
105 b = sd_cat(body, b, "<!DOCTYPE html><meta charset=utf-8><meta name=viewport content=\"width=device-width,initial-scale=1\"><title>Nishi document portal</title>" as *u8)
106 b = sd_cat(body, b, "<style>body{font-family:-apple-system,Segoe UI,sans-serif;max-width:720px;margin:6vh auto;padding:0 20px;color:#1c1c1e}input,select{width:100%;padding:9px;margin:.4rem 0;box-sizing:border-box;border:1px solid #ccc;border-radius:7px}button{padding:10px 18px;border:0;border-radius:7px;background:#0a6;color:#fff;font-size:1rem}.row{display:flex;gap:14px}.row label{font-size:.9rem}.e{color:#b00;min-height:1.2em}</style>" as *u8)
107 // NISHI-FIRST (2026-07-05 doctrine): the login is a REAL <form> -- native POST works with ZERO JS
108 // (nishi browser / any no-JS client); the JS below upgrades it to the SPA flow for third-party
109 // browsers (the last-mile shim). Hidden ui=1 rides ONLY the native submit -> server answers HTML.
110 b = sd_cat(body, b, "<div id=login><h2>🔒 Nishi document portal</h2><form id=lf method=post action=/admin/login><input type=hidden name=ui value=1><input id=h name=handle placeholder=\"handle\" autocomplete=username autofocus><input id=p name=passphrase type=password placeholder=\"passphrase\" autocomplete=current-password><button type=submit id=b>Sign in</button></form><p id=e class=e></p></div>" as *u8)
111 b = sd_cat(body, b, "<div id=up hidden><h2>Upload a document</h2><input id=dom placeholder=\"domain (e.g. andelinwest.com)\"><input id=f type=file>" as *u8)
112 b = sd_cat(body, b, "<div class=row><label><input type=radio name=vis value=public checked> Public (visitor-facing)</label><label><input type=radio name=vis value=private> Private (client vault)</label></div>" as *u8)
113 b = sd_cat(body, b, "<div class=row><label><input type=checkbox id=ps checked> List in onsite search</label><label><input type=checkbox id=ab> Allow AI to publish blog posts</label></div>" as *u8)
114 b = sd_cat(body, b, "<button id=u>Upload</button><p id=r class=e></p></div>" as *u8)
115 b = sd_cat(body, b, "<script>var L=document.getElementById('login'),U=document.getElementById('up'),E=document.getElementById('e');" as *u8)
116 b = sd_cat(body, b, "document.getElementById('lf').onsubmit=function(){E.textContent='';var b='handle='+encodeURIComponent(h.value)+'&passphrase='+encodeURIComponent(p.value);fetch('/admin/login',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:b}).then(function(r){if(r.ok){return r.json()}throw 0}).then(function(j){sessionStorage.nx_sess=j.token;L.hidden=true;U.hidden=false}).catch(function(){E.textContent='Wrong handle or passphrase.'});return false};" as *u8)
117 b = sd_cat(body, b, "document.getElementById('u').onclick=function(){var R=document.getElementById('r');R.textContent='';var fr=new FileReader();fr.onload=function(){var vis=document.querySelector('input[name=vis]:checked').value;var q='/admin/upload?domain='+encodeURIComponent(dom.value)+'&visibility='+vis+'&pub_search='+(document.getElementById('ps').checked?1:0)+'&ai_blog='+(document.getElementById('ab').checked?1:0);fetch(q,{method:'POST',headers:{'X-Nishi-Session':sessionStorage.nx_sess||''},body:fr.result}).then(function(r){return r.text()}).then(function(t){R.style.color='#0a6';R.textContent=t}).catch(function(){R.textContent='Upload failed.'})};fr.readAsText(document.getElementById('f').files[0])};</script>" as *u8)
118 var o: i64 = 0
119 o = sd_cat(out, o, "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\nConnection: close\r\nContent-Length: " as *u8)
120 o = sd_catn(out, o, b)
121 o = sd_cat(out, o, "\r\n\r\n" as *u8)
122 var i: i64 = 0
123 while i < b { out[o] = body[i]; o = o + 1; i = i + 1 }
124 return o
125}
126
127// Per-domain invite-token store path = "<storefile>.invites" (storefile = argv[3], carried on
128// ctx.account_store). Each realm/domain has its OWN storefile, so its invite tokens are isolated
129// by construction (per-domain isolation). DERIVED (not a new argv slot) => the daemon argv contract
130// <port> <keysfile> <storefile> <realm> <budget> stays unchanged (backward-compatible).
131func dad_invite_path(ctx: *NxAuthContext, out: *u8) -> i64 {
132 let sp: *u8 = ctx.account_store as *u8
133 var i: i64 = 0
134 while sp[i] != (0 as u8) { out[i] = sp[i]; i = i + 1 }
135 let suf: *u8 = ".invites" as *u8
136 var j: i64 = 0
137 while suf[j] != (0 as u8) { out[i] = suf[j]; i = i + 1; j = j + 1 }
138 out[i] = 0 as u8
139 return i
140}
141
142// ==== NISHI-FIRST no-JS session plumbing (2026-07-05 doctrine: nishi os/browser first; JS = the
143// third-party last-mile shim). The no-cookie C1 cardinal is PRESERVED: no Set-Cookie ever -- the no-JS
144// path carries the Ed25519 session token in the ?s= query of same-portal links (short-TTL, realm-scoped,
145// SAME validator). The query-session helpers now live in the SHARED nx_site_auth (nx_sa_validate_qs /
146// nx_sa_qs_raw / nx_sa_tok_urlenc) so mail/siteedit/every surface inherit them -- DRY, one auth home. ====
147
148// the no-JS signed-in LANDING page (HTML answer to a form login): links carry the session in ?s=.
149func dad_landing(out: *u8, b64: *u8, b64n: i64) -> i64 {
150 let body: *u8 = sys_mmap(DAD_OUTCAP)
151 var b: i64 = 0
152 b = sd_cat(body, b, "<!DOCTYPE html><meta charset=utf-8><title>Signed in</title><body style=\"font-family:sans-serif;max-width:720px;margin:6vh auto\"><h2>Signed in</h2><p>No-JS session active (nishi-first). Continue to:</p><p><a href=\"/admin/ui?s=" as *u8)
153 b = nx_sa_tok_urlenc(b64, b64n, body, b)
154 b = sd_cat(body, b, "\">the document portal</a></p>" as *u8)
155 var o: i64 = 0
156 o = sd_cat(out, o, "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\nConnection: close\r\nContent-Length: " as *u8)
157 o = sd_catn(out, o, b)
158 o = sd_cat(out, o, "\r\n\r\n" as *u8)
159 var i: i64 = 0
160 while i < b { out[o] = body[i]; o = o + 1; i = i + 1 }
161 return o
162}
163
164// the no-JS admin UI: shows the authenticated handle + a paste-text upload FORM (multipart file
165// pickers are the next rung; textarea covers the nishi browser + any no-JS client TODAY).
166// ---- session-uid -> handle map (the no-cookie session carries the 32-byte uid HASH, not the plaintext
167// handle; without this the authed page shows binary hash bytes in "Signed in as"). Mirrors the mail portal's
168// .mailmap: login/register append "MAP <64hex-uid> <handle>\n" to "<storefile>.uidmap" (append-only, rule 13),
169// /admin/ui resolves hash->handle. Same OPAQUE realm -> the docportal now shows the handle like the mail portal.
170func dad_hex32(src: *u8, out: *u8) -> i64 {
171 let hx: *u8 = "0123456789abcdef" as *u8
172 var i: i64 = 0
173 while i < 32 { out[i * 2] = hx[((src[i] as i64) >> 4) & 15]; out[i * 2 + 1] = hx[(src[i] as i64) & 15]; i = i + 1 }
174 out[64] = 0 as u8
175 return 64
176}
177func dad_uidmap_path(ctx: *NxAuthContext, out: *u8) -> i64 {
178 let sp: *u8 = ctx.account_store as *u8
179 var i: i64 = 0
180 while sp[i] != (0 as u8) { out[i] = sp[i]; i = i + 1 }
181 let suf: *u8 = ".uidmap" as *u8
182 var j: i64 = 0
183 while suf[j] != (0 as u8) { out[i] = suf[j]; i = i + 1; j = j + 1 }
184 out[i] = 0 as u8
185 return i
186}
187func dad_map_put(path: *u8, hexuid: *u8, h: *u8, hn: i64) -> i64 {
188 let row: *u8 = sys_mmap(256)
189 var o: i64 = sd_cat(row, 0, "MAP " as *u8)
190 var i: i64 = 0
191 while i < 64 { row[o] = hexuid[i]; o = o + 1; i = i + 1 }
192 row[o] = 32 as u8; o = o + 1
193 i = 0
194 while i < hn { row[o] = h[i]; o = o + 1; i = i + 1 }
195 row[o] = 10 as u8; o = o + 1
196 let fd: i64 = sys_openat_append(path, 0x1a4)
197 if fd < 0 { return 0 - 1 }
198 sys_write(fd, row, o)
199 sys_close(fd)
200 return 0
201}
202func dad_map_get(path: *u8, hexuid: *u8, out: *u8, cap: i64) -> i64 {
203 let szp: *i64 = sys_mmap(16) as *i64
204 szp[0] = 0
205 let b: *u8 = sys_read_file(path, szp)
206 if (b as i64) == 0 { sys_munmap(szp as *u8, 16); return 0 }
207 let n: i64 = szp[0]
208 var best: i64 = 0
209 var cur: i64 = 0
210 while cur < n {
211 var le: i64 = cur
212 while le < n && (b[le] as i64) != 10 { le = le + 1 }
213 if le - cur > 69 {
214 if (b[cur] as i64) == 77 {
215 var m: i64 = 1
216 var k: i64 = 0
217 while k < 64 { if b[cur + 4 + k] != hexuid[k] { m = 0; k = 64 } else { k = k + 1 } }
218 if m == 1 {
219 var w: i64 = 0
220 var s: i64 = cur + 69
221 while s < le { if w < cap - 1 { out[w] = b[s]; w = w + 1 } s = s + 1 }
222 out[w] = 0 as u8
223 best = w
224 }
225 }
226 }
227 cur = le + 1
228 }
229 // FREE the buffer sys_read_file mmap'd (cap 4 GiB + 16) -- it is NEVER freed by sys_read_file (the caller owns
230 // it), so this per-request map lookup was leaking the whole (append-only, growing) map file resident every call
231 // = the measured 27 GB docportal RSS (the F-class sys_read_file pattern). munmap bounds it to zero residual.
232 sys_munmap(b, 4294967312)
233 sys_munmap(szp as *u8, 16)
234 return best
235}
236
237func dad_ui_page(out: *u8, handle: *u8, hn: i64, sraw: *u8, sn: i64) -> i64 {
238 let body: *u8 = sys_mmap(DAD_OUTCAP)
239 var b: i64 = 0
240 b = sd_cat(body, b, "<!DOCTYPE html><meta charset=utf-8><title>Nishi document portal</title><body style=\"font-family:sans-serif;max-width:720px;margin:6vh auto\"><h2>Document portal</h2><p>Signed in as <b>" as *u8)
241 var i: i64 = 0
242 while i < hn { body[b] = handle[i]; b = b + 1; i = i + 1 }
243 b = sd_cat(body, b, "</b> (no-JS mode)</p><form method=post action=\"/admin/uploadtext?s=" as *u8)
244 i = 0
245 while i < sn { body[b] = sraw[i]; b = b + 1; i = i + 1 }
246 b = sd_cat(body, b, "\"><p><input name=domain value=\"andelinwest.com\" style=\"width:100%;padding:8px\"></p><p><select name=visibility><option value=public>Public (visitor-facing)</option><option value=private>Private (client vault)</option></select> <label><input type=checkbox name=pub_search value=1 checked> List in onsite search</label> <label><input type=checkbox name=ai_blog value=1> Allow AI blog</label></p><p><textarea name=text rows=12 style=\"width:100%\" placeholder=\"paste the document text here\"></textarea></p><button type=submit>Upload text</button></form>" as *u8)
247 // REAL file upload (multipart/form-data): a standard <input type=file>. Any file, binary-safe.
248 b = sd_cat(body, b, "<hr><h3>Or upload a file</h3><form enctype=multipart/form-data method=post action=\"/admin/uploadfile?s=" as *u8)
249 i = 0
250 while i < sn { body[b] = sraw[i]; b = b + 1; i = i + 1 }
251 b = sd_cat(body, b, "\"><p><input name=domain value=\"andelinwest.com\" style=\"width:100%;padding:8px\"></p><p><select name=visibility><option value=public>Public (visitor-facing)</option><option value=private>Private (client vault)</option></select> <label><input type=checkbox name=pub_search value=1 checked> List in onsite search</label></p><p><input type=file name=file></p><button type=submit>Upload file</button></form>" as *u8)
252 var o: i64 = 0
253 o = sd_cat(out, o, "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\nConnection: close\r\nContent-Length: " as *u8)
254 o = sd_catn(out, o, b)
255 o = sd_cat(out, o, "\r\n\r\n" as *u8)
256 i = 0
257 while i < b { out[o] = body[i]; o = o + 1; i = i + 1 }
258 return o
259}
260
261// no-JS upload shim: form-urlencoded fields (domain/visibility/pub_search/ai_blog/text) -> the
262// CANONICAL /admin/upload request (query params on the request line + raw text body) -> da_handle.
263// One storage path, two front doors (rule 15/19: same engine, no contract fork).
264func dad_uploadtext(ctx: *NxAuthContext, req: *u8, req_n: i64, subject: *u8, out: *u8) -> i64 {
265 let body_off: i64 = sd_body_off(req, req_n)
266 let body: *u8 = ((req as i64) + body_off) as *u8
267 let body_n: i64 = req_n - body_off
268 let f_off: *i64 = sys_mmap(8) as *i64
269 let f_len: *i64 = sys_mmap(8) as *i64
270 // domain
271 let domb: *u8 = sys_mmap(256)
272 var dom_n: i64 = 0
273 if sd_form_field(body, body_n, "domain" as *u8, 6, f_off, f_len) == 1 {
274 dom_n = sd_urldecode(((body as i64) + f_off[0]) as *u8, f_len[0], domb, 255)
275 }
276 if dom_n < 1 { return sd_cat(out, 0, "HTTP/1.1 400 Bad Request\r\nContent-Type: text/plain\r\nConnection: close\r\nContent-Length: 14\r\n\r\nmissing domain" as *u8) }
277 // visibility (default public)
278 let visb: *u8 = sys_mmap(64)
279 var vis_n: i64 = 0
280 if sd_form_field(body, body_n, "visibility" as *u8, 10, f_off, f_len) == 1 {
281 vis_n = sd_urldecode(((body as i64) + f_off[0]) as *u8, f_len[0], visb, 63)
282 }
283 if vis_n < 1 { visb[0] = 112 as u8; visb[1] = 117 as u8; visb[2] = 98 as u8; visb[3] = 108 as u8; visb[4] = 105 as u8; visb[5] = 99 as u8; vis_n = 6 }
284 // checkboxes: present -> 1, absent -> 0
285 var ps: i64 = 0
286 if sd_form_field(body, body_n, "pub_search" as *u8, 10, f_off, f_len) == 1 { ps = 1 }
287 var ab: i64 = 0
288 if sd_form_field(body, body_n, "ai_blog" as *u8, 7, f_off, f_len) == 1 { ab = 1 }
289 // the document text
290 if sd_form_field(body, body_n, "text" as *u8, 4, f_off, f_len) != 1 { return sd_cat(out, 0, "HTTP/1.1 400 Bad Request\r\nContent-Type: text/plain\r\nConnection: close\r\nContent-Length: 12\r\n\r\nmissing text" as *u8) }
291 let txt: *u8 = sys_mmap(DAD_REQCAP)
292 let txt_n: i64 = sd_urldecode(((body as i64) + f_off[0]) as *u8, f_len[0], txt, DAD_REQCAP - 1)
293 if txt_n < 1 { return sd_cat(out, 0, "HTTP/1.1 400 Bad Request\r\nContent-Type: text/plain\r\nConnection: close\r\nContent-Length: 10\r\n\r\nempty text" as *u8) }
294 // synthesize the canonical upload request
295 let syn: *u8 = sys_mmap(DAD_REQCAP + 4096)
296 var s: i64 = 0
297 s = sd_cat(syn, s, "POST /admin/upload?domain=" as *u8)
298 var i: i64 = 0
299 while i < dom_n { syn[s] = domb[i]; s = s + 1; i = i + 1 }
300 s = sd_cat(syn, s, "&visibility=" as *u8)
301 i = 0
302 while i < vis_n { syn[s] = visb[i]; s = s + 1; i = i + 1 }
303 s = sd_cat(syn, s, "&pub_search=" as *u8)
304 if ps == 1 { syn[s] = 49 as u8 } else { syn[s] = 48 as u8 }
305 s = s + 1
306 s = sd_cat(syn, s, "&ai_blog=" as *u8)
307 if ab == 1 { syn[s] = 49 as u8 } else { syn[s] = 48 as u8 }
308 s = s + 1
309 s = sd_cat(syn, s, " HTTP/1.1\r\n\r\n" as *u8)
310 i = 0
311 while i < txt_n { syn[s] = txt[i]; s = s + 1; i = i + 1 }
312 let dctx: *DaCtx = sys_mmap(16) as *DaCtx
313 dctx.user_level = 2
314 dctx.subject = subject
315 return da_handle(dctx, syn, s, out)
316}
317
318// ===== REAL multipart/form-data FILE upload (standards-correct: what an <input type=file> submits; binary-
319// safe, any file type). Composes the shipped nx_multipart parser + feeds the extracted file bytes into the
320// SAME da_handle /admin/upload store path the paste-text shim uses (one storage engine, three front doors). =====
321
322// extract the boundary token from the request's Content-Type header. returns length (0 if none).
323func dad_ct_boundary(req: *u8, req_n: i64, out: *u8) -> i64 {
324 let off: *i64 = sys_mmap(8) as *i64
325 let ln: *i64 = sys_mmap(8) as *i64
326 if nx_http_header_find(req, req_n, "Content-Type" as *u8, 12, off, ln) != NXHF_FOUND { return 0 }
327 let v: *u8 = ((req as i64) + off[0]) as *u8
328 let vn: i64 = ln[0]
329 let key: *u8 = "boundary=" as *u8
330 var bstart: i64 = 0 - 1
331 var i: i64 = 0
332 while i + 9 <= vn {
333 var m: i64 = 1
334 var j: i64 = 0
335 while j < 9 { if (v[i+j] as i64) != (key[j] as i64) { m = 0; j = 9 } else { j = j + 1 } }
336 if m == 1 { bstart = i + 9; i = vn } else { i = i + 1 }
337 }
338 if bstart < 0 { return 0 }
339 var w: i64 = 0
340 var k: i64 = bstart
341 while k < vn { let c: i64 = v[k] as i64; if c == 59 { k = vn } else { if c != 34 { out[w] = c as u8; w = w + 1 } k = k + 1 } }
342 out[w] = 0 as u8
343 return w
344}
345
346// extract a `key` value (e.g. name=" or filename=") from a part's header block body[hoff..hoff+hlen).
347// returns value length (0 if absent). key includes the trailing quote char it opens on.
348func dad_cd_extract(body: *u8, hoff: i64, hlen: i64, key: *u8, out: *u8) -> i64 {
349 var kl: i64 = 0
350 while key[kl] != (0 as u8) { kl = kl + 1 }
351 let end: i64 = hoff + hlen
352 var start: i64 = 0 - 1
353 var i: i64 = hoff
354 while i + kl <= end {
355 var m: i64 = 1
356 var j: i64 = 0
357 while j < kl { if (body[i+j] as i64) != (key[j] as i64) { m = 0; j = kl } else { j = j + 1 } }
358 if m == 1 { start = i + kl; i = end } else { i = i + 1 }
359 }
360 if start < 0 { return 0 }
361 var w: i64 = 0
362 var k: i64 = start
363 while k < end { if (body[k] as i64) == 34 { k = end } else { out[w] = body[k]; w = w + 1; k = k + 1 } }
364 out[w] = 0 as u8
365 return w
366}
367
368const DAD_MAXPARTS: i64 = 16
369
370// parse the multipart body -> pull the `file` part bytes + the domain/visibility/pub_search/ai_blog text
371// parts -> synthesize the canonical /admin/upload request -> da_handle (STAFF). Returns response length.
372func dad_uploadfile(ctx: *NxAuthContext, req: *u8, req_n: i64, subject: *u8, out: *u8) -> i64 {
373 let boundary: *u8 = sys_mmap(256)
374 let blen: i64 = dad_ct_boundary(req, req_n, boundary)
375 if blen <= 0 { return sd_cat(out, 0, "HTTP/1.1 400 Bad Request\r\nContent-Type: text/plain\r\nConnection: close\r\nContent-Length: 21\r\n\r\nno multipart boundary" as *u8) }
376 let body_off: i64 = sd_body_off(req, req_n)
377 let body: *u8 = ((req as i64) + body_off) as *u8
378 let body_n: i64 = req_n - body_off
379 let parts: *MultipartPart = sys_mmap(32 * DAD_MAXPARTS) as *MultipartPart
380 let np: i64 = multipart_parse(body, body_n, boundary, blen, parts, DAD_MAXPARTS)
381 if np <= 0 { return sd_cat(out, 0, "HTTP/1.1 400 Bad Request\r\nContent-Type: text/plain\r\nConnection: close\r\nContent-Length: 19\r\n\r\nmalformed multipart" as *u8) }
382 let domb: *u8 = sys_mmap(256); var dom_n: i64 = 0
383 let visb: *u8 = sys_mmap(64); var vis_n: i64 = 0
384 var ps: i64 = 0
385 var ab: i64 = 0
386 var file_off: i64 = 0 - 1
387 var file_len: i64 = 0
388 let nmb: *u8 = sys_mmap(128)
389 var i: i64 = 0
390 while i < np {
391 let p: *MultipartPart = ((parts as i64) + i * 32) as *MultipartPart
392 let nm_n: i64 = dad_cd_extract(body, p.headers_off, p.headers_len, "name=\"" as *u8, nmb)
393 if nm_n == 4 { if sd_starts(nmb, 4, "file" as *u8) == 1 { file_off = p.body_off; file_len = p.body_len } }
394 if nm_n == 6 { if sd_starts(nmb, 6, "domain" as *u8) == 1 { var w: i64 = 0; while w < p.body_len { domb[w] = body[p.body_off + w]; w = w + 1 } domb[p.body_len] = 0 as u8; dom_n = p.body_len } }
395 if nm_n == 10 { if sd_starts(nmb, 10, "visibility" as *u8) == 1 { var w2: i64 = 0; while w2 < p.body_len { visb[w2] = body[p.body_off + w2]; w2 = w2 + 1 } visb[p.body_len] = 0 as u8; vis_n = p.body_len } }
396 if nm_n == 10 { if sd_starts(nmb, 10, "pub_search" as *u8) == 1 { ps = 1 } }
397 if nm_n == 7 { if sd_starts(nmb, 7, "ai_blog" as *u8) == 1 { ab = 1 } }
398 i = i + 1
399 }
400 if file_len <= 0 { return sd_cat(out, 0, "HTTP/1.1 400 Bad Request\r\nContent-Type: text/plain\r\nConnection: close\r\nContent-Length: 18\r\n\r\nno file part found" as *u8) }
401 if dom_n <= 0 { return sd_cat(out, 0, "HTTP/1.1 400 Bad Request\r\nContent-Type: text/plain\r\nConnection: close\r\nContent-Length: 14\r\n\r\nmissing domain" as *u8) }
402 if vis_n <= 0 { visb[0] = 112 as u8; visb[1] = 117 as u8; visb[2] = 98 as u8; visb[3] = 108 as u8; visb[4] = 105 as u8; visb[5] = 99 as u8; vis_n = 6 }
403 // synthesize the canonical /admin/upload request (query metadata + raw file body) -> the SAME store path
404 let syn: *u8 = sys_mmap(body_n + 4096)
405 var s: i64 = sd_cat(syn, 0, "POST /admin/upload?domain=" as *u8)
406 var q: i64 = 0
407 while q < dom_n { syn[s] = domb[q]; s = s + 1; q = q + 1 }
408 s = sd_cat(syn, s, "&visibility=" as *u8)
409 q = 0
410 while q < vis_n { syn[s] = visb[q]; s = s + 1; q = q + 1 }
411 s = sd_cat(syn, s, "&pub_search=" as *u8)
412 if ps == 1 { syn[s] = 49 as u8 } else { syn[s] = 48 as u8 }
413 s = s + 1
414 s = sd_cat(syn, s, "&ai_blog=" as *u8)
415 if ab == 1 { syn[s] = 49 as u8 } else { syn[s] = 48 as u8 }
416 s = s + 1
417 s = sd_cat(syn, s, " HTTP/1.1\r\n\r\n" as *u8)
418 var f: i64 = 0
419 while f < file_len { syn[s] = body[file_off + f]; s = s + 1; f = f + 1 }
420 let dctx: *DaCtx = sys_mmap(16) as *DaCtx
421 dctx.user_level = 2
422 dctx.subject = subject
423 return da_handle(dctx, syn, s, out)
424}
425
426// ---- L2 EARLY BYTES (search rung L2, 2026-09-14). The SERP's status line, headers and page shell go out BEFORE the
427// search runs, framed chunked because the length is unknown at that moment, and the socket's owner keeps the edge's
428// per-read window alive with heartbeat chunks while a GRANDCHILD computes the rest of the page into a per-request file.
429// ONE writer per socket by construction (this child); the search process never touches the wire. The heartbeat cadence
430// is DERIVED from proxy_routes.conf's timeout= for this host's /search row (a third of it, so two beats may be lost
431// before the edge gives up); a route with no token gets the edge's default window. A slow query is a slow page, never
432// a 503. dss_serve, the Content-Length page the 49-tooth gate drives, is untouched.
433const DAD_EDGE_READ_TMO_S: i64 = 15 // sites.elf NX_SD2_BACKEND_TIMEOUT_S: the per-read window a route gets when proxy_routes.conf carries no timeout= token
434const DAD_HB_DIV: i64 = 3 // heartbeats at a third of the window
435const DAD_HB_POLL_MS: i64 = 200 // how often the writer asks whether the search process has finished
436const DAD_WNOHANG: i64 = 1 // wait4 flag: poll, never block -- the writer must keep breathing
437const DAD_MS_PER_S: i64 = 1000
438const DAD_BODY_MODE: i64 = 420 // 0644: the grandchild's per-request body file
439static dad_routes_g: *u8
440static dad_routes_n: i64
441func dad_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
442// is this request the HTML SERP (the only route that streams)?
443func dad_is_search(req: *u8, req_n: i64) -> i64 {
444 let poff: *i64 = sys_mmap(8) as *i64
445 let plen: *i64 = sys_mmap(8) as *i64
446 poff[0] = 0
447 plen[0] = 0
448 sd_find_path(req, req_n, poff, plen)
449 let path: *u8 = ((req as i64) + poff[0]) as *u8
450 return sd_starts(path, plen[0], "/search" as *u8)
451}
452// heartbeat cadence in ms for this host's /search route: the route's per-read window over DAD_HB_DIV
453func dad_hb_ms(host: *u8) -> i64 {
454 if (dad_routes_g as i64) == 0 {
455 let nb: *i64 = sys_mmap(16) as *i64
456 dad_routes_g = sys_read_file("proxy_routes.conf" as *u8, nb)
457 if (dad_routes_g as i64) == 0 { dad_routes_n = 0 } else { dad_routes_n = nb[0] }
458 }
459 var tmo: i64 = 0
460 if dad_routes_n > 0 {
461 let port: *i64 = sys_mmap(8) as *i64
462 let mode: *i64 = sys_mmap(8) as *i64
463 let t: *i64 = sys_mmap(8) as *i64
464 t[0] = 0
465 pr_lookup_tmo(dad_routes_g, dad_routes_n, host, dad_slen(host), "/search" as *u8, 7, port, mode, t)
466 tmo = t[0]
467 }
468 if tmo <= 0 { tmo = DAD_EDGE_READ_TMO_S }
469 return tmo * DAD_MS_PER_S / DAD_HB_DIV
470}
471// L2b GUARD (2026-09-15): early bytes reach a client ONLY through a route the front door STREAMS. The buffered
472// branch of sites.elf reads the backend to EOF before it answers, so a chunked heartbeat stream behind a
473// `buffered` row is a stall for the whole per-read window -- measured: every hourly search journey FAIL at
474// ~15.5 s from epoch 1789418137, the first beat after the E4 rebuild re-shipped the streaming dispatch that L2
475// had rolled back by binary only. The route table is the ONE authority on how the front door forwards, so this
476// reads the SAME row dad_hb_ms reads and streams iff it says `stream`; no row, no table, or any other mode
477// answers whole (the proven Content-Length path). Data-driven: flipping the row to `stream` (hot-reloaded by
478// sites.elf) turns streaming on with no rebuild -- once L2b lands there. The pure form takes the table so the
479// gate can drive both directions without a proxy_routes.conf in its CWD.
480func dad_early_ok_tab(routes: *u8, rn: i64, host: *u8) -> i64 {
481 if rn <= 0 { return 0 }
482 if (routes as i64) == 0 { return 0 }
483 let sp: *u8 = "/search" as *u8
484 let port: *i64 = sys_mmap(8) as *i64
485 let mode: *i64 = sys_mmap(8) as *i64
486 let t: *i64 = sys_mmap(8) as *i64
487 port[0] = 0
488 mode[0] = PR_MODE_BUFFERED
489 t[0] = 0
490 pr_lookup_tmo(routes, rn, host, dad_slen(host), sp, dad_slen(sp), port, mode, t)
491 if mode[0] == PR_MODE_STREAM { return 1 }
492 return 0
493}
494func dad_early_ok(req: *u8, req_n: i64) -> i64 {
495 let host: *u8 = sys_mmap(256)
496 dsv_host(req, req_n, host, 255)
497 if (dad_routes_g as i64) == 0 {
498 let nb: *i64 = sys_mmap(16) as *i64
499 dad_routes_g = sys_read_file("proxy_routes.conf" as *u8, nb)
500 if (dad_routes_g as i64) == 0 { dad_routes_n = 0 } else { dad_routes_n = nb[0] }
501 }
502 return dad_early_ok_tab(dad_routes_g, dad_routes_n, host)
503}
504func dad_serve_early(cfd: i64, req: *u8, req_n: i64, out: *u8) -> i64 {
505 let hostbuf: *u8 = sys_mmap(256)
506 dsv_host(req, req_n, hostbuf, 255)
507 let t0: i64 = dsv_now_us()
508 let q: *u8 = sys_mmap(DSV_MAGIC_1024)
509 let effdom: *u8 = sys_mmap(256)
510 let box: *i64 = sys_mmap(32) as *i64
511 let qn: i64 = dsv_serp_parse(hostbuf, req, req_n, q, effdom, box)
512 let body: *u8 = sys_mmap(DSV_BODYCAP)
513 let b1: i64 = dsv_serp_prelude(body, q, qn, box[1])
514 let o1: i64 = dsv_respond_early(out, body, b1)
515 sys_write(cfd, out, o1) // THE EARLY BYTES: status line, headers and the page shell, before the search runs
516 let slow: i64 = dss_serve_slow_ms(req, req_n, "knowledge/search_probe_slow.conf" as *u8)
517 // the rest of the page is computed by a grandchild into a per-request file; this process keeps the wire alive
518 let path: *u8 = sys_mmap(128)
519 var po: i64 = dsv_cat(path, 0, "/tmp/nx_serp_" as *u8)
520 po = dsv_catn(path, po, t0)
521 po = dsv_cat(path, po, ".body" as *u8)
522 let tmp: *u8 = sys_mmap(128)
523 var to: i64 = dsv_cat(tmp, 0, path)
524 to = dsv_cat(tmp, to, ".tmp" as *u8)
525 let kid: i64 = sys_fork()
526 if kid == 0 {
527 if slow > 0 { sys_sleep_ms(slow) }
528 let b2: i64 = dsv_serp_rest(effdom, q, qn, box[1], box[2], body, b1, t0)
529 let fd: i64 = sys_openat_wr(tmp, DAD_BODY_MODE)
530 if fd >= 0 { sys_write(fd, (body as i64 + b1) as *u8, b2 - b1); sys_close(fd); sys_renameat(tmp, path) }
531 sys_exit(0)
532 }
533 let hb: i64 = dad_hb_ms(hostbuf)
534 let hbbuf: *u8 = sys_mmap(64)
535 let hbn: i64 = dsv_chunk(hbbuf, 0, "<!-- -->" as *u8, 0, 8)
536 let wst: *i64 = sys_mmap(16) as *i64
537 var waited: i64 = 0
538 var done: i64 = 0
539 if kid < 0 { done = 1 }
540 while done == 0 {
541 let r: i64 = sys_wait4(kid, wst, DAD_WNOHANG)
542 if r == kid { done = 1 } else { if r < 0 { done = 1 } else {
543 sys_sleep_ms(DAD_HB_POLL_MS)
544 waited = waited + DAD_HB_POLL_MS
545 if waited >= hb { sys_write(cfd, hbbuf, hbn); waited = 0 }
546 } }
547 }
548 let nb2: *i64 = sys_mmap(16) as *i64
549 let rest: *u8 = sys_read_file(path, nb2)
550 var o2: i64 = 0
551 if (rest as i64) != 0 { o2 = dsv_chunk(out, 0, rest, 0, nb2[0]); sys_unlinkat(path) }
552 else { let fail: *u8 = "<p class=meta>search failed to answer; try again</p></main></body></html>" as *u8; o2 = dsv_chunk(out, 0, fail, 0, dad_slen(fail)) }
553 o2 = dsv_chunk_end(out, o2)
554 sys_write(cfd, out, o2)
555 return 0
556}
557
558// ---- THE ROUTER: request bytes -> response bytes (no socket). The gate drives this directly. ----
559func dad_handle(ctx: *NxAuthContext, req: *u8, req_n: i64, out: *u8) -> i64 {
560 let poff: *i64 = sys_mmap(8) as *i64; let plen: *i64 = sys_mmap(8) as *i64
561 poff[0] = 0; plen[0] = 0
562 sd_find_path(req, req_n, poff, plen)
563 let path: *u8 = ((req as i64) + poff[0]) as *u8
564 let pn: i64 = plen[0]
565 let is_post: i64 = (req[0] == 80 as u8) as i64
566 var o: i64 = 0
567 // PUBLIC sovereign onsite /search (no auth, seg_store-native, NO tsv) -- matched FIRST, before the /admin chain.
568 // Domain from the Host header -> dss_serve queries dp-<Host>-pub- (this daemon fronts andelinwest.com).
569 if sd_starts(path, pn, "/search" as *u8) == 1 {
570 let hostbuf: *u8 = sys_mmap(256)
571 dsv_host(req, req_n, hostbuf, 255)
572 return dss_serve(hostbuf, req, req_n, out)
573 }
574 // PUBLIC /doc?cid=<cid> document view (the search loop's click-through; -pub- shard only). Boundary-guarded:
575 // exactly "/doc" or "/doc?..." -- a raw prefix would also swallow /doctor-style paths (path includes the query).
576 if sd_starts(path, pn, "/doc" as *u8) == 1 {
577 var docb: i64 = 0
578 if pn == 4 { docb = 1 }
579 if pn > 4 { if path[4] == (63 as u8) { docb = 1 } }
580 if docb == 1 {
581 let hostbuf2: *u8 = sys_mmap(256)
582 dsv_host(req, req_n, hostbuf2, 255)
583 return dsv_doc_serve(hostbuf2, req, req_n, out)
584 }
585 }
586 // API-FIRST surface: versioned public JSON (CORS, structured errors). Same engine, machine contract --
587 // the HTML SERP above is just one client of it. Boundary-guarded like /doc.
588 if sd_starts(path, pn, "/api/search" as *u8) == 1 {
589 var apib: i64 = 0
590 if pn == 11 { apib = 1 }
591 if pn > 11 { if path[11] == (63 as u8) { apib = 1 } }
592 if apib == 1 {
593 let hostbuf3: *u8 = sys_mmap(256)
594 dsv_host(req, req_n, hostbuf3, 255)
595 return dss_api_search(hostbuf3, req, req_n, out)
596 }
597 }
598 if sd_starts(path, pn, "/api/doc" as *u8) == 1 {
599 var apid: i64 = 0
600 if pn == 8 { apid = 1 }
601 if pn > 8 { if path[8] == (63 as u8) { apid = 1 } }
602 if apid == 1 {
603 let hostbuf4: *u8 = sys_mmap(256)
604 dsv_host(req, req_n, hostbuf4, 255)
605 return dss_api_doc(hostbuf4, req, req_n, out)
606 }
607 }
608 if sd_starts(path, pn, "/api/suggest" as *u8) == 1 {
609 var apis: i64 = 0
610 if pn == 12 { apis = 1 }
611 if pn > 12 { if path[12] == (63 as u8) { apis = 1 } }
612 if apis == 1 {
613 let hostbuf5: *u8 = sys_mmap(256)
614 dsv_host(req, req_n, hostbuf5, 255)
615 return dss_api_suggest(hostbuf5, req, req_n, out)
616 }
617 }
618 // machine-readable OpenAPI 3.1 contract (enterprise: client codegen / Swagger / contract testing).
619 // Exact "/api/openapi.json" or with a query string.
620 if sd_starts(path, pn, "/api/openapi.json" as *u8) == 1 {
621 var apio: i64 = 0
622 if pn == 17 { apio = 1 }
623 if pn > 17 { if path[17] == (63 as u8) { apio = 1 } }
624 if apio == 1 {
625 let hostbuf6: *u8 = sys_mmap(256)
626 dsv_host(req, req_n, hostbuf6, 255)
627 return dss_api_openapi(hostbuf6, out)
628 }
629 }
630 if sd_starts(path, pn, "/admin/register" as *u8) == 1 {
631 // INVITE-GATED OPAQUE self-registration: a remote firm presents a SECRET single-use, expiring
632 // invite token (minted out-of-band by inv_issue) instead of a guessable handle. Matched BEFORE
633 // the "/admin/" session gate (which is a prefix of this path) and as a sibling of /admin/login.
634 // Gate shape mirrors nx_lan_signup_routes.lsd_route POST /register -- swapping its LAN-IP gate
635 // for the inv_check token gate. On success: olg_register (OPAQUE) -> inv_consume -> 200 mnemonic.
636 if is_post == 1 {
637 let body_off: i64 = sd_body_off(req, req_n)
638 let body: *u8 = ((req as i64) + body_off) as *u8
639 let body_n: i64 = req_n - body_off
640 let hoff: *i64 = sys_mmap(8) as *i64; let hn: *i64 = sys_mmap(8) as *i64
641 let poff2: *i64 = sys_mmap(8) as *i64; let pnn: *i64 = sys_mmap(8) as *i64
642 let ioff: *i64 = sys_mmap(8) as *i64; let inn: *i64 = sys_mmap(8) as *i64
643 var got: i64 = 0
644 if sd_form_field(body, body_n, "handle" as *u8, 6, hoff, hn) == 1 {
645 if sd_form_field(body, body_n, "pw" as *u8, 2, poff2, pnn) == 1 {
646 if sd_form_field(body, body_n, "invite" as *u8, 6, ioff, inn) == 1 { got = 1 }
647 }
648 }
649 var done: i64 = 0
650 if got == 1 {
651 let hbuf: *u8 = sys_mmap(256); let pbuf: *u8 = sys_mmap(512); let ibuf: *u8 = sys_mmap(128)
652 let h_dec: i64 = sd_urldecode(((body as i64) + hoff[0]) as *u8, hn[0], hbuf, 255)
653 let p_dec: i64 = sd_urldecode(((body as i64) + poff2[0]) as *u8, pnn[0], pbuf, 511)
654 let i_dec: i64 = sd_urldecode(((body as i64) + ioff[0]) as *u8, inn[0], ibuf, 127)
655 if h_dec > 0 { if p_dec > 0 { if i_dec > 0 {
656 let invpath: *u8 = sys_mmap(512)
657 dad_invite_path(ctx, invpath)
658 let now_s: i64 = sys_now_realtime_sec()
659 // THE GATE: token must be a live, realm-scoped, unconsumed invite (level > 0).
660 let lvl: i64 = inv_check(invpath, ibuf, i_dec, ctx.realm_id, ctx.realm_id_n, now_s)
661 if lvl <= 0 {
662 o = sd_cat(out, 0, "HTTP/1.1 403 Forbidden\r\nContent-Type: application/json\r\nConnection: close\r\nContent-Length: 37\r\n\r\n{\"error\":\"invalid or expired invite\"}" as *u8)
663 done = 1
664 } else {
665 let mn: *u8 = sys_mmap(512); let mnn: *i64 = sys_mmap(16) as *i64
666 if olg_register(ctx, hbuf, h_dec, pbuf, p_dec, mn, 512, mnn) == NX_MAUTH_OK {
667 // uidmap: record uid-hash -> handle at register so /admin/ui greets by name
668 let uidh2: *u8 = sys_mmap(32)
669 if nx_ncs_derive_user_id_hash(ctx.realm_id, ctx.realm_id_n, hbuf, h_dec, uidh2) == NX_NCS_OK {
670 let uhex2: *u8 = sys_mmap(72); dad_hex32(uidh2, uhex2)
671 let ump2: *u8 = sys_mmap(512); dad_uidmap_path(ctx, ump2)
672 dad_map_put(ump2, uhex2, hbuf, h_dec)
673 }
674 inv_consume(invpath, ibuf, i_dec, now_s) // single-use: burn the invite
675 o = sd_cat(out, 0, "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nConnection: close\r\nContent-Length: " as *u8)
676 o = sd_catn(out, o, 15 + mnn[0])
677 o = sd_cat(out, o, "\r\n\r\n{\"mnemonic\":\"" as *u8)
678 var z: i64 = 0; while z < mnn[0] { out[o] = mn[z]; o = o + 1; z = z + 1 }
679 o = sd_cat(out, o, "\"}" as *u8)
680 done = 1
681 } else {
682 o = sd_cat(out, 0, "HTTP/1.1 400 Bad Request\r\nContent-Type: application/json\r\nConnection: close\r\nContent-Length: 27\r\n\r\n{\"error\":\"register failed\"}" as *u8)
683 done = 1
684 }
685 }
686 } } }
687 }
688 if done == 0 { o = sd_cat(out, 0, "HTTP/1.1 400 Bad Request\r\nContent-Type: application/json\r\nConnection: close\r\nContent-Length: 27\r\n\r\n{\"error\":\"register failed\"}" as *u8) }
689 } else { o = dad_shell(out) }
690 } else { if sd_starts(path, pn, "/admin/login" as *u8) == 1 {
691 if is_post == 1 {
692 let body_off: i64 = sd_body_off(req, req_n)
693 let body: *u8 = ((req as i64) + body_off) as *u8
694 let body_n: i64 = req_n - body_off
695 let hoff: *i64 = sys_mmap(8) as *i64; let hn: *i64 = sys_mmap(8) as *i64
696 let poff2: *i64 = sys_mmap(8) as *i64; let pnn: *i64 = sys_mmap(8) as *i64
697 var got: i64 = 0
698 if sd_form_field(body, body_n, "handle" as *u8, 6, hoff, hn) == 1 {
699 if sd_form_field(body, body_n, "passphrase" as *u8, 10, poff2, pnn) == 1 { got = 1 }
700 }
701 // ui=1 rides ONLY the native <form> submit (nishi-first no-JS path) -> answer HTML, not JSON.
702 var uiflag: i64 = 0
703 let uoff: *i64 = sys_mmap(8) as *i64
704 let un: *i64 = sys_mmap(8) as *i64
705 if sd_form_field(body, body_n, "ui" as *u8, 2, uoff, un) == 1 { uiflag = 1 }
706 var ok: i64 = 0
707 if got == 1 {
708 let hbuf: *u8 = sys_mmap(256); let pbuf: *u8 = sys_mmap(512)
709 let h_dec: i64 = sd_urldecode(((body as i64) + hoff[0]) as *u8, hn[0], hbuf, 255)
710 let p_dec: i64 = sd_urldecode(((body as i64) + poff2[0]) as *u8, pnn[0], pbuf, 511)
711 if h_dec > 0 { if p_dec > 0 {
712 let tok: *u8 = sys_mmap(NX_MAUTH_SESSION_TOKEN_BYTES)
713 let tok_n: *i64 = sys_mmap(8) as *i64; tok_n[0] = 0
714 if nx_modern_auth_login(ctx, hbuf, h_dec, pbuf, p_dec, tok, NX_MAUTH_SESSION_TOKEN_BYTES, tok_n) == NX_MAUTH_OK {
715 // uidmap: the session token carries the uid HASH -> record hash->handle so /admin/ui greets by name
716 let uidh: *u8 = sys_mmap(32)
717 if nx_ncs_derive_user_id_hash(ctx.realm_id, ctx.realm_id_n, hbuf, h_dec, uidh) == NX_NCS_OK {
718 let uhex: *u8 = sys_mmap(72); dad_hex32(uidh, uhex)
719 let ump: *u8 = sys_mmap(512); dad_uidmap_path(ctx, ump)
720 dad_map_put(ump, uhex, hbuf, h_dec)
721 }
722 let b64: *u8 = sys_mmap(256); let b64_n: i64 = b64_encode(tok, NX_MAUTH_SESSION_TOKEN_BYTES, b64)
723 if uiflag == 1 { o = dad_landing(out, b64, b64_n) }
724 else {
725 o = sd_cat(out, o, "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nConnection: close\r\nContent-Length: " as *u8)
726 o = sd_catn(out, o, 12 + b64_n)
727 o = sd_cat(out, o, "\r\n\r\n{\"token\":\"" as *u8)
728 var z: i64 = 0; while z < b64_n { out[o] = b64[z]; o = o + 1; z = z + 1 }
729 o = sd_cat(out, o, "\"}" as *u8)
730 }
731 ok = 1
732 }
733 } }
734 }
735 if ok == 0 {
736 if uiflag == 1 { o = sd_cat(out, 0, "HTTP/1.1 401 Unauthorized\r\nContent-Type: text/html; charset=utf-8\r\nConnection: close\r\nContent-Length: 106\r\n\r\n<!DOCTYPE html><meta charset=utf-8><p>Wrong handle or passphrase.</p><p><a href=\"/admin\">Try again</a></p>" as *u8) }
737 else { o = sd_emit_401_json(out) }
738 }
739 } else { o = dad_shell(out) }
740 } else { if sd_starts(path, pn, "/admin/ui" as *u8) == 1 {
741 // NISHI-FIRST no-JS admin page: session from the ?s= query (same Ed25519 validator) -> the
742 // handle-greeting + paste-upload form. Renders + navigates in the nishi browser TODAY.
743 let hb: *u8 = sys_mmap(256)
744 let hbn: *i64 = sys_mmap(8) as *i64
745 let now_ui: i64 = sys_now_realtime_sec()
746 if nx_sa_validate_qs(ctx, path, pn, now_ui, hb, 255, hbn) == NX_MAUTH_OK {
747 let voff: *i64 = sys_mmap(8) as *i64
748 let vlen: i64 = nx_sa_qs_raw(path, pn, voff)
749 let sp: *u8 = ((path as i64) + voff[0]) as *u8
750 // resolve the session uid HASH -> plaintext handle (else "Signed in as" shows raw hash bytes)
751 var disp_h: *u8 = hb
752 var disp_n: i64 = hbn[0]
753 if hbn[0] == 32 {
754 let uhex: *u8 = sys_mmap(72); dad_hex32(hb, uhex)
755 let ump: *u8 = sys_mmap(512); dad_uidmap_path(ctx, ump)
756 let rhb: *u8 = sys_mmap(128)
757 let rhn: i64 = dad_map_get(ump, uhex, rhb, 127)
758 if rhn > 0 { disp_h = rhb; disp_n = rhn }
759 }
760 o = dad_ui_page(out, disp_h, disp_n, sp, vlen)
761 } else { o = sd_emit_401_json(out) }
762 } else { if sd_starts(path, pn, "/admin/uploadtext" as *u8) == 1 {
763 // NISHI-FIRST no-JS upload: auth via ?s= OR the header, then the form->canonical-request shim.
764 let now_ut: i64 = sys_now_realtime_sec()
765 let hb2: *u8 = sys_mmap(256)
766 let hn2: *i64 = sys_mmap(8) as *i64
767 hn2[0] = 0
768 var authed: i64 = 0
769 if nx_sa_validate_qs(ctx, path, pn, now_ut, hb2, 255, hn2) == NX_MAUTH_OK { authed = 1 }
770 if authed == 0 { if nx_sa_validate_handle(ctx, req, req_n, now_ut, hb2, 255, hn2) == NX_MAUTH_OK { authed = 1 } }
771 if authed == 1 {
772 let subhex2: *u8 = sys_mmap(72); subhex2[0] = 0 as u8
773 if hn2[0] == 32 { dad_hex32(hb2, subhex2) }
774 if is_post == 1 { o = dad_uploadtext(ctx, req, req_n, subhex2, out) }
775 else { o = sd_emit_401_json(out) }
776 } else { o = sd_emit_401_json(out) }
777 } else { if sd_starts(path, pn, "/admin/uploadfile" as *u8) == 1 {
778 // REAL multipart/form-data file upload (what an <input type=file> submits): auth via ?s= OR header,
779 // then dad_uploadfile parses the parts + feeds the file bytes into the SAME /admin/upload store path.
780 let now_uf: i64 = sys_now_realtime_sec()
781 let hb3: *u8 = sys_mmap(256)
782 let hn3: *i64 = sys_mmap(8) as *i64
783 hn3[0] = 0
784 var authed_f: i64 = 0
785 if nx_sa_validate_qs(ctx, path, pn, now_uf, hb3, 255, hn3) == NX_MAUTH_OK { authed_f = 1 }
786 if authed_f == 0 { if nx_sa_validate_handle(ctx, req, req_n, now_uf, hb3, 255, hn3) == NX_MAUTH_OK { authed_f = 1 } }
787 if authed_f == 1 {
788 let subhex3: *u8 = sys_mmap(72); subhex3[0] = 0 as u8
789 if hn3[0] == 32 { dad_hex32(hb3, subhex3) }
790 if is_post == 1 { o = dad_uploadfile(ctx, req, req_n, subhex3, out) }
791 else { o = sd_emit_401_json(out) }
792 } else { o = sd_emit_401_json(out) }
793 } else { if sd_starts(path, pn, "/admin/" as *u8) == 1 {
794 // session-gated: the /admin/<action> paths (upload/policy/whoami) validate the no-cookie token, then route
795 // to da_handle at STAFF level (admin.<d> = the management capability). Unauthenticated -> 401 BEFORE
796 // da_handle ever runs (deny-by-default, two layers). Bare /admin falls through to the public login shell.
797 let now_s: i64 = sys_now_realtime_sec()
798 // P2: emit the session SUBJECT via nx_sa_validate_handle (verified to accept EXACTLY the same sessions as
799 // nx_sa_validate -- same sig/MAC + TTL + realm check, computed before + independent of the subject copy).
800 // The non-null hb/hbn + hb-cap>=32 are REQUIRED (else deny / null-deref). Same ?s= buffer pattern used above.
801 let hb: *u8 = sys_mmap(256)
802 let hbn: *i64 = sys_mmap(8) as *i64
803 hbn[0] = 0
804 if nx_sa_validate_handle(ctx, req, req_n, now_s, hb, 255, hbn) == NX_MAUTH_OK {
805 let dctx: *DaCtx = sys_mmap(16) as *DaCtx
806 dctx.user_level = 2
807 let subhex: *u8 = sys_mmap(72); subhex[0] = 0 as u8
808 if hbn[0] == 32 { dad_hex32(hb, subhex) }
809 dctx.subject = subhex
810 o = da_handle(dctx, req, req_n, out)
811 } else { o = sd_emit_401_json(out) }
812 } else { o = dad_shell(out) } } } } } }
813 return o
814}
815
816func main(argc: i64, argv: *i64) -> i64 {
817 if argc < 6 { sys_write(2, "usage: nx_docportal_admin_daemon <port> <keysfile> <storefile> <realm> <budget>\n" as *u8, 79); return 1 }
818 let port: i64 = sd_atoi(argv[1] as *u8)
819 let keysfile: *u8 = argv[2] as *u8
820 let storefile: *u8 = argv[3] as *u8
821 let realm: *u8 = argv[4] as *u8
822 let budget: i64 = sd_atoi(argv[5] as *u8)
823 let realm_n: i64 = sd_len(realm)
824
825 // fail-fast: arm the realm context at startup (Rule 20), exactly like nx_status_daemon
826 let oprf_seed: *u8 = sys_mmap(32); let akp: *u8 = sys_mmap(32); let akb: *u8 = sys_mmap(33)
827 let edp: *u8 = sys_mmap(32); let edb: *u8 = sys_mmap(32)
828 sys_write(1, "DBG start: args parsed\n" as *u8, 23)
829 if nx_uas_server_keys_load_or_init(keysfile, oprf_seed, akp, akb, edp, edb) != NX_UAS_OK { sys_write(2, "FATAL: server-key bundle\n" as *u8, 24); return 2 }
830 sys_write(1, "DBG: server-keys OK\n" as *u8, 20)
831 let ctx: *NxAuthContext = sys_mmap(256) as *NxAuthContext
832 if nx_auth_context_init(ctx, realm, realm_n, realm, realm_n, storefile as i64, oprf_seed, edp, edb, 900, 8192, 1, 1, 5, 1) != NX_MAUTH_OK { sys_write(2, "FATAL: context init\n" as *u8, 20); return 3 }
833 sys_write(1, "DBG: context init OK\n" as *u8, 21)
834
835 // API-FIRST: announce the search API in the ecosystem tool registry (idempotent check-first; the NAS
836 // CWD is nishihost/, so this writes the LIVE knowledge/toolreg- shard -> /api/tools + MCP tools/list
837 // discover nishi_search with zero adapter -- the "services register at their own startup" pattern).
838 let trp: *i64 = sys_mmap(16) as *i64
839 let trl: *i64 = sys_mmap(16) as *i64
840 if tool_get("nishi_search" as *u8, trp, trl) != 1 {
841 tool_register("nishi_search" as *u8, "Sovereign nishi search: a domain's docs + site pages, the nishi library (Host nishifamily.com), and the crawled open web (scope=web). Versioned JSON, CORS, structured errors." as *u8, "GET /api/search?q=<terms>[&scope=web] (per-domain via Host)" as *u8, "GREEN" as *u8)
842 }
843 if tool_get("nishi_doc" as *u8, trp, trl) != 1 {
844 tool_register("nishi_doc" as *u8, "Fetch a public document by content id from the nishi search corpus (JSON; text bounded + truncation-flagged)." as *u8, "GET /api/doc?cid=<cid>[&scope=web]" as *u8, "GREEN" as *u8)
845 }
846
847 let addr: *u8 = sys_mmap(16)
848 if nx_http_server_addr_loopback(addr, port) != 16 { sys_write(2, "FATAL: addr_loopback != 16\n" as *u8, 27); return 4 }
849 sys_write(1, "DBG: addr_loopback OK\n" as *u8, 22)
850 // ⚠LISTEN FIRST IS DELIBERATE -- I TRIED THE OPPOSITE AND MEASURED IT WORSE. DO NOT "FIX" THIS.
851 // The cold-start story, fully measured 2026-08-15: this parent spends ~8-12 s pre-warming a 1.59GB
852 // web shard. With listen() FIRST that cost is ABSORBED BY THE ACCEPT QUEUE -- connections wait in the
853 // 64-deep backlog and the first request completes in ~8.1 s, with NO failures. Moving the pre-warm
854 // above listen() closes the port for the whole warm-up instead, and the edge turns ECONNREFUSED into
855 // a user-visible 503: MEASURED 503 for 12.09 s, then 200 in 1.11 s. That trades one slow success for
856 // twelve seconds of hard failure, which is strictly worse for a visitor and is the exact symptom this
857 // daemon was reported for. Reverted.
858 // ★★★★★★A FAST FAILURE IS NOT AUTOMATICALLY BETTER THAN A SLOW SUCCESS -- THAT DEPENDS ENTIRELY ON
859 // WHETHER THE CALLER CAN DO ANYTHING WITH THE FAILURE. Here it cannot: the visitor sees 503.
860 // WHAT WAS GENUINELY WRONG WAS THE MEASUREMENT, NOT THE ORDER. Queue time is invisible to EVERY
861 // timer this daemon has -- the parent's starts at accept(), the child's after fork() -- so the daemon
862 // honestly reported <=1405 ms per request while curl measured 8144 ms end to end, and I attributed
863 // that gap in turn to the search engine, the did-you-mean walk, and the edge proxy before finding it
864 // here. ★★★★★A LATENCY NOBODY'S CLOCK STARTS FOR IS BLAMED ON WHOEVER IS MEASURED LAST.
865 // The remedy is therefore to make the cost VISIBLE (the warm_ms line below) and eventually CHEAPER --
866 // never to hide it behind a closed port.
867 // ★ORDER DECIDED BY MATCHED MEASUREMENT, NOT BY ARGUMENT (2026-08-15). Both orders were built,
868 // deployed and probed from t=0 with the IDENTICAL protocol (300ms poll, count non-200s, record the
869 // first 200 and its duration):
870 // pre-warm BEFORE listen -> first 200 at t=12.09s, that request 1.11s
871 // listen BEFORE pre-warm -> first 200 at t=20.4s, that request 10.70s
872 // Pre-warm-first wins on BOTH axes, so it is what ships. I briefly reverted it after calling it
873 // "worse", which was WRONG: I had compared its t=0 probe against a listen-first number taken after a
874 // 30s sleep, i.e. against an already-warm daemon. ★★★★★★A COMPARISON BETWEEN TWO CONFIGURATIONS IS
875 // ONLY A COMPARISON IF BOTH WERE MEASURED IN THE SAME STATE -- otherwise you are timing the sleep.
876 // WHY listen-first is worse despite producing no ECONNREFUSED: the port opens instantly but the
877 // parent is then busy warming for ~9.8s, so arriving connections sit in the 64-deep backlog and are
878 // served LATE -- one measured at 10.70s, which burns most of the edge's 15s NX_SD2_BACKEND_TIMEOUT_S.
879 // Refusing fast during startup is the honest signal: the daemon is not ready, the caller learns in
880 // microseconds, and readiness arrives sooner overall.
881 // THE REAL FIX IS NEITHER ORDER -- IT IS THE 9.8s ITSELF. STARTUP warm_ms=9796 and, on the refresh
882 // path, REQ done parent_us=9783316: the same ~9.8s ss_open2 of the 1.59GB web shard, once at startup
883 // and again on whichever accept triggers a refresh (blocking the single-threaded parent, which is why
884 // it presents as a RUN of slow requests rather than one). Until that open is incremental, this order
885 // is the best available placement of an unavoidable cost, not a solution to it.
886 // SERVE-SCALE: pre-warm the web-shard handle in the PARENT so every forked child inherits it COW --
887 // per-request ss_open on the bulk-CC shard measured ~0.7s/query without this. Refreshed per accept
888 // below (stat-signature check, ~free; reopen only when new segments actually ship).
889 let t_warm: i64 = dsv_now_us()
890 if dss_web_cache_refresh() == 1 { sys_write(1, "web shard handle pre-warmed\n" as *u8, 28) }
891 // Arm the shared query memo HERE -- in the parent, before any fork. sys_mmap_shared's own contract
892 // says "allocate in the PARENT before fork", and this daemon forks per request, so a memo armed
893 // anywhere else would be COW-private to one child and invisible to every other request.
894 if dsq_init() == 1 { sys_write(1, "query memo armed (shared, fixed-capacity)\n" as *u8, 42) }
895 // ANNOUNCE THE COLD-START COST. Every request queued behind this warm-up pays it, and no per-request
896 // timer can see that, so without this line the number is unobtainable from the daemon's own log --
897 // which is precisely how it stayed unexplained. Print it once, at the only place that knows it.
898 let wb: *u8 = sys_mmap(96)
899 var wn: i64 = dsv_cat(wb, 0, "STARTUP warm_ms=" as *u8)
900 wn = dsv_catn(wb, wn, (dsv_now_us() - t_warm) / 1000)
901 wn = dsv_cat(wb, wn, " -- the port is opened AFTER this, so nothing queued behind it\n" as *u8)
902 sys_write(1, wb, wn)
903
904 // ONLY NOW open the port: every resource a request needs is resident and shared, so the backlog can
905 // no longer hide a cold-start cost, and the first accepted connection is served at warm speed.
906 let lv: *i64 = sys_mmap(8) as *i64
907 let lfd: i64 = nx_http_server_listen(addr, 64, lv)
908 if lfd < 0 { sys_write(2, "FATAL: listen failed (port busy or denied)\n" as *u8, 43); return 4 }
909 sys_write(1, "nx_docportal_admin_daemon listening loopback (READY: warm before listen)\n" as *u8, 73)
910
911 let req: *u8 = sys_mmap(DAD_REQCAP)
912 let out: *u8 = sys_mmap(DAD_OUTCAP)
913 let av: *i64 = sys_mmap(8) as *i64 // HOISTED out of the loop -- reused each accept (was leaking a page/iter)
914 let wst: *i64 = sys_mmap(16) as *i64 // HOISTED -- child reap status, reused
915 // Seeded from the pre-warm above, not from zero: the shard was JUST opened, so the first
916 // request must not immediately reopen it. Without this seed the gate would let exactly one
917 // full 1.59GB reopen through on the very first query after every restart -- the slowest
918 // possible moment, right when the daemon is already cold.
919 var last_refresh: i64 = sys_now_realtime_sec()
920 var served: i64 = 0
921 // Sensed ONCE, in the parent, then held. Re-sensing per accept would read a load average this
922 // daemon is itself creating and ratchet its own ceiling down toward 1 under exactly the load it
923 // exists to absorb. PRINT THE VALUE: a ceiling nobody can see is a ceiling nobody can debug.
924 let dad_cap: i64 = dad_worker_cap()
925 var dad_live: i64 = 0
926 let capbuf: *u8 = sys_mmap(96)
927 var cbn: i64 = 0
928 cbn = dsv_cat(capbuf, cbn, "worker cap=" as *u8)
929 cbn = dsv_catn(capbuf, cbn, dad_cap)
930 cbn = dsv_cat(capbuf, cbn, " ncpu=" as *u8)
931 cbn = dsv_catn(capbuf, cbn, sl_ncpu())
932 cbn = dsv_cat(capbuf, cbn, " reserve=" as *u8)
933 cbn = dsv_catn(capbuf, cbn, DAD_CPU_RESERVE)
934 cbn = dsv_cat(capbuf, cbn, " (capacity bound; load deliberately NOT subtracted)\n" as *u8)
935 sys_write(1, capbuf, cbn)
936 // HOISTED per-request diagnostic buffer -- reused every iteration, never mmap'd inside the loop
937 // (the per-call-allocation law this file has already had to learn twice).
938 let logbuf: *u8 = sys_mmap(128)
939 while served < budget {
940 let cfd: i64 = nx_http_server_accept_one(lfd, av)
941 if cfd < 0 {
942 // ACCEPT BACKOFF (2026-08-01). This branch retried INSTANTLY, so any persistent accept
943 // error became a 100pct-CPU busy loop while the socket stayed LISTENING -- which every
944 // port-probe health check reads as a healthy daemon. 50ms costs nothing on the happy
945 // path (this branch is not taken when accepts succeed) and turns a spin into a
946 // survivable retry. It also stops a failing daemon from eating its 20M `served` budget
947 // in minutes.
948 sys_sleep_ms(DAD_ACCEPT_BACKOFF_MS)
949 served = served + 1
950 }
951 if cfd >= 0 {
952 // PER-REQUEST TRACE (2026-08-01). This daemon emitted NOTHING after `pre-warmed`, so when
953 // it wedged there was no way to tell which stage died: accept, refresh, fork, or handle.
954 // I spent an hour narrowing it from the outside with CPU counters and fd counts and still
955 // could not say. Three cheap writes make the next occurrence self-diagnosing.
956 // ★★★★★A DAEMON THAT LOGS ONLY ITS STARTUP CAN ONLY EVER TELL YOU IT STARTED.
957 sys_write(1, "REQ accepted\n" as *u8, 13)
958 // ROOT-CAUSE FIX (2026-08-01), found by the trace two lines above.
959 // The wedge was HERE: the log read accepted=52, refresh-ok=51, done=51 -- the daemon
960 // accepted a request and never returned from dss_web_cache_refresh(). That call compares
961 // a manifest signature and, on ANY change, does ss_close + ss_open2 over the whole web
962 // shard: 94 segments, 1.59GB. It ran ON EVERY ACCEPTED REQUEST.
963 // WHY IT ONLY BIT NOW: the manifest used to be frozen because the crawler was committing
964 // nothing (the segments=0 poisoned-segid bug I root-fixed earlier tonight). With the
965 // crawler working the shard grows again -- 87 segments at session start, 94 now -- so the
966 // cached handle is invalidated constantly and a full reopen lands on the request path.
967 // ★★★★★A CORRECT FIX IN ONE ORGAN CAN EXPOSE A LATENT O(N) COST IN ANOTHER.
968 // THE GATE: at most one refresh per DAD_REFRESH_MIN_SEC. Freshness is bounded by that
969 // interval instead of by request arrival, so a growing shard can no longer put a
970 // 1.59GB reopen in front of every query. Serving a few seconds of slightly stale index
971 // is strictly better than not serving at all.
972 // Reap FIRST so the in-flight count below is current, not left over from a child that has
973 // already exited. WNOHANG, so this costs nothing on the happy path.
974 // WHY THESE TWO NUMBERS ARE LOGGED (2026-08-15): the refresh gate below fires only on
975 // `dad_live == 0`, and the log proved that predicate routinely unreachable -- ~30 consecutive
976 // refresh-defer lines before one refresh-ok, exactly spanning a window where every web request
977 // measured 9.39-9.80 s while site scope stayed at 0.05-0.08 s. Correlation alone cannot say
978 // whether staleness makes requests slow (so they overlap, so dad_live never reaches 0) or the
979 // defers are a symptom of slowness caused elsewhere. Those have OPPOSITE remedies, the window
980 // cannot be reproduced on demand (it needs the crawler shipping segments), and guessing it is
981 // how three attributions went wrong today. Logging dad_live beside each refresh decision and
982 // the per-request duration beside each completion makes the NEXT occurrence self-diagnosing
983 // instead of requiring the condition to be caught live.
984 let t_acc: i64 = dsv_now_us()
985 dad_live = dad_live - dad_reap_done(wst)
986 let now_s: i64 = sys_now_realtime_sec()
987 if now_s - last_refresh >= DAD_REFRESH_MIN_SEC {
988 // DEFER THE REOPEN WHILE REQUESTS ARE IN FLIGHT (2026-08-14).
989 // dss_refresh_and_invalidate closes and reopens the whole web shard -- 94 segments,
990 // 1.59GB -- measured at 3-8s, and worse under swap pressure. On the accept path that
991 // makes ONE unlucky visitor per refresh window wear the entire cost.
992 // MEASURED TODAY, end to end: a /search?q=test request took 15.038s and the edge
993 // returned 503, while the very next request for the SAME query served in 0.25s. That
994 // single spike is the last surviving cause of the 503 this daemon was reported for.
995 // Deferring until no child is in flight means the reopen can never be charged to a
996 // request that is already running; new connections wait in the listen backlog (64)
997 // instead of one of them timing out. This is only possible because the accept loop now
998 // tracks dad_live -- before the concurrency fix there was no such thing as "idle".
999 // HARD CEILING, so bounded staleness never becomes unbounded: if deferral has pushed us
1000 // past DAD_REFRESH_MAX_SEC we refresh regardless. Without it a permanently busy daemon
1001 // would never refresh at all, trading a latency spike for an index that silently stops
1002 // updating -- strictly worse, and invisible.
1003 if dad_live == 0 {
1004 dss_refresh_and_invalidate()
1005 last_refresh = now_s
1006 sys_write(1, "REQ refresh-ok\n" as *u8, 15)
1007 } else {
1008 if now_s - last_refresh >= DAD_REFRESH_MAX_SEC {
1009 dss_refresh_and_invalidate()
1010 last_refresh = now_s
1011 sys_write(1, "REQ refresh-forced\n" as *u8, 19)
1012 } else {
1013 var lbd: i64 = dsv_cat(logbuf, 0, "REQ refresh-defer live=" as *u8)
1014 lbd = dsv_catn(logbuf, lbd, dad_live)
1015 lbd = dsv_cat(logbuf, lbd, "\n" as *u8)
1016 sys_write(1, logbuf, lbd)
1017 }
1018 }
1019 } else {
1020 sys_write(1, "REQ refresh-skip\n" as *u8, 17)
1021 }
1022 // FORK-PER-REQUEST (the durable leak fix): dad_handle mmaps DAD_OUTCAP (512 KB) + boxes INTERNALLY on
1023 // every request and frees NONE -- the measured ~50 MB/s -> 27 GB RSS leak (budget=20M means it never
1024 // self-recycles). A child that handles then sys_exit(0) reclaims EVERY page BY CONSTRUCTION; the parent
1025 // reaps it with a blocking wait4 (no zombies -- the nx_mp_serve leak class avoided). Functionally safe:
1026 // sessions are file-validated (no in-mem state), upload writes hit shared fds so they persist, and the
1027 // parent's ctx/req/out stay pristine (the child mutates only COW copies). Leak fixed -> process stays
1028 // small -> fork stays cheap. Requests serialize, which is fine for a loopback admin daemon.
1029 let kid: i64 = sys_fork()
1030 if kid == 0 {
1031 // TIME THE REQUEST WHERE THE REQUEST ACTUALLY HAPPENS (2026-08-15). The first version of
1032 // this instrument timed accept->"REQ done" IN THE PARENT and printed ms=0 on every line,
1033 // because the parent forks and immediately loops -- the child does all the work. That is
1034 // the estate's own first question about any instrument (does its SUBJECT match the thing
1035 // it claims to measure?) and I got it wrong on the first cut. A PARENT CANNOT TIME WORK IT
1036 // HANDED TO A CHILD; the number it produces is real, it is just about a different event.
1037 let t_kid: i64 = dsv_now_us()
1038 let om: *i64 = sys_mmap(8) as *i64; let opo: *i64 = sys_mmap(8) as *i64; let opl: *i64 = sys_mmap(8) as *i64
1039 let ocl: *i64 = sys_mmap(8) as *i64; let obo: *i64 = sys_mmap(8) as *i64; let orn: *i64 = sys_mmap(8) as *i64
1040 let rrc: i64 = nx_http_server_read_request(cfd, req, DAD_REQCAP, om, opo, opl, ocl, obo, orn)
1041 if rrc == NXS_OK {
1042 // L2: the HTML SERP streams (early bytes + heartbeats) ONLY behind a `stream` route row (L2b guard,
1043 // dad_early_ok); a buffered row and every other route answer whole as before
1044 var early: i64 = 0
1045 if dad_is_search(req, orn[0]) == 1 { early = dad_early_ok(req, orn[0]) }
1046 if early == 1 { dad_serve_early(cfd, req, orn[0], out) } else {
1047 let oN: i64 = dad_handle(ctx, req, orn[0], out)
1048 nx_http_server_send_response_nokeep_close(cfd, out, oN)
1049 }
1050 }
1051 // Written by the CHILD on its own inherited fd 1, after the response is on the wire, so
1052 // it covers read+handle+send -- the duration a visitor actually experiences.
1053 let kb: *u8 = sys_mmap(64)
1054 var kn: i64 = dsv_cat(kb, 0, "REQ child ms=" as *u8)
1055 kn = dsv_catn(kb, kn, (dsv_now_us() - t_kid) / 1000)
1056 kn = dsv_cat(kb, kn, "\n" as *u8)
1057 sys_write(1, kb, kn)
1058 sys_close(cfd)
1059 sys_exit(0)
1060 }
1061 sys_close(cfd)
1062 if kid > 0 { dad_live = dad_live + 1 }
1063 // NON-BLOCKING REAP + BACKPRESSURE (2026-08-14). What stood here was
1064 // if kid > 0 { sys_wait4(kid, wst, 0) }
1065 // a BLOCKING wait before the next accept -- concurrency EXACTLY 1. The comment above
1066 // justified that as "fine for a loopback admin daemon", and it WAS true when written.
1067 // It stopped being true when proxy_routes.conf routed PUBLIC nishifamily.com /search
1068 // and /doc to this daemon: one 10.5s web query then blocked every other visitor for
1069 // its whole duration, the buffered proxy queue backed up, and /search served 503.
1070 // MEASURED 2026-08-14 before this fix: 6 concurrent queries completed at
1071 // 1.8/3.2/4.6/5.9/6.3/7.2s -- a perfect serialization staircase -- while 6 concurrent
1072 // STATIC requests stayed flat at 0.035s, proving the edge was never the limit. This box
1073 // has 20 cores and /search was using exactly one of them at a time.
1074 // A COMMENT'S ASSUMPTION OUTLIVED THE ARCHITECTURE CHANGE THAT INVALIDATED IT.
1075 dad_live = dad_live - dad_reap_done(wst)
1076 // Block ONLY at the ceiling, and only until one slot frees: backpressure, not
1077 // serialization. It bounds fork depth on a shared box instead of admitting unbounded
1078 // children. The else-branch resets to 0 because wait4 <= 0 while children are believed
1079 // live means there is nothing left to reap -- the COUNT was wrong, not the kernel.
1080 while dad_live >= dad_cap {
1081 if sys_wait4(0 - 1, wst, 0) > 0 { dad_live = dad_live - 1 } else { dad_live = 0 }
1082 }
1083 // NAMED FOR WHAT IT MEASURES, NOT FOR WHAT A READER WANTS IT TO MEAN: this is the PARENT's
1084 // accept->fork->reap cost in MICROseconds, not the request duration (the child reports that
1085 // as `REQ child ms=`). Labelled `ms` it printed 0 on every line and read as "every request is
1086 // instant" -- a fabricated-looking constant in exactly the place someone debugging latency
1087 // would look first. A COUNTER NAMED FOR AN EVENT IT DOES NOT COVER IS WORSE THAN NO COUNTER.
1088 var lbz: i64 = dsv_cat(logbuf, 0, "REQ done parent_us=" as *u8)
1089 lbz = dsv_catn(logbuf, lbz, dsv_now_us() - t_acc)
1090 lbz = dsv_cat(logbuf, lbz, " live=" as *u8)
1091 lbz = dsv_catn(logbuf, lbz, dad_live)
1092 lbz = dsv_cat(logbuf, lbz, "\n" as *u8)
1093 sys_write(1, logbuf, lbz)
1094 served = served + 1
1095 }
1096 }
1097 sys_close(lfd)
1098 return 0
1099}