code wiki / _hdl_build / nx_mgmt_promote_gate.nx

nx_mgmt_promote_gate.nx

buildroot/runtime/_hdl_build/nx_mgmt_promote_gate.nx

26898 B455 linesdepth 18pulls 86 transitivereach 0 importersview sourcekind gate/prooftopic mgmt
docsdependenciesstructsconstsfunctions

about

nx_mgmt_promote_gate.nx -- referee for POST /api/promote: fail-closed, never-brick, AND lease-clean. In-process (no socket): crafts request BYTES and feeds the pure handler ma_do_promote, with the whole filesystem rooted at /tmp/nx_promote_gt so nothing it does can touch the serving root. mau_build_path builds BARE relative paths, which is what makes that isolation total rather than hopeful. MIGRATED ONTO nx_gate_verdict 2026-08-20 (was D001: a hand-rolled `fails` counter and a bare sys_exit, so /api/gate_run could read a verdict but nx_gate_green could not, and the declared tooth count could drift from the executed one). Every tooth is now a gv_check, so declared == executed by construction. WHAT IS NEW HERE, AND WHY. /api/promote took its 300s lease and then RETURNED THROUGH THREE EXITS THAT NEVER RELEASED IT: the stage-rename failure, and both md_promote_staged_ex failure exits (one of which answers 200 NOTHING-STAGED, so it reads as a harmless no-op while holding the lane shut for five minutes). The release was written once, at the bottom of the success path. That defect was independently rediscovered five times in three weeks because NOTHING WATCHED THE LEASE PLANE -- every existing tooth read the RESPONSE TEXT, and the response of a leaking call is indistinguishable from a clean one. So these teeth ask the PLANE, not the handler: they try to take the lease afterwards. Busy means leaked. The second defect on the same endpoint was a magic number pair: the lease is held for 300s and the refusal advertised retry_after_s:30, so a caller obeying the advice hammered a lock that could not be free for another 270. Both now render from MA_PROMOTE_LEASE_TTL_S, and a tooth below builds its expected substring FROM THAT SAME CONST -- so the two cannot drift apart again without this going RED. license_tier: ORIGINAL expect_exit: 0 No hw writes (Rule 26).

dependencies 3 imports · 0 importers

nx_mgmt_api.nx nx_syscalls.nx nx_gate_verdict.nx nx_mgmt_promote_gate.nx

imports: nx_mgmt_api.nxnx_syscalls.nxnx_gate_verdict.nx

imported by: nobody (leaf or entry point)

call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown

main legacy_promote_gate gv_ctr sys_mmap nxa_die sys_write sys_exit nxa_lock_take nxa_lock_addr sys_write ↻ nxa_lock_give nxa_lock_addr ↻ nxa_report_overrun sys_write ↻ nxa_dump_printable sys_write ↻ nxa_dump_sizes sys_write ↻ gv_head gv_puts sys_write ↻ sys_mkdir sys_chdir sys_mmap ↻ sys_mmap ↻ gcat wfile sys_openat_wr sys_write ↻ sys_close lease_clear sys_mmap ↻ lease_state gv_need gv_puts ↻ mkreq glen gcat ↻ sys_mmap ↻ gcatn

structs

none

consts

28const PG_MODE_FILE: i64 = 0x1a4
29const PG_MODE_DIR: i64 = 0x1ed
30const PG_BUF: i64 = 262144
31const PG_SMALL: i64 = 256

functions

33func glen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
called by 3: gcontainsmkreqmain
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 }
called by 1: mkreq
36func gcontains(hay: *u8, n: i64, needle: *u8) -> i64
49func mkreq(dst: *u8, line: *u8, body: *u8) -> i64
64func wfile(path: *u8, buf: *u8, n: i64) -> i64
71func bytes_eq(path: *u8, want: *u8, wn: i64) -> i64
80func file_absent(path: *u8) -> i64
93func lease_state(lname: *u8) -> i64
101func lease_free_for(target: *u8) -> i64
117func lease_clear(target: *u8) -> i64
called by 1: legacy_promote_gate calls 1: sys_mmap
127func tc_refusal_gate() -> i64
192func main(argc: i64, argv: *i64) -> i64
202func legacy_promote_gate() -> i64