code wiki / _hdl_build / nx_dev_api_serve.nx

nx_dev_api_serve.nx source

↩ module page · 192 lines · 9388 B

1// nx_dev_api_serve.nx -- loopback HTTP server for the sovereign DEV/CI API (nx_dev_api). Composes 2// nx_http_server + da_handle. ADDITIVE + ISOLATED (a new service on a caller port, touches nothing 3// existing = never-brick). Once LIVE + reverse-proxied at /api/dev, EVERY build is an HTTP call: 4// POST https://nishifamily.com/api/dev/build organ=<name> -> {exit, output} (no WSL, no shell) 5// nx_dev_api_serve serve [port] -- accept-loop 6// nx_dev_api_serve probe <port> <p> -- sovereign plain-HTTP self-test 7// ⚠ SECURITY: /api/dev executes code server-side -> deploy LOOPBACK-only + behind the auth-gated edge 8// (mgmt admin), never a raw public route. The charset boundary (da_valid_name) blocks traversal/injection. 9// license_tier: ORIGINAL 10import "nx_http_server.nx" 11import "nx_connect.nx" // bounded connect: a raw sys_connect hangs ~127s on a black-holed host 12import "nx_dev_api.nx" 13const DSV_MAGIC_2048: i64 = 2048 14const DSV_MAGIC_4096: i64 = 4096 15 16const DSV_PORT_DEFAULT: i64 = 18097 17const DSV_REQ_CAP: i64 = 262144 18const DSV_RESP_CAP: i64 = 524288 19 20func dsv_puts(s: *u8) -> i64 { sys_write(1, s, da_slen(s)); return 0 } 21func dsv_atoi(s: *u8) -> i64 { var v: i64=0; var i: i64=0; while s[i]!=(0 as u8){ let c: i64=s[i] as i64; if c>=48 { if c<=57 { v=v*10+(c-48) } } i=i+1 } return v } 22 23// The buffered/gated edge proxy needs Content-Length to forward the body; da_http_head emits only "Connection: close". 24// Rewrite the finished response to insert Content-Length after the head. (nx_fin_serve sets it -> /finance proxies; 25// da_handle didn't -> /api/dev returned status=0 through the edge though the daemon was live.) 26func dsv_add_content_length(src: *u8, n: i64, dst: *u8) -> i64 { 27 var h: i64 = 0 - 1; var i: i64 = 0 28 while i + 3 < n { if src[i]==(13 as u8) { if src[i+1]==(10 as u8) { if src[i+2]==(13 as u8) { if src[i+3]==(10 as u8) { h=i; i=n } } } } i=i+1 } 29 if h < 0 { var k: i64=0; while k<n { dst[k]=src[k]; k=k+1 } return n } 30 let bodylen: i64 = n - (h + 4) 31 var o: i64 = 0; var j: i64 = 0 32 while j < h { dst[o]=src[j]; o=o+1; j=j+1 } 33 o = da_cat(dst, o, "\r\nContent-Length: " as *u8) 34 o = da_catn(dst, o, bodylen) 35 o = da_cat(dst, o, "\r\n\r\n" as *u8) 36 var b: i64 = h + 4 37 while b < n { dst[o]=src[b]; o=o+1; b=b+1 } 38 return o 39} 40 41func dsv_serve(port: i64) -> i64 { 42 let addr: *u8 = sys_mmap(16) 43 nx_http_server_addr_loopback(addr, port) 44 let v: *i64 = sys_mmap(16) as *i64 45 let lfd: i64 = nx_http_server_listen(addr, 16, v) 46 if lfd < 0 { dsv_puts("DEV-API LISTEN-FAIL\n" as *u8); sys_exit(1); return 1 } 47 let bnr: *u8 = sys_mmap(64); var bo: i64 = da_cat(bnr, 0, "nx_dev_api_serve LIVE on 127.0.0.1:" as *u8); bo = da_catn(bnr, bo, port); bnr[bo]=10 as u8; sys_write(1, bnr, bo+1) 48 let req: *u8 = sys_mmap(DSV_REQ_CAP) 49 let out: *u8 = sys_mmap(DSV_RESP_CAP) 50 let out2: *u8 = sys_mmap(DSV_RESP_CAP + 64) 51 var run: i64 = 1 52 while run == 1 { 53 let cfd: i64 = nx_http_server_accept_one(lfd, v) 54 if cfd >= 0 { 55 let n: i64 = sys_read(cfd, req, DSV_REQ_CAP) 56 if n > 0 { 57 let rn: i64 = da_handle(req, n, out) 58 let rn2: i64 = dsv_add_content_length(out, rn, out2) 59 nx_http_server_send_response(cfd, out2, rn2) 60 } else { sys_close(cfd) } 61 } 62 } 63 return 0 64} 65 66func dsv_probe(port: i64, path: *u8) -> i64 { 67 let addr: *u8 = sys_mmap(16) 68 nx_http_server_addr_loopback(addr, port) 69 let fd: i64 = sys_socket(2, 1, 0) 70 if fd < 0 { dsv_puts("PROBE socket-fail\n" as *u8); sys_exit(1); return 1 } 71 if nx_connect_bounded(fd, addr, 16, NX_CONN_DEFAULT_MS) < 0 { dsv_puts("PROBE connect-fail\n" as *u8); sys_close(fd); sys_exit(1); return 1 } 72 let req: *u8 = sys_mmap(DSV_MAGIC_2048) 73 var o: i64 = da_cat(req, 0, "GET " as *u8) 74 o = da_cat(req, o, path) 75 o = da_cat(req, o, " HTTP/1.1\r\nHost: 127.0.0.1\r\nConnection: close\r\n\r\n" as *u8) 76 sys_write(fd, req, o) 77 let buf: *u8 = sys_mmap(DSV_RESP_CAP) 78 var total: i64 = 0 79 var run: i64 = 1 80 while run == 1 { 81 let r: i64 = sys_read(fd, ((buf as i64) + total) as *u8, DSV_RESP_CAP - total) 82 if r <= 0 { run = 0 } else { total = total + r } 83 } 84 sys_close(fd) 85 sys_write(1, buf, total) 86 dsv_puts("\n" as *u8) 87 return 0 88} 89 90// POST organ=<name> to /api/dev/gate on a live server -> print the JSON verdict. The end-to-end proof: 91// an HTTP call COMPILES + GATES an organ (no WSL, no shell). mode 'b'. 92func dsv_buildpost(port: i64, organ: *u8) -> i64 { 93 let addr: *u8 = sys_mmap(16) 94 nx_http_server_addr_loopback(addr, port) 95 let fd: i64 = sys_socket(2, 1, 0) 96 if fd < 0 { dsv_puts("POST socket-fail\n" as *u8); sys_exit(1); return 1 } 97 if nx_connect_bounded(fd, addr, 16, NX_CONN_DEFAULT_MS) < 0 { dsv_puts("POST connect-fail\n" as *u8); sys_close(fd); sys_exit(1); return 1 } 98 let bodyb: *u8 = sys_mmap(256) 99 var bl: i64 = da_cat(bodyb, 0, "organ=" as *u8) 100 bl = da_cat(bodyb, bl, organ) 101 let req: *u8 = sys_mmap(DSV_MAGIC_4096) 102 var o: i64 = da_cat(req, 0, "POST /api/dev/gate HTTP/1.1\r\nHost: 127.0.0.1\r\nContent-Type: application/x-www-form-urlencoded\r\n" as *u8) 103 // carry the operator dev_build capability from dev_cap.tok, if present (X-Nishi-Cap header) 104 let tsz: *i64 = sys_mmap(16) as *i64; tsz[0]=0 105 let tok: *u8 = sys_read_file("dev_cap.tok" as *u8, tsz) 106 if (tok as i64) != 0 { if tsz[0] > 0 { 107 o = da_cat(req, o, "X-Nishi-Cap: " as *u8) 108 var ti: i64 = 0 109 while ti < tsz[0] { let c: i64 = tok[ti] as i64; if c==10 { ti=tsz[0] } else { if c==13 { ti=tsz[0] } else { req[o]=tok[ti]; o=o+1; ti=ti+1 } } } 110 o = da_cat(req, o, "\r\n" as *u8) 111 } } 112 o = da_cat(req, o, "Content-Length: " as *u8) 113 o = da_catn(req, o, bl) 114 o = da_cat(req, o, "\r\nConnection: close\r\n\r\n" as *u8) 115 o = da_cat(req, o, bodyb) 116 sys_write(fd, req, o) 117 let buf: *u8 = sys_mmap(DSV_RESP_CAP) 118 var total: i64 = 0 119 var run: i64 = 1 120 while run == 1 { 121 let r: i64 = sys_read(fd, ((buf as i64) + total) as *u8, DSV_RESP_CAP - total) 122 if r <= 0 { run = 0 } else { total = total + r } 123 } 124 sys_close(fd) 125 sys_write(1, buf, total) 126 dsv_puts("\n" as *u8) 127 return 0 128} 129 130// POST organ=<name> to /api/dev/run on a live server -> print the JSON. The end-to-end RUN proof: an HTTP call 131// RUNS an allowlisted organ over the socket (no WSL). mode 'r'. Carries dev_cap.tok as X-Nishi-Cap if present. 132func dsv_runpost(port: i64, organ: *u8) -> i64 { 133 let addr: *u8 = sys_mmap(16) 134 nx_http_server_addr_loopback(addr, port) 135 let fd: i64 = sys_socket(2, 1, 0) 136 if fd < 0 { dsv_puts("POST socket-fail\n" as *u8); sys_exit(1); return 1 } 137 if nx_connect_bounded(fd, addr, 16, NX_CONN_DEFAULT_MS) < 0 { dsv_puts("POST connect-fail\n" as *u8); sys_close(fd); sys_exit(1); return 1 } 138 let bodyb: *u8 = sys_mmap(256) 139 var bl: i64 = da_cat(bodyb, 0, "organ=" as *u8) 140 bl = da_cat(bodyb, bl, organ) 141 let req: *u8 = sys_mmap(DSV_MAGIC_4096) 142 var o: i64 = da_cat(req, 0, "POST /api/dev/run HTTP/1.1\r\nHost: 127.0.0.1\r\nContent-Type: application/x-www-form-urlencoded\r\n" as *u8) 143 let tsz: *i64 = sys_mmap(16) as *i64; tsz[0]=0 144 let tok: *u8 = sys_read_file("dev_cap.tok" as *u8, tsz) 145 if (tok as i64) != 0 { if tsz[0] > 0 { 146 o = da_cat(req, o, "X-Nishi-Cap: " as *u8) 147 var ti: i64 = 0 148 while ti < tsz[0] { let c: i64 = tok[ti] as i64; if c==10 { ti=tsz[0] } else { if c==13 { ti=tsz[0] } else { req[o]=tok[ti]; o=o+1; ti=ti+1 } } } 149 o = da_cat(req, o, "\r\n" as *u8) 150 } } 151 o = da_cat(req, o, "Content-Length: " as *u8) 152 o = da_catn(req, o, bl) 153 o = da_cat(req, o, "\r\nConnection: close\r\n\r\n" as *u8) 154 o = da_cat(req, o, bodyb) 155 sys_write(fd, req, o) 156 let buf: *u8 = sys_mmap(DSV_RESP_CAP) 157 var total: i64 = 0 158 var run: i64 = 1 159 while run == 1 { 160 let r: i64 = sys_read(fd, ((buf as i64) + total) as *u8, DSV_RESP_CAP - total) 161 if r <= 0 { run = 0 } else { total = total + r } 162 } 163 sys_close(fd) 164 sys_write(1, buf, total) 165 dsv_puts("\n" as *u8) 166 return 0 167} 168 169func main(argc: i64, argv: *i64) -> i64 { 170 if argc < 2 { dsv_puts("usage: nx_dev_api_serve serve [port] | probe <port> <path> | buildpost <port> <organ> | runpost <port> <organ>\n" as *u8); sys_exit(2); return 2 } 171 let mode: *u8 = argv[1] as *u8 172 if mode[0]==(115 as u8) { // 'serve' 173 var port: i64 = DSV_PORT_DEFAULT 174 if argc >= 3 { port = dsv_atoi(argv[2] as *u8) } 175 return dsv_serve(port) 176 } 177 if mode[0]==(112 as u8) { // 'probe' 178 if argc < 4 { dsv_puts("usage: nx_dev_api_serve probe <port> <path>\n" as *u8); sys_exit(2); return 2 } 179 return dsv_probe(dsv_atoi(argv[2] as *u8), argv[3] as *u8) 180 } 181 if mode[0]==(98 as u8) { // 'buildpost' 182 if argc < 4 { dsv_puts("usage: nx_dev_api_serve buildpost <port> <organ>\n" as *u8); sys_exit(2); return 2 } 183 return dsv_buildpost(dsv_atoi(argv[2] as *u8), argv[3] as *u8) 184 } 185 if mode[0]==(114 as u8) { // 'runpost' 186 if argc < 4 { dsv_puts("usage: nx_dev_api_serve runpost <port> <organ>\n" as *u8); sys_exit(2); return 2 } 187 return dsv_runpost(dsv_atoi(argv[2] as *u8), argv[3] as *u8) 188 } 189 dsv_puts("unknown mode\n" as *u8) 190 sys_exit(2) 191 return 2 192}