code wiki / (root) / nx_deploy_lib.nx

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}