code wiki / (root) / nx_intake_keep.nx

nx_intake_keep.nx source

↩ module page · 136 lines · 5475 B

1// nx_intake_keep.nx -- keep the public intake route ALIVE, without touching the supervisor. 2// 3// THE PROBLEM. nx_site_intake_serve answers :8033 and every contact form on every generated site depends on 4// it -- the meal pages literally suppress their forms when it stops answering. But it is NOT in nx_hostctl's 5// guard list, and that list is COMPILED IN, so adding it means rebuilding and deploying the supervisor. 6// 7// WHY NOT JUST DEPLOY THE SUPERVISOR. Measured, not assumed: hostctl's source is unchanged since 2026-07-31, 8// yet a fresh build differs from the live binary by +26,232 bytes -- so the delta is the TOOLCHAIN, not the 9// source. Rebuilding the supervisor means shipping whatever nx_cc has become, and nx_cc had an escaped gate 10// mutant land in it on 2026-08-06. A defect in a page is a page; a defect in the thing that respawns every 11// daemon is the host. That trade is bad at any blast radius, so this takes the other road. 12// 13// THE OTHER ROAD. The tickless clock is ALREADY supervised by hostctl and dispatches from a DATA registry 14// (knowledge/store/clockjobs-, added via nx_store_put, read-only to the clock). So supervision becomes a row, 15// not a recompile -- the same "new capability is data" move the rest of this lane runs on. 16// 17// FAIL-SAFE BY CONSTRUCTION: it spawns ONLY when the port refuses a connection, so a healthy daemon is never 18// disturbed and two keepers racing cannot produce two servers (the loser's bind fails and it exits). 19// 20// INSTRUMENTED SO A HEALTHY RUN IS STILL VISIBLE: spawns append to a log (rare, meaningful), while every 21// successful check REWRITES a heartbeat file. A log alone would show zero rows on a healthy system and zero 22// rows on a dead keeper -- indistinguishable. The heartbeat is what separates "nothing to do" from "not running". 23// license_tier: ORIGINAL No hw writes (Rule 26). 24import "nx_syscalls.nx" 25 26const IK_PORT: i64 = 8033 27const IK_MODE: i64 = 420 28const IK_ELF: *u8 = "./nx_site_intake_serve.elf" as *u8 29const IK_LOG: *u8 = "knowledge/status/intake_keep.log" as *u8 30const IK_BEAT: *u8 = "knowledge/status/intake_keep.beat" as *u8 31 32func ik_p(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 33func ik_n(v: i64) -> i64 { 34 let b: *u8 = sys_mmap(32) 35 let t: *u8 = sys_mmap(32) 36 var m: i64 = v 37 var k: i64 = 0 38 if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m } 39 if m == 0 { t[0] = 48 as u8; k = 1 } 40 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 41 var i: i64 = 0 42 while i < k { b[i] = t[k - 1 - i]; i = i + 1 } 43 sys_write(1, b, k) 44 return 0 45} 46func ik_cat(d: *u8, o: i64, s: *u8) -> i64 { var x: i64 = o; var i: i64 = 0; while s[i] != (0 as u8) { d[x] = s[i]; x = x + 1; i = i + 1 } return x } 47func ik_catn(d: *u8, o: i64, v: i64) -> i64 { 48 if v < 0 { d[o] = 45 as u8; return ik_catn(d, o + 1, 0 - v) } 49 var m: i64 = v 50 var x: i64 = o 51 let t: *u8 = sys_mmap(32) 52 var k: i64 = 0 53 if m == 0 { t[0] = 48 as u8; k = 1 } 54 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 55 var i: i64 = 0 56 while i < k { d[x + i] = t[k - 1 - i]; i = i + 1 } 57 return x + k 58} 59 60// A connect() that succeeds is the only proof that something is SERVING. Checking for a process by name would 61// pass on a wedged daemon that holds the socket and never answers. 62func ik_port_open(port: i64) -> i64 { 63 let fd: i64 = sys_socket(2, 1, 0) 64 if fd < 0 { return 0 } 65 let addr: *u8 = sys_mmap(16) 66 var i: i64 = 0 67 while i < 16 { addr[i] = 0 as u8; i = i + 1 } 68 addr[0] = 2 as u8 69 addr[2] = ((port >> 8) & 255) as u8 70 addr[3] = (port & 255) as u8 71 addr[4] = 127 as u8 72 addr[7] = 1 as u8 73 let r: i64 = sys_connect(fd, addr, 16) 74 sys_close(fd) 75 if r < 0 { return 0 } 76 return 1 77} 78 79func ik_beat(now: i64) -> i64 { 80 let fd: i64 = sys_openat_wr(IK_BEAT, IK_MODE) 81 if fd < 0 { return 0 - 1 } 82 let b: *u8 = sys_mmap(128) 83 var o: i64 = ik_cat(b, 0, "intake_keep checked :8033 at epoch " as *u8) 84 o = ik_catn(b, o, now) 85 b[o] = 10 as u8; o = o + 1 86 sys_write(fd, b, o) 87 sys_close(fd) 88 return 0 89} 90 91func ik_log(now: i64, pid: i64) -> i64 { 92 let fd: i64 = sys_openat_append(IK_LOG, IK_MODE) 93 if fd < 0 { return 0 - 1 } 94 let b: *u8 = sys_mmap(256) 95 var o: i64 = ik_catn(b, 0, now) 96 o = ik_cat(b, o, "\tSPAWN\t:8033 refused a connection, respawned nx_site_intake_serve pid=" as *u8) 97 o = ik_catn(b, o, pid) 98 b[o] = 10 as u8; o = o + 1 99 sys_write(fd, b, o) 100 sys_close(fd) 101 return 0 102} 103 104func main(argc: i64, argv: *i64) -> i64 { 105 let now: i64 = sys_now_realtime_sec() 106 107 if ik_port_open(IK_PORT) == 1 { 108 ik_beat(now) 109 ik_p("NX-INTAKE-KEEP OK :8033 serving, nothing to do\n" as *u8) 110 sys_exit(0) 111 return 0 112 } 113 114 // refused -> bring it back. The child is orphaned to init when we exit, so it outlives both this organ 115 // and the clock window that dispatched it. 116 let pid: i64 = sys_fork() 117 if pid == 0 { 118 let av: *i64 = sys_mmap(8 * 4) as *i64 119 av[0] = IK_ELF as i64 120 av[1] = 0 121 let ev: *i64 = sys_mmap(8 * 2) as *i64 122 ev[0] = 0 123 sys_execve(IK_ELF, av, ev) 124 sys_exit(127) 125 } 126 127 ik_beat(now) 128 ik_log(now, pid) 129 ik_p("NX-INTAKE-KEEP RESPAWNED :8033 was refusing; started " as *u8) 130 ik_p(IK_ELF) 131 ik_p(" pid=" as *u8) 132 ik_n(pid) 133 ik_p("\n" as *u8) 134 sys_exit(0) 135 return 0 136}