code wiki / _hdl_build / nx_gallery_gw_guard.nx
nx_gallery_gw_guard.nx source
↩ module page · 139 lines · 7249 B
1// nx_gallery_gw_guard.nx -- SOVEREIGN crash-revive supervisor for the LIVE gallery stack (backend serve
2// :18090 + opaque gateway :18190). BOTH currently run root/ORPHANED (PPID 1) with NO supervisor: a crash =
3// permanent outage of the gallery AND the /gallery/curator surface until a human relaunches by hand. This
4// guard, dispatched ARG-LESS by the sovereign clock every ~30s (cwd = nishihost, runs as root like memhealer),
5// CONNECT-probes each tier on loopback; a REFUSED port (dead listener) => fork + setsid + chdir(/volume1/ai/
6// galx) + execve the service with its CANONICAL args, DETACHED (parent returns, so the daemon reparents to
7// init exactly like the originals). Connect-based liveness is deliberate: the kernel completes the TCP
8// handshake for ANY bound listener, so a busy-but-alive daemon is NEVER falsely relaunched (no double-bind);
9// only a truly dead port triggers a relaunch. It never deletes, never writes persistent/HW state -> never-brick
10// BY CONSTRUCTION (rule 26). Arg-less = PRODUCTION (supervise the two real tiers). `<port> <path> [args...]`
11// = TEST one service against a SPARE victim port (proves the exact relaunch mechanics with ZERO live risk).
12// license_tier: ORIGINAL
13import "nx_syscalls.nx"
14import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc)
15
16import "nx_connect.nx" // bounded connect: a raw sys_connect hangs ~127s on a black-holed host
17const GWG_CWD: *u8 = "/volume1/ai/galx"
18const GWG_SERVE: *u8 = "/volume1/ai/galx/nx_gallery_serve.elf"
19const GWG_GATEWAY: *u8 = "/volume1/ai/galx/nx_gallery_gateway.elf"
20const GWG_LAUNCHLOG: *u8 = "/volume1/ai/galx/gw.log"
21const GWG_STATUS: *u8 = "/volume1/homes/elderwesto/nishihost/gw_guard.log"
22const GWG_EVENTS: *u8 = "/volume1/homes/elderwesto/nishihost/gw_guard_events.log"
23const GWG_BACKEND_PORT: i64 = 18090
24const GWG_GATEWAY_PORT: i64 = 18190
25const GWG_PROBE_TMO_S: i64 = 3
26const GWG_MODE: i64 = 0x1a4 // 0644
27
28func gwg_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
29func gwg_puts(fd: i64, s: *u8) -> i64 { sys_write(fd, s, gwg_slen(s)); return 0 }
30// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer
31// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the
32// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls).
33// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign.
34func gwg_putn(fd: i64, v: i64) -> i64 { nxi_fd(fd, v); return 0 }
35func gwg_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{return v} if c>57{return v} v=v*10+(c-48); i=i+1 } return v }
36
37// append a relaunch EVENT (rare -> bounded growth) so the history survives the truncate-status below.
38func gwg_event(now: i64, what: *u8) -> i64 {
39 let ef: i64 = sys_openat_append(GWG_EVENTS, GWG_MODE)
40 if ef >= 0 {
41 gwg_puts(ef, "GWGUARD-RELAUNCH t=" as *u8); gwg_putn(ef, now)
42 gwg_puts(ef, " " as *u8); gwg_puts(ef, what); gwg_puts(ef, "\n" as *u8)
43 sys_close(ef)
44 }
45 return 0
46}
47
48// connect() to 127.0.0.1:port. 0 = the port is BOUND+listening (kernel handshake) -> alive. nonzero = dead.
49func gwg_port_alive(port: i64) -> i64 {
50 let fd: i64 = sys_socket(2, 1, 0)
51 if fd < 0 { return 1 } // can't even make a socket -> assume alive (NEVER false-relaunch on a probe fault)
52 sys_set_socket_timeout(fd, GWG_PROBE_TMO_S)
53 let a: *u8 = sys_mmap(16)
54 a[0]=2 as u8; a[1]=0 as u8
55 a[2]=((port>>8)&0xff) as u8; a[3]=(port&0xff) as u8
56 a[4]=127 as u8; a[5]=0 as u8; a[6]=0 as u8; a[7]=1 as u8
57 var zi: i64=8; while zi<16 { a[zi]=0 as u8; zi=zi+1 }
58 let rc: i64 = nx_connect_bounded(fd, a, 16, NX_CONN_DEFAULT_MS)
59 sys_close(fd)
60 if rc == 0 { return 1 }
61 return 0
62}
63
64// fork + setsid + chdir + redirect-log + execve the service DETACHED. The child becomes the daemon; the
65// parent does NOT wait4 (it would block forever on a running daemon) -> after the guard exits the daemon
66// reparents to init, matching the originals. argv MUST be a NULL-terminated *i64 vector.
67func gwg_launch(path: *u8, argv: *i64) -> i64 {
68 let pid: i64 = sys_fork()
69 if pid == 0 {
70 nx_setsid()
71 sys_chdir(GWG_CWD)
72 let lf: i64 = sys_openat_append(GWG_LAUNCHLOG, GWG_MODE)
73 if lf >= 0 { sys_dup3(lf, 1, 0); sys_dup3(lf, 2, 0) }
74 let envp: *i64 = sys_mmap(16) as *i64
75 envp[0] = "PATH=/usr/bin:/bin" as *u8 as i64; envp[1] = 0
76 sys_execve(path, argv, envp)
77 sys_exit(127)
78 }
79 return pid
80}
81
82// ensure the service on `port` is alive; relaunch if not. returns 1 iff a relaunch was issued.
83func gwg_ensure(port: i64, path: *u8, argv: *i64) -> i64 {
84 if gwg_port_alive(port) == 1 { return 0 }
85 gwg_launch(path, argv)
86 sys_poll(0 as *u8, 0, 300) // brief settle so a down-backend gets a head start before the gateway
87 return 1
88}
89
90func main(argc: i64, argv: *i64) -> i64 {
91 // ---- TEST MODE: `<check_port> <exec_path> [service args...]` supervise ONE service vs a SPARE victim ----
92 if argc >= 3 {
93 let tport: i64 = gwg_atoi(argv[1] as *u8)
94 let tpath: *u8 = argv[2] as *u8
95 let tv: *i64 = sys_mmap(8 * (argc + 2)) as *i64
96 var i: i64 = 2; var k: i64 = 0
97 while i < argc { tv[k] = argv[i]; k = k + 1; i = i + 1 }
98 tv[k] = 0
99 let did: i64 = gwg_ensure(tport, tpath, tv)
100 gwg_puts(1, "GWGUARD-TEST port=" as *u8); gwg_putn(1, tport)
101 if did == 1 { gwg_puts(1, " RELAUNCHED\n" as *u8) } else { gwg_puts(1, " already-alive\n" as *u8) }
102 sys_exit(0); return 0
103 }
104
105 // ---- PRODUCTION: supervise BACKEND first (the gateway depends on it), then the GATEWAY ----
106 let sv: *i64 = sys_mmap(32) as *i64
107 sv[0] = "nx_gallery_serve.elf" as *u8 as i64
108 sv[1] = "18090" as *u8 as i64
109 sv[2] = 0
110 let gv: *i64 = sys_mmap(96) as *i64
111 gv[0] = "nx_gallery_gateway.elf" as *u8 as i64
112 gv[1] = "18190" as *u8 as i64
113 gv[2] = "knowledge/status/galx_gw_keys" as *u8 as i64
114 gv[3] = "knowledge/status/galx_gw_store" as *u8 as i64
115 gv[4] = "1000000000" as *u8 as i64
116 gv[5] = "18090" as *u8 as i64
117 gv[6] = "0" as *u8 as i64
118 gv[7] = "65536" as *u8 as i64
119 gv[8] = "3" as *u8 as i64
120 gv[9] = "4" as *u8 as i64
121 gv[10] = 0
122
123 let br: i64 = gwg_ensure(GWG_BACKEND_PORT, GWG_SERVE, sv)
124 let gr: i64 = gwg_ensure(GWG_GATEWAY_PORT, GWG_GATEWAY, gv)
125
126 // truncate-write the CURRENT status (liveness proof, no growth); events log keeps relaunch HISTORY.
127 let now: i64 = sys_now_realtime_sec()
128 let st: i64 = sys_openat_wr(GWG_STATUS, GWG_MODE)
129 if st >= 0 {
130 gwg_puts(st, "GWGUARD t=" as *u8); gwg_putn(st, now)
131 gwg_puts(st, " backend=" as *u8); if br==1 { gwg_puts(st, "RELAUNCHED" as *u8) } else { gwg_puts(st, "up" as *u8) }
132 gwg_puts(st, " gateway=" as *u8); if gr==1 { gwg_puts(st, "RELAUNCHED" as *u8) } else { gwg_puts(st, "up" as *u8) }
133 gwg_puts(st, "\n" as *u8)
134 sys_close(st)
135 }
136 if br==1 { gwg_event(now, "backend-serve-18090" as *u8) }
137 if gr==1 { gwg_event(now, "gateway-18190" as *u8) }
138 sys_exit(0); return 0
139}