code wiki / wiki / nx_wiki_main.nx

nx_wiki_main.nx source

↩ module page · 522 lines · 25753 B

1// nx_wiki_main.nx -- wiki daemon entry point. 2// 3// Forks the nx_audit_server_routed::main 140-line accept-loop 4// pattern. Reuses every existing HTTP / health / log primitive; 5// zero new daemon primitives introduced (per duplicate-primitives 6// audit). Per the small-sharp+composable standard: this file is 7// the wiki's site-specific main() that wires the hub primitives + 8// the wiki route dispatcher into a runnable daemon. 9// 10// COMPOSES: 11// nx_http_server bind / listen / accept / read / send 12// nx_kv_store session storage (caller-allocated) 13// nx_hash_facade password hash compare 14// nx_log_jsonl per-request structured log 15// nx_syscalls (sys_read_file) read admin-hash file at startup 16// nx_syscalls (sys_now_ms) wall-clock for session expiry 17// hub/nx_admin_login_flow admin-login flow init 18// wiki/nx_wiki_routes route dispatcher 19// 20// Status: V1 SEED. 2026-05-27. 21// 22// WINNER-TIER: BASELINE-C provisional (forks audit-server's shipped 23// keepalive + per-conn-budget shape; throughput parity 24// expected; paired bench pending wiki workload fixture) 25// INCUMBENTS: nginx (single-process), Caddy, Apache HTTPD MPM, 26// substrate's own nx_audit_server_routed 27// NUMBERS: V1 ships the daemon; paired req/sec vs audit-server 28// pending real load 29// GAP: nginx multi-worker + master process management; 30// V1 ships single-process with supervisor-restart on 31// request-budget exhaustion (matches audit-server) 32// PLAN: M-next: multi-worker fork pattern when wiki workload 33// demands > 1 process's throughput 34// EXEMPTION REASON: n/a; provisional pending measurement 35// 36// V1 HONEST SCOPE LIMITS (per NISHI_CODE_HYGIENE_STANDARD M6): 37// - Bind: loopback-only (127.0.0.1:51850). Operator wires a 38// reverse-proxy / nginx-front-end to expose at nishifamily.com/wiki. 39// V2 supports listening on non-loopback addresses + TLS via 40// future nx_tls_shim. 41// - Admin hash: loaded from file at NX_WIKI_MAIN_ADMIN_HASH_PATH 42// ("/tmp/nishi_wiki_admin_hash" V1; operator setup script writes 43// 8-byte big-endian i64 hash). Missing file -> daemon refuses 44// to start with explicit verdict (no silent default; not pretend). 45// - Session storage: in-memory KV (lost on restart V1; persistent 46// via nx_blob_store backing in V2). 47// - Random for session tokens: caller-supplied buffer rotated 48// per-request from sys_getrandom (V1) -> future nx_csprng (V2). 49 50import "nx_syscalls.nx" 51import "nx_http_server.nx" 52import "nx_kv_store.nx" 53import "nx_hash_facade.nx" 54import "nx_log_jsonl.nx" 55import "nx_http_header_find.nx" 56import "hub/nx_admin_login_flow.nx" 57import "hub/nx_search_handler_flow.nx" 58import "wiki/nx_wiki_routes.nx" 59import "wiki/nx_wiki_index_builder.nx" 60import "wiki/nx_wiki_page_save.nx" 61import "wiki/nx_wiki_content_loader.nx" 62import "wiki/nx_wiki_archive_router.nx" 63import "wiki/nx_artifact_store.nx" 64import "wiki/nx_pipeline_walker.nx" 65import "wiki/nx_pipeline_recursive_walker.nx" 66import "wiki/nx_pipeline_graph_builder.nx" 67import "hub/nx_dep_graph.nx" 68import "nx_search_inverted.nx" 69 70// ===== Sealed verdict surface ================================================= 71const NX_WIKI_MAIN_OK: i64 = 0 72const NX_WIKI_MAIN_BIND_FAILED: i64 = 1300 73const NX_WIKI_MAIN_NO_ADMIN_HASH: i64 = 1301 74const NX_WIKI_MAIN_KV_INIT_FAILED: i64 = 1302 75const NX_WIKI_MAIN_CONFIG_INIT_FAILED: i64 = 1303 76const NX_WIKI_MAIN_RANDOM_FAILED: i64 = 1304 77const NX_WIKI_MAIN_REQUEST_BUDGET_HIT: i64 = 1305 78 79// ===== Named constants (M7 hygiene compliance) ================================================= 80// 81// All sizing + paths + identifiers named; no magic literals in body. 82 83const NX_WIKI_MAIN_BACKLOG: i64 = 64 84const NX_WIKI_MAIN_REALM: *u8 = "Nishi Wiki Admin" as *u8 85const NX_WIKI_MAIN_REALM_N: i64 = 16 86const NX_WIKI_MAIN_ADMIN_USERNAME: *u8 = "elderwesto" as *u8 87const NX_WIKI_MAIN_ADMIN_USERNAME_N: i64 = 10 88const NX_WIKI_MAIN_ADMIN_HASH_PATH: *u8 = "/tmp/nishi_wiki_admin_hash" as *u8 89 90// Linux x86_64 syscall numbers we need beyond nx_syscalls.nx defaults. 91const NX_WIKI_MAIN_SYS_GETRANDOM: i64 = 318 // x86_64 92 93// Session storage sizing (caller-allocated per nx_kv_store contract). 94const NX_WIKI_MAIN_KV_DATA_CAP: i64 = 1048576 // 1 MiB session data 95const NX_WIKI_MAIN_KV_INDEX_CAP: i64 = 65536 // 64 KiB index (~4000 sessions) 96 97// CSPRNG buffer (16 bytes per session-token mint). 98const NX_WIKI_MAIN_RANDOM_BYTES: i64 = 16 99 100// Per-request log structure name. 101const NX_WIKI_MAIN_LOG_COMPONENT: *u8 = "nx_wiki_main" as *u8 102const NX_WIKI_MAIN_LOG_COMPONENT_N: i64 = 12 103 104// ===== Load admin password hash from file ================================================= 105// 106// File contents: 8 bytes big-endian i64 (the result of operator's 107// nx_hash_facade_compute_bytes("M0nkey2#") at setup). V1 reads the 108// file via sys_read_file_x86_64; V2 may add fcntl-locked atomic 109// read. 110// 111// Returns: 112// >=0 -> the hash value as i64 113// < 0 -> negated NX_WIKI_MAIN_NO_ADMIN_HASH verdict 114 115func nx_wiki_main_load_admin_hash(path: *u8) -> i64 { 116 let len_out: *i64 = (sys_mmap(8)) as *i64 117 len_out[0] = 0 118 let body: *u8 = sys_read_file(path, len_out) 119 if (body as i64) == 0 { return 0 - NX_WIKI_MAIN_NO_ADMIN_HASH } 120 if len_out[0] < 8 { return 0 - NX_WIKI_MAIN_NO_ADMIN_HASH } 121 // Big-endian i64 read. 122 var hash: i64 = 0 123 var i: i64 = 0 124 while i < 8 { 125 hash = (hash << 8) | (body[i] as i64) 126 i = i + 1 127 } 128 return hash 129} 130 131// ===== Fill random_16 buffer via getrandom syscall ================================================= 132// 133// Calls Linux x86_64 SYS_GETRANDOM (318). V1 blocks if entropy pool 134// uninitialized; V2 can switch to GRND_NONBLOCK + fallback. Returns 135// NX_WIKI_MAIN_OK on success. 136 137func nx_wiki_main_fill_random(out_16: *u8) -> i64 { 138 if (out_16 as i64) == 0 { return 0 - NX_WIKI_MAIN_RANDOM_FAILED } 139 let rc: i64 = __syscall(NX_WIKI_MAIN_SYS_GETRANDOM, 140 out_16 as i64, 141 NX_WIKI_MAIN_RANDOM_BYTES, 142 0, 0, 0, 0) 143 if rc != NX_WIKI_MAIN_RANDOM_BYTES { return 0 - NX_WIKI_MAIN_RANDOM_FAILED } 144 return NX_WIKI_MAIN_OK 145} 146 147// ===== Per-request log emit ================================================= 148// 149// SYSTEM-class fields only per privacy-by-default (no IP / UA / user 150// data). Status + method + path + duration. 151 152func nx_wiki_main_log_request(ts_ms: i64, 153 method_kind: i64, 154 path: *u8, path_n: i64, 155 status: i64, 156 duration_ms: i64) -> i64 { 157 let log_buf: *u8 = sys_mmap(2048) 158 let log_off: *i64 = (sys_mmap(8)) as *i64 159 log_off[0] = 0 160 let scratch: *u8 = sys_mmap(64) 161 162 // Build msg = method-name + " " + path (capped at 256). 163 let msg_buf: *u8 = sys_mmap(512) 164 var msg_off: i64 = 0 165 let mname: *u8 = nxar_method_name(method_kind) 166 let mname_n: i64 = nxar_method_name_len(method_kind) 167 var i: i64 = 0 168 while i < mname_n { 169 msg_buf[msg_off + i] = mname[i] 170 i = i + 1 171 } 172 msg_off = msg_off + mname_n 173 msg_buf[msg_off] = 0x20 as u8 174 msg_off = msg_off + 1 175 var cap_path: i64 = path_n 176 if cap_path > 256 { cap_path = 256 } 177 var j: i64 = 0 178 while j < cap_path { 179 msg_buf[msg_off + j] = path[j] 180 j = j + 1 181 } 182 msg_off = msg_off + cap_path 183 184 var level: i64 = NXL_INFO 185 if status >= 400 { if status <= 499 { level = NXL_WARN } } 186 if status >= 500 { if status <= 599 { level = NXL_ERROR } } 187 188 nx_log_emit_line_with_int(log_buf, log_off, 2048, 189 ts_ms, level, 190 NX_WIKI_MAIN_LOG_COMPONENT, 191 NX_WIKI_MAIN_LOG_COMPONENT_N, 192 msg_buf, msg_off, 193 "status" as *u8, 6, 194 status, scratch) 195 sys_write(1, log_buf, log_off[0]) 196 return NX_WIKI_MAIN_OK 197} 198 199// Method-name helpers (mirror nx_audit_server_routed conventions). 200func nxar_method_name(k: i64) -> *u8 { 201 if k == NX_WIKI_ROUTE_METHOD_GET { return "GET" as *u8 } 202 if k == NX_WIKI_ROUTE_METHOD_POST { return "POST" as *u8 } 203 if k == NX_WIKI_ROUTE_METHOD_OPTIONS { return "OPTIONS" as *u8 } 204 return "?" as *u8 205} 206func nxar_method_name_len(k: i64) -> i64 { 207 if k == NX_WIKI_ROUTE_METHOD_GET { return 3 } 208 if k == NX_WIKI_ROUTE_METHOD_POST { return 4 } 209 if k == NX_WIKI_ROUTE_METHOD_OPTIONS { return 7 } 210 return 1 211} 212 213// ===== Main daemon loop ================================================= 214// 215// Mirror of nx_audit_server_routed::main but for the wiki: 216// 1. Bind 127.0.0.1:51850 217// 2. Load admin hash (refuse to start if missing) 218// 3. Init session KV store (1 MiB data + 64 KiB index) 219// 4. Init NxAdminLoginConfig 220// 5. Accept loop with per-conn keepalive budget (100) + 221// per-process request budget (1M); supervisor restarts on exhaustion 222// 6. Per request: parse -> dispatch via nx_wiki_route_dispatch -> 223// send -> log 224 225func main() -> i64 { 226 // ----- 1. Bind ----- 227 let addr: *u8 = sys_mmap(16) 228 // DEPLOY (2026-06-16): bind ALL interfaces (was loopback-only) so the daemon is reachable for 229 // LAN verification before the sites-daemon reverse-proxy is wired; 0.0.0.0 still includes 230 // 127.0.0.1 so the eventual loopback proxy path is unaffected. :51850 is not publicly forwarded. 231 let a_rc: i64 = nx_http_server_addr_any(addr, NX_WIKI_ROUTE_PORT) 232 if a_rc != 16 { return NX_WIKI_MAIN_BIND_FAILED } 233 234 let lv: *i64 = (sys_mmap(8)) as *i64 235 let lfd: i64 = nx_http_server_listen(addr, NX_WIKI_MAIN_BACKLOG, lv) 236 if lfd < 0 { return NX_WIKI_MAIN_BIND_FAILED } 237 238 // ----- 2. Load admin hash (NX_WIKI_MAIN_ADMIN_HASH_PATH) ----- 239 let admin_hash: i64 = nx_wiki_main_load_admin_hash(NX_WIKI_MAIN_ADMIN_HASH_PATH) 240 if admin_hash < 0 { 241 // Refuse to start without explicit operator-supplied hash file. 242 // NO silent default; NO pretend functionality. 243 return NX_WIKI_MAIN_NO_ADMIN_HASH 244 } 245 246 // ----- 3. Init session KV store ----- 247 let sessions: *NxKvStore = (sys_mmap(NX_KV_STORE_BYTES)) as *NxKvStore 248 let kv_data: *u8 = sys_mmap(NX_WIKI_MAIN_KV_DATA_CAP) 249 let kv_index: *u8 = sys_mmap(NX_WIKI_MAIN_KV_INDEX_CAP) 250 let kv_rc: i64 = nx_kv_store_init(sessions, 251 kv_data, NX_WIKI_MAIN_KV_DATA_CAP, 252 kv_index, NX_WIKI_MAIN_KV_INDEX_CAP) 253 if kv_rc != NXKV_OK { return NX_WIKI_MAIN_KV_INIT_FAILED } 254 255 // ----- 4. Init NxAdminLoginConfig ----- 256 let admin_cfg: *NxAdminLoginConfig = (sys_mmap(96)) as *NxAdminLoginConfig 257 let cfg_rc: i64 = nx_admin_login_config_init(admin_cfg, 258 NX_WIKI_MAIN_REALM, 259 NX_WIKI_MAIN_REALM_N, 260 NX_WIKI_MAIN_ADMIN_USERNAME, 261 NX_WIKI_MAIN_ADMIN_USERNAME_N, 262 admin_hash, 263 sessions, 264 NX_ALOGIN_DEFAULT_SESSION_MAX_AGE_S) 265 if cfg_rc != NX_ALOGIN_OK { return NX_WIKI_MAIN_CONFIG_INIT_FAILED } 266 267 // ----- 5. Doc names buffer (V1 empty; V2 populated by index_builder) ----- 268 let doc_names: *u8 = sys_mmap(4096) 269 // V1: zero entries. Wikilinks all render as broken until 270 // nx_wiki_index_builder.nx ships + populates this buffer. 271 let doc_names_count: i64 = 0 272 273 // ----- 6. Random buffer (refreshed per request) ----- 274 let random_16: *u8 = sys_mmap(NX_WIKI_MAIN_RANDOM_BYTES) 275 276 // ----- 6b. Search flow (V1 search arc; LIVE for /wiki/search) ----- 277 // Counter cap = 100_000 docs (well below 4M cap; right-sized for V1 278 // wiki where index is empty until nx_wiki_index_builder ships). 279 // docs_cap = 100 (default per NxSearchOnsiteCtx); postings = 10_000. 280 let search_flow: *NxSearchFlow = (sys_mmap(256)) as *NxSearchFlow 281 let sf_rc: i64 = nx_search_flow_init(search_flow, 282 100000, // counter_cap (max rowid + 1) 283 100, // docs_cap (top-N results) 284 10000) // postings_cap per term 285 if sf_rc != NX_SHF_OK { return NX_WIKI_MAIN_CONFIG_INIT_FAILED } 286 287 // ----- 6c. Wiki doc store + index builder (V+2 auto-discover LIVE) ----- 288 // Allocate NxWikiDocStore + NxWikiIndexBuilder; populate by RECURSIVELY 289 // walking the silicon content dir (V+2: nx_wcl_load_discover_tree 290 // replaced the hardcoded V1 seed list -- every *.md top-level AND in a 291 // subdir becomes a page + search hit with no code edit). The hardcoded 292 // §1 manifest remains as the loader's graceful fallback. 293 let doc_store: *NxWikiDocStore = (sys_mmap(256)) as *NxWikiDocStore 294 let store_rc: i64 = nx_wiki_doc_store_init(doc_store, 295 100, // docs_cap 296 65536, // titles_pool_cap 297 65536, // urls_pool_cap 298 4194304) // bodies_pool_cap (4 MB) 299 if store_rc != NX_WIB_OK { return NX_WIKI_MAIN_CONFIG_INIT_FAILED } 300 301 let builder: *NxWikiIndexBuilder = (sys_mmap(64)) as *NxWikiIndexBuilder 302 let builder_rc: i64 = nx_wiki_index_builder_init(builder, doc_store, 100) 303 if builder_rc != NX_WIB_OK { return NX_WIKI_MAIN_CONFIG_INIT_FAILED } 304 305 // Docs root: WSL2-accessible path to the nishi-silicon repo on the 306 // dev host (NX_WCL_SILICON_ROOT in nx_wiki_content_loader). Production 307 // deploys override via env (queued env-config wiring per Cardinal 17 308 // configuration hierarchy). 309 // 310 // FOLLOW-ON: honor nx_nishi_page_validator results (skip 311 // non-conformant docs with a structured log line) inside the walk. 312 313 // V+2 AUTO-DISCOVER: recursively walk the silicon content dir 314 // (top-level AND subdir charters like hdl/*.md) via the sovereign 315 // getdents64 work-stack in nx_wiki_content_loader. Every *.md becomes 316 // /wiki/<path-aware-slug> + a search hit with NO code edit -- drop a 317 // new charter into the dir (or a subdir) and it appears on next start. 318 // 319 // Per Cardinal 14 (graceful degradation): nx_wcl_load_discover_tree 320 // internally FALLS BACK to the hardcoded §1 manifest if the root dir 321 // can't be walked (e.g. a deploy host without the repo mounted), so 322 // the daemon always starts with a populated index. Per the desync 323 // landmine the function counts via the doc-store delta (ground truth), 324 // not a returned status code. 325 let n_loaded: i64 = nx_wcl_load_discover_tree(builder) 326 // graceful: a per-file miss is skipped inside the loader; n_loaded is 327 // the count actually indexed (>=0). An empty index is still a valid 328 // state for the search engine (empty intersection = 0 results). 329 330 // Finalize even if some reads failed -- empty index is a valid 331 // state per nx_search_onsite_engine (empty intersection = 0 results). 332 let finalize_rc: i64 = nx_wiki_index_builder_finalize(builder) 333 if finalize_rc != NX_WIB_OK { return NX_WIKI_MAIN_CONFIG_INIT_FAILED } 334 335 // R1 EDIT OVERLAY: prefer a seg_store-persisted version of a page 336 // (key wikicur:<slug> under knowledge/store/wikipage-) over the 337 // filesystem .md seed, so admin edits survive restart (cardinal 13 338 // additive-data: history lives in the store, not the container fs). 339 // Per cardinal 14 a page whose persisted bytes do not fit is skipped; 340 // the daemon always starts with a populated, serveable index. 341 let n_overlaid: i64 = nx_wiki_page_overlay_store(doc_store) 342 343 // Hand the finalized index + store to the search arc. 344 let search_idx: *NxInvIndex = nx_wiki_index_builder_get_index(builder) 345 if (search_idx as i64) == 0 { return NX_WIKI_MAIN_CONFIG_INIT_FAILED } 346 347 // ----- 6d. Archive store (V1 archive subsystem; cite-time snapshots) ----- 348 let archive_store: *NxArchiveStore = (sys_mmap(256)) as *NxArchiveStore 349 let arch_rc: i64 = nx_archive_store_init(archive_store, 100) 350 if arch_rc != NX_ARCH_OK { return NX_WIKI_MAIN_CONFIG_INIT_FAILED } 351 352 // V1 seed snapshots demonstrating cite-time-snapshot pattern. 353 // V2 adds live fetch via nx_http_client + robots.txt + crawl_policy. 354 // V2 also adds wiki-build-pass walker that scans nishi-silicon 355 // docs for external citations and auto-archives each. 356 nx_archive_store_add(archive_store, 357 "https://en.wikipedia.org/wiki/Inverted_index" as *u8, 44, 358 "2026-05-27T00:00:00Z" as *u8, 20, 359 "Snapshot of Wikipedia: Inverted index. Cited in NISHI_SEARCH_CHARTER as foundational data structure (Salton 1971). V1 seed snapshot; V2 swaps for live fetch via nx_http_client per ARCHIVE_INTEGRATION_CHARTER section 5." as *u8, 360 221, 361 NX_ARCH_CT_PLAIN) 362 nx_archive_store_add(archive_store, 363 "https://en.wikipedia.org/wiki/BM25" as *u8, 34, 364 "2026-05-27T00:00:00Z" as *u8, 20, 365 "Snapshot of Wikipedia: BM25. Cited in NISHI_SEARCH_CHARTER section 5.2 as V2 ranker target (Robertson and Zaragoza 2009). V1 seed snapshot." as *u8, 366 145, 367 NX_ARCH_CT_PLAIN) 368 nx_archive_store_add(archive_store, 369 "https://spdx.org/licenses/" as *u8, 26, 370 "2026-05-27T00:00:00Z" as *u8, 20, 371 "Snapshot of SPDX License List. Cited in NISHI_PAGE_FORMAT_V1 section 2.8 as the canonical source for nishi-license meta tag values. V1 seed snapshot." as *u8, 372 157, 373 NX_ARCH_CT_PLAIN) 374 375 // ----- 6e. Pipeline artifact store (V2.0 P-1..P-5 + V2.5 LIVE) ----- 376 // V2.5: recursive walker via sovereign nx_dir_list + getdents64 377 // discovers ALL .nx / .md / .sh artifacts under operator-supplied 378 // roots. Replaces V2.0's hardcoded 24-path list. 379 // V3+ swap: per-file last-commit-hash via nx_git_walk (queued). 380 let artifact_store: *NxArtifactStore = (sys_mmap(512)) as *NxArtifactStore 381 let art_rc: i64 = nx_artifact_store_init(artifact_store, 1000) // V2.5 cap; expected ~300-500 artifacts 382 if art_rc != NX_ART_OK { return NX_WIKI_MAIN_CONFIG_INIT_FAILED } 383 384 // V2.5: recursive walker roots (covers ALL substrate artifacts). 385 // The hardcoded 24-path list from V2.0 is replaced by recursive 386 // discovery under these roots. 387 let pipeline_roots: *i64 = (sys_mmap(8 * 8)) as *i64 388 var rr: i64 = 0 389 pipeline_roots[rr] = ("/mnt/c/Users/elder/nishi-core/nxc2/runtime" as *u8) as i64; rr = rr + 1 390 pipeline_roots[rr] = ("/mnt/c/Users/elder/nishi-core/nxc2/docs" as *u8) as i64; rr = rr + 1 391 pipeline_roots[rr] = ("/mnt/c/Users/elder/nishi-core/nxc2/bench" as *u8) as i64; rr = rr + 1 392 pipeline_roots[rr] = ("/mnt/c/Users/elder/nishi-silicon" as *u8) as i64; rr = rr + 1 393 394 let walk_counters: *i64 = (sys_mmap(3 * 8)) as *i64 395 let walk_rc: i64 = nx_pipeline_recursive_walk_roots(artifact_store, 396 pipeline_roots, rr, 397 walk_counters) 398 // Per Cardinal 14: walker tolerates per-file errors; STORE_FULL 399 // is the only fatal verdict (and per V2.5 cap 1000, unlikely) 400 if walk_rc == 0 - NX_PREC_STORE_FULL { return NX_WIKI_MAIN_CONFIG_INIT_FAILED } 401 // walk_counters[0]=files walked; [1]=dirs visited; [2]=files skipped 402 403 // V2.0 fallback: keep the hardcoded list around as a typed array 404 // for the doc-store seed list (separate from pipeline). pp=0 here 405 // (legacy variable referenced in older code paths; preserved for 406 // compatibility with any path-list iteration below). 407 let pipeline_paths: *i64 = (sys_mmap(8)) as *i64 408 var pp: i64 = 0 409 410 // V2.0 hardcoded 24-path list deleted (replaced by V2.5 recursive 411 // walker above). Per Cardinal 25 (build intelligence; never strip): 412 // the deletion is justified because the recursive walker is the 413 // strict superset (discovers the 24 paths AND all others). 414 415 // ----- 6f. Dep graph + Tarjan SCC (V2.0 P-8 + P-8.5 LIVE) ----- 416 // Builds directed graph from extracted .nx imports; runs Tarjan SCC; 417 // surfaces at /wiki/pipeline (cycles + per-artifact rev-deps). 418 let dep_graph: *NxDepGraph = (sys_mmap(256)) as *NxDepGraph 419 let dg_rc: i64 = nx_dep_graph_init(dep_graph, 1000, 10000) 420 if dg_rc != NX_DEPG_OK { return NX_WIKI_MAIN_CONFIG_INIT_FAILED } 421 422 let dg_counters: *i64 = (sys_mmap(5 * 8)) as *i64 423 let dg_build_rc: i64 = nx_pipeline_build_dep_graph(artifact_store, dep_graph, 424 dg_counters) 425 // Per Cardinal 14: dep graph build is non-fatal; daemon starts even if 426 // some imports unresolved 427 // dg_counters[0]=edges added; [1]=imports unresolved; [2]=ambiguous; 428 // [3]=artifacts re-read; [4]=read failures 429 430 // ----- 7. Per-process counters ----- 431 var n_requests: i64 = 0 432 var n_errors: i64 = 0 433 var n_served: i64 = 0 434 let t_start: i64 = sys_now_ms() 435 436 // ----- 8. Accept loop ----- 437 while n_served < NX_WIKI_ROUTE_REQUEST_BUDGET { 438 let av: *i64 = (sys_mmap(8)) as *i64 439 let cfd: i64 = nx_http_server_accept_one(lfd, av) 440 if cfd < 0 { 441 n_errors = n_errors + 1 442 n_served = n_served + 1 443 if n_served >= NX_WIKI_ROUTE_REQUEST_BUDGET { return NX_WIKI_MAIN_REQUEST_BUDGET_HIT } 444 } 445 if cfd >= 0 { 446 // Keepalive: process up to NX_WIKI_ROUTE_PER_CONN_BUDGET 447 // requests per TCP connection. 448 var reqs_on_conn: i64 = 0 449 var conn_alive: i64 = 1 450 while conn_alive == 1 { 451 if reqs_on_conn >= NX_WIKI_ROUTE_PER_CONN_BUDGET { conn_alive = 0 } 452 if n_served >= NX_WIKI_ROUTE_REQUEST_BUDGET { conn_alive = 0 } 453 if conn_alive == 1 { 454 let req_buf: *u8 = sys_mmap(NX_WIKI_ROUTE_REQ_CAP) 455 let om: *i64 = (sys_mmap(8)) as *i64 456 let opo: *i64 = (sys_mmap(8)) as *i64 457 let opl: *i64 = (sys_mmap(8)) as *i64 458 let ocl: *i64 = (sys_mmap(8)) as *i64 459 let obo: *i64 = (sys_mmap(8)) as *i64 460 let orn: *i64 = (sys_mmap(8)) as *i64 461 462 let rrc: i64 = nx_http_server_read_request(cfd, 463 req_buf, 464 NX_WIKI_ROUTE_REQ_CAP, 465 om, opo, opl, 466 ocl, obo, orn) 467 if rrc != NXS_OK { conn_alive = 0 } 468 if rrc == NXS_OK { 469 let path_ptr: *u8 = (req_buf as i64 + opo[0]) as *u8 470 let path_n: i64 = opl[0] 471 let resp_buf: *u8 = sys_mmap(NX_WIKI_ROUTE_RESP_CAP) 472 let resp_n: *i64 = (sys_mmap(8)) as *i64 473 resp_n[0] = 0 474 475 let now_ms: i64 = sys_now_ms() 476 let now_s: i64 = now_ms / 1000 477 478 // Refresh random_16 from CSPRNG (best-effort; 479 // failure means new sessions won't be minted 480 // safely but existing routes still work). 481 nx_wiki_main_fill_random(random_16) 482 483 let dispatch_rc: i64 = nx_wiki_route_dispatch( 484 req_buf, obo[0], 485 path_ptr, path_n, 486 om[0], 487 admin_cfg, 488 now_s, 489 search_flow, search_idx, doc_store, archive_store, artifact_store, 490 resp_buf, NX_WIKI_ROUTE_RESP_CAP, 491 resp_n) 492 493 if dispatch_rc != NX_WIKI_ROUTE_OK { 494 n_errors = n_errors + 1 495 } 496 497 // Send response (no-keepalive close per audit-server pattern). 498 let send_rc: i64 = nx_http_server_send_response_nokeep_close( 499 cfd, resp_buf, resp_n[0]) 500 if send_rc != NXS_OK { conn_alive = 0 } 501 n_requests = n_requests + 1 502 503 // Log request. 504 let req_end_ms: i64 = sys_now_ms() 505 let req_dur: i64 = req_end_ms - now_ms 506 var log_status: i64 = 200 507 if dispatch_rc != NX_WIKI_ROUTE_OK { log_status = 500 } 508 nx_wiki_main_log_request(now_ms, om[0], 509 path_ptr, path_n, 510 log_status, req_dur) 511 } 512 n_served = n_served + 1 513 reqs_on_conn = reqs_on_conn + 1 514 } 515 } 516 sys_close(cfd) 517 } 518 } 519 520 sys_close(lfd) 521 return NX_WIKI_MAIN_REQUEST_BUDGET_HIT 522}