code wiki / _hdl_build / nx_mgmt_promote_gate.nx
nx_mgmt_promote_gate.nx source
↩ module page · 455 lines · 26898 B
1// nx_mgmt_promote_gate.nx -- referee for POST /api/promote: fail-closed, never-brick, AND lease-clean.
2// In-process (no socket): crafts request BYTES and feeds the pure handler ma_do_promote, with the whole
3// filesystem rooted at /tmp/nx_promote_gt so nothing it does can touch the serving root. mau_build_path
4// builds BARE relative paths, which is what makes that isolation total rather than hopeful.
5//
6// MIGRATED ONTO nx_gate_verdict 2026-08-20 (was D001: a hand-rolled `fails` counter and a bare sys_exit,
7// so /api/gate_run could read a verdict but nx_gate_green could not, and the declared tooth count could
8// drift from the executed one). Every tooth is now a gv_check, so declared == executed by construction.
9//
10// WHAT IS NEW HERE, AND WHY. /api/promote took its 300s lease and then RETURNED THROUGH THREE EXITS THAT
11// NEVER RELEASED IT: the stage-rename failure, and both md_promote_staged_ex failure exits (one of which
12// answers 200 NOTHING-STAGED, so it reads as a harmless no-op while holding the lane shut for five
13// minutes). The release was written once, at the bottom of the success path. That defect was independently
14// rediscovered five times in three weeks because NOTHING WATCHED THE LEASE PLANE -- every existing tooth
15// read the RESPONSE TEXT, and the response of a leaking call is indistinguishable from a clean one.
16// So these teeth ask the PLANE, not the handler: they try to take the lease afterwards. Busy means leaked.
17//
18// The second defect on the same endpoint was a magic number pair: the lease is held for 300s and the
19// refusal advertised retry_after_s:30, so a caller obeying the advice hammered a lock that could not be
20// free for another 270. Both now render from MA_PROMOTE_LEASE_TTL_S, and a tooth below builds its
21// expected substring FROM THAT SAME CONST -- so the two cannot drift apart again without this going RED.
22//
23// license_tier: ORIGINAL expect_exit: 0 No hw writes (Rule 26).
24import "nx_mgmt_api.nx"
25import "nx_syscalls.nx"
26import "nx_gate_verdict.nx"
27
28const PG_MODE_FILE: i64 = 0x1a4
29const PG_MODE_DIR: i64 = 0x1ed
30const PG_BUF: i64 = 262144
31const PG_SMALL: i64 = 256
32
33func glen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
34func gcat(d: *u8, o: i64, s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { d[o+i] = s[i]; i = i + 1 } return o + i }
35func gcatn(d: *u8, o: i64, s: *u8, n: i64) -> i64 { var i: i64 = 0; while i < n { d[o+i] = s[i]; i = i + 1 } return o + n }
36func gcontains(hay: *u8, n: i64, needle: *u8) -> i64 {
37 let nn: i64 = glen(needle)
38 var i: i64 = 0
39 while i + nn <= n {
40 var m: i64 = 1
41 var j: i64 = 0
42 while j < nn { if (hay[i+j] as i64) != (needle[j] as i64) { m = 0; j = nn } else { j = j + 1 } }
43 if m == 1 { return 1 }
44 i = i + 1
45 }
46 return 0
47}
48// build "POST <line> HTTP/1.1\r\n...\r\n\r\n<body>" into dst; returns total length.
49func mkreq(dst: *u8, line: *u8, body: *u8) -> i64 {
50 let bn: i64 = glen(body)
51 var o: i64 = gcat(dst, 0, line)
52 o = gcat(dst, o, " HTTP/1.1\r\nHost: x\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: " as *u8)
53 let t: *u8 = sys_mmap(24)
54 var m: i64 = bn
55 var k: i64 = 0
56 if m == 0 { t[0] = 48 as u8; k = 1 }
57 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
58 var z: i64 = 0
59 while z < k { dst[o] = t[k-1-z]; o = o + 1; z = z + 1 }
60 o = gcat(dst, o, "\r\n\r\n" as *u8)
61 o = gcatn(dst, o, body, bn)
62 return o
63}
64func wfile(path: *u8, buf: *u8, n: i64) -> i64 {
65 let fd: i64 = sys_openat_wr(path, PG_MODE_FILE)
66 if fd < 0 { return 0 - 1 }
67 sys_write(fd, buf, n)
68 sys_close(fd)
69 return 0
70}
71func bytes_eq(path: *u8, want: *u8, wn: i64) -> i64 {
72 let szp: *i64 = sys_mmap(16) as *i64
73 let b: *u8 = sys_read_file(path, szp)
74 if (b as i64) == 0 { return 0 }
75 if szp[0] != wn { return 0 }
76 var i: i64 = 0
77 while i < wn { if (b[i] as i64) != (want[i] as i64) { return 0 } i = i + 1 }
78 return 1
79}
80func file_absent(path: *u8) -> i64 {
81 let fd: i64 = sys_openat_rd(path)
82 if fd < 0 { return 1 }
83 sys_close(fd)
84 return 0
85}
86
87// ---- THE LEASE PROBE: ASK THE PLANE, NEVER THE RESPONSE ------------------------------------------
88// A handler that leaks its lease answers EXACTLY the same bytes as one that does not, so no assertion
89// over the response can see this class at all. The only witness is the lease plane itself, and the only
90// question it answers honestly is "can I take this now". Returns 0 FREE, 1 HELD, 2 UNOBSERVABLE --
91// three states, because a fork failure or an absent nx_lease.elf must not read as either verdict.
92// The probe TTL is 1 second, so even a crashed probe cannot hold the name it just tested.
93func lease_state(lname: *u8) -> i64 {
94 let rc: i64 = md_lease_run("acquire" as *u8, lname, "nx-promote-gate-probe" as *u8, "1" as *u8, 4, "/tmp/nx_promote_gt/probe.out" as *u8)
95 if rc == 3 { return 1 }
96 if rc != 0 { return 2 }
97 md_lease_run("release" as *u8, lname, "nx-promote-gate-probe" as *u8, "0" as *u8, 3, "/tmp/nx_promote_gt/probe.out" as *u8)
98 return 0
99}
100// 1 iff the promote lease for <target> is demonstrably NOT held.
101func lease_free_for(target: *u8) -> i64 {
102 let lb: *u8 = sys_mmap(PG_SMALL)
103 md_lease_name_pfx("promote-" as *u8, target, lb)
104 if lease_state(lb) == 0 { return 1 }
105 return 0
106}
107
108// ---- CLEAR THE FIXTURE LEASES AT SETUP, NEVER ONLY AT TEARDOWN -------------------------------
109// A teardown does not run when a run crashes -- and worse here than usual, because THE VERY DEFECT
110// THIS GATE EXISTS TO CATCH LEAKS A 300 SECOND LEASE. So a RED run poisons the next five minutes of
111// runs with a lease-busy refusal that is indistinguishable from a second, unrelated failure.
112// MEASURED 2026-08-20 during this gate's own bite proof: the mutant run leaked promote-nxpromoteleak,
113// and the NEXT run reported four failures where its mutation could only cause two. A GATE THAT IS NOT
114// IDEMPOTENT REPORTS ON ITS FIRST RUN AND LIES ABOUT EVERY RUN AFTER, and a gate whose subject can
115// leak state must clear that state BEFORE it measures, not after. Owner is required by nx_lease, so
116// all three owners this file can produce are cleared; a release that does not apply is a no-op.
117func lease_clear(target: *u8) -> i64 {
118 let lb: *u8 = sys_mmap(PG_SMALL)
119 md_lease_name_pfx("promote-" as *u8, target, lb)
120 md_lease_run("release" as *u8, lb, "mgmt-api-promote" as *u8, "0" as *u8, 3, "/tmp/nx_promote_gt/probe.out" as *u8)
121 md_lease_run("release" as *u8, lb, "neg-control-holder" as *u8, "0" as *u8, 3, "/tmp/nx_promote_gt/probe.out" as *u8)
122 md_lease_run("release" as *u8, lb, "nx-promote-gate-probe" as *u8, "0" as *u8, 3, "/tmp/nx_promote_gt/probe.out" as *u8)
123 return 0
124}
125
126
127func tc_refusal_gate() -> i64 {
128 sys_mkdir("/tmp/nx_toolchain_refusal_gate" as *u8, 0x1ed)
129 if sys_chdir("/tmp/nx_toolchain_refusal_gate" as *u8) != 0 { return 2 }
130 sys_mkdir("buildroot" as *u8, 0x1ed)
131 sys_mkdir("buildroot/_offc" as *u8, 0x1ed)
132 sys_mkdir("knowledge" as *u8, 0x1ed)
133 let nm: *u8 = "nx_sov_build_run.elf" as *u8
134 let live: *u8 = "buildroot/_offc/nx_sov_build_run.elf" as *u8
135 let prev: *u8 = "buildroot/_offc/nx_sov_build_run.elf.prev" as *u8
136 let staged: *u8 = "nx_sov_build_run.elf.new" as *u8
137 let older: *u8 = sys_mmap(MD_TC_MIN_ELF)
138 let newer: *u8 = sys_mmap(MD_TC_MIN_ELF)
139 older[0]=127 as u8; older[1]=69 as u8; older[2]=76 as u8; older[3]=70 as u8
140 newer[0]=127 as u8; newer[1]=69 as u8; newer[2]=76 as u8; newer[3]=70 as u8
141 older[4]=1 as u8; newer[4]=2 as u8
142 if wfile(live, newer, MD_TC_MIN_ELF) != 0 { return 2 }
143 if wfile(prev, newer, MD_TC_MIN_ELF) != 0 { return 2 }
144 if wfile(staged, older, MD_TC_MIN_ELF) != 0 { return 2 }
145 if wfile(MD_PROV_HIST, older, 0) != 0 { return 2 }
146 let oldhash: i64 = md_prov_hash(staged)
147 let newhash: i64 = md_prov_hash(live)
148 if oldhash == 0 { return 2 }
149 if newhash == 0 { return 2 }
150 if oldhash == newhash { return 2 }
151 if md_prov_record(live, oldhash) != 0 { return 2 }
152 if md_prov_record(live, newhash) != 0 { return 2 }
153 let history: i64 = md_prov_hash(MD_PROV_HIST)
154 let out: *u8 = sys_mmap(262144)
155 let installed: *i64 = sys_mmap(8) as *i64
156 let ctr: *i64 = gv_ctr()
157 var attempt: i64 = 0
158 while attempt < 2 {
159 let n: i64 = ma_tc_install_checked(nm, out, installed)
160 gv_check("toolchain-refusal-1" as *u8, 1 - (n <= 0), ctr)
161 gv_check("toolchain-refusal-2" as *u8, 1 - (installed[0] != (0 - 2)), ctr)
162 gv_check("toolchain-refusal-3" as *u8, 1 - (gcontains(out,n,"HTTP/1.1 400" as *u8) != 1), ctr)
163 gv_check("toolchain-refusal-4" as *u8, 1 - (gcontains(out,n,"TOOLCHAIN_PROVENANCE_REFUSED" as *u8) != 1), ctr)
164 gv_check("toolchain-refusal-5" as *u8, 1 - (gcontains(out,n,"TOOLCHAIN-PROMOTED" as *u8) != 0), ctr)
165 gv_check("toolchain-refusal-6" as *u8, 1 - (bytes_eq(live,newer,MD_TC_MIN_ELF) != 1), ctr)
166 gv_check("toolchain-refusal-7" as *u8, 1 - (bytes_eq(prev,newer,MD_TC_MIN_ELF) != 1), ctr)
167 gv_check("toolchain-refusal-8" as *u8, 1 - (bytes_eq(staged,older,MD_TC_MIN_ELF) != 1), ctr)
168 gv_check("toolchain-refusal-9" as *u8, 1 - (md_prov_hash(MD_PROV_HIST) != history), ctr)
169 attempt = attempt + 1
170 }
171 if wfile(staged,older,4) != 0 { return 2 }
172 let bad: i64 = ma_tc_install_checked(nm,out,installed)
173 gv_check("toolchain-refusal-10" as *u8, 1 - (bad <= 0), ctr)
174 gv_check("toolchain-refusal-11" as *u8, 1 - (installed[0] != 0), ctr)
175 gv_check("toolchain-refusal-12" as *u8, 1 - (gcontains(out,bad,"no valid staged" as *u8) != 1), ctr)
176 gv_check("toolchain-refusal-13" as *u8, 1 - (bytes_eq(live,newer,MD_TC_MIN_ELF) != 1), ctr)
177 gv_check("toolchain-refusal-14" as *u8, 1 - (bytes_eq(prev,newer,MD_TC_MIN_ELF) != 1), ctr)
178 gv_check("toolchain-refusal-15" as *u8, 1 - (bytes_eq(staged,older,4) != 1), ctr)
179 gv_check("toolchain-refusal-16" as *u8, 1 - (md_prov_hash(MD_PROV_HIST) != history), ctr)
180 older[4] = 3 as u8
181 if wfile(staged,older,MD_TC_MIN_ELF) != 0 { return 2 }
182 let accepted: i64 = ma_tc_install_checked(nm,out,installed)
183 gv_check("accepted-install-continues-to-canary-boundary" as *u8,accepted == 0,ctr)
184 gv_check("accepted-install-reports-real-size" as *u8,installed[0] == MD_TC_MIN_ELF,ctr)
185 gv_check("accepted-install-live-is-candidate" as *u8,bytes_eq(live,older,MD_TC_MIN_ELF),ctr)
186 gv_check("accepted-install-banks-previous-live" as *u8,bytes_eq(prev,newer,MD_TC_MIN_ELF),ctr)
187 gv_check("accepted-install-consumes-stage" as *u8,file_absent(staged),ctr)
188 gv_check("rollback-reports-restoration" as *u8,md_tc_rollback(nm),ctr)
189 gv_check("rollback-restores-exact-previous-bytes" as *u8,bytes_eq(live,newer,MD_TC_MIN_ELF),ctr)
190 return gv_verdict("TOOLCHAIN-REFUSAL" as *u8,ctr,"refusal preserves artifacts; accepted install and rollback restore exact bytes" as *u8)
191}
192func main(argc: i64, argv: *i64) -> i64 {
193 if argc == 1 { return legacy_promote_gate() }
194 if argc == 2 {
195 if glen(argv[1] as *u8) == 17 {
196 if gcontains(argv[1] as *u8,17,"toolchain-refusal" as *u8) == 1 { return tc_refusal_gate() }
197 }
198 }
199 return 2
200}
201
202func legacy_promote_gate() -> i64 {
203 let ctr: *i64 = gv_ctr()
204 gv_head("nx_mgmt_promote_gate -- POST /api/promote: fail-closed, never-brick, and LEASE-CLEAN on every exit" as *u8)
205 sys_mkdir("/tmp/nx_promote_gt" as *u8, PG_MODE_DIR)
206 sys_chdir("/tmp/nx_promote_gt" as *u8)
207 let out: *u8 = sys_mmap(PG_BUF)
208 let req: *u8 = sys_mmap(PG_BUF)
209
210 // fixture bytes: a valid ELF header, a distinct "old live", and a non-ELF blob.
211 let elfnew: *u8 = sys_mmap(16)
212 elfnew[0] = 0x7f as u8
213 elfnew[1] = 69 as u8
214 elfnew[2] = 76 as u8
215 elfnew[3] = 70 as u8
216 elfnew[4] = 78 as u8
217 elfnew[5] = 69 as u8
218 elfnew[6] = 87 as u8
219 let oldlive: *u8 = sys_mmap(16)
220 oldlive[0] = 79 as u8
221 oldlive[1] = 76 as u8
222 oldlive[2] = 68 as u8
223 let garbage: *u8 = sys_mmap(16)
224 garbage[0] = 71 as u8
225 garbage[1] = 65 as u8
226 garbage[2] = 82 as u8
227
228 // ---- FIXTURE: A DECLARED-KIND CONF THE HANDLER CAN ACTUALLY READ ----------------------------
229 // ok_kind_of_path opens knowledge/status/organ_kind.conf RELATIVE TO CWD. Under /tmp that file did
230 // not exist, so every "daemon target" tooth here was silently testing the UNDECLARED fall-through
231 // instead of the daemon refusal it was named for -- a tooth measuring a different subject than its
232 // name claims. Assembling the conf at runtime restores the tooth's meaning and keeps it hermetic.
233 sys_mkdir("knowledge" as *u8, PG_MODE_DIR)
234 sys_mkdir("knowledge/status" as *u8, PG_MODE_DIR)
235 let kconf: *u8 = sys_mmap(PG_SMALL)
236 var kn: i64 = gcat(kconf, 0, "sites daemon\n" as *u8)
237 kn = gcat(kconf, kn, "nx_tools_api_serve daemon\n" as *u8)
238 wfile("knowledge/status/organ_kind.conf" as *u8, kconf, kn)
239
240 // clean slate (idempotent: a previous run's artefacts must not decide this one's verdict)
241 fio_unlink("sites.sov.elf.new" as *u8)
242 fio_unlink("sites.elf" as *u8)
243 fio_unlink("nxpromotepos.sov.elf.new" as *u8)
244 fio_unlink("nxpromotepos.elf" as *u8)
245 fio_unlink("nxpromotepos.elf.prev" as *u8)
246 fio_unlink("nxpromotepos.elf.new" as *u8)
247 fio_unlink("nxpromoteleak.sov.elf.new" as *u8)
248 fio_unlink("nxpromoteleak.elf" as *u8)
249 fio_unlink("nx_ecomat_seed.elf" as *u8)
250 fio_unlink("nx_ecomat_seed.sov.elf.new" as *u8)
251 fio_unlink("nx_ecomat_beat.sov.elf.new" as *u8)
252 fio_unlink("nx_ecomat_beat.elf" as *u8)
253 fio_unlink("nx_ecomat_beat.elf.prev" as *u8)
254 fio_unlink("nxpromotebusy.sov.elf.new" as *u8)
255 fio_unlink("nxpromotebusy.elf" as *u8)
256 fio_unlink("nxpromotedig.sov.elf.new" as *u8)
257 fio_unlink("nxpromotedig.elf" as *u8)
258 fio_unlink("nxpromotenoconf.sov.elf.new" as *u8)
259 fio_unlink("nxpromotenoconf.elf" as *u8)
260
261 lease_clear("nxpromotepos" as *u8)
262 lease_clear("nxpromoteleak" as *u8)
263 lease_clear("nxpromotebusy" as *u8)
264 lease_clear("nxpromotedig" as *u8)
265 lease_clear("nxpromotenoconf" as *u8)
266 lease_clear("nx_ecomat_seed" as *u8)
267 lease_clear("nx_ecomat_beat" as *u8)
268 lease_clear("sites" as *u8)
269
270 // ---- PRECONDITION: the lease plane must answer, or every lease tooth below is unobservable ----
271 // A round trip on a name nothing else uses. If this cannot be done the gate SKIPs rather than
272 // reporting the subject broken -- "I could not look" is not "I looked and it is broken".
273 var planeok: i64 = 0
274 if lease_state("promote-nxpromoteplaneprobe" as *u8) == 0 { planeok = 1 }
275 gv_need("lease plane answers an acquire/release round trip" as *u8, planeok, ctr)
276
277 // ---- T1 ANTI-VACUITY, FIRST: A GENUINE PROMOTE STILL WORKS -----------------------------------
278 // This tooth is what a lazy "fix" cannot pass. Never acquiring the lease at all, or refusing every
279 // request, would satisfy every lease-is-free assertion below and silently break promotion. The
280 // positive control has to come first for exactly that reason.
281 wfile("nxpromotepos.sov.elf.new" as *u8, elfnew, 7)
282 wfile("nxpromotepos.elf" as *u8, oldlive, 3)
283 let n1: i64 = mkreq(req, "POST /api/promote" as *u8, "target=nxpromotepos&confirm=yes&allow_unverified_bytes=yes&allow_capability_loss=yes" as *u8)
284 let r1: i64 = ma_do_promote(req, n1, out)
285 var t1: i64 = 1
286 if gcontains(out, r1, "200 OK" as *u8) == 0 { t1 = 0 }
287 if gcontains(out, r1, "PROMOTED" as *u8) == 0 { t1 = 0 }
288 if bytes_eq("nxpromotepos.elf" as *u8, elfnew, 7) == 0 { t1 = 0 }
289 if bytes_eq("nxpromotepos.elf.prev" as *u8, oldlive, 3) == 0 { t1 = 0 }
290 if file_absent("nxpromotepos.sov.elf.new" as *u8) == 0 { t1 = 0 }
291 gv_check("anti-vacuity-a-genuine-promote-still-installs-and-banks-prev" as *u8, t1, ctr)
292
293 // ---- T2 the success path releases what it took ------------------------------------------------
294 gv_check("lease-released-after-a-successful-promote" as *u8, lease_free_for("nxpromotepos" as *u8), ctr)
295
296 // ---- T3 THE PRE-LEASE NO-OP MUST NOT TOUCH THE PLANE ------------------------------------------
297 // Corrects the record: the FIRST NOTHING-STAGED sits BEFORE the acquire, so a bare probe never
298 // leaked. Pinning that here stops the next reader re-deriving it from the same wrong hypothesis.
299 // THE TARGET NAME IS PART OF THE FIXTURE. An UNDECLARED name with nothing staged is refused by the
300 // promotable check LONG before the branch this tooth is named for, so the first draft of it passed
301 // judgement on name policy instead. A legacy-allowlisted name reaches the staged-artefact check.
302 wfile("nx_ecomat_seed.elf" as *u8, oldlive, 3)
303 let n3: i64 = mkreq(req, "POST /api/promote" as *u8, "target=nx_ecomat_seed&confirm=yes" as *u8)
304 let r3: i64 = ma_do_promote(req, n3, out)
305 var t3: i64 = 1
306 if gcontains(out, r3, "NOTHING-STAGED" as *u8) == 0 { t3 = 0 }
307 if lease_free_for("nx_ecomat_seed" as *u8) == 0 { t3 = 0 }
308 gv_check("neg-control-the-pre-lease-nothing-staged-no-op-never-takes-the-lease" as *u8, t3, ctr)
309
310 // ---- T4 THE DECISIVE ONE: A POST-ACQUIRE FAILURE EXIT MUST RELEASE ----------------------------
311 // The lever is deterministic and needs no timing: make <t>.elf.new a DIRECTORY, so the very first
312 // mutating step after the acquire -- renaming the staged file onto it -- fails with EISDIR. That is
313 // one of the three exits that used to return while still holding the lease.
314 wfile("nxpromoteleak.sov.elf.new" as *u8, elfnew, 7)
315 wfile("nxpromoteleak.elf" as *u8, oldlive, 3)
316 sys_mkdir("nxpromoteleak.elf.new" as *u8, PG_MODE_DIR)
317 let n4: i64 = mkreq(req, "POST /api/promote" as *u8, "target=nxpromoteleak&confirm=yes&allow_unverified_bytes=yes&allow_capability_loss=yes" as *u8)
318 let r4: i64 = ma_do_promote(req, n4, out)
319 gv_check("lease-released-after-a-post-acquire-failure-exit" as *u8, lease_free_for("nxpromoteleak" as *u8), ctr)
320
321 // ---- T5 ...AND THAT EXIT STILL REFUSES, so T4 cannot be passed by promoting anyway -------------
322 var t5: i64 = 1
323 if gcontains(out, r4, "stage-rename failed" as *u8) == 0 { t5 = 0 }
324 if bytes_eq("nxpromoteleak.elf" as *u8, oldlive, 3) == 0 { t5 = 0 }
325 gv_check("neg-control-that-same-failure-exit-still-refuses-and-leaves-live-untouched" as *u8, t5, ctr)
326
327 // ---- T6 THE ADVERTISED BACKOFF IS THE CONFIGURED TTL ------------------------------------------
328 // The expected substring is BUILT FROM MA_PROMOTE_LEASE_TTL_S, not typed here. That is the whole
329 // point: a reader who changes the lock duration moves this tooth with it, and a reader who hardcodes
330 // a different number into the message moves the message away from this tooth. One source or RED.
331 wfile("nxpromotebusy.sov.elf.new" as *u8, elfnew, 7)
332 wfile("nxpromotebusy.elf" as *u8, oldlive, 3)
333 let hb: *u8 = sys_mmap(PG_SMALL)
334 md_lease_name_pfx("promote-" as *u8, "nxpromotebusy" as *u8, hb)
335 let heldrc: i64 = md_lease_run("acquire" as *u8, hb, "neg-control-holder" as *u8, "60" as *u8, 4, "/tmp/nx_promote_gt/probe.out" as *u8)
336 var heldok: i64 = 0
337 if heldrc == 0 { heldok = 1 }
338 gv_need("a foreign holder can be planted on the promote lease" as *u8, heldok, ctr)
339 let n6: i64 = mkreq(req, "POST /api/promote" as *u8, "target=nxpromotebusy&confirm=yes&allow_unverified_bytes=yes&allow_capability_loss=yes" as *u8)
340 let r6: i64 = ma_do_promote(req, n6, out)
341 // TS9 (2026-08-30): a 300 s exclusive lock is NOT a retry situation -- the refusal advertises the
342 // DISTINCT CLASS instead of a wait scalar (ts_retry_after_derived; RFC 9110 puts no accuracy
343 // obligation on Retry-After, so any scalar synchronises the herd -- measured on this very lease).
344 var t6: i64 = 1
345 if gcontains(out, r6, "lease-busy" as *u8) == 0 { t6 = 0 }
346 if gcontains(out, r6, "neg-control-holder" as *u8) == 0 { t6 = 0 }
347 if gcontains(out, r6, "LEASE-HELD-DO-NOT-RETRY" as *u8) == 0 { t6 = 0 }
348 gv_check("lease-busy-refusal-advertises-the-do-not-retry-class-not-a-wait-scalar" as *u8, t6, ctr)
349
350 // ---- T7 and the stale 10x-short advice is gone ------------------------------------------------
351 var t7: i64 = 1
352 if gcontains(out, r6, "\"retry_after_s\":" as *u8) == 1 { t7 = 0 }
353 if bytes_eq("nxpromotebusy.sov.elf.new" as *u8, elfnew, 7) == 0 { t7 = 0 }
354 gv_check("neg-control-no-wait-scalar-of-ANY-value-rides-the-lease-refusal-and-a-busy-refusal-stages-nothing" as *u8, t7, ctr)
355 md_lease_run("release" as *u8, hb, "neg-control-holder" as *u8, "0" as *u8, 3, "/tmp/nx_promote_gt/probe.out" as *u8)
356
357 // ---- T8 a DECLARED daemon must be sent to the health-checked /api/deploy ----------------------
358 wfile("sites.sov.elf.new" as *u8, elfnew, 7)
359 let n8: i64 = mkreq(req, "POST /api/promote" as *u8, "target=sites&confirm=yes" as *u8)
360 let r8: i64 = ma_do_promote(req, n8, out)
361 var t8: i64 = 1
362 if gcontains(out, r8, "400" as *u8) == 0 { t8 = 0 }
363 if gcontains(out, r8, "/api/deploy" as *u8) == 0 { t8 = 0 }
364 if bytes_eq("sites.sov.elf.new" as *u8, elfnew, 7) == 0 { t8 = 0 }
365 if file_absent("sites.elf" as *u8) == 0 { t8 = 0 }
366 gv_check("neg-control-declared-daemon-refused-to-api-deploy-staged-untouched" as *u8, t8, ctr)
367
368 // ---- T9 path escape ---------------------------------------------------------------------------
369 let n9: i64 = mkreq(req, "POST /api/promote" as *u8, "target=../etc/x&confirm=yes" as *u8)
370 let r9: i64 = ma_do_promote(req, n9, out)
371 var t9: i64 = 1
372 if gcontains(out, r9, "400" as *u8) == 0 { t9 = 0 }
373 if gcontains(out, r9, "invalid target name" as *u8) == 0 { t9 = 0 }
374 gv_check("neg-control-path-escape-refused-by-sanitize" as *u8, t9, ctr)
375
376 // ---- T10 nothing staged and nothing live ------------------------------------------------------
377 let n10: i64 = mkreq(req, "POST /api/promote" as *u8, "target=nx_ecomat_beat&confirm=yes" as *u8)
378 let r10: i64 = ma_do_promote(req, n10, out)
379 var t10: i64 = 1
380 if gcontains(out, r10, "400" as *u8) == 0 { t10 = 0 }
381 // PHRASE MOVED 2026-08-20 (lane J per-conjunct refusals): the organ path's no-staged-no-live answer
382 // is its own named text now; "no valid staged" survives only on the toolchain-upload path.
383 if gcontains(out, r10, "no staged .elf.new and no live artefact" as *u8) == 0 { t10 = 0 }
384 gv_check("neg-control-no-staged-and-no-live-artefact-refused" as *u8, t10, ctr)
385
386 // ---- T11 a non-ELF staged blob is never installed ---------------------------------------------
387 // NOTE the ordering dependency with the tooth above: it needs nx_ecomat_beat to have NO artefacts,
388 // this one stages them. Sequential, not incidental.
389 wfile("nx_ecomat_beat.sov.elf.new" as *u8, garbage, 3)
390 wfile("nx_ecomat_beat.elf" as *u8, oldlive, 3)
391 let n11: i64 = mkreq(req, "POST /api/promote" as *u8, "target=nx_ecomat_beat&confirm=yes&allow_unverified_bytes=yes" as *u8)
392 let r11: i64 = ma_do_promote(req, n11, out)
393 var t11: i64 = 1
394 if gcontains(out, r11, "PROMOTED" as *u8) == 1 { t11 = 0 }
395 if bytes_eq("nx_ecomat_beat.elf" as *u8, oldlive, 3) == 0 { t11 = 0 }
396 if lease_free_for("nx_ecomat_beat" as *u8) == 0 { t11 = 0 }
397 gv_check("neg-control-non-elf-staged-never-installed-live-untouched-lease-untouched" as *u8, t11, ctr)
398
399 // ---- T12 confirm=yes is mandatory -------------------------------------------------------------
400 wfile("nxpromotenoconf.sov.elf.new" as *u8, elfnew, 7)
401 wfile("nxpromotenoconf.elf" as *u8, oldlive, 3)
402 let n12: i64 = mkreq(req, "POST /api/promote" as *u8, "target=nxpromotenoconf" as *u8)
403 let r12: i64 = ma_do_promote(req, n12, out)
404 var t12: i64 = 1
405 if gcontains(out, r12, "400" as *u8) == 0 { t12 = 0 }
406 if gcontains(out, r12, "confirm=yes" as *u8) == 0 { t12 = 0 }
407 if bytes_eq("nxpromotenoconf.sov.elf.new" as *u8, elfnew, 7) == 0 { t12 = 0 }
408 if bytes_eq("nxpromotenoconf.elf" as *u8, oldlive, 3) == 0 { t12 = 0 }
409 gv_check("neg-control-missing-confirm-refused-staged-and-live-untouched" as *u8, t12, ctr)
410
411 // ---- T13 the digest is required, and refusing for it must not take the lease ------------------
412 wfile("nxpromotedig.sov.elf.new" as *u8, elfnew, 7)
413 wfile("nxpromotedig.elf" as *u8, oldlive, 3)
414 let n13: i64 = mkreq(req, "POST /api/promote" as *u8, "target=nxpromotedig&confirm=yes" as *u8)
415 let r13: i64 = ma_do_promote(req, n13, out)
416 var t13: i64 = 1
417 if gcontains(out, r13, "digest-required" as *u8) == 0 { t13 = 0 }
418 if bytes_eq("nxpromotedig.elf" as *u8, oldlive, 3) == 0 { t13 = 0 }
419 if lease_free_for("nxpromotedig" as *u8) == 0 { t13 = 0 }
420 gv_check("neg-control-expect-sha256-required-live-untouched-lease-untouched" as *u8, t13, ctr)
421
422 // ---- T14 a declared daemon in the TOOLS family is refused the same way ------------------------
423 let n14: i64 = mkreq(req, "POST /api/promote" as *u8, "target=nx_tools_api_serve&confirm=yes" as *u8)
424 let r14: i64 = ma_do_promote(req, n14, out)
425 var t14: i64 = 1
426 if gcontains(out, r14, "/api/deploy" as *u8) == 0 { t14 = 0 }
427 let n14b: i64 = mkreq(req, "POST /api/promote" as *u8, "target=nx_shelltool&confirm=yes" as *u8)
428 let r14b: i64 = ma_do_promote(req, n14b, out)
429 // PHRASE MOVED 2026-08-20: a tool with a LIVE artefact and nothing staged now gets the recoverable
430 // NOTHING-STAGED receipt (live_sha reported so a lost promote response can be reconciled).
431 if gcontains(out, r14b, "NOTHING-STAGED" as *u8) == 0 { t14 = 0 }
432 gv_check("neg-control-declared-tools-daemon-refused-while-a-tool-organ-reaches-the-staging-check" as *u8, t14, ctr)
433
434 // cleanup -- leaves the fixture dir reusable; the .elf.new DIRECTORY is deliberately left in place
435 // because sys_rmdir is not composed here and re-creating it is idempotent anyway.
436 fio_unlink("sites.sov.elf.new" as *u8)
437 fio_unlink("sites.elf" as *u8)
438 fio_unlink("nxpromotepos.elf" as *u8)
439 fio_unlink("nxpromotepos.elf.prev" as *u8)
440 fio_unlink("nxpromoteleak.sov.elf.new" as *u8)
441 fio_unlink("nxpromoteleak.elf" as *u8)
442 fio_unlink("nx_ecomat_seed.elf" as *u8)
443 fio_unlink("nx_ecomat_beat.sov.elf.new" as *u8)
444 fio_unlink("nx_ecomat_beat.elf" as *u8)
445 fio_unlink("nxpromotebusy.sov.elf.new" as *u8)
446 fio_unlink("nxpromotebusy.elf" as *u8)
447 fio_unlink("nxpromotedig.sov.elf.new" as *u8)
448 fio_unlink("nxpromotedig.elf" as *u8)
449 fio_unlink("nxpromotenoconf.sov.elf.new" as *u8)
450 fio_unlink("nxpromotenoconf.elf" as *u8)
451
452 let rc: i64 = gv_verdict("MGMT-PROMOTE-GATE" as *u8, ctr, "the promote lease is released on every exit that takes it, and the backoff the endpoint advertises is rendered from the same constant that sets the lock duration" as *u8)
453 sys_exit(rc)
454 return rc
455}