code wiki / wiki / nx_wiki_https_daemon_mv.nx

nx_wiki_https_daemon_mv.nx source

↩ module page · 454 lines · 20045 B

1// nx_wiki_https_daemon_mv.nx -- V-HOST-2.5: multi-vhost wiki HTTPS daemon. 2// 3// One substrate process serves N sovereign wiki sites 4// (e.g., nishifamily.com + andelinwest.com + jasonewest.com). 5// 6// Composes: 7// wiki/nx_wiki_vhost_table -- per-host wiki state registry 8// wiki/nx_wiki_https_daemon -- single-vhost reference for per-conn pattern 9// All existing TLS / wiki / search / archive / artifact primitives 10// 11// Per Cardinal 19 (API stability): NEW file parallel to single-vhost 12// daemon. Existing nx_wiki_https_daemon unchanged. Operator picks which 13// to boot based on site count. 14// 15// V-HOST-2.5 SCOPE: 16// - Multi-SAN cert (one cert covering all hosted domains; SAN list 17// per V-HOST-4d nx_cert_gen) 18// - Per-Host-header dispatch via NxWikiVHostTable 19// - Per-site doc_store + archive_store + artifact_store 20// - Shared admin_cfg + search_flow (V+1: per-site) 21// - Fallback vhost on no match (or 404) 22// 23// V-HOST-2.5 NON-SCOPE (queued): 24// - SNI-aware cert selection during TLS handshake (V-HOST-2.5.5) 25// - Per-site cert (V-HOST-2.5.5) 26// - Per-site admin auth (V-HOST-2.5.7) 27// - Hot-reload vhost table (V-PROV-3) 28// 29// Status: V-HOST-2.5. 2026-05-27. 30 31import "nx_syscalls.nx" 32import "nx_csprng.nx" 33import "nx_http_server.nx" 34import "nx_http_io.nx" 35import "nx_http_header_find.nx" 36import "nx_tls13_server_session.nx" 37import "nx_tls13_server_session_run.nx" 38import "nx_tls13_server_session_app_data.nx" 39import "hub/nx_modern_auth_flow.nx" 40import "hub/nx_user_account_store.nx" 41import "hub/nx_search_handler_flow.nx" 42import "nx_kv_store.nx" 43import "nx_search_inverted.nx" 44import "wiki/nx_wiki_routes.nx" 45import "wiki/nx_wiki_index_builder.nx" 46import "wiki/nx_wiki_archive_router.nx" 47import "wiki/nx_artifact_store.nx" 48import "wiki/nx_wiki_vhost_table.nx" 49import "wiki/nx_wiki_content_loader.nx" 50 51// ===== Sealed verdict surface (codes 3830-3839) ================================================= 52const NX_MV_OK: i64 = 0 53const NX_MV_BAD_INPUT: i64 = 3830 54const NX_MV_CERT_LOAD_FAILED: i64 = 3831 55const NX_MV_BIND_FAILED: i64 = 3832 56const NX_MV_HS_FAILED: i64 = 3833 57const NX_MV_RECV_FAILED: i64 = 3834 58const NX_MV_PARSE_FAILED: i64 = 3835 59const NX_MV_NO_VHOST: i64 = 3836 60const NX_MV_DISPATCH_FAILED: i64 = 3837 61const NX_MV_SEND_FAILED: i64 = 3838 62 63// ===== Named constants (M7) ================================================= 64const NX_MV_LISTEN_PORT: i64 = 8443 65const NX_MV_BACKLOG: i64 = 32 66const NX_MV_REQ_REC_CAP: i64 = 16384 67const NX_MV_PLAIN_CAP: i64 = 16384 68const NX_MV_RESP_CAP: i64 = 1048576 69const NX_MV_OUT_REC_CAP: i64 = 1048768 70const NX_MV_REQUEST_BUDGET: i64 = 100000 71 72// Persistent, user-writable on Synology (/var is not writable without 73// root and may not persist). Matches the nx_dns deploy convention. 74// V-PROV will make this env/config-driven. 75const NX_MV_DEFAULT_CERT_PATH: *u8 = "/volume1/homes/elderwesto/nishihost/certs/multi_san_cert.der" as *u8 76const NX_MV_DEFAULT_PRIV_PATH: *u8 = "/volume1/homes/elderwesto/nishihost/certs/multi_san_priv.bin" as *u8 77 78// V-MODAUTH-2/4 auth state (same persistent volume convention as certs). 79const NX_MV_AUTH_DIR: *u8 = "/volume1/homes/elderwesto/nishihost/auth" as *u8 80const NX_MV_AUTH_KEYS_PATH: *u8 = "/volume1/homes/elderwesto/nishihost/auth/nishi_auth.keys.v1" as *u8 81const NX_MV_AUTH_STORE_PATH: *u8 = "/volume1/homes/elderwesto/nishihost/auth/nishi_auth_accounts.v1.log" as *u8 82 83const NX_MV_REALM: *u8 = "Nishi Wiki" as *u8 84const NX_MV_REALM_N: i64 = 10 85const NX_MV_ADMIN_USER: *u8 = "elderwesto" as *u8 86const NX_MV_ADMIN_USER_N: i64 = 10 87 88// Per-site content kinds (per NISHI_WIKI_CONTENT_INDEX section 4 89// per-site visibility curation). Passed to nx_mv_setup_site to decide 90// which loader to invoke before the index is finalized. 91const NX_MV_CONTENT_MINIMAL: i64 = 0 // landing only (placeholder sites) 92const NX_MV_CONTENT_FULL: i64 = 1 // every charter (nishifamily.com) 93const NX_MV_CONTENT_PORTFOLIO: i64 = 2 // curated 6-charter subset (jasonewest.com) 94 95// ===== 404 response (when Host header has no matching vhost AND no fallback) ================================================= 96 97func _nx_mv_emit_404_response(out: *u8, out_n: *i64, cap: i64) -> i64 { 98 let body: *u8 = "<!DOCTYPE html>\n<html><head><title>404 No Vhost Match</title></head><body><h1>404 No Vhost Match</h1><p>The Host header did not match any configured vhost on this substrate.</p></body></html>\n" as *u8 99 let body_n: i64 = 200 100 let hdr: *u8 = "HTTP/1.1 404 Not Found\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: 200\r\n\r\n" as *u8 101 let hdr_n: i64 = 89 102 if hdr_n + body_n > cap { return 0 - NX_MV_SEND_FAILED } 103 var i: i64 = 0 104 while i < hdr_n { out[i] = hdr[i]; i = i + 1 } 105 var j: i64 = 0 106 while j < body_n { out[hdr_n + j] = body[j]; j = j + 1 } 107 out_n[0] = hdr_n + body_n 108 return NX_MV_OK 109} 110 111// ===== Per-conn handle ================================================= 112// 113// V-HOST-2.5 flow: 114// 1. TLS handshake (shared multi-SAN cert; client accepted any 115// vhost in cert SAN list during handshake) 116// 2. Decrypt request 117// 3. Parse HTTP: extract path + method + Host header 118// 4. Lookup vhost in NxWikiVHostTable by Host header 119// 5. If match: dispatch to that vhost's wiki state via nx_wiki_route_dispatch 120// 6. If no match + no fallback: 404 + send 121// 7. Encrypt response + send 122 123func nx_mv_handle_one( 124 client_fd: i64, 125 cert_der: *u8, cert_der_len: i64, 126 ed25519_priv: *u8, 127 server_random: *u8, server_x25519_priv: *u8, 128 vhost_table: *NxWikiVHostTable, 129 doc_names: *u8, 130 random_16: *u8, now_s: i64 131) -> i64 { 132 // 1. TLS handshake (Ed25519 sig per V-HOST-2+1) 133 let s_raw: i64 = nx_tls13_server_session_run_ed25519( 134 client_fd, 135 server_random, server_x25519_priv, 136 cert_der, cert_der_len, 137 ed25519_priv) 138 if s_raw <= 0 { return 0 - NX_MV_HS_FAILED } 139 let s: *Tls13ServerSession = s_raw as *Tls13ServerSession 140 141 // 2. Read + decrypt request 142 let rec_buf: *u8 = sys_mmap(NX_MV_REQ_REC_CAP) 143 let rec_n: i64 = sys_read(client_fd, rec_buf, NX_MV_REQ_REC_CAP) 144 if rec_n <= 0 { return 0 - NX_MV_RECV_FAILED } 145 let plain: *u8 = sys_mmap(NX_MV_PLAIN_CAP) 146 let plain_n: i64 = nx_tls13_server_session_app_recv(s, rec_buf, rec_n, 147 plain, NX_MV_PLAIN_CAP) 148 if plain_n <= 0 { return 0 - NX_MV_RECV_FAILED } 149 150 // 3. Parse HTTP 151 let om: *i64 = (sys_mmap(8)) as *i64 152 let opo: *i64 = (sys_mmap(8)) as *i64 153 let opl: *i64 = (sys_mmap(8)) as *i64 154 let headers_end: *i64 = (sys_mmap(8)) as *i64 155 let parse_rc: i64 = nx_http_parse_request(plain, plain_n, 156 om, opo, opl, headers_end) 157 if parse_rc != NXS_OK { return 0 - NX_MV_PARSE_FAILED } 158 let path_ptr: *u8 = (plain as i64 + opo[0]) as *u8 159 let path_n: i64 = opl[0] 160 161 // 4. Extract Host header value 162 let host_off: *i64 = (sys_mmap(8)) as *i64 163 let host_len: *i64 = (sys_mmap(8)) as *i64 164 host_off[0] = 0 165 host_len[0] = 0 166 nx_http_header_find(plain, headers_end[0], "Host" as *u8, 4, host_off, host_len) 167 168 // 5. Lookup vhost 169 let host_ptr: *u8 = (plain as i64 + host_off[0]) as *u8 170 let vhost_idx: i64 = nx_wiki_vhost_lookup_by_host(vhost_table, host_ptr, host_len[0]) 171 172 let resp_buf: *u8 = sys_mmap(NX_MV_RESP_CAP) 173 let resp_n: *i64 = (sys_mmap(8)) as *i64 174 resp_n[0] = 0 175 176 if vhost_idx < 0 { 177 // 6. No match -> 404 178 let rc_404: i64 = _nx_mv_emit_404_response(resp_buf, resp_n, NX_MV_RESP_CAP) 179 if rc_404 != NX_MV_OK { return rc_404 } 180 } 181 if vhost_idx >= 0 { 182 let v: *NxWikiVHost = nx_wiki_vhost_at(vhost_table, vhost_idx) 183 if (v as i64) == 0 { return 0 - NX_MV_NO_VHOST } 184 185 // 5b. Dispatch to this vhost's wiki state 186 let disp_rc: i64 = nx_wiki_route_dispatch( 187 plain, headers_end[0], 188 path_ptr, path_n, 189 om[0], 190 v.admin_cfg, 191 now_s, 192 v.search_flow, v.search_idx, v.doc_store, v.archive_store, v.artifact_store, 193 resp_buf, NX_MV_RESP_CAP, 194 resp_n) 195 if disp_rc != NX_WIKI_ROUTE_OK { return 0 - NX_MV_DISPATCH_FAILED } 196 } 197 198 // 7. Encrypt + send 199 let out_rec: *u8 = sys_mmap(NX_MV_OUT_REC_CAP) 200 let out_n: i64 = nx_tls13_server_session_app_send( 201 s, resp_buf, resp_n[0], out_rec, NX_MV_OUT_REC_CAP) 202 if out_n <= 0 { return 0 - NX_MV_SEND_FAILED } 203 if sys_write(client_fd, out_rec, out_n) != out_n { 204 return 0 - NX_MV_SEND_FAILED 205 } 206 207 return NX_MV_OK 208} 209 210// ===== Per-site state allocator (helper; called N times at startup) ================================================= 211// 212// Allocates fresh per-site state structs + seeds with a minimal landing 213// page. Operator extends with real content via NxWikiDocStore.add at startup. 214 215func nx_mv_setup_site( 216 hostname: *u8, hostname_n: i64, 217 landing_summary: *u8, landing_summary_n: i64, 218 content_kind: i64, 219 admin_cfg: *NxAuthContext, 220 out_search_flow: *i64, 221 out_search_idx: *i64, 222 out_doc_store: *i64, 223 out_archive_store: *i64, 224 out_artifact_store: *i64 225) -> i64 { 226 let search_flow: *NxSearchFlow = (sys_mmap(256)) as *NxSearchFlow 227 nx_search_flow_init(search_flow, 100000, 100, 10000) 228 out_search_flow[0] = search_flow as i64 229 230 let doc_store: *NxWikiDocStore = (sys_mmap(256)) as *NxWikiDocStore 231 nx_wiki_doc_store_init(doc_store, 100, 65536, 65536, 4194304) 232 out_doc_store[0] = doc_store as i64 233 234 let builder: *NxWikiIndexBuilder = (sys_mmap(64)) as *NxWikiIndexBuilder 235 nx_wiki_index_builder_init(builder, doc_store, 100) 236 237 // Landing page (every site gets one). 238 nx_wiki_index_builder_add(builder, 239 hostname, hostname_n, 240 "/" as *u8, 1, 241 landing_summary, landing_summary_n) 242 243 // Per-site content load BEFORE finalize (per 244 // NISHI_WIKI_CONTENT_INDEX section 4 per-site visibility curation). 245 if content_kind == NX_MV_CONTENT_FULL { 246 nx_wcl_load_full_manifest(builder) 247 } 248 if content_kind == NX_MV_CONTENT_PORTFOLIO { 249 nx_wcl_load_jasonewest_portfolio(builder) 250 } 251 // NX_MV_CONTENT_MINIMAL: no extra loads (landing only) 252 253 nx_wiki_index_builder_finalize(builder) 254 out_search_idx[0] = (nx_wiki_index_builder_get_index(builder)) as i64 255 256 let archive_store: *NxArchiveStore = (sys_mmap(256)) as *NxArchiveStore 257 nx_archive_store_init(archive_store, 100) 258 out_archive_store[0] = archive_store as i64 259 260 let artifact_store: *NxArtifactStore = (sys_mmap(512)) as *NxArtifactStore 261 nx_artifact_store_init(artifact_store, 100) 262 out_artifact_store[0] = artifact_store as i64 263 264 return NX_MV_OK 265} 266 267// ===== Main daemon ================================================= 268 269func main() -> i64 { 270 // 1. Load multi-SAN cert + priv 271 let cert_len_box: *i64 = (sys_mmap(8)) as *i64 272 cert_len_box[0] = 0 273 let cert_der: *u8 = sys_read_file(NX_MV_DEFAULT_CERT_PATH, cert_len_box) 274 if (cert_der as i64) == 0 { return NX_MV_CERT_LOAD_FAILED } 275 let cert_der_len: i64 = cert_len_box[0] 276 277 let priv_len_box: *i64 = (sys_mmap(8)) as *i64 278 priv_len_box[0] = 0 279 let ed25519_priv: *u8 = sys_read_file(NX_MV_DEFAULT_PRIV_PATH, priv_len_box) 280 if (ed25519_priv as i64) == 0 { return NX_MV_CERT_LOAD_FAILED } 281 if priv_len_box[0] != 32 { return NX_MV_CERT_LOAD_FAILED } 282 283 // 2. server_random + x25519 priv 284 let server_random: *u8 = sys_mmap(32) 285 let server_x25519_priv: *u8 = sys_mmap(32) 286 nx_csprng_fill(server_random, 32) 287 nx_csprng_fill(server_x25519_priv, 32) 288 289 // 3. Shared admin config (V1; per-site V+1) 290 // V2 (2026-06-10): OPAQUE ARMED. Persistent V-MODAUTH-4 key bundle 291 // (oprf_seed + Ed25519 session keys; created 0600 on first boot, 292 // idempotent after) + durable account store, on the same persistent 293 // user-writable volume as the certs (nx_dns deploy convention). 294 // If the bundle cannot be created/read (e.g., dev box without the 295 // volume), ctx stays UNARMED: session validation still works with 296 // ephemeral keys and POST /wiki/admin/login serves the honest 503. 297 sys_mkdir(NX_MV_AUTH_DIR, 0x1C0) // 0700, best-effort (exists = fine) 298 let oprf_seed: *u8 = sys_mmap(32) 299 let ake_priv_unused: *u8 = sys_mmap(32) 300 let ake_pub_unused: *u8 = sys_mmap(33) 301 let server_ed25519_priv: *u8 = sys_mmap(NX_MAUTH_ED25519_PRIV_BYTES) 302 let server_ed25519_pub: *u8 = sys_mmap(NX_MAUTH_ED25519_PUB_BYTES) 303 var auth_armed: i64 = 1 304 if nx_uas_server_keys_load_or_init(NX_MV_AUTH_KEYS_PATH, oprf_seed, 305 ake_priv_unused, ake_pub_unused, 306 server_ed25519_priv, server_ed25519_pub) != NX_UAS_OK { 307 auth_armed = 0 308 nx_csprng_fill(server_ed25519_priv, NX_MAUTH_ED25519_PRIV_BYTES) 309 nx_csprng_fill(server_ed25519_pub, NX_MAUTH_ED25519_PUB_BYTES) 310 } 311 var auth_store_i64: i64 = 0 312 var oprf_seed_arg: *u8 = (0 as i64) as *u8 313 if auth_armed == 1 { 314 auth_store_i64 = NX_MV_AUTH_STORE_PATH as i64 315 oprf_seed_arg = oprf_seed 316 } 317 318 let admin_cfg: *NxAuthContext = (sys_mmap(256)) as *NxAuthContext 319 nx_auth_context_init(admin_cfg, 320 NX_MV_REALM, NX_MV_REALM_N, 321 NX_MV_REALM, NX_MV_REALM_N, 322 auth_store_i64, // account_store path (V-MODAUTH-4) 323 oprf_seed_arg, // oprf_seed (V-MODAUTH-2 OPAQUE) 324 server_ed25519_priv, 325 server_ed25519_pub, 326 NX_MAUTH_DEFAULT_SESSION_TTL_S, 327 32768, // argon2id m = 32 MiB (vault KDF v2 measured budget) 328 NX_MAUTH_DEFAULT_ARGON2ID_T_COST, 329 1, // argon2id p = 1 (substrate single-lane RFC 9106) 330 NX_MAUTH_DEFAULT_RATE_LIMIT_PER_MIN, 331 1) // allow_recovery (M3 NOT_IMPLEMENTED yet) 332 333 // 4. Build vhost table 334 let vhost_table: *NxWikiVHostTable = (sys_mmap(64)) as *NxWikiVHostTable 335 nx_wiki_vhost_table_init(vhost_table) 336 337 // Allocate per-site state for 3 demo sites: 338 // nishifamily.com 339 // andelinwest.com 340 // jasonewest.com 341 let sf1: *i64 = (sys_mmap(8)) as *i64 342 let si1: *i64 = (sys_mmap(8)) as *i64 343 let ds1: *i64 = (sys_mmap(8)) as *i64 344 let as1: *i64 = (sys_mmap(8)) as *i64 345 let ar1: *i64 = (sys_mmap(8)) as *i64 346 // nishifamily.com -- family hub + Nishi sovereign substrate wiki 347 // Loads FULL charter manifest (every charter from 348 // NISHI_WIKI_CONTENT_INDEX section 1 becomes a /wiki/<slug> page). 349 nx_mv_setup_site( 350 "nishifamily.com" as *u8, 15, 351 "Nishi Family hub. Hosts the Nishi sovereign ecosystem wiki + dashboards. See /wiki/ for substrate documentation, /wiki/pipeline for living artifact status, /wiki/dashboard for product-ops view." as *u8, 218, 352 NX_MV_CONTENT_FULL, 353 admin_cfg, sf1, si1, ds1, as1, ar1) 354 nx_wiki_vhost_add(vhost_table, 355 "nishifamily.com" as *u8, 15, 356 sf1[0] as *NxSearchFlow, si1[0] as *NxInvIndex, 357 ds1[0] as *NxWikiDocStore, as1[0] as *NxArchiveStore, 358 ar1[0] as *NxArtifactStore, admin_cfg) 359 360 let sf2: *i64 = (sys_mmap(8)) as *i64 361 let si2: *i64 = (sys_mmap(8)) as *i64 362 let ds2: *i64 = (sys_mmap(8)) as *i64 363 let as2: *i64 = (sys_mmap(8)) as *i64 364 let ar2: *i64 = (sys_mmap(8)) as *i64 365 // andelinwest.com -- LAWYER PRACTICE site (compliance-critical; see NISHI_SITES_INVENTORY.md ยง2) 366 // PLACEHOLDER landing only -- DO NOT DEPLOY PUBLIC until per-state bar advertising 367 // rules audit + ADA scan + operator-attorney content review complete. 368 // andelinwest.com -- LAWYER PRACTICE site: NO substrate-internal 369 // charters surfaced by default (per NISHI_WIKI_CONTENT_INDEX 370 // section 4; operator-attorney decides if a curated subset like 371 // privacy + consent + ADA charters is appropriate via V+1 config). 372 nx_mv_setup_site( 373 "andelinwest.com" as *u8, 15, 374 "Andelin West Law. PLACEHOLDER -- this site requires legal-firm UX + per-state bar-association advertising-rule compliance + ADA accessibility audit + operator-attorney content review before public deployment. See NISHI_SITES_INVENTORY.md section 2 for compliance checklist." as *u8, 269, 375 NX_MV_CONTENT_MINIMAL, 376 admin_cfg, sf2, si2, ds2, as2, ar2) 377 nx_wiki_vhost_add(vhost_table, 378 "andelinwest.com" as *u8, 15, 379 sf2[0] as *NxSearchFlow, si2[0] as *NxInvIndex, 380 ds2[0] as *NxWikiDocStore, as2[0] as *NxArchiveStore, 381 ar2[0] as *NxArtifactStore, admin_cfg) 382 383 let sf3: *i64 = (sys_mmap(8)) as *i64 384 let si3: *i64 = (sys_mmap(8)) as *i64 385 let ds3: *i64 = (sys_mmap(8)) as *i64 386 let as3: *i64 = (sys_mmap(8)) as *i64 387 let ar3: *i64 = (sys_mmap(8)) as *i64 388 // jasonewest.com -- operator's PORTFOLIO site (job + consulting business-driver) 389 // Loads CURATED PORTFOLIO subset (6 charters showing operator's 390 // substrate work as portfolio evidence per NISHI_WIKI_CONTENT_INDEX 391 // section 4: personal-data-sovereignty + modern-auth + sovereign- 392 // hosting + superiority-bar + bench-winner-standard + silicon-z2a). 393 nx_mv_setup_site( 394 "jasonewest.com" as *u8, 14, 395 "Jason E. West. Portfolio for consulting + employment opportunities. Hosting work on the Nishi sovereign substrate ecosystem -- substrate-bits-up engineering across compilers, TLS, search, distributed systems, web platform. See projects + case studies + contact for engagement inquiries." as *u8, 287, 396 NX_MV_CONTENT_PORTFOLIO, 397 admin_cfg, sf3, si3, ds3, as3, ar3) 398 nx_wiki_vhost_add(vhost_table, 399 "jasonewest.com" as *u8, 14, 400 sf3[0] as *NxSearchFlow, si3[0] as *NxInvIndex, 401 ds3[0] as *NxWikiDocStore, as3[0] as *NxArchiveStore, 402 ar3[0] as *NxArtifactStore, admin_cfg) 403 404 // Fallback: first vhost (nishifamily.com); operator can change to NX_WVT_FALLBACK_NONE 405 // to enforce strict Host header matching (404 on mismatch). 406 nx_wiki_vhost_set_fallback(vhost_table, 0) 407 408 // 5. Shared per-conn helpers 409 let doc_names: *u8 = sys_mmap(4096) 410 let random_16: *u8 = sys_mmap(16) 411 412 // 6. Bind listen 413 let addr_buf: *u8 = sys_mmap(16) 414 nx_http_server_addr_any(addr_buf, NX_MV_LISTEN_PORT) 415 let lv: *i64 = (sys_mmap(8)) as *i64 416 let lfd: i64 = nx_http_server_listen(addr_buf, NX_MV_BACKLOG, lv) 417 if lfd < 0 { return NX_MV_BIND_FAILED } 418 419 let banner: *u8 = "nx_wiki_https_daemon_mv: 3 vhosts (nishifamily/andelinwest/jasonewest); 0.0.0.0:8443\n" as *u8 420 sys_write(1, banner, 84) 421 422 // 7. Accept loop 423 var n_served: i64 = 0 424 var n_errors: i64 = 0 425 while n_served < NX_MV_REQUEST_BUDGET { 426 let av: *i64 = (sys_mmap(8)) as *i64 427 let cfd: i64 = nx_http_server_accept_one(lfd, av) 428 if cfd < 0 { 429 n_errors = n_errors + 1 430 n_served = n_served + 1 431 } 432 if cfd >= 0 { 433 let now_ms: i64 = sys_now_ms() 434 let now_s: i64 = now_ms / 1000 435 nx_csprng_fill(random_16, 16) 436 437 let rc: i64 = nx_mv_handle_one( 438 cfd, 439 cert_der, cert_der_len, 440 ed25519_priv, 441 server_random, server_x25519_priv, 442 vhost_table, 443 doc_names, random_16, now_s) 444 445 if rc != NX_MV_OK { n_errors = n_errors + 1 } 446 447 sys_close(cfd) 448 n_served = n_served + 1 449 } 450 } 451 452 sys_close(lfd) 453 return NX_MV_OK 454}