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}