code wiki / wiki / nx_wiki_https_daemon.nx

nx_wiki_https_daemon.nx source

↩ module page · 288 lines · 12217 B

1// nx_wiki_https_daemon.nx -- V-HOST-2: sovereign HTTPS wiki daemon. 2// 3// Composes existing TLS 1.3 handshake (sovereign Ed25519 OR ECDSA path) + 4// wiki HTTP router into a single daemon serving sovereign HTTPS for 5// the wiki at andelinwest.com / nishifamily.com / etc. 6// 7// FINAL substrate piece per NISHI_ANDELINWEST_DEPLOY_PLAYBOOK. After 8// this ships, the deploy is operator-side ops only: 9// 1. nx_cert_gen (V-HOST-4 SHIPPED) -> Ed25519 cert 10// 2. Porkbun A records (operator panel; 5 min) 11// 3. Router port-forward 443 -> substrate host 12// 4. Boot this daemon 13// 5. https://andelinwest.com/ LIVE 14// 15// COMPOSES (per "avoid duplicate primitives"): 16// nx_http_server (TCP listen + accept; existing primitive) 17// nx_tls13_server_session_run_ed25519 (V-HOST-2+1-2) 18// OR nx_tls13_server_session_run (existing ECDSA path) 19// nx_tls13_server_session_app_recv (decrypt request bytes) 20// nx_tls13_server_session_app_send (encrypt response bytes) 21// wiki/nx_wiki_routes.nx_wiki_route_dispatch (existing wiki router) 22// nx_csprng (server_random + x25519 priv) 23// nx_http_header_find (req parse; existing helper) 24// 25// V-HOST-2 SCOPE: 26// - Single-vhost daemon (one cert + one wiki state); per-vhost 27// dispatch is V-HOST-2.5 refactor when N > 1 sites need this daemon 28// - Defaults to Ed25519 cert path (V-HOST-2+1 modern) 29// - Reads cert + priv from operator-config paths 30// - Accept loop: per connection runs TLS handshake + dispatches one 31// HTTP request to wiki router + sends encrypted response 32// - Per Cardinal 14 graceful degradation: per-conn errors don't 33// kill the daemon 34// 35// V-HOST-2 NON-SCOPE (queued for V-HOST-2.5 + V-HOST-5): 36// - Multi-vhost dispatch (one daemon serving N domains) 37// - Keep-alive (single request per TLS session V1) 38// - Session resumption (PSK / 0-RTT) 39// - Unified per-vhost handler dispatch (currently wiki-only) 40// 41// Status: V-HOST-2. 2026-05-27. 42 43import "nx_syscalls.nx" 44import "nx_csprng.nx" 45import "nx_http_server.nx" 46import "nx_http_io.nx" 47import "nx_tls13_server_session.nx" 48import "nx_tls13_server_session_run.nx" 49import "nx_tls13_server_session_app_data.nx" 50// Wiki state + router (composed at startup; mirrors nx_wiki_main step 4-6f) 51import "hub/nx_admin_login_flow.nx" 52import "hub/nx_search_handler_flow.nx" 53import "nx_kv_store.nx" 54import "nx_hash_facade.nx" 55import "nx_search_inverted.nx" 56import "wiki/nx_wiki_routes.nx" 57import "wiki/nx_wiki_index_builder.nx" 58import "wiki/nx_wiki_archive_router.nx" 59import "wiki/nx_artifact_store.nx" 60 61// ===== Sealed verdict surface (codes 3800-3819) ================================================= 62const NX_WHD_OK: i64 = 0 63const NX_WHD_BAD_INPUT: i64 = 3800 64const NX_WHD_CERT_LOAD_FAILED: i64 = 3801 65const NX_WHD_BIND_FAILED: i64 = 3802 66const NX_WHD_HS_FAILED: i64 = 3803 67const NX_WHD_RECV_FAILED: i64 = 3804 68const NX_WHD_PARSE_FAILED: i64 = 3805 69const NX_WHD_DISPATCH_FAILED: i64 = 3806 70const NX_WHD_SEND_FAILED: i64 = 3807 71 72// ===== Named constants (M7) ================================================= 73const NX_WHD_LISTEN_PORT: i64 = 8443 // V1 demo; production binds 443 via cap_net_bind 74const NX_WHD_BACKLOG: i64 = 32 75const NX_WHD_REQ_REC_CAP: i64 = 16384 // 16 KB encrypted request 76const NX_WHD_PLAIN_CAP: i64 = 16384 // 16 KB decrypted request 77const NX_WHD_RESP_CAP: i64 = 1048576 // 1 MB response body 78const NX_WHD_OUT_REC_CAP: i64 = 1048768 // resp + TLS overhead 79const NX_WHD_REQUEST_BUDGET: i64 = 100000 // hard cap on total requests served per process 80 81// V-HOST-2 defaults; operator overrides via config (V-PROV-3 HOT-RELOAD) 82const NX_WHD_DEFAULT_CERT_PATH: *u8 = "/tmp/nx_cert_gen_demo_ed25519_cert.der" as *u8 83const NX_WHD_DEFAULT_PRIV_PATH: *u8 = "/tmp/nx_cert_gen_demo_ed25519_priv.bin" as *u8 84 85// Admin credentials (operator-private; V-PROV-7 sovereign hash CLI replaces this constant pattern) 86const NX_WHD_REALM: *u8 = "Nishi Wiki" as *u8 87const NX_WHD_REALM_N: i64 = 10 88const NX_WHD_ADMIN_USER: *u8 = "elderwesto" as *u8 89const NX_WHD_ADMIN_USER_N: i64 = 10 90 91// ===== Per-conn handle: TLS handshake + one HTTP req/resp ================================================= 92 93func nx_whd_handle_one( 94 client_fd: i64, 95 cert_der: *u8, cert_der_len: i64, 96 ed25519_priv: *u8, 97 server_random: *u8, server_x25519_priv: *u8, 98 admin_cfg: *NxAdminLoginConfig, 99 doc_names: *u8, doc_names_count: i64, 100 random_16: *u8, now_s: i64, 101 search_flow: *NxSearchFlow, search_idx: *NxInvIndex, 102 doc_store: *NxWikiDocStore, 103 archive_store: *NxArchiveStore, 104 artifact_store: *NxArtifactStore 105) -> i64 { 106 // 1. Run TLS 1.3 handshake (Ed25519 sig per V-HOST-2+1) 107 let s_raw: i64 = nx_tls13_server_session_run_ed25519( 108 client_fd, 109 server_random, server_x25519_priv, 110 cert_der, cert_der_len, 111 ed25519_priv) 112 if s_raw <= 0 { return 0 - NX_WHD_HS_FAILED } 113 let s: *Tls13ServerSession = s_raw as *Tls13ServerSession 114 115 // 2. Read encrypted request record 116 let rec_buf: *u8 = sys_mmap(NX_WHD_REQ_REC_CAP) 117 let rec_n: i64 = sys_read(client_fd, rec_buf, NX_WHD_REQ_REC_CAP) 118 if rec_n <= 0 { return 0 - NX_WHD_RECV_FAILED } 119 120 // 3. Decrypt to plaintext HTTP 121 let plain: *u8 = sys_mmap(NX_WHD_PLAIN_CAP) 122 let plain_n: i64 = nx_tls13_server_session_app_recv(s, rec_buf, rec_n, 123 plain, NX_WHD_PLAIN_CAP) 124 if plain_n <= 0 { return 0 - NX_WHD_RECV_FAILED } 125 126 // 4. Parse HTTP request (URL + method) 127 let om: *i64 = (sys_mmap(8)) as *i64 128 let opo: *i64 = (sys_mmap(8)) as *i64 129 let opl: *i64 = (sys_mmap(8)) as *i64 130 let headers_end: *i64 = (sys_mmap(8)) as *i64 131 let parse_rc: i64 = nx_http_parse_request(plain, plain_n, 132 om, opo, opl, headers_end) 133 if parse_rc != NXS_OK { return 0 - NX_WHD_PARSE_FAILED } 134 let path_ptr: *u8 = (plain as i64 + opo[0]) as *u8 135 let path_n: i64 = opl[0] 136 137 // 5. Dispatch to wiki router (composes ALL wiki routes; per 138 // project-wiki-v1-complete-2026-05-27 zero 501s remaining) 139 let resp_buf: *u8 = sys_mmap(NX_WHD_RESP_CAP) 140 let resp_n: *i64 = (sys_mmap(8)) as *i64 141 resp_n[0] = 0 142 let disp_rc: i64 = nx_wiki_route_dispatch( 143 plain, headers_end[0], 144 path_ptr, path_n, 145 om[0], 146 admin_cfg, 147 now_s, 148 search_flow, search_idx, doc_store, archive_store, artifact_store, 149 resp_buf, NX_WHD_RESP_CAP, 150 resp_n) 151 if disp_rc != NX_WIKI_ROUTE_OK { return 0 - NX_WHD_DISPATCH_FAILED } 152 153 // 6. Encrypt response as TLS application_data record 154 let out_rec: *u8 = sys_mmap(NX_WHD_OUT_REC_CAP) 155 let out_n: i64 = nx_tls13_server_session_app_send( 156 s, resp_buf, resp_n[0], out_rec, NX_WHD_OUT_REC_CAP) 157 if out_n <= 0 { return 0 - NX_WHD_SEND_FAILED } 158 159 // 7. Write encrypted response to socket 160 if sys_write(client_fd, out_rec, out_n) != out_n { 161 return 0 - NX_WHD_SEND_FAILED 162 } 163 164 return NX_WHD_OK 165} 166 167// ===== Main daemon: setup wiki state + accept loop ================================================= 168 169func main() -> i64 { 170 // ----- 1. Load cert + priv ----- 171 let cert_len_box: *i64 = (sys_mmap(8)) as *i64 172 cert_len_box[0] = 0 173 let cert_der: *u8 = sys_read_file(NX_WHD_DEFAULT_CERT_PATH, cert_len_box) 174 if (cert_der as i64) == 0 { return NX_WHD_CERT_LOAD_FAILED } 175 let cert_der_len: i64 = cert_len_box[0] 176 177 let priv_len_box: *i64 = (sys_mmap(8)) as *i64 178 priv_len_box[0] = 0 179 let ed25519_priv: *u8 = sys_read_file(NX_WHD_DEFAULT_PRIV_PATH, priv_len_box) 180 if (ed25519_priv as i64) == 0 { return NX_WHD_CERT_LOAD_FAILED } 181 if priv_len_box[0] != 32 { return NX_WHD_CERT_LOAD_FAILED } 182 183 // ----- 2. Generate server_random + x25519 priv (per-process; rotated V+1) ----- 184 let server_random: *u8 = sys_mmap(32) 185 let server_x25519_priv: *u8 = sys_mmap(32) 186 nx_csprng_fill(server_random, 32) 187 nx_csprng_fill(server_x25519_priv, 32) 188 189 // ----- 3. Wiki state setup (mirrors nx_wiki_main step 4-6f) ----- 190 // Operator-config V-PROV-3 replaces hardcoded paths. 191 let admin_hash_path: *u8 = "/var/nishi-secrets/andelinwest_admin.hash" as *u8 192 let admin_hash_len_box: *i64 = (sys_mmap(8)) as *i64 193 admin_hash_len_box[0] = 0 194 let admin_hash: *u8 = sys_read_file(admin_hash_path, admin_hash_len_box) 195 if (admin_hash as i64) == 0 { 196 // No admin hash file -> daemon runs in PUBLIC-READ-ONLY mode 197 // (operator can wire admin auth via V-PROV-3 once nx_basic_auth 198 // hash CLI ships per V-PROV-7) 199 } 200 201 let sessions: *NxKvStore = (sys_mmap(64)) as *NxKvStore 202 nx_kv_store_init(sessions, 100, 1024) 203 204 let admin_cfg: *NxAdminLoginConfig = (sys_mmap(96)) as *NxAdminLoginConfig 205 nx_admin_login_config_init(admin_cfg, 206 NX_WHD_REALM, NX_WHD_REALM_N, 207 NX_WHD_ADMIN_USER, NX_WHD_ADMIN_USER_N, 208 admin_hash, 209 sessions, 210 86400) 211 212 let doc_names: *u8 = sys_mmap(4096) 213 let doc_names_count: i64 = 0 214 let random_16: *u8 = sys_mmap(16) 215 216 // Search flow (per nx_wiki_main pattern) 217 let search_flow: *NxSearchFlow = (sys_mmap(256)) as *NxSearchFlow 218 nx_search_flow_init(search_flow, 100000, 100, 10000) 219 220 // Doc store + builder (composes V-PROV-2 patterns; V1 uses 11 nishi-silicon docs) 221 let doc_store: *NxWikiDocStore = (sys_mmap(256)) as *NxWikiDocStore 222 nx_wiki_doc_store_init(doc_store, 100, 65536, 65536, 4194304) 223 224 let builder: *NxWikiIndexBuilder = (sys_mmap(64)) as *NxWikiIndexBuilder 225 nx_wiki_index_builder_init(builder, doc_store, 100) 226 // V1 seed doc: hardcoded landing page so daemon serves SOMETHING 227 nx_wiki_index_builder_add(builder, 228 "andelinwest.com" as *u8, 15, 229 "/" as *u8, 1, 230 "Andelin West sovereign Nishi site. First V-HOST-2 daemon GREEN." as *u8, 231 62) 232 nx_wiki_index_builder_finalize(builder) 233 let search_idx: *NxInvIndex = nx_wiki_index_builder_get_index(builder) 234 235 // Archive store (V1 empty; operator seeds via charter ยง10) 236 let archive_store: *NxArchiveStore = (sys_mmap(256)) as *NxArchiveStore 237 nx_archive_store_init(archive_store, 100) 238 239 // Artifact store (V2.0 pipeline; V1 uses empty store for V-HOST-2 minimal) 240 let artifact_store: *NxArtifactStore = (sys_mmap(512)) as *NxArtifactStore 241 nx_artifact_store_init(artifact_store, 100) 242 243 // ----- 4. Bind listen socket ----- 244 let addr_buf: *u8 = sys_mmap(16) 245 nx_http_server_addr_any(addr_buf, NX_WHD_LISTEN_PORT) 246 let lv: *i64 = (sys_mmap(8)) as *i64 247 let lfd: i64 = nx_http_server_listen(addr_buf, NX_WHD_BACKLOG, lv) 248 if lfd < 0 { return NX_WHD_BIND_FAILED } 249 250 let banner: *u8 = "nx_wiki_https_daemon listening on 0.0.0.0:8443\n" as *u8 251 sys_write(1, banner, 47) 252 253 // ----- 5. Accept loop ----- 254 var n_served: i64 = 0 255 var n_errors: i64 = 0 256 while n_served < NX_WHD_REQUEST_BUDGET { 257 let av: *i64 = (sys_mmap(8)) as *i64 258 let cfd: i64 = nx_http_server_accept_one(lfd, av) 259 if cfd < 0 { 260 n_errors = n_errors + 1 261 n_served = n_served + 1 262 } 263 if cfd >= 0 { 264 let now_ms: i64 = sys_now_ms() 265 let now_s: i64 = now_ms / 1000 266 nx_csprng_fill(random_16, 16) 267 268 let rc: i64 = nx_whd_handle_one( 269 cfd, 270 cert_der, cert_der_len, 271 ed25519_priv, 272 server_random, server_x25519_priv, 273 admin_cfg, 274 doc_names, doc_names_count, 275 random_16, now_s, 276 search_flow, search_idx, doc_store, archive_store, artifact_store) 277 278 // Per Cardinal 14: per-conn failures non-fatal 279 if rc != NX_WHD_OK { n_errors = n_errors + 1 } 280 281 sys_close(cfd) 282 n_served = n_served + 1 283 } 284 } 285 286 sys_close(lfd) 287 return NX_WHD_OK 288}