code wiki / _hdl_build / nx_vizsla_serve.nx
nx_vizsla_serve.nx source
↩ module page · 336 lines · 13390 B
1// nx_vizsla_serve.nx -- NISHI VIZSLA: the sovereign HTTP transport that makes the first-party UI LIVE in the
2// Nishi browser. Operator 2026-06-22: manage relationships IN the first party nishi browser, privacy-first.
3// Binds 127.0.0.1 ONLY (never exposed), no third-party, your data never leaves the box.
4// GET /<tid> -> fork nx_vizsla_render -> serve that tenant's sovereign 0-JS page (GET / -> elderwesto)
5// POST /v/submit -> read the form body -> fork nx_vizsla_action submit -> 303 redirect back to GET /<tid>
6// Socket accept loop is the proven gallery_serve idiom (fork-per-conn, reap). A `handle <base> <reqfile>` CLI
7// mode runs the router over a request read from a file (gateable without socket timing). tid validated
8// [A-Za-z0-9_-] = no path traversal. license_tier: ORIGINAL
9import "nx_syscalls.nx"
10const K_MAGIC_719468: i64 = 719468
11const K_MAGIC_146097: i64 = 146097
12const K_MAGIC_146096: i64 = 146096
13const K_MAGIC_1460: i64 = 1460
14const K_MAGIC_36524: i64 = 36524
15const K_MAGIC_86400: i64 = 86400
16const K_MAGIC_65536: i64 = 65536
17const K_MAGIC_1048576: i64 = 1048576
18const K_MAGIC_18097: i64 = 18097
19
20func vs_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
21
22func vs_cat(dst: *u8, off: i64, s: *u8) -> i64 {
23 var i: i64 = 0
24 while s[i] != (0 as u8) { dst[off + i] = s[i]; i = i + 1 }
25 return off + i
26}
27
28func vs_catn(dst: *u8, off: i64, v: i64) -> i64 {
29 var o: i64 = off
30 var m: i64 = v
31 if m < 0 { dst[o] = 45 as u8; o = o + 1; m = 0 - m }
32 let t: *u8 = sys_mmap(28)
33 var k: i64 = 0
34 if m == 0 { t[0] = 48 as u8; k = 1 }
35 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
36 var i: i64 = 0
37 while i < k { dst[o + i] = t[k - 1 - i]; i = i + 1 }
38 return o + k
39}
40
41// req[0,rn) begins with pat ?
42func vs_match(req: *u8, rn: i64, pat: *u8) -> i64 {
43 let pl: i64 = vs_slen(pat)
44 if pl > rn { return 0 }
45 var j: i64 = 0
46 while j < pl { if req[j] != pat[j] { return 0 } j = j + 1 }
47 return 1
48}
49
50func vs_valid_tid(s: *u8) -> i64 {
51 if s[0] == (0 as u8) { return 0 }
52 var i: i64 = 0
53 while s[i] != (0 as u8) {
54 let c: i64 = s[i] as i64
55 var ok: i64 = 0
56 if c >= 48 { if c <= 57 { ok = 1 } }
57 if c >= 65 { if c <= 90 { ok = 1 } }
58 if c >= 97 { if c <= 122 { ok = 1 } }
59 if c == 45 { ok = 1 }
60 if c == 95 { ok = 1 }
61 if ok == 0 { return 0 }
62 i = i + 1
63 }
64 return 1
65}
66
67// from "GET /<tid> HTTP.." -> tid (until ' ' or '?'); empty if "GET / "
68func vs_pathtid(req: *u8, rn: i64, out: *u8) -> i64 {
69 var p: i64 = 0
70 while p < rn { if req[p] == (32 as u8) { break } p = p + 1 } // skip method
71 p = p + 1
72 if p < rn { if req[p] == (47 as u8) { p = p + 1 } } // skip leading '/'
73 var o: i64 = 0
74 while p < rn {
75 let c: i64 = req[p] as i64
76 if c == 32 { break }
77 if c == 63 { break }
78 if c == 47 { break }
79 if o < 127 { out[o] = c as u8; o = o + 1 }
80 p = p + 1
81 }
82 out[o] = 0 as u8
83 return o
84}
85
86func vs_keyeq(req: *u8, rn: i64, p: i64, key: *u8, kl: i64) -> i64 {
87 if p + kl + 1 > rn { return 0 }
88 var j: i64 = 0
89 while j < kl { if req[p + j] != key[j] { return 0 } j = j + 1 }
90 if req[p + kl] != (61 as u8) { return 0 }
91 return 1
92}
93
94// body starts after "\r\n\r\n"; find key= in body -> raw value (until '&'); 1 if found
95func vs_bodyfield(req: *u8, rn: i64, key: *u8, out: *u8) -> i64 {
96 var b: i64 = 0 - 1
97 var i: i64 = 0
98 while i + 3 < rn {
99 if req[i] == (13 as u8) { if req[i+1] == (10 as u8) { if req[i+2] == (13 as u8) { if req[i+3] == (10 as u8) { b = i + 4; i = rn } } } }
100 i = i + 1
101 }
102 if b < 0 { out[0] = 0 as u8; return 0 }
103 let kl: i64 = vs_slen(key)
104 var p: i64 = b
105 while p < rn {
106 var atb: i64 = 0
107 if p == b { atb = 1 }
108 if p > b { if req[p - 1] == (38 as u8) { atb = 1 } }
109 var hit: i64 = 0
110 if atb == 1 { hit = vs_keyeq(req, rn, p, key, kl) }
111 if hit == 1 {
112 var q: i64 = p + kl + 1
113 var o: i64 = 0
114 while q < rn { if req[q] == (38 as u8) { break } if o < 255 { out[o] = req[q]; o = o + 1 } q = q + 1 }
115 out[o] = 0 as u8
116 return 1
117 }
118 p = p + 1
119 }
120 out[0] = 0 as u8
121 return 0
122}
123
124// days since epoch -> civil (out[0]=y out[1]=m out[2]=d)
125func vs_civil(z0: i64, out: *i64) -> i64 {
126 var z: i64 = z0 + K_MAGIC_719468
127 var era: i64 = z / K_MAGIC_146097
128 if z < 0 { era = (z - K_MAGIC_146096) / K_MAGIC_146097 }
129 let doe: i64 = z - era * K_MAGIC_146097
130 let yoe: i64 = (doe - doe / K_MAGIC_1460 + doe / K_MAGIC_36524 - doe / K_MAGIC_146096) / 365
131 let y0: i64 = yoe + era * 400
132 let doy: i64 = doe - (365 * yoe + yoe / 4 - yoe / 100)
133 let mp: i64 = (5 * doy + 2) / 153
134 let d: i64 = doy - (153 * mp + 2) / 5 + 1
135 var m: i64 = mp + 3
136 if mp >= 10 { m = mp - 9 }
137 var y: i64 = y0
138 if m <= 2 { y = y0 + 1 }
139 out[0] = y; out[1] = m; out[2] = d
140 return 0
141}
142
143func vs_today(out: *u8) -> i64 {
144 let sec: i64 = sys_now_realtime_sec()
145 let days: i64 = sec / K_MAGIC_86400
146 let ymd: *i64 = sys_mmap(8 * 4) as *i64
147 vs_civil(days, ymd)
148 var o: i64 = 0
149 o = vs_catn(out, o, ymd[0]); out[o] = 45 as u8; o = o + 1
150 if ymd[1] < 10 { out[o] = 48 as u8; o = o + 1 }
151 o = vs_catn(out, o, ymd[1]); out[o] = 45 as u8; o = o + 1
152 if ymd[2] < 10 { out[o] = 48 as u8; o = o + 1 }
153 o = vs_catn(out, o, ymd[2]); out[o] = 0 as u8
154 return o
155}
156
157func vs_readall(path: *u8, szp: *i64) -> *u8 {
158 let fd: i64 = sys_openat_rd(path)
159 if fd < 0 { szp[0] = 0; return 0 as *u8 }
160 let sz: i64 = sys_lseek(fd, 0, 2)
161 sys_lseek(fd, 0, 0)
162 let buf: *u8 = sys_mmap(sz + 64)
163 var got: i64 = 0
164 var n: i64 = 1
165 while n > 0 { n = sys_read(fd, (buf as i64 + got) as *u8, K_MAGIC_65536); if n > 0 { got = got + n } }
166 sys_close(fd)
167 szp[0] = got
168 return buf
169}
170
171// fork render -> /tmp page -> wrap as HTTP 200 in rbuf; returns total length
172func vs_render(base: *u8, tid: *u8, rbuf: *u8) -> i64 {
173 let today: *u8 = sys_mmap(32); vs_today(today)
174 let tmp: *u8 = sys_mmap(256)
175 var to: i64 = 0
176 to = vs_cat(tmp, to, "/tmp/vs_pg_" as *u8); to = vs_catn(tmp, to, sys_now_us()); to = vs_cat(tmp, to, ".html" as *u8); tmp[to] = 0 as u8
177 let pid: i64 = sys_fork()
178 if pid == 0 {
179 let nul: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4)
180 if nul >= 0 { sys_dup3(nul, 1, 0); sys_dup3(nul, 2, 0) }
181 let argv: *i64 = sys_mmap(64) as *i64
182 argv[0] = "_offc/nx_vizsla_render.elf" as *u8 as i64
183 argv[1] = "page" as *u8 as i64
184 argv[2] = base as i64
185 argv[3] = tid as i64
186 argv[4] = today as i64
187 argv[5] = tmp as i64
188 argv[6] = 0
189 let envp: *i64 = sys_mmap(16) as *i64
190 envp[0] = 0
191 sys_execve_clean("_offc/nx_vizsla_render.elf" as *u8, argv, envp)
192 sys_exit(127)
193 }
194 let st: *i64 = sys_mmap(16) as *i64
195 sys_wait4(pid, st, 0)
196 let szp: *i64 = sys_mmap(16) as *i64
197 let hb: *u8 = vs_readall(tmp, szp)
198 let hn: i64 = szp[0]
199 if hn <= 0 {
200 return vs_cat(rbuf, 0, "HTTP/1.1 404 Not Found\r\nContent-Type: text/plain\r\nContent-Length: 14\r\nConnection: close\r\n\r\nno such tenant" as *u8)
201 }
202 var o: i64 = 0
203 o = vs_cat(rbuf, o, "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\nCache-Control: no-store\r\nReferrer-Policy: no-referrer\r\nConnection: close\r\nContent-Length: " as *u8)
204 o = vs_catn(rbuf, o, hn)
205 o = vs_cat(rbuf, o, "\r\n\r\n" as *u8)
206 var k: i64 = 0
207 while k < hn { rbuf[o] = hb[k]; o = o + 1; k = k + 1 }
208 return o
209}
210
211func vs_redirect(tid: *u8, rbuf: *u8) -> i64 {
212 var o: i64 = 0
213 o = vs_cat(rbuf, o, "HTTP/1.1 303 See Other\r\nLocation: /" as *u8)
214 o = vs_cat(rbuf, o, tid)
215 o = vs_cat(rbuf, o, "\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" as *u8)
216 return o
217}
218
219// fork the blessed action controller: submit <base> <tid> <action> <body>
220func vs_run_action(base: *u8, tid: *u8, action: *u8, body: *u8) -> i64 {
221 let pid: i64 = sys_fork()
222 if pid == 0 {
223 let nul: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4)
224 if nul >= 0 { sys_dup3(nul, 1, 0); sys_dup3(nul, 2, 0) }
225 let argv: *i64 = sys_mmap(64) as *i64
226 argv[0] = "_offc/nx_vizsla_action.elf" as *u8 as i64
227 argv[1] = "submit" as *u8 as i64
228 argv[2] = base as i64
229 argv[3] = tid as i64
230 argv[4] = action as i64
231 argv[5] = body as i64
232 argv[6] = 0
233 let envp: *i64 = sys_mmap(16) as *i64
234 envp[0] = 0
235 sys_execve_clean("_offc/nx_vizsla_action.elf" as *u8, argv, envp)
236 sys_exit(127)
237 }
238 let st: *i64 = sys_mmap(16) as *i64
239 sys_wait4(pid, st, 0)
240 return (st[0] >> 8) & 0xff
241}
242
243// the router: GET -> render, POST /v/submit -> action + 303
244func vs_handle(req: *u8, rn: i64, base: *u8, rbuf: *u8) -> i64 {
245 if vs_match(req, rn, "POST /v/submit" as *u8) == 1 {
246 let tid: *u8 = sys_mmap(256)
247 let action: *u8 = sys_mmap(256)
248 vs_bodyfield(req, rn, "tid" as *u8, tid)
249 vs_bodyfield(req, rn, "action" as *u8, action)
250 var rtid: *u8 = "elderwesto" as *u8
251 if vs_valid_tid(tid) == 1 { rtid = tid }
252 // the body (after headers) is what the action organ parses for id/name/...
253 var b: i64 = 0 - 1
254 var i: i64 = 0
255 while i + 3 < rn {
256 if req[i] == (13 as u8) { if req[i+1] == (10 as u8) { if req[i+2] == (13 as u8) { if req[i+3] == (10 as u8) { b = i + 4; i = rn } } } }
257 i = i + 1
258 }
259 if b >= 0 {
260 let bl: i64 = rn - b
261 let body: *u8 = sys_mmap(bl + 8)
262 var c: i64 = 0
263 while c < bl { body[c] = req[b + c]; c = c + 1 }
264 body[bl] = 0 as u8
265 if vs_valid_tid(tid) == 1 { if action[0] != (0 as u8) { vs_run_action(base, tid, action, body) } }
266 }
267 return vs_redirect(rtid, rbuf)
268 }
269 let tid: *u8 = sys_mmap(256)
270 vs_pathtid(req, rn, tid)
271 var rtid: *u8 = "elderwesto" as *u8
272 if vs_valid_tid(tid) == 1 { rtid = tid }
273 return vs_render(base, rtid, rbuf)
274}
275
276func main(argc: i64, argv: *i64) -> i64 {
277 // explicit subcommand so a bare run (how the build runner invokes it) does NOT block in accept()
278 if argc < 2 { let u: *u8 = "VIZSLA-SERVE usage: serve [port] [base] | handle <base> <reqfile>\n" as *u8; sys_write(2, u, vs_slen(u)); return 1 }
279 let cmd: *u8 = argv[1] as *u8
280
281 // handle mode (gate hook): run the router over a request read from a file
282 if vs_match(cmd, vs_slen(cmd), "handle" as *u8) == 1 {
283 if argc < 4 { let u: *u8 = "VIZSLA-SERVE handle needs <base> <reqfile>\n" as *u8; sys_write(2, u, vs_slen(u)); return 1 }
284 let base: *u8 = argv[2] as *u8
285 let szp: *i64 = sys_mmap(16) as *i64
286 let req: *u8 = vs_readall(argv[3] as *u8, szp)
287 let rn: i64 = szp[0]
288 let rbuf: *u8 = sys_mmap(K_MAGIC_1048576)
289 let rlen: i64 = vs_handle(req, rn, base, rbuf)
290 sys_write(1, rbuf, rlen)
291 return 0
292 }
293
294 if vs_match(cmd, vs_slen(cmd), "serve" as *u8) == 0 { let u: *u8 = "VIZSLA-SERVE unknown command (serve|handle)\n" as *u8; sys_write(2, u, vs_slen(u)); return 1 }
295
296 // serve mode: serve [port] [base]
297 var port: i64 = K_MAGIC_18097
298 var base: *u8 = "knowledge/vizsla/" as *u8
299 if argc >= 3 { var pv: i64 = 0; var pi: i64 = 0; let pa: *u8 = argv[2] as *u8; while pa[pi] != (0 as u8) { if pa[pi] >= (48 as u8) { if pa[pi] <= (57 as u8) { pv = pv * 10 + ((pa[pi] as i64) - 48) } } pi = pi + 1 } if pv > 0 { port = pv } }
300 if argc >= 4 { base = argv[3] as *u8 }
301
302 let addr: *u8 = sys_mmap(16)
303 addr[0] = 2 as u8; addr[1] = 0 as u8; addr[2] = ((port >> 8) & 0xff) as u8; addr[3] = (port & 0xff) as u8
304 addr[4] = 127 as u8; addr[5] = 0 as u8; addr[6] = 0 as u8; addr[7] = 1 as u8
305 addr[8] = 0 as u8; addr[9] = 0 as u8; addr[10] = 0 as u8; addr[11] = 0 as u8; addr[12] = 0 as u8; addr[13] = 0 as u8; addr[14] = 0 as u8; addr[15] = 0 as u8
306 let lfd: i64 = sys_socket(2, 1, 0)
307 if lfd < 0 { return 10 }
308 let optval: *u8 = sys_mmap(4); optval[0] = 1 as u8; optval[1] = 0 as u8; optval[2] = 0 as u8; optval[3] = 0 as u8
309 sys_setsockopt(lfd, 1, 2, optval, 4)
310 if sys_bind(lfd, addr, 16) < 0 { return 20 }
311 if sys_listen(lfd, 64) < 0 { return 30 }
312 let banner: *u8 = "VIZSLA-SERVE listening on http://127.0.0.1:" as *u8; sys_write(2, banner, vs_slen(banner))
313 let pbuf: *u8 = sys_mmap(16); var pbo: i64 = vs_catn(pbuf, 0, port); pbuf[pbo] = 10 as u8; sys_write(2, pbuf, pbo + 1)
314
315 let req: *u8 = sys_mmap(K_MAGIC_65536)
316 let reapst: *i64 = sys_mmap(16) as *i64
317 while 1 == 1 {
318 let cfd: i64 = sys_accept(lfd)
319 if cfd < 0 { continue }
320 let pid: i64 = sys_fork()
321 if pid == 0 {
322 sys_close(lfd)
323 let rn: i64 = sys_read(cfd, req, K_MAGIC_65536)
324 let rbuf: *u8 = sys_mmap(K_MAGIC_1048576)
325 let rlen: i64 = vs_handle(req, rn, base, rbuf)
326 sys_write(cfd, rbuf, rlen)
327 sys_close(cfd)
328 sys_exit(0)
329 }
330 sys_close(cfd)
331 var reaped: i64 = 1
332 while reaped > 0 { reaped = sys_wait4(0 - 1, reapst, 1) }
333 }
334 sys_close(lfd)
335 return 0
336}