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}