nx_deploy_lib.nx source
↩ module page · 207 lines · 11021 B
1// nx_deploy_lib.nx -- the SAFETY CORE that turns "pushing live" from an F-level manual shit-show into ONE
2// reliable gated call. Operator: "im tired of pushing live being such a shit show its f level right now."
3// What was missing (per the deploy evaluation): VALIDATE-before-promote, HEALTH-verify-after, AUTO-ROLLBACK
4// on fail. This lib is those guarantees, pure + gateable; nx_deploy.nx wires them around the PROVEN organs
5// (nx_aw_hostctl deploy/status/rollback, atomic rename, sovereign SSH). license_tier: ORIGINAL
6import "nx_syscalls.nx"
7import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc)
8const K_MAGIC_2277: i64 = 2277
9const K_MAGIC_4096: i64 = 4096
10const K_MAGIC_4095: i64 = 4095
11const K_MAGIC_262144: i64 = 262144
12const K_MAGIC_262140: i64 = 262140
13
14func dp_len(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
15func dp_w(fd: i64, s: *u8) -> i64 { let n: i64=dp_len(s); sys_write(fd,s,n); return 0 }
16// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer
17// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the
18// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls).
19// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign.
20func dp_wn(fd: i64, v: i64) -> i64 { nxi_fd(fd, v); return 0 }
21func dp_read(path: *u8, buf: *u8, cap: i64) -> i64 {
22 let fd: i64 = sys_openat_rd(path)
23 if fd < 0 { return 0 }
24 var n: i64 = 0
25 var r: i64 = sys_read(fd, buf, cap - 1)
26 while r > 0 { n = n + r; if n >= cap - 1 { r = 0 } else { r = sys_read(fd, buf + n, cap - 1 - n) } }
27 sys_close(fd)
28 return n
29}
30func dp_writefile(path: *u8, buf: *u8, n: i64) -> i64 {
31 let fd: i64 = sys_openat_wr(path, 0x1a4)
32 if fd < 0 { return 0 - 1 }
33 sys_write(fd, buf, n)
34 sys_close(fd)
35 return 0
36}
37
38// VALIDATE-BEFORE-PROMOTE (fail-closed): never ship/promote a corrupt artifact. kind 0=binary(ELF magic),
39// 1=html/text(must contain a '<' tag), 2=any-non-empty. returns 1 valid / 0 invalid.
40func dep_validate(path: *u8, kind: i64) -> i64 {
41 let buf: *u8 = sys_mmap(K_MAGIC_4096)
42 let n: i64 = dp_read(path, buf, K_MAGIC_4095)
43 if n <= 0 { return 0 }
44 if kind == 0 {
45 if n < 4 { return 0 }
46 if buf[0] != (127 as u8) { return 0 }
47 if buf[1] != (69 as u8) { return 0 }
48 if buf[2] != (76 as u8) { return 0 }
49 if buf[3] != (70 as u8) { return 0 }
50 return 1
51 }
52 if kind == 1 {
53 var i: i64 = 0
54 while i < n { if buf[i] == (60 as u8) { return 1 } i = i + 1 }
55 return 0
56 }
57 return 1
58}
59
60// THE DEPLOY STATE MACHINE (the safety logic). Given the step results, decide:
61// 0 = SUCCESS (promote stuck + healthy) ; 1 = ROLLBACK (promoted but unhealthy, or promote failed) ;
62// 2 = ABORT (invalid artifact or ship failed -- nothing was promoted, site untouched).
63// This is what guarantees a deploy NEVER leaves the site down: unhealthy -> automatic rollback.
64func dep_decide(valid: i64, ship_rc: i64, promote_rc: i64, health_ok: i64) -> i64 {
65 if valid == 0 { return 2 }
66 if ship_rc != 0 { return 2 }
67 if promote_rc != 0 { return 1 }
68 if health_ok == 0 { return 1 }
69 return 0
70}
71
72// fork+exec a proven ELF with args (args[i] = *u8 cast to i64); wait; return its exit code (-1 on signal).
73func dep_run(elf: *u8, args: *i64, nargs: i64) -> i64 {
74 let pid: i64 = sys_fork()
75 if pid == 0 {
76 let argv: *i64 = sys_mmap(8 * (nargs + 2)) as *i64
77 let envp: *i64 = sys_mmap(16) as *i64; envp[0] = 0
78 argv[0] = elf as i64
79 var i: i64 = 0
80 while i < nargs { argv[i+1] = args[i]; i = i + 1 }
81 argv[nargs+1] = 0
82 sys_execve_clean(elf, argv, envp)
83 sys_exit(127)
84 }
85 let st: *i64 = sys_mmap(16) as *i64
86 sys_wait4(pid, st, 0)
87 if (st[0] % 128) != 0 { return 0 - 1 }
88 return (st[0] >> 8) & 0xff
89}
90
91func dp_contains(buf: *u8, n: i64, pat: *u8) -> i64 {
92 let pl: i64 = dp_len(pat)
93 if pl == 0 { return 0 }
94 var i: i64 = 0
95 while i + pl <= n {
96 var k: i64 = 0
97 var hit: i64 = 1
98 while k < pl { if buf[i+k] != pat[k] { hit = 0; k = pl } else { k = k + 1 } }
99 if hit == 1 { return 1 }
100 i = i + 1
101 }
102 return 0
103}
104// THE REAL health check: the deployed daemon's "...: UP" line must be PRESENT in the status OUTPUT. NOT just
105// status exiting 0 -- that false-green reported HEALTHY while sites.elf(web:8443) was DOWN (proven live).
106func dep_status_healthy(buf: *u8, n: i64, up_token: *u8) -> i64 { return dp_contains(buf, n, up_token) }
107
108// BOUNDED variant of dep_run_capture (seq1383). THE DEFECT IT FIXES: dep_run_capture waits UNBOUNDED, so a
109// child that never exits hangs its caller forever. Called from a REQUEST HANDLER that is a denial-of-service on
110// the entire daemon BY CONSTRUCTION -- it took the mgmt control plane down for ~20 minutes, and the only remedy
111// was ssh, because mgmt's own recovery path runs through mgmt.
112// TWO STRUCTURAL FIXES, not one:
113// (1) BOUNDED WAIT: poll wait4 with WNOHANG until a deadline, then SIGKILL. The caller can no longer be hung
114// by a misbehaving child, whatever that child does.
115// (2) PROCESS GROUP: the child calls setpgid(0,0) so it leads its own group, and on timeout we signal the whole
116// GROUP (kill(-pgid)). The original hang left an ORPHANED GRANDCHILD (a daemon the gate had forked) still
117// holding inherited fds -- killing only the direct child would not have reclaimed it. Orphaned descendants
118// are now impossible rather than merely unlikely.
119// Returns: >=0 child exit code · -1 signalled · -2 TIMEOUT (distinct, never folded into a RED verdict).
120func dep_run_capture_bounded(elf: *u8, args: *i64, nargs: i64, outfile: *u8, deadline_ms: i64) -> i64 {
121 let pid: i64 = sys_fork()
122 if pid == 0 {
123 __syscall(109, 0, 0, 0, 0, 0, 0) // setpgid(0,0): lead our own group so the parent can reap the whole tree
124 // seq1463: hand the child a CLEAN SIGNAL SLATE. SIG_IGN survives fork AND execve, so a
125 // verifier launched by a daemon that ignores SIGPIPE inherits that ignore and its
126 // disease-controls silently cannot fire -- false RED, or worse, false GREEN.
127 sys_default_signal(13)
128 let fd: i64 = sys_openat_wr(outfile, 0x1a4)
129 if fd >= 0 { sys_dup3(fd, 1, 0); sys_dup3(fd, 2, 0) }
130 let argv: *i64 = sys_mmap(8 * (nargs + 2)) as *i64
131 let envp: *i64 = sys_mmap(16) as *i64; envp[0] = 0
132 argv[0] = elf as i64
133 var i: i64 = 0
134 while i < nargs { argv[i+1] = args[i]; i = i + 1 }
135 argv[nargs+1] = 0
136 sys_execve_clean(elf, argv, envp)
137 sys_exit(127)
138 }
139 let st: *i64 = sys_mmap(16) as *i64
140 var waited: i64 = 0
141 var step: i64 = 25
142 while waited < deadline_ms {
143 let r: i64 = sys_wait4(pid, st, 1) // WNOHANG
144 if r == pid {
145 if (st[0] % 128) != 0 { return 0 - 1 }
146 return (st[0] >> 8) & 0xff
147 }
148 if r < 0 { return 0 - 1 }
149 sys_sleep_ms(step)
150 waited = waited + step
151 }
152 // DEADLINE EXPIRED. seq1425: the first cut KILLED AND HOPED -- the caller was freed but the gate and its
153 // grandchild survived, converting a hang into a LEAK. Guessing which syscall failed cost a whole replay
154 // cycle over ssh, so this path now RECORDS ITS OWN EVIDENCE: every rc is captured and written to
155 // /tmp/nx_gaterun_diag.txt, which /api/gate_run folds into its JSON. The next timeout DIAGNOSES ITSELF.
156 // getpgid(pid) is the decisive datum: pg==pid proves setpgid(0,0) took effect, so a group-kill is safe;
157 // pg!=pid proves it did NOT, which is exactly the race that made kill(-pid) reach mgmt's own group.
158 // seq1418: NEVER group-kill on faith. If setpgid(0,0) has not yet taken effect in the freshly forked child
159 // (it races execve), the child is STILL IN OUR OWN PROCESS GROUP, and kill(-pid) would then reach the PARENT
160 // -- the mgmt daemon killing itself. Prime suspect for mgmt genuinely dying during the seq1383 replay.
161 // So ASK first: only signal the group when the child provably LEADS its own group (pgid == pid).
162 let pg: i64 = __syscall(121, pid, 0, 0, 0, 0, 0) // getpgid(pid)
163 if pg == pid { __syscall(129, 0 - pid, 9, 0, 0, 0, 0) } // rv64 kill=129; raw x86 62 is an RV64 KEY and was being translated to lseek(8) -- the kill never happened (debt idx K_MAGIC_2277)
164 __syscall(129, pid, 9, 0, 0, 0, 0) // rv64 kill=129; raw x86 62 is an RV64 KEY translated to lseek(8) -- the SIGKILL never happened (debt idx K_MAGIC_2277)
165 var reap: i64 = 0
166 while reap < 40 {
167 let r2: i64 = sys_wait4(pid, st, 1)
168 if r2 == pid { reap = 40 } else { if r2 < 0 { reap = 40 } else { sys_sleep_ms(25); reap = reap + 1 } }
169 }
170 // seq1425 EVIDENCE IN THE RETURN VALUE (no new helpers, no log file to lose): distinguish the THREE
171 // timeout outcomes instead of collapsing them into one opaque -2, which is what forced a replay over ssh.
172 // kill(pid,0) probes liveness without signalling: rc 0 = STILL ALIVE. pg==pid proves setpgid(0,0) took
173 // effect. So the verdict NAMES which half failed instead of leaving the caller to guess.
174 let alive: i64 = __syscall(129, pid, 0, 0, 0, 0, 0) // kill(pid,0): 0 = still alive, <0 = gone. rv64 kill=129; raw x86 62 is an RV64 KEY translated to lseek(8), so this LIVENESS CHECK WAS MEANINGLESS (debt idx K_MAGIC_2277)
175 if alive != 0 { return 0 - 2 } // TIMEOUT, child reaped cleanly (the good path)
176 if pg == pid { return 0 - 3 } // TIMEOUT, child SURVIVED a group-kill that WAS legitimate
177 return 0 - 4 // TIMEOUT, child SURVIVED and setpgid never took effect
178}
179
180// run an ELF capturing its stdout+stderr to outfile (so we can parse the result, not just trust the exit code).
181func dep_run_capture(elf: *u8, args: *i64, nargs: i64, outfile: *u8) -> i64 {
182 let pid: i64 = sys_fork()
183 if pid == 0 {
184 let fd: i64 = sys_openat_wr(outfile, 0x1a4)
185 if fd >= 0 { sys_dup3(fd, 1, 0); sys_dup3(fd, 2, 0) }
186 let argv: *i64 = sys_mmap(8 * (nargs + 2)) as *i64
187 let envp: *i64 = sys_mmap(16) as *i64; envp[0] = 0
188 argv[0] = elf as i64
189 var i: i64 = 0
190 while i < nargs { argv[i+1] = args[i]; i = i + 1 }
191 argv[nargs+1] = 0
192 sys_execve_clean(elf, argv, envp)
193 sys_exit(127)
194 }
195 let st: *i64 = sys_mmap(16) as *i64
196 sys_wait4(pid, st, 0)
197 if (st[0] % 128) != 0 { return 0 - 1 }
198 return (st[0] >> 8) & 0xff
199}
200// hostctl status -> capture -> is the deployed daemon actually UP? (the fix for the false-green).
201func dep_health(helf: *u8, up_token: *u8, tmpfile: *u8) -> i64 {
202 let stargs: *i64 = sys_mmap(16) as *i64; stargs[0] = "status" as i64
203 dep_run_capture(helf, stargs, 1, tmpfile)
204 let buf: *u8 = sys_mmap(K_MAGIC_262144)
205 let n: i64 = dp_read(tmpfile, buf, K_MAGIC_262140)
206 return dep_status_healthy(buf, n, up_token)
207}