nx_wiki_routes.nx source
↩ module page · 690 lines · 34380 B
1// nx_wiki_routes.nx -- wiki HTTP route dispatcher (main daemon entry).
2//
3// Forks the nx_audit_server_routed.nx pattern (687-line template).
4// Reuses every existing HTTP / health / metrics / per-conn-budget
5// primitive per the duplicate-primitives audit; zero new HTTP /
6// daemon primitives introduced.
7//
8// COMPOSES (per NISHI_SMALL_SHARP_COMPOSABLE_STANDARD ยง4.1 M7):
9// nx_http_server bind / listen / accept / read / send
10// nx_http_health /health + /ready endpoints
11// nx_http_metrics /metrics (Prometheus text)
12// nx_log_jsonl per-request structured log emit
13// wiki/nx_wiki_admin_wiring /wiki/admin/* handler
14// wiki/nx_wiki_doc_render /wiki/<doc-name> handler
15// hub/nx_admin_login_flow (transitively via admin_wiring)
16//
17// COMPOSED BY:
18// wiki/nx_wiki_main.nx daemon main() entry (future commit)
19//
20// Status: V1 SEED. 2026-05-27.
21//
22// WINNER-TIER: BASELINE-C provisional (forks the audit-server's
23// shipped patterns; throughput + latency parity with
24// the audit server expected since it's the same
25// keepalive + per-conn-budget shape; paired bench
26// pending real wiki workload fixture)
27// INCUMBENTS: nginx (route dispatch), Caddy, Apache HTTPD,
28// substrate's own nx_audit_server_routed (closest
29// reference; same family)
30// NUMBERS: V1 ships the route table + dispatcher; paired
31// throughput bench vs nginx + audit-server pending
32// wiki-doc-set fixture
33// GAP: nginx has rewrite rules + load balancing + reverse
34// proxy + cache; V1 ships JUST wiki-specific routes
35// (small-sharp per the standard; nginx-class features
36// add as needed when wiki workload demands)
37// PLAN: M-next: paired throughput vs audit-server (same
38// hardware); add nx_http_metrics integration for
39// wiki-specific counters
40// EXEMPTION REASON: n/a; provisional pending measurement
41//
42// Route table (V1):
43// GET /wiki/ index page (link list of known docs)
44// GET /wiki/<doc-name> render doc (nx_wiki_doc_render)
45// GET /wiki/admin admin login (nx_wiki_admin_wiring)
46// POST /wiki/admin admin form submit (same)
47// GET /wiki/admin/logout logout (same)
48// GET /wiki/admin/dashboard protected dashboard (501 V1; queued)
49// GET /wiki/search search handler (501 V1; queued)
50// GET /wiki/archive/* archive router (501 V1; queued)
51// GET /health reuse nx_http_health
52// GET /ready reuse nx_http_health
53// GET /metrics reuse nx_http_metrics
54// * 404 Not Found
55//
56// HONEST V1 SCOPE per NISHI_CODE_HYGIENE_STANDARD M6 (anti-pretend-stub):
57// /wiki/admin/dashboard, /wiki/search, /wiki/archive/* return
58// HTTP 501 Not Implemented with body "Handler queued per
59// <module-name>; commits pending" -- explicit + auditable; the
60// route exists but tells the operator unambiguously that the
61// handler is V1-scope-deferred. NOT pretend functionality.
62
63import "nx_syscalls.nx"
64import "nx_http_server.nx"
65import "nx_http_health_emit.nx" // the /health EMITTER (2 call sites below). Was importing
66 // nx_http_health.nx, which since the 2026-07-21 dedupe is the URL PROBER.
67import "nx_http_metrics.nx"
68import "nx_log_jsonl.nx"
69import "nx_http_header_find.nx"
70import "wiki/nx_wiki_admin_wiring.nx"
71import "wiki/nx_wiki_doc_render.nx"
72import "wiki/nx_wiki_search_wiring.nx"
73import "wiki/nx_wiki_index_builder.nx"
74import "wiki/nx_wiki_doc_handler.nx"
75import "wiki/nx_wiki_edit_handler.nx"
76import "wiki/nx_wiki_status.nx"
77import "wiki/nx_wiki_article.nx"
78import "wiki/nx_wiki_queue.nx"
79import "wiki/nx_wiki_dashboard.nx"
80import "wiki/nx_wiki_archive_router.nx"
81import "wiki/nx_pipeline_route.nx"
82import "wiki/nx_artifact_store.nx"
83// wiki R4: static link-graph view (GET /wiki/graph). Composes the doc store +
84// the shared forward-link scanner (via nx_wiki_backlinks, pulled transitively)
85// into a JS-free <svg>. nx_wiki_graph imports nx_wiki_index_builder bare; the
86// resolver canonicalizes so it dedupes with wiki/nx_wiki_index_builder above.
87import "wiki/nx_wiki_graph.nx"
88import "hub/nx_search_handler_flow.nx"
89import "nx_search_inverted.nx"
90
91// ===== Sealed verdict surface =================================================
92const NX_WIKI_ROUTE_OK: i64 = 0
93const NX_WIKI_ROUTE_BAD_INPUT: i64 = 1200
94const NX_WIKI_ROUTE_BUF_OVERFLOW: i64 = 1201
95const NX_WIKI_ROUTE_HANDLER_FAILED: i64 = 1202
96const NX_WIKI_ROUTE_NOT_IMPLEMENTED: i64 = 1203
97const NX_WIKI_ROUTE_UNKNOWN: i64 = 1204
98
99// ===== Daemon-wide sizing constants (M7) =================================================
100const NX_WIKI_ROUTE_PORT: i64 = 51850
101const NX_WIKI_ROUTE_REQ_CAP: i64 = 16384
102const NX_WIKI_ROUTE_RESP_CAP: i64 = 1048576 // 1 MiB (docs can be large)
103const NX_WIKI_ROUTE_REQUEST_BUDGET: i64 = 1000000
104const NX_WIKI_ROUTE_PER_CONN_BUDGET: i64 = 100 // keepalive cap per audit-server pattern
105
106// HTTP method-kind (per nx_http_io convention)
107const NX_WIKI_ROUTE_METHOD_GET: i64 = 1
108const NX_WIKI_ROUTE_METHOD_POST: i64 = 2
109const NX_WIKI_ROUTE_METHOD_OPTIONS: i64 = 3
110
111// ===== Path-prefix matching helpers (forks audit-server pattern) =================================================
112//
113// Returns 1 if path[0..prefix_n] == prefix AND the next byte is
114// `/` or end-of-path. Used for /wiki/admin/* and /wiki/archive/*.
115
116func nx_wiki_route_starts_with(path: *u8, path_n: i64,
117 prefix: *u8, prefix_n: i64) -> i64 {
118 if path_n < prefix_n { return 0 }
119 var i: i64 = 0
120 while i < prefix_n {
121 if i >= NX_WIKI_ROUTE_REQ_CAP { return 0 } // M3 bounded
122 if path[i] != prefix[i] { return 0 }
123 i = i + 1
124 }
125 if path_n == prefix_n { return 1 }
126 if path[prefix_n] == 0x2f as u8 { return 1 } // '/'
127 return 0
128}
129
130// Returns 1 if path ends with suffix (e.g. "/edit"). Used to route
131// /wiki/<slug>/edit to the R1 edit handler before the generic doc catch.
132func nx_wiki_route_ends_with(path: *u8, path_n: i64,
133 suffix: *u8, suffix_n: i64) -> i64 {
134 if path_n < suffix_n { return 0 }
135 let base: i64 = path_n - suffix_n
136 var i: i64 = 0
137 while i < suffix_n {
138 if i >= NX_WIKI_ROUTE_REQ_CAP { return 0 }
139 if path[base + i] != suffix[i] { return 0 }
140 i = i + 1
141 }
142 return 1
143}
144
145func nx_wiki_route_eq(path: *u8, path_n: i64,
146 target: *u8, target_n: i64) -> i64 {
147 if path_n != target_n { return 0 }
148 var i: i64 = 0
149 while i < path_n {
150 if i >= NX_WIKI_ROUTE_REQ_CAP { return 0 } // M3 bounded
151 if path[i] != target[i] { return 0 }
152 i = i + 1
153 }
154 return 1
155}
156
157// ===== Response builders =================================================
158//
159// Each returns NX_WIKI_ROUTE_OK + final resp byte count via out-param.
160
161// Append helpers (M5 bounds + M8 propagation).
162func nx_wiki_route_append(out: *u8, off: *i64, cap: i64,
163 src: *u8, src_n: i64) -> i64 {
164 if off[0] + src_n > cap { return 0 - NX_WIKI_ROUTE_BUF_OVERFLOW }
165 var i: i64 = 0
166 while i < src_n {
167 if i >= cap { return 0 - NX_WIKI_ROUTE_BUF_OVERFLOW } // M3
168 out[off[0] + i] = src[i]
169 i = i + 1
170 }
171 off[0] = off[0] + src_n
172 return NX_WIKI_ROUTE_OK
173}
174
175func nx_wiki_route_append_z(out: *u8, off: *i64, cap: i64, src: *u8) -> i64 {
176 if (src as i64) == 0 { return 0 - NX_WIKI_ROUTE_BAD_INPUT }
177 var n: i64 = 0
178 while n < cap {
179 if src[n] == (0 as u8) { return nx_wiki_route_append(out, off, cap, src, n) }
180 n = n + 1
181 }
182 return 0 - NX_WIKI_ROUTE_BUF_OVERFLOW
183}
184
185// ===== / (wiki index) =================================================
186//
187// V1 emits a static HTML page with links to the route catalog. V2
188// will read the doc index (nx_wiki_index_builder, queued) + emit
189// dynamic doc-list.
190
191func nx_wiki_route_serve_index(out: *u8, out_cap: i64, out_n: *i64) -> i64 {
192 out_n[0] = 0
193 let body: *u8 = "<!DOCTYPE html><html><head><meta charset=\"utf-8\"><title>Nishi Wiki</title><style>body{font-family:system-ui,sans-serif;max-width:680px;margin:2em auto;padding:0 1em;line-height:1.5}h1{margin-bottom:.2em}ul{padding-left:1.2em}a{color:#27c;text-decoration:none}a:hover{text-decoration:underline}.status{color:#888;font-size:.9em}</style></head><body><h1>Nishi Sovereign Substrate Wiki</h1><p>Living-doc platform per NISHI_PRODUCT_OPS_PLATFORM.md. Routes:</p><ul><li><a href=\"/wiki/admin\">/wiki/admin</a> — admin login (elderwesto)</li><li><a href=\"/wiki/search\">/wiki/search</a> <span class=\"status\">(queued: nx_wiki_search_handler)</span></li><li><a href=\"/wiki/admin/dashboard\">/wiki/admin/dashboard</a> <span class=\"status\">(queued: nx_wiki_dashboard)</span></li><li><a href=\"/wiki/archive/\">/wiki/archive/*</a> <span class=\"status\">(queued: nx_wiki_archive_router)</span></li><li><a href=\"/health\">/health</a></li><li><a href=\"/ready\">/ready</a></li><li><a href=\"/metrics\">/metrics</a></li></ul><p class=\"status\">V1 build: doc render + admin login shipped; search / dashboard / archive subsystems shipping per ZERO_TO_ADVANCED.md per loop-discipline cardinal (one focused commit at a time).</p></body></html>" as *u8
194 let body_n: i64 = 1098 // hand-counted; V2 dynamic-len helper
195
196 let header: *u8 = "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: " as *u8
197 let off: *i64 = (sys_mmap(8)) as *i64
198 off[0] = 0
199 let rc1: i64 = nx_wiki_route_append_z(out, off, out_cap, header)
200 if rc1 != NX_WIKI_ROUTE_OK { return rc1 }
201
202 // Append body_n decimal.
203 let scratch: *u8 = sys_mmap(16)
204 var n: i64 = body_n
205 var k: i64 = 0
206 while n > 0 {
207 if k >= 16 { return 0 - NX_WIKI_ROUTE_BUF_OVERFLOW }
208 scratch[k] = (0x30 + (n - (n / 10) * 10)) as u8
209 n = n / 10
210 k = k + 1
211 }
212 var ri: i64 = k - 1
213 while ri >= 0 {
214 if off[0] >= out_cap { return 0 - NX_WIKI_ROUTE_BUF_OVERFLOW }
215 out[off[0]] = scratch[ri]
216 off[0] = off[0] + 1
217 ri = ri - 1
218 }
219 let crlf2: *u8 = "\r\n\r\n" as *u8
220 let rc2: i64 = nx_wiki_route_append(out, off, out_cap, crlf2, 4)
221 if rc2 != NX_WIKI_ROUTE_OK { return rc2 }
222
223 // Body
224 let rc3: i64 = nx_wiki_route_append(out, off, out_cap, body, body_n)
225 if rc3 != NX_WIKI_ROUTE_OK { return rc3 }
226
227 out_n[0] = off[0]
228 return NX_WIKI_ROUTE_OK
229}
230
231// ===== /wiki/graph (wiki R4: static link-graph view) =========================
232//
233// Builds the link graph over the doc store (nodes = pages; edges = resolvable
234// forward [[wikilinks]]) and renders it as a self-contained, JS-FREE <svg>
235// inside minimal page chrome. Static SVG = sovereign + renders identically
236// everywhere (no <script>, no event handlers). Returns HTTP 200 + HTML.
237// Graceful (Cardinal 14): with no/empty store the SVG is an empty ring, still a
238// well-formed 200 (never a 501 for this live route).
239const NX_WIKI_GRAPH_PAGE_CAP: i64 = 786432 // 768 KB SVG+chrome scratch
240
241func nx_wiki_route_serve_graph(doc_store: *NxWikiDocStore,
242 out: *u8, out_cap: i64, out_n: *i64) -> i64 {
243 out_n[0] = 0
244
245 // Build the graph + render the SVG into a scratch buffer.
246 let svg: *u8 = sys_mmap(NX_WIKI_GRAPH_PAGE_CAP)
247 let svg_used: *i64 = (sys_mmap(8)) as *i64
248 svg_used[0] = 0
249 var svg_n: i64 = 0
250 if (doc_store as i64) != 0 {
251 if doc_store.valid == 1 {
252 let g: *NxWikiGraph = (sys_mmap(256)) as *NxWikiGraph
253 let rc_gb: i64 = nx_wiki_graph_build(doc_store, g)
254 if rc_gb == NX_WGRAPH_OK {
255 let rc_sv: i64 = nx_wiki_graph_render_svg(g, doc_store, svg,
256 NX_WIKI_GRAPH_PAGE_CAP, svg_used)
257 if rc_sv == NX_WGRAPH_OK { svg_n = svg_used[0] }
258 }
259 }
260 }
261
262 // Page body = chrome (sticky nav reused from the doc page) + the SVG figure.
263 // Built into a body buffer so we can emit an exact Content-Length.
264 let body: *u8 = sys_mmap(NX_WIKI_GRAPH_PAGE_CAP)
265 var bo: i64 = 0
266 let pre: *u8 = "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>Link graph | Nishi Wiki</title>\n<style>body{margin:0;font-family:system-ui,sans-serif;background:#fff;color:#1a202c}header.nx-top{position:sticky;top:0;border-bottom:1px solid #e2e8f0;background:#fff}.nx-nav{max-width:1080px;margin:0 auto;display:flex;align-items:center;gap:1.1em;padding:.65em 1.2em}.nx-brand{font-weight:800;color:#1a202c;text-decoration:none}.nx-brand span{color:#2c7a7b}.nx-nav a{color:#5a6573;text-decoration:none;font-weight:600;font-size:.92em}main{max-width:1080px;margin:0 auto;padding:1.6em 1.2em}h1{font-size:1.6em}figure{margin:0}svg.nx-graph{width:100%;height:auto;border:1px solid #e2e8f0;border-radius:10px;background:#fcfdff}</style>\n</head>\n<body>\n<header class=\"nx-top\"><nav class=\"nx-nav\"><a class=\"nx-brand\" href=\"/wiki/\">Nishi<span>Wiki</span></a><a href=\"/wiki/\">Home</a><a href=\"/wiki/graph\">Graph</a><a href=\"/wiki/dashboard\">Dashboard</a></nav></header>\n<main><h1>Wiki link graph</h1><p>Pages are nodes; resolvable <code>[[wikilinks]]</code> are edges. Static, JavaScript-free SVG.</p><figure>" as *u8
267 var pi: i64 = 0
268 while pre[pi] != (0 as u8) { body[bo + pi] = pre[pi]; pi = pi + 1 }
269 bo = bo + pi
270 // SVG (or a tiny empty-state note when the graph could not render).
271 if svg_n > 0 {
272 var si: i64 = 0
273 while si < svg_n {
274 if si >= NX_WIKI_GRAPH_PAGE_CAP { si = svg_n }
275 if si < svg_n { body[bo + si] = svg[si]; si = si + 1 }
276 }
277 bo = bo + svg_n
278 }
279 if svg_n == 0 {
280 let empt: *u8 = "<p>No pages indexed yet.</p>" as *u8
281 var ei: i64 = 0
282 while empt[ei] != (0 as u8) { body[bo + ei] = empt[ei]; ei = ei + 1 }
283 bo = bo + ei
284 }
285 let post: *u8 = "</figure></main>\n</body>\n</html>\n" as *u8
286 var qi: i64 = 0
287 while post[qi] != (0 as u8) { body[bo + qi] = post[qi]; qi = qi + 1 }
288 bo = bo + qi
289
290 // HTTP/1.1 200 + Content-Type + dynamic Content-Length + CRLFCRLF + body.
291 let off: *i64 = (sys_mmap(8)) as *i64
292 off[0] = 0
293 let header: *u8 = "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: " as *u8
294 let rc1: i64 = nx_wiki_route_append_z(out, off, out_cap, header)
295 if rc1 != NX_WIKI_ROUTE_OK { return rc1 }
296 // body length decimal
297 let scratch: *u8 = sys_mmap(16)
298 var n: i64 = bo
299 var k: i64 = 0
300 if n == 0 { scratch[0] = 0x30 as u8; k = 1 }
301 while n > 0 {
302 if k >= 16 { return 0 - NX_WIKI_ROUTE_BUF_OVERFLOW }
303 scratch[k] = (0x30 + (n - (n / 10) * 10)) as u8
304 n = n / 10
305 k = k + 1
306 }
307 var ri: i64 = k - 1
308 while ri >= 0 {
309 if off[0] >= out_cap { return 0 - NX_WIKI_ROUTE_BUF_OVERFLOW }
310 out[off[0]] = scratch[ri]
311 off[0] = off[0] + 1
312 ri = ri - 1
313 }
314 let crlf2: *u8 = "\r\n\r\n" as *u8
315 let rc2: i64 = nx_wiki_route_append(out, off, out_cap, crlf2, 4)
316 if rc2 != NX_WIKI_ROUTE_OK { return rc2 }
317 let rc3: i64 = nx_wiki_route_append(out, off, out_cap, body, bo)
318 if rc3 != NX_WIKI_ROUTE_OK { return rc3 }
319
320 out_n[0] = off[0]
321 return NX_WIKI_ROUTE_OK
322}
323
324// ===== Honest 501 for queued handlers =================================================
325//
326// Per NISHI_CODE_HYGIENE_STANDARD M6 anti-pretend-stub: the route
327// exists + returns a clear "handler queued; module name + plan"
328// response. NOT pretend functionality; explicit + auditable.
329
330func nx_wiki_route_serve_501(out: *u8, out_cap: i64, out_n: *i64,
331 handler_name: *u8, handler_name_n: i64) -> i64 {
332 out_n[0] = 0
333 let header: *u8 = "HTTP/1.1 501 Not Implemented\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: " as *u8
334 let body_pre: *u8 = "<!DOCTYPE html><html><body><h1>501 Not Implemented</h1><p>This route's handler is queued for a future commit per loop-discipline. Module name: <code>" as *u8
335 let body_pre_n: i64 = 175
336 let body_post: *u8 = "</code></p><p>The wiki ships handlers one focused commit at a time per the cardinal cluster. See <code>NISHI_PRODUCT_OPS_PLATFORM.md</code> for the full build plan.</p></body></html>" as *u8
337 let body_post_n: i64 = 196
338 let body_total: i64 = body_pre_n + handler_name_n + body_post_n
339
340 let off: *i64 = (sys_mmap(8)) as *i64
341 off[0] = 0
342 let rc1: i64 = nx_wiki_route_append_z(out, off, out_cap, header)
343 if rc1 != NX_WIKI_ROUTE_OK { return rc1 }
344
345 // Length decimal
346 let scratch: *u8 = sys_mmap(16)
347 var n: i64 = body_total
348 var k: i64 = 0
349 while n > 0 {
350 if k >= 16 { return 0 - NX_WIKI_ROUTE_BUF_OVERFLOW }
351 scratch[k] = (0x30 + (n - (n / 10) * 10)) as u8
352 n = n / 10
353 k = k + 1
354 }
355 var ri: i64 = k - 1
356 while ri >= 0 {
357 if off[0] >= out_cap { return 0 - NX_WIKI_ROUTE_BUF_OVERFLOW }
358 out[off[0]] = scratch[ri]
359 off[0] = off[0] + 1
360 ri = ri - 1
361 }
362 let crlf2: *u8 = "\r\n\r\n" as *u8
363 let rc2: i64 = nx_wiki_route_append(out, off, out_cap, crlf2, 4)
364 if rc2 != NX_WIKI_ROUTE_OK { return rc2 }
365
366 let rc3: i64 = nx_wiki_route_append(out, off, out_cap, body_pre, body_pre_n)
367 if rc3 != NX_WIKI_ROUTE_OK { return rc3 }
368 let rc4: i64 = nx_wiki_route_append(out, off, out_cap, handler_name, handler_name_n)
369 if rc4 != NX_WIKI_ROUTE_OK { return rc4 }
370 let rc5: i64 = nx_wiki_route_append(out, off, out_cap, body_post, body_post_n)
371 if rc5 != NX_WIKI_ROUTE_OK { return rc5 }
372
373 out_n[0] = off[0]
374 return NX_WIKI_ROUTE_OK
375}
376
377// ===== Plain 404 =================================================
378
379func nx_wiki_route_serve_404(out: *u8, out_cap: i64, out_n: *i64) -> i64 {
380 let resp: *u8 = "HTTP/1.1 404 Not Found\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: 56\r\n\r\n<!DOCTYPE html><html><body><h1>404</h1><p>Wiki: not found.</p></body></html>" as *u8
381 let resp_n: i64 = 167
382 out_n[0] = 0
383 let off: *i64 = (sys_mmap(8)) as *i64
384 off[0] = 0
385 let rc: i64 = nx_wiki_route_append(out, off, out_cap, resp, resp_n)
386 if rc != NX_WIKI_ROUTE_OK { return rc }
387 out_n[0] = off[0]
388 return NX_WIKI_ROUTE_OK
389}
390
391// ===== 405 Method Not Allowed =================================================
392
393func nx_wiki_route_serve_405(out: *u8, out_cap: i64, out_n: *i64) -> i64 {
394 let resp: *u8 = "HTTP/1.1 405 Method Not Allowed\r\nContent-Type: text/plain\r\nContent-Length: 19\r\n\r\nMethod not allowed\n" as *u8
395 let resp_n: i64 = 117
396 out_n[0] = 0
397 let off: *i64 = (sys_mmap(8)) as *i64
398 off[0] = 0
399 let rc: i64 = nx_wiki_route_append(out, off, out_cap, resp, resp_n)
400 if rc != NX_WIKI_ROUTE_OK { return rc }
401 out_n[0] = off[0]
402 return NX_WIKI_ROUTE_OK
403}
404
405// ===== Top-level dispatcher =================================================
406//
407// Caller (the daemon main loop) hands in the parsed HTTP request +
408// pre-allocated response buffer + auxiliary state (admin config,
409// random bytes, now_s). Dispatcher routes by method + path; calls
410// the appropriate handler; returns NX_WIKI_ROUTE_OK + final resp
411// byte count via out_resp_n.
412
413// SITES-LIVE 2026-05-27 SIGNATURE TRIM (15 args; native ELF parser
414// IR cap is 16 args/call). Dropped per V-MODAUTH-6 + simplification:
415// - random_16: cookie-session minting removed; no longer needed
416// - doc_names_{buf,cap,count}: wikilink resolution V+1 feature; not
417// load-bearing for any V1 route currently. Wikilinks render as
418// broken until V+1 wikilink-resolver wired through new arg.
419func nx_wiki_route_dispatch(http_req_buf: *u8, http_headers_end: i64,
420 url_path: *u8, url_path_n: i64,
421 method_kind: i64,
422 admin_cfg: *NxAuthContext,
423 now_s: i64,
424 search_flow: *NxSearchFlow,
425 search_idx: *NxInvIndex,
426 doc_store: *NxWikiDocStore,
427 archive_store: *NxArchiveStore,
428 artifact_store: *NxArtifactStore,
429 resp_buf: *u8, resp_cap: i64,
430 out_resp_n: *i64) -> i64 {
431 if (url_path as i64) == 0 { return 0 - NX_WIKI_ROUTE_BAD_INPUT }
432 if (resp_buf as i64) == 0 { return 0 - NX_WIKI_ROUTE_BAD_INPUT }
433 if (out_resp_n as i64) == 0 { return 0 - NX_WIKI_ROUTE_BAD_INPUT }
434 out_resp_n[0] = 0
435
436 // /health
437 if nx_wiki_route_eq(url_path, url_path_n, "/health" as *u8, 7) == 1 {
438 if method_kind != NX_WIKI_ROUTE_METHOD_GET {
439 return nx_wiki_route_serve_405(resp_buf, resp_cap, out_resp_n)
440 }
441 let scratch: *u8 = sys_mmap(64)
442 let rc: i64 = nx_http_emit_health(resp_buf, resp_cap, out_resp_n,
443 NXHL_STATUS_PASS, 0, 1, scratch)
444 if rc != 0 { return 0 - NX_WIKI_ROUTE_HANDLER_FAILED }
445 return NX_WIKI_ROUTE_OK
446 }
447
448 // /ready
449 if nx_wiki_route_eq(url_path, url_path_n, "/ready" as *u8, 6) == 1 {
450 if method_kind != NX_WIKI_ROUTE_METHOD_GET {
451 return nx_wiki_route_serve_405(resp_buf, resp_cap, out_resp_n)
452 }
453 let scratch: *u8 = sys_mmap(64)
454 let rc: i64 = nx_http_emit_health(resp_buf, resp_cap, out_resp_n,
455 NXHL_STATUS_PASS, 0, 1, scratch)
456 if rc != 0 { return 0 - NX_WIKI_ROUTE_HANDLER_FAILED }
457 return NX_WIKI_ROUTE_OK
458 }
459
460 // /wiki/admin (and subpaths)
461 if nx_wiki_route_starts_with(url_path, url_path_n, "/wiki/admin" as *u8, 11) == 1 {
462 // GET and POST both go to the wiring; wiring dispatches by method internally
463 if method_kind != NX_WIKI_ROUTE_METHOD_GET {
464 if method_kind != NX_WIKI_ROUTE_METHOD_POST {
465 return nx_wiki_route_serve_405(resp_buf, resp_cap, out_resp_n)
466 }
467 }
468 // /wiki/admin/dashboard -- LIVE (V1 product-ops dashboard).
469 // Same handler as /wiki/dashboard; admin route reserved for
470 // V2 dashboard variants with privileged stats (request counters,
471 // error rates, telemetry) that only authenticated admins see.
472 if nx_wiki_route_eq(url_path, url_path_n,
473 "/wiki/admin/dashboard" as *u8, 21) == 1 {
474 let rc: i64 = nx_wiki_dashboard_handle(doc_store,
475 resp_buf, resp_cap, out_resp_n)
476 if rc != NX_WDB_OK { return 0 - NX_WIKI_ROUTE_HANDLER_FAILED }
477 return NX_WIKI_ROUTE_OK
478 }
479 let rc: i64 = nx_wiki_admin_handle(admin_cfg,
480 http_req_buf, http_headers_end,
481 url_path, url_path_n,
482 method_kind,
483 now_s,
484 resp_buf, resp_cap, out_resp_n)
485 if rc != NX_WAW_OK { return 0 - NX_WIKI_ROUTE_HANDLER_FAILED }
486 return NX_WIKI_ROUTE_OK
487 }
488
489 // /wiki/search -- LIVE (wiki R3: BM25-RANKED + ARCHIVE-UNIFIED).
490 // Wired via wiki/nx_wiki_search_wiring.nx (THIN per-site adapter).
491 // Ranking upgraded from the onsite engine's presence/AND to real Okapi
492 // BM25 (nx_wiki_bm25_search -> nx_bm25), and search now covers BOTH the
493 // live doc store AND the no-link-rot archive (seg_store term index over
494 // knowledge/store/wikiarchive-) -- the wiki+archive unified toward the
495 // Library. A populated doc_store uses the BM25 path; if the store is not
496 // yet built, falls back to the onsite-engine V2 path (still well-formed
497 // HTTP 200, never a 501 for a live route).
498 if nx_wiki_route_starts_with(url_path, url_path_n, "/wiki/search" as *u8, 12) == 1 {
499 if method_kind != NX_WIKI_ROUTE_METHOD_GET {
500 return nx_wiki_route_serve_405(resp_buf, resp_cap, out_resp_n)
501 }
502 if (search_flow as i64) == 0 {
503 return nx_wiki_route_serve_501(resp_buf, resp_cap, out_resp_n,
504 "search_flow not initialized at startup" as *u8, 38)
505 }
506 var has_store: i64 = 0
507 if (doc_store as i64) != 0 { if doc_store.valid == 1 { if doc_store.doc_count > 0 { has_store = 1 } } }
508 if has_store == 1 {
509 // BM25-ranked + archive-unified (production prefix WAR_PREFIX).
510 let rcb: i64 = nx_wiki_search_handle_bm25(search_flow, doc_store, WAR_PREFIX,
511 url_path, url_path_n,
512 resp_buf, resp_cap, out_resp_n)
513 if rcb != NX_WSW_OK { return 0 - NX_WIKI_ROUTE_HANDLER_FAILED }
514 return NX_WIKI_ROUTE_OK
515 }
516 let rc: i64 = nx_wiki_search_handle_v2(search_flow, search_idx, doc_store,
517 url_path, url_path_n,
518 resp_buf, resp_cap, out_resp_n)
519 if rc != NX_WSW_OK { return 0 - NX_WIKI_ROUTE_HANDLER_FAILED }
520 return NX_WIKI_ROUTE_OK
521 }
522
523 // /wiki/pipeline + /wiki/pipeline/<stage> -- V2.0 P-3 (substrate self-audit)
524 if nx_wiki_route_starts_with(url_path, url_path_n, "/wiki/pipeline" as *u8, 14) == 1 {
525 if method_kind != NX_WIKI_ROUTE_METHOD_GET {
526 return nx_wiki_route_serve_405(resp_buf, resp_cap, out_resp_n)
527 }
528 if (artifact_store as i64) == 0 {
529 return nx_wiki_route_serve_501(resp_buf, resp_cap, out_resp_n,
530 "artifact_store not initialized at startup" as *u8, 41)
531 }
532 let rc: i64 = nx_pipeline_route(artifact_store, url_path, url_path_n,
533 resp_buf, resp_cap, out_resp_n)
534 if rc != NX_PROUTE_OK { return 0 - NX_WIKI_ROUTE_HANDLER_FAILED }
535 return NX_WIKI_ROUTE_OK
536 }
537
538 // /wiki/dashboard -- LIVE (V1 product-ops dashboard; public read).
539 if nx_wiki_route_eq(url_path, url_path_n, "/wiki/dashboard" as *u8, 15) == 1 {
540 if method_kind != NX_WIKI_ROUTE_METHOD_GET {
541 return nx_wiki_route_serve_405(resp_buf, resp_cap, out_resp_n)
542 }
543 let rc: i64 = nx_wiki_dashboard_handle(doc_store,
544 resp_buf, resp_cap, out_resp_n)
545 if rc != NX_WDB_OK { return 0 - NX_WIKI_ROUTE_HANDLER_FAILED }
546 return NX_WIKI_ROUTE_OK
547 }
548
549 // /wiki/archive + /wiki/wayback/<hash> -- LIVE (V1 archive router).
550 if nx_wiki_route_starts_with(url_path, url_path_n, "/wiki/archive" as *u8, 13) == 1 {
551 let rc: i64 = nx_wiki_archive_route(archive_store, url_path, url_path_n,
552 resp_buf, resp_cap, out_resp_n)
553 if rc == NX_ARCH_OK { return NX_WIKI_ROUTE_OK }
554 return nx_wiki_route_serve_404(resp_buf, resp_cap, out_resp_n)
555 }
556 if nx_wiki_route_starts_with(url_path, url_path_n, "/wiki/wayback/" as *u8, 14) == 1 {
557 let rc: i64 = nx_wiki_archive_route(archive_store, url_path, url_path_n,
558 resp_buf, resp_cap, out_resp_n)
559 if rc == NX_ARCH_OK { return NX_WIKI_ROUTE_OK }
560 return nx_wiki_route_serve_404(resp_buf, resp_cap, out_resp_n)
561 }
562
563 // /wiki/graph -- LIVE (wiki R4: static link-graph view). Exact-match BEFORE
564 // the generic /wiki/<doc-name> catch so "graph" is not rendered as a doc.
565 // Renders a JS-free <svg> of the page link graph over the doc store.
566 if nx_wiki_route_eq(url_path, url_path_n, "/wiki/graph" as *u8, 11) == 1 {
567 if method_kind != NX_WIKI_ROUTE_METHOD_GET {
568 return nx_wiki_route_serve_405(resp_buf, resp_cap, out_resp_n)
569 }
570 let rc: i64 = nx_wiki_route_serve_graph(doc_store, resp_buf, resp_cap, out_resp_n)
571 if rc != NX_WIKI_ROUTE_OK { return 0 - NX_WIKI_ROUTE_HANDLER_FAILED }
572 return NX_WIKI_ROUTE_OK
573 }
574
575 // /wiki/status -- LIVE distance-to-S-class scorecard (sovereign,
576 // Nishi-rendered from Nishi data; no shell/TSV). Exact-match BEFORE
577 // the generic /wiki/<doc-name> catch so "status" is not 404'd as a doc.
578 if nx_wiki_route_eq(url_path, url_path_n, "/wiki/status" as *u8, 12) == 1 {
579 if method_kind != NX_WIKI_ROUTE_METHOD_GET {
580 return nx_wiki_route_serve_405(resp_buf, resp_cap, out_resp_n)
581 }
582 let rc: i64 = nx_wiki_status_handle(resp_buf, resp_cap, out_resp_n)
583 if rc != NX_WIKI_ROUTE_OK { return 0 - NX_WIKI_ROUTE_HANDLER_FAILED }
584 return NX_WIKI_ROUTE_OK
585 }
586
587 // /wiki/queue -- the live partnership work queue (dynamic; reads the journal)
588 if nx_wiki_route_eq(url_path, url_path_n, "/wiki/queue" as *u8, 11) == 1 {
589 if method_kind != NX_WIKI_ROUTE_METHOD_GET {
590 return nx_wiki_route_serve_405(resp_buf, resp_cap, out_resp_n)
591 }
592 let rc: i64 = nx_wiki_queue_handle(resp_buf, resp_cap, out_resp_n)
593 if rc != NX_WIKI_ROUTE_OK { return 0 - NX_WIKI_ROUTE_HANDLER_FAILED }
594 return NX_WIKI_ROUTE_OK
595 }
596
597 // /wiki/components -- Gitea-style component index
598 if nx_wiki_route_eq(url_path, url_path_n, "/wiki/components" as *u8, 16) == 1 {
599 if method_kind != NX_WIKI_ROUTE_METHOD_GET {
600 return nx_wiki_route_serve_405(resp_buf, resp_cap, out_resp_n)
601 }
602 let rc: i64 = nx_wiki_components_handle(resp_buf, resp_cap, out_resp_n)
603 if rc != NX_WIKI_ROUTE_OK { return 0 - NX_WIKI_ROUTE_HANDLER_FAILED }
604 return NX_WIKI_ROUTE_OK
605 }
606
607 // /wiki/component/<slug> -- researched + benchmarked component article.
608 // Prefix "/wiki/component" (15) matches "/wiki/component/<slug>" via the
609 // segment-boundary rule; slug begins at offset 16.
610 if nx_wiki_route_starts_with(url_path, url_path_n, "/wiki/component" as *u8, 15) == 1 {
611 if method_kind != NX_WIKI_ROUTE_METHOD_GET {
612 return nx_wiki_route_serve_405(resp_buf, resp_cap, out_resp_n)
613 }
614 if url_path_n <= 16 {
615 let rci: i64 = nx_wiki_components_handle(resp_buf, resp_cap, out_resp_n)
616 if rci != NX_WIKI_ROUTE_OK { return 0 - NX_WIKI_ROUTE_HANDLER_FAILED }
617 return NX_WIKI_ROUTE_OK
618 }
619 let slug: *u8 = (url_path as i64 + 16) as *u8
620 let slug_n: i64 = url_path_n - 16
621 let rc: i64 = nx_wiki_article_handle(slug, slug_n, resp_buf, resp_cap, out_resp_n)
622 if rc != NX_WIKI_ROUTE_OK { return 0 - NX_WIKI_ROUTE_HANDLER_FAILED }
623 return NX_WIKI_ROUTE_OK
624 }
625
626 // /wiki/<slug>/edit -- R1 AUTH-GATED page edit (GET form + POST save).
627 // MUST precede the generic /wiki/<doc-name> catch (which is GET-only and
628 // would 405 the POST). Both verbs gate on the reused session validator
629 // (nx_modern_auth_validate_session) inside nx_wiki_edit_handle; an
630 // unauthenticated edit is refused with 401, persisting nothing.
631 if nx_wiki_route_starts_with(url_path, url_path_n, "/wiki" as *u8, 5) == 1 {
632 if nx_wiki_route_ends_with(url_path, url_path_n, "/edit" as *u8, 5) == 1 {
633 if method_kind != NX_WIKI_ROUTE_METHOD_GET {
634 if method_kind != NX_WIKI_ROUTE_METHOD_POST {
635 return nx_wiki_route_serve_405(resp_buf, resp_cap, out_resp_n)
636 }
637 }
638 let rc: i64 = nx_wiki_edit_handle(doc_store, admin_cfg,
639 http_req_buf, http_headers_end,
640 url_path, url_path_n,
641 method_kind, now_s,
642 resp_buf, resp_cap, out_resp_n)
643 if rc != NX_WEH_OK { return 0 - NX_WIKI_ROUTE_HANDLER_FAILED }
644 return NX_WIKI_ROUTE_OK
645 }
646 }
647
648 // /wiki/<doc-name> -- render a doc
649 if nx_wiki_route_starts_with(url_path, url_path_n, "/wiki" as *u8, 5) == 1 {
650 if method_kind != NX_WIKI_ROUTE_METHOD_GET {
651 return nx_wiki_route_serve_405(resp_buf, resp_cap, out_resp_n)
652 }
653 // /wiki/ index page (special-case)
654 if url_path_n == 6 {
655 // "/wiki/" exactly
656 return nx_wiki_route_serve_index(resp_buf, resp_cap, out_resp_n)
657 }
658 if url_path_n == 5 {
659 // "/wiki" exactly
660 return nx_wiki_route_serve_index(resp_buf, resp_cap, out_resp_n)
661 }
662 // /wiki/<doc-name> -- LIVE (V1 doc handler + V2.0 P-4 pipeline banner).
663 // Composes doc_store URL lookup + nx_wiki_doc_render write_header_v1
664 // (V1 page format conformant) + V2.0 pipeline-stage banner via
665 // hub/nx_pipeline_banner (silent no-op if URL not in artifact-store
666 // map) + markdown body emit + footer. 404 if doc not in store.
667 let rc: i64 = nx_wiki_doc_handle(doc_store, artifact_store,
668 url_path, url_path_n,
669 resp_buf, resp_cap, out_resp_n)
670 if rc == NX_WDH_OK { return NX_WIKI_ROUTE_OK }
671 if rc == 0 - NX_WDH_NOT_FOUND {
672 return nx_wiki_route_serve_404(resp_buf, resp_cap, out_resp_n)
673 }
674 return 0 - NX_WIKI_ROUTE_HANDLER_FAILED
675 }
676
677 // / -- redirect to /wiki/
678 if nx_wiki_route_eq(url_path, url_path_n, "/" as *u8, 1) == 1 {
679 let redirect: *u8 = "HTTP/1.1 302 Found\r\nLocation: /wiki/\r\nContent-Length: 0\r\n\r\n" as *u8
680 let off: *i64 = (sys_mmap(8)) as *i64
681 off[0] = 0
682 let rc: i64 = nx_wiki_route_append_z(resp_buf, off, resp_cap, redirect)
683 if rc != NX_WIKI_ROUTE_OK { return rc }
684 out_resp_n[0] = off[0]
685 return NX_WIKI_ROUTE_OK
686 }
687
688 // /* -> 404
689 return nx_wiki_route_serve_404(resp_buf, resp_cap, out_resp_n)
690}