nx_deploy_leash_gate.nx source
↩ module page · 95 lines · 4731 B
1// nx_deploy_leash_gate.nx -- gate for deployments-on-a-leash (owner accountability, anti-walk-away).
2// T1 PURE verdict table: alive+fresh=HEALTHY · alive+stale-beat=ABANDONED (the walk-away case,
3// invisible to pid-checks) · dead pid=DEAD (trumps fresh beat).
4// T2 DEAD roundtrip: registered deployment with a dead pid -> status reports DEAD, no reclaim.
5// T3 ABANDONED+RECLAIM: LIVE pid (this gate) with a FORCED-ancient beat -> walk reclaims: its
6// declared stop-file is touched + watch mark RECLAIMED (reads as RED in nx_watch = loud).
7// T4 HEALTHY: live pid + fresh beat -> HEALTHY, stop-file NOT touched.
8// T5 empty registry -> clean walk, verdict 0.
9// license_tier: ORIGINAL expect_exit: 0
10import "nx_deploy_leash.nx"
11
12func lz_reset() -> i64 {
13 sov_put(DL_STORE, "d:ids" as *u8, "x" as *u8, 0)
14 return 0
15}
16
17func main(argc: i64, argv: *i64) -> i64 {
18 std_putln("LEASH-GATE: deployments on a leash (owner-renewal liveness, reclaim on walk-away)" as *u8)
19 var pass: i64 = 0
20 // T1 pure table
21 let vh: i64 = dl_verdict(1, 10, 1000)
22 let va: i64 = dl_verdict(1, 5000, 1000)
23 let vd: i64 = dl_verdict(0, 10, 1000)
24 if vh == DL_HEALTHY && va == DL_ABANDONED && vd == DL_DEAD { pass = pass + 1; std_putln("T1 PASS verdict table (healthy/abandoned/dead; dead trumps fresh beat)" as *u8) }
25 if vh != DL_HEALTHY || va != DL_ABANDONED || vd != DL_DEAD { std_putln("T1 FAIL verdict table" as *u8) }
26 // T2 dead deployment
27 lz_reset()
28 ml_unlink("/tmp/lz_gate_dead_stop" as *u8)
29 dl_register("lz_dead" as *u8, "gate" as *u8, "fixture" as *u8, 88888888, 60000, "/tmp/lz_gate_dead_stop" as *u8)
30 let s2: i64 = dl_walk(0)
31 let sf2: i64 = sys_openat_rd("/tmp/lz_gate_dead_stop" as *u8)
32 if sf2 >= 0 { sys_close(sf2) }
33 if s2 == DL_DEAD && sf2 < 0 { pass = pass + 1; std_putln("T2 PASS dead pid -> DEAD, no reclaim action" as *u8) }
34 if s2 != DL_DEAD || sf2 >= 0 { std_puts("T2 FAIL s="); std_pdec(s2); std_puts("\n" as *u8) }
35 // T3 abandoned (live pid, ancient beat) -> walk reclaims via the DECLARED stop-file
36 lz_reset()
37 ml_unlink("/tmp/lz_gate_aband_stop" as *u8)
38 let me: i64 = sq_getpid_leash()
39 dl_register("lz_aband" as *u8, "gate" as *u8, "fixture" as *u8, me, 1000, "/tmp/lz_gate_aband_stop" as *u8)
40 let anc: *u8 = sys_mmap(64) as *u8
41 var ao: i64 = 0
42 anc[0] = 66 as u8; anc[1] = 69 as u8; anc[2] = 65 as u8; anc[3] = 84 as u8; anc[4] = 124 as u8
43 ao = 5 + std_itoa(sys_now_us() - 600000000, anc + 5)
44 anc[ao] = 0 as u8
45 sov_put_str("knowledge/store/watch_leash" as *u8, "lz_aband" as *u8, anc)
46 let s3: i64 = dl_walk(1)
47 let sf3: i64 = sys_openat_rd("/tmp/lz_gate_aband_stop" as *u8)
48 if sf3 >= 0 { sys_close(sf3) }
49 if s3 == DL_ABANDONED && sf3 >= 0 { pass = pass + 1; std_putln("T3 PASS walk-away reclaimed (live pid + stale beat -> stop-file touched + RECLAIMED mark)" as *u8) }
50 if s3 != DL_ABANDONED || sf3 < 0 { std_puts("T3 FAIL s="); std_pdec(s3); std_puts(" sf="); std_pdec(sf3); std_puts("\n" as *u8) }
51 // T4 healthy (live pid, fresh beat)
52 lz_reset()
53 ml_unlink("/tmp/lz_gate_ok_stop" as *u8)
54 dl_register("lz_ok" as *u8, "gate" as *u8, "fixture" as *u8, me, 600000, "/tmp/lz_gate_ok_stop" as *u8)
55 nw_mark("leash" as *u8, "lz_ok" as *u8, "BEAT" as *u8)
56 let s4: i64 = dl_walk(1)
57 let sf4: i64 = sys_openat_rd("/tmp/lz_gate_ok_stop" as *u8)
58 if sf4 >= 0 { sys_close(sf4) }
59 if s4 == DL_HEALTHY && sf4 < 0 { pass = pass + 1; std_putln("T4 PASS fresh-beat deployment HEALTHY, untouched" as *u8) }
60 if s4 != DL_HEALTHY || sf4 >= 0 { std_puts("T4 FAIL s="); std_pdec(s4); std_puts("\n" as *u8) }
61 // T5 empty registry
62 lz_reset()
63 let s5: i64 = dl_walk(1)
64 if s5 == 0 { pass = pass + 1; std_putln("T5 PASS empty registry -> clean walk" as *u8) }
65 if s5 != 0 { std_putln("T5 FAIL empty" as *u8) }
66 std_puts("LEASH-GATE pass=" as *u8)
67 std_pdec(pass)
68 if pass == 5 {
69 std_putln("/5 verdict=GREEN" as *u8)
70 sys_exit(0)
71 return 0
72 }
73 std_putln("/5 verdict=RED" as *u8)
74 sys_exit(1)
75 return 1
76}
77
78// own pid via /proc/self/stat (getpid has NO working const syscall form -- see gotchas 2026-07-16).
79func sq_getpid_leash() -> i64 {
80 let fd: i64 = sys_openat_rd("/proc/self/stat" as *u8)
81 if fd < 0 { return 0 - 1 }
82 let b: *u8 = sys_mmap(128) as *u8
83 let n: i64 = sys_read(fd, b, 127)
84 sys_close(fd)
85 if n <= 0 { return 0 - 1 }
86 var v: i64 = 0
87 var i: i64 = 0
88 var seen: i64 = 0
89 while i < n {
90 let c: i64 = b[i] as i64
91 if c >= 48 && c <= 57 { v = v * 10 + (c - 48); seen = 1; i = i + 1 } else { i = n }
92 }
93 if seen == 0 { return 0 - 1 }
94 return v
95}