code wiki / (root) / nx_edge_daemon.nx

nx_edge_daemon.nx source

↩ module page · 272 lines · 10105 B

1// nx_edge_daemon.nx -- bits-up HTTPS daemon serving nishifamily.com. 2// 3// Operator directive (2026-05-20, verbatim correction): the substrate 4// has the deploy capability built bits-up. This file IS that daemon. 5// 6// Reads cert + ECDSA priv + static HTML from disk, binds to a port, 7// accepts connections, runs the full TLS 1.3 handshake per shipped 8// stones, serves the HTML over the encrypted channel. 9// 10// Composes shipped substrate: 11// sys_read_file file load 12// nx_socket (sys_socket/bind/listen/accept) TCP layer 13// nx_tls13_server_session_* 10 handshake stones 14// nx_edge_serve_static_response HTTPS response 15// 16// All pure NishiLang. No C in the serving path. 17// 18// license_tier: ORIGINAL 19 20import "nx_syscalls.nx" 21import "nx_x25519_ephemeral.nx" 22import "nx_tls13.nx" 23import "nx_tls13_record.nx" 24import "nx_tls13_server_session.nx" 25import "nx_tls13_server_session_recv_ch.nx" 26import "nx_tls13_server_session_emit_sh.nx" 27import "nx_tls13_server_session_derive_hs.nx" 28import "nx_tls13_server_session_derive_traffic.nx" 29import "nx_tls13_server_session_emit_ee.nx" 30import "nx_tls13_server_session_emit_certificate.nx" 31import "nx_tls13_server_session_emit_cv.nx" 32import "nx_tls13_server_session_emit_sf.nx" 33import "nx_tls13_server_session_recv_cf.nx" 34import "nx_tls13_server_session_app_data.nx" 35import "nx_edge_serve_static.nx" 36import "nx_http_server.nx" 37const NX_MAGIC_2048: i64 = 2048 38const NX_MAGIC_4096: i64 = 4096 39const NX_MAGIC_8192: i64 = 8192 40const NX_MAGIC_8443: i64 = 8443 41 42// Daemon verdict enum. 43const NX_EDGE_OK: i64 = 0 44const NX_EDGE_ERR_CERT_LOAD: i64 = 1 45const NX_EDGE_ERR_PRIV_LOAD: i64 = 2 46const NX_EDGE_ERR_HTML_LOAD: i64 = 3 47const NX_EDGE_ERR_LISTEN: i64 = 4 48const NX_EDGE_ERR_ACCEPT: i64 = 5 49const NX_EDGE_ERR_HANDSHAKE: i64 = 6 50const NX_EDGE_ERR_SERVE: i64 = 7 51 52// Per-connection: drive the full TLS 1.3 handshake + serve the static 53// HTML response. Returns NX_EDGE_OK on success or a non-OK verdict. 54// 55// Assumes single-read suffices for ClientHello + single-read for 56// client Finished. Production-grade buffering (read until full 57// record) is a future enhancement; for first deployment, real clients 58// send their CH in one MTU. 59 60func nx_edge_handle_connection( 61 client_fd: i64, 62 cert_der: *u8, cert_der_len: i64, 63 server_ecdsa_priv: *u8, 64 server_random: *u8, server_x25519_priv: *u8, 65 html: *u8, html_len: i64 66) -> i64 { 67 // 1. Read ClientHello bytes. 68 let ch_buf: *u8 = sys_mmap(NX_MAGIC_2048) 69 let ch_n: i64 = sys_read(client_fd, ch_buf, NX_MAGIC_2048) 70 if ch_n <= 0 { return NX_EDGE_ERR_HANDSHAKE } 71 72 // 2. Build session + drive handshake stones 2..7.5. 73 let s: *Tls13ServerSession = nx_tls13_server_session_new( 74 server_random, server_x25519_priv) 75 if (s as i64) == 0 { return NX_EDGE_ERR_HANDSHAKE } 76 77 if nx_tls13_server_session_recv_ch(s, ch_buf, ch_n) != NX_TLS13_SSESSION_OK { 78 return NX_EDGE_ERR_HANDSHAKE 79 } 80 81 // Server's flight 1: SH + EE + Certificate + CV + SF concatenated. 82 let flight_cap: i64 = NX_MAGIC_4096 83 let flight: *u8 = sys_mmap(flight_cap) 84 var fw: i64 = 0 85 86 let sh_buf: *u8 = sys_mmap(512) 87 let n_sh: i64 = nx_tls13_server_session_emit_sh(s, sh_buf, 512) 88 if n_sh <= 0 { return NX_EDGE_ERR_HANDSHAKE } 89 var i: i64 = 0 90 while i < n_sh { flight[fw + i] = sh_buf[i]; i = i + 1 } 91 fw = fw + n_sh 92 93 if nx_tls13_server_session_derive_hs_secrets(s) != NX_TLS13_SSESSION_OK { 94 return NX_EDGE_ERR_HANDSHAKE 95 } 96 if nx_tls13_server_session_derive_traffic(s) != NX_TLS13_SSESSION_OK { 97 return NX_EDGE_ERR_HANDSHAKE 98 } 99 100 let ee_buf: *u8 = sys_mmap(256) 101 let n_ee: i64 = nx_tls13_server_session_emit_ee(s, ee_buf, 256) 102 if n_ee <= 0 { return NX_EDGE_ERR_HANDSHAKE } 103 var j: i64 = 0 104 while j < n_ee { flight[fw + j] = ee_buf[j]; j = j + 1 } 105 fw = fw + n_ee 106 107 let cert_buf: *u8 = sys_mmap(NX_MAGIC_2048) 108 let n_cert: i64 = nx_tls13_server_session_emit_certificate( 109 s, cert_der, cert_der_len, cert_buf, NX_MAGIC_2048) 110 if n_cert <= 0 { return NX_EDGE_ERR_HANDSHAKE } 111 var ki: i64 = 0 112 while ki < n_cert { flight[fw + ki] = cert_buf[ki]; ki = ki + 1 } 113 fw = fw + n_cert 114 115 let cv_buf: *u8 = sys_mmap(512) 116 let n_cv: i64 = nx_tls13_server_session_emit_cv( 117 s, server_ecdsa_priv, cv_buf, 512) 118 if n_cv <= 0 { return NX_EDGE_ERR_HANDSHAKE } 119 var li: i64 = 0 120 while li < n_cv { flight[fw + li] = cv_buf[li]; li = li + 1 } 121 fw = fw + n_cv 122 123 let sf_buf: *u8 = sys_mmap(256) 124 let n_sf: i64 = nx_tls13_server_session_emit_sf(s, sf_buf, 256) 125 if n_sf <= 0 { return NX_EDGE_ERR_HANDSHAKE } 126 var mi: i64 = 0 127 while mi < n_sf { flight[fw + mi] = sf_buf[mi]; mi = mi + 1 } 128 fw = fw + n_sf 129 130 // Single write of the full server flight. 131 let wn: i64 = sys_write(client_fd, flight, fw) 132 if wn != fw { return NX_EDGE_ERR_HANDSHAKE } 133 134 // 3. Read client Finished bytes. 135 let cf_buf: *u8 = sys_mmap(256) 136 let cf_n: i64 = sys_read(client_fd, cf_buf, 256) 137 if cf_n <= 0 { return NX_EDGE_ERR_HANDSHAKE } 138 139 if nx_tls13_server_session_recv_cf(s, cf_buf, cf_n) != NX_TLS13_SSESSION_OK { 140 return NX_EDGE_ERR_HANDSHAKE 141 } 142 143 // 4. Read encrypted HTTP request (we don't parse it for v1 -- just 144 // advance the read so the client knows we got it). Real HTTP 145 // routing composes shipped nx_http_router as a future 146 // enhancement. 147 let req_buf: *u8 = sys_mmap(NX_MAGIC_4096) 148 sys_read(client_fd, req_buf, NX_MAGIC_4096) 149 150 // 5. Serve the static HTML response. 151 let resp_buf: *u8 = sys_mmap(NX_MAGIC_8192) 152 let n_resp: i64 = nx_edge_serve_static_response(s, html, html_len, resp_buf, NX_MAGIC_8192) 153 if n_resp <= 0 { return NX_EDGE_ERR_SERVE } 154 if sys_write(client_fd, resp_buf, n_resp) != n_resp { 155 return NX_EDGE_ERR_SERVE 156 } 157 158 return NX_EDGE_OK 159} 160 161// Listen + accept loop entry point. 162// 163// Inputs: 164// cert_der_path null-terminated file path to DER-encoded X.509 cert 165// priv_path null-terminated file path to 32-byte raw ECDSA-P256 priv 166// html_path null-terminated file path to the static HTML body 167// port_host listen port (host byte order, e.g. 8443) 168// server_random caller-supplied 32-byte entropy (one-shot for daemon 169// lifetime; production rotates per-connection -- queued) 170// server_x_priv caller-supplied 32-byte X25519 ephemeral priv 171// max_conns maximum connections to serve before exit (-1 = forever) 172// 173// Returns NX_EDGE_OK on graceful exit (max_conns reached) or a 174// non-OK verdict on setup failure. 175 176func nx_edge_daemon_run( 177 cert_der_path: *u8, 178 priv_path: *u8, 179 html_path: *u8, 180 port_host: i64, 181 server_random: *u8, 182 server_x_priv: *u8, 183 max_conns: i64 184) -> i64 { 185 // Load cert DER from disk. 186 let cert_len_box: *i64 = sys_mmap(8) as *i64 187 let cert_der: *u8 = sys_read_file(cert_der_path, cert_len_box) 188 if (cert_der as i64) == 0 { return NX_EDGE_ERR_CERT_LOAD } 189 if *cert_len_box <= 0 { return NX_EDGE_ERR_CERT_LOAD } 190 191 // Load ECDSA priv key (raw 32 bytes). 192 let priv_len_box: *i64 = sys_mmap(8) as *i64 193 let server_ecdsa_priv: *u8 = sys_read_file(priv_path, priv_len_box) 194 if (server_ecdsa_priv as i64) == 0 { return NX_EDGE_ERR_PRIV_LOAD } 195 if *priv_len_box != 32 { return NX_EDGE_ERR_PRIV_LOAD } 196 197 // Load static HTML body. 198 let html_len_box: *i64 = sys_mmap(8) as *i64 199 let html: *u8 = sys_read_file(html_path, html_len_box) 200 if (html as i64) == 0 { return NX_EDGE_ERR_HTML_LOAD } 201 if *html_len_box <= 0 { return NX_EDGE_ERR_HTML_LOAD } 202 203 // Bind + listen on 0.0.0.0:port_host. 204 let addr_buf: *u8 = sys_mmap(16) 205 nx_http_server_addr_any(addr_buf, port_host) 206 let listen_verdict: *i64 = sys_mmap(8) as *i64 207 let listen_fd: i64 = nx_http_server_listen(addr_buf, 64, listen_verdict) 208 if listen_fd < 0 { return NX_EDGE_ERR_LISTEN } 209 210 // Accept loop. -1 = forever; positive = serve N then return. 211 var served: i64 = 0 212 var keep_going: i64 = 1 213 while keep_going == 1 { 214 let client_fd: i64 = sys_accept(listen_fd) 215 if client_fd < 0 { 216 // Transient EINTR etc. Production-grade would handle 217 // errno; v1 just exits. 218 sys_close(listen_fd) 219 return NX_EDGE_ERR_ACCEPT 220 } 221 222 nx_edge_handle_connection( 223 client_fd, 224 cert_der, *cert_len_box, 225 server_ecdsa_priv, 226 server_random, server_x_priv, 227 html, *html_len_box) 228 229 sys_close(client_fd) 230 231 served = served + 1 232 if max_conns >= 0 { 233 if served >= max_conns { keep_going = 0 } 234 } 235 } 236 237 sys_close(listen_fd) 238 return NX_EDGE_OK 239} 240 241// Operator entry point. Hardcoded paths for v1 -- operator places 242// files at these locations on the Synology before launching. 243// 244// /volume1/nishi/cert/nishifamily.com.der DER cert (from DSM or nx_acme) 245// /volume1/nishi/cert/nishifamily.com.ecdsa.priv 32-byte raw priv 246// /volume1/nishi/dist/index.html built by nx_build_site 247// 248// Listens on :8443 for parallel-soak deployment alongside nginx. 249// After soak, operator rebuilds with port 443 + stops nginx. 250 251func main() -> i64 { 252 let cert_path: *u8 = "/volume1/nishi/cert/nishifamily.com.der\x00" 253 let priv_path: *u8 = "/volume1/nishi/cert/nishifamily.com.ecdsa.priv\x00" 254 let html_path: *u8 = "/volume1/nishi/dist/index.html\x00" 255 256 // One-shot entropy for the daemon -- production must rotate 257 // per-connection. v1 deployment caveat documented. 258 let srv_random: *u8 = sys_mmap(32) 259 let srv_x_priv: *u8 = sys_mmap(32) 260 var i: i64 = 0 261 while i < 32 { 262 srv_random[i] = ((i * 7 + 11) & 0xff) as u8 263 srv_x_priv[i] = ((i * 13 + 5) & 0xff) as u8 264 i = i + 1 265 } 266 267 return nx_edge_daemon_run( 268 cert_path, priv_path, html_path, 269 NX_MAGIC_8443, 270 srv_random, srv_x_priv, 271 -1) 272}