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}