code wiki / _hdl_build / nx_deploy_stage_lib.nx

nx_deploy_stage_lib.nx source

↩ module page · 148 lines · 7024 B

1// nx_deploy_stage_lib.nx -- the missing sovereign primitive: copy a validated staged ELF into a deploy 2// `.new` slot. Closes the build->deploy name-bridge gap the api-first doctrine names as the last non-sovereign 3// rename: /api/build stages <buildtarget>.sov.elf.new but a daemon whose deploy artifact has a DIFFERENT base 4// (e.g. sites: build target nx_sites_daemon_v2, deploy slot sites.elf.new) can't be reached by the seq768 5// same-base auto-bridge, and nx_fs_write has only write/edit (no move), nx_restage targets buildroot/_offc. 6// SAFE BY CONSTRUCTION: the dst MUST end in ".new" -> this organ can NEVER overwrite a live binary; it only 7// STAGES. The never-brick promote (bank .prev, health-probe, auto-rollback) stays entirely in /api/deploy. 8// src MUST be a nonzero ELF (magic 0x7f 'E' 'L' 'F' + size>1024) so a truncated/garbage stage is refused. 9// license_tier: ORIGINAL genealogy: deploy-loop sovereignty (rename-gap closer) 10import "nx_syscalls.nx" 11const DS_MAGIC_1024: i64 = 1024 12const DS_MAGIC_4096: i64 = 4096 13const DS_MAGIC_2000: i64 = 2000 14 15const DS_MINSZ: i64 = 1024 // smallest plausible ELF; a smaller "binary" is a failed/torn build 16const DS_CAPMAX: i64 = 67108864 // 64MiB copy ceiling (edge daemon ~0.7MiB; guards a runaway mmap) 17 18func ds_w(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 19func ds_len(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n } 20func ds_cat(d: *u8, off: i64, s: *u8) -> i64 { var i: i64=0; var o: i64=off; while s[i]!=(0 as u8){d[o]=s[i]; o=o+1; i=i+1} return o } 21func ds_catn(d: *u8, off: i64, v: i64) -> i64 { 22 let t: *u8 = sys_mmap(28); var m: i64=v; var o: i64=off; if m<0 { d[o]=45 as u8; o=o+1; m=0-m } 23 var k: i64=0; if m==0 { t[0]=48 as u8; k=1 } while m>0 { t[k]=(48+(m%10)) as u8; m=m/10; k=k+1 } 24 var i: i64=0; while i<k { d[o]=t[k-1-i]; o=o+1; i=i+1 } return o 25} 26 27// dst path must end with ".new" -> refuses any live-binary target. 1=ok, 0=refused. 28func ds_dst_is_new(dst: *u8) -> i64 { 29 let n: i64 = ds_len(dst) 30 if n < 4 { return 0 } 31 if dst[n-4] != (46 as u8) { return 0 } // . 32 if dst[n-3] != (110 as u8) { return 0 } // n 33 if dst[n-2] != (101 as u8) { return 0 } // e 34 if dst[n-1] != (119 as u8) { return 0 } // w 35 return 1 36} 37 38// verify src is a nonzero ELF; returns size, or negative rc. sets magic ok. 39func ds_src_size(src: *u8) -> i64 { 40 let fd: i64 = sys_openat_rd(src) 41 if fd < 0 { return 0 - 3 } 42 let hdr: *u8 = sys_mmap(16) 43 let hn: i64 = sys_read(fd, hdr, 4) 44 if hn < 4 { sys_close(fd); return 0 - 4 } 45 var okmagic: i64 = 1 46 if hdr[0] != (127 as u8) { okmagic = 0 } 47 if hdr[1] != (69 as u8) { okmagic = 0 } 48 if hdr[2] != (76 as u8) { okmagic = 0 } 49 if hdr[3] != (70 as u8) { okmagic = 0 } 50 if okmagic == 0 { sys_close(fd); return 0 - 4 } 51 let sz: i64 = sys_lseek(fd, 0, 2) 52 sys_close(fd) 53 if sz < DS_MINSZ { return 0 - 6 } 54 if sz > DS_CAPMAX { return 0 - 7 } 55 return sz 56} 57 58// core: atomically stage src -> dst (dst.tmp then renameat). returns 0 ok, negative rc. 59// -2 dst not a .new slot | -3 src open | -4 bad/absent ELF magic | -6 too small | -7 too big 60// -8 dst.tmp open | -9 short write | -10 rename fail | -11 size mismatch after copy 61func ds_stage(src: *u8, dst: *u8) -> i64 { 62 if ds_dst_is_new(dst) == 0 { return 0 - 2 } 63 let sz: i64 = ds_src_size(src) 64 if sz < 0 { return sz } 65 let fd: i64 = sys_openat_rd(src) 66 if fd < 0 { return 0 - 3 } 67 let buf: *u8 = sys_mmap(sz + 16) 68 var got: i64 = 0 69 var nrd: i64 = sys_read(fd, buf, sz) 70 while nrd > 0 { got = got + nrd; if got >= sz { nrd = 0 } else { nrd = sys_read(fd, ((buf as i64)+got) as *u8, sz-got) } } 71 sys_close(fd) 72 if got != sz { return 0 - 9 } 73 // dst.tmp = dst + ".stg" 74 let tmp: *u8 = sys_mmap(512) 75 var to: i64 = ds_cat(tmp, 0, dst) 76 to = ds_cat(tmp, to, ".stg" as *u8) 77 tmp[to] = 0 as u8 78 let wf: i64 = sys_openat_wr(tmp, 420) // 0644 79 if wf < 0 { return 0 - 8 } 80 var wr: i64 = 0 81 var nw: i64 = sys_write(wf, buf, sz) 82 while nw > 0 { wr = wr + nw; if wr >= sz { nw = 0 } else { nw = sys_write(wf, ((buf as i64)+wr) as *u8, sz-wr) } } 83 sys_close(wf) 84 if wr != sz { return 0 - 9 } 85 if sys_renameat(tmp, dst) != 0 { return 0 - 10 } 86 // verify landed size 87 let vf: i64 = sys_openat_rd(dst) 88 if vf < 0 { return 0 - 11 } 89 let vsz: i64 = sys_lseek(vf, 0, 2) 90 sys_close(vf) 91 if vsz != sz { return 0 - 11 } 92 return 0 93} 94 95func ds_emit(src: *u8, dst: *u8, rc: i64, sz: i64) -> i64 { 96 let j: *u8 = sys_mmap(DS_MAGIC_1024) 97 var p: i64 = ds_cat(j, 0, "{\"organ\":\"nx_deploy_stage\",\"src\":\"" as *u8) 98 p = ds_cat(j, p, src) 99 p = ds_cat(j, p, "\",\"dst\":\"" as *u8) 100 p = ds_cat(j, p, dst) 101 p = ds_cat(j, p, "\",\"rc\":" as *u8) 102 p = ds_catn(j, p, rc) 103 p = ds_cat(j, p, ",\"bytes\":" as *u8) 104 p = ds_catn(j, p, sz) 105 p = ds_cat(j, p, ",\"verdict\":\"" as *u8) 106 if rc == 0 { p = ds_cat(j, p, "STAGED" as *u8) } else { p = ds_cat(j, p, "REFUSED" as *u8) } 107 p = ds_cat(j, p, "\",\"note\":\"dst must end .new (never a live binary); src must be a nonzero ELF; promote via /api/deploy (never-brick)\"}\n" as *u8) 108 sys_write(1, j, p) 109 return 0 110} 111 112// selftest: build a fake ELF fixture, stage it to a .new, verify byte-identical + size; refuse non-.new dst; 113// refuse a non-ELF src. 0 = GREEN. 114func ds_selftest() -> i64 { 115 var f: i64 = 0 116 // fixture: \x7fELF + 2000 bytes of pattern 117 let fx: *u8 = sys_mmap(DS_MAGIC_4096) 118 fx[0]=127 as u8; fx[1]=69 as u8; fx[2]=76 as u8; fx[3]=70 as u8 119 var i: i64 = 4 120 while i < DS_MAGIC_2000 { fx[i] = (65 + (i % 26)) as u8; i = i + 1 } 121 let wf: i64 = sys_openat_wr("dskat_src.elf" as *u8, 420) 122 if wf < 0 { return 1 } 123 sys_write(wf, fx, DS_MAGIC_2000); sys_close(wf) 124 // stage to a .new slot 125 let rc: i64 = ds_stage("dskat_src.elf" as *u8, "dskat_dst.elf.new" as *u8) 126 if rc != 0 { f = f + 1 } 127 // verify byte-identical + size 128 let vf: i64 = sys_openat_rd("dskat_dst.elf.new" as *u8) 129 if vf < 0 { f = f + 1 } else { 130 let vb: *u8 = sys_mmap(DS_MAGIC_4096) 131 let vn: i64 = sys_read(vf, vb, DS_MAGIC_4096) 132 sys_close(vf) 133 if vn != DS_MAGIC_2000 { f = f + 1 } 134 var j: i64 = 0 135 var mism: i64 = 0 136 while j < DS_MAGIC_2000 { if vb[j] != fx[j] { mism = 1; j = DS_MAGIC_2000 } else { j = j + 1 } } 137 if mism == 1 { f = f + 1 } 138 } 139 // refuse non-.new dst 140 let r2: i64 = ds_stage("dskat_src.elf" as *u8, "dskat_live.elf" as *u8) 141 if r2 != (0 - 2) { f = f + 1 } 142 // refuse non-ELF src 143 let bf: i64 = sys_openat_wr("dskat_bad.txt" as *u8, 420) 144 if bf >= 0 { sys_write(bf, "not an elf at all, plain text, over 1024 bytes..." as *u8, 48); sys_close(bf) } 145 let r3: i64 = ds_stage("dskat_bad.txt" as *u8, "dskat_bad.elf.new" as *u8) 146 if r3 != (0 - 4) { if r3 != (0 - 6) { f = f + 1 } } // -4 bad magic (or -6 too small); either refuses 147 return f 148}