nx_swarm_lib.nx source
↩ module page · 172 lines · 5816 B
1// nx_swarm_lib.nx -- SWARM FABRIC shared lib: the NODE-row contract (validate/freshness), bounded
2// reads, and the CLI path law. Single source of truth for the fabric wire format -- consumed by
3// nx_swarm_beat (store) + nx_swarm_place (placement brain). Extracted 2026-07-15 (SF-R2).
4// Row contract (v2 positional, emitted by nx_node_beacon):
5// NODE <name> <cpu> <load> <ncpu> <lpc> <memu> <mema> <memt> <conns> <ts_us> <temp_mc> <batt_pct>
6// license_tier: ORIGINAL
7import "nx_syscalls.nx"
8import "nx_framed_append.nx"
9const K_MAGIC_60000000: i64 = 60000000
10const K_MAGIC_1000000: i64 = 1000000
11
12// CLI path guard: must end ".log" or ".snap"; no ".."; bare basename (no '/') OR under /tmp/.
13func sb_path_ok(p: *u8) -> i64 {
14 let n: i64 = fa_len(p)
15 if n < 5 { return 0 }
16 // no ".." anywhere
17 var i: i64 = 0
18 while i + 1 < n {
19 if (p[i] as i64) == 46 { if (p[i + 1] as i64) == 46 { return 0 } }
20 i = i + 1
21 }
22 // extension .log or .snap
23 var ext_ok: i64 = 0
24 if n >= 4 {
25 if (p[n-4] as i64) == 46 { if (p[n-3] as i64) == 108 { if (p[n-2] as i64) == 111 { if (p[n-1] as i64) == 103 { ext_ok = 1 } } } }
26 }
27 if n >= 5 {
28 if (p[n-5] as i64) == 46 { if (p[n-4] as i64) == 115 { if (p[n-3] as i64) == 110 { if (p[n-2] as i64) == 97 { if (p[n-1] as i64) == 112 { ext_ok = 1 } } } } }
29 }
30 if ext_ok == 0 { return 0 }
31 // bare basename (no '/') is fine
32 var has_slash: i64 = 0
33 i = 0
34 while i < n { if (p[i] as i64) == 47 { has_slash = 1 } i = i + 1 }
35 if has_slash == 0 { return 1 }
36 // otherwise must start "/tmp/"
37 if n < 6 { return 0 }
38 if (p[0] as i64) != 47 { return 0 }
39 if (p[1] as i64) != 116 { return 0 }
40 if (p[2] as i64) != 109 { return 0 }
41 if (p[3] as i64) != 112 { return 0 }
42 if (p[4] as i64) != 47 { return 0 }
43 return 1
44}
45
46// bounded whole-file read; bytes read or -1 on open fail
47func sb_read(path: *u8, buf: *u8, cap: i64) -> i64 {
48 let fd: i64 = sys_openat_rd(path)
49 if fd < 0 { return 0 - 1 }
50 var tot: i64 = 0
51 var go: i64 = 1
52 while go == 1 {
53 if tot >= cap { go = 0 } else {
54 let r: i64 = sys_read(fd, (buf as i64 + tot) as *u8, cap - tot)
55 if r <= 0 { go = 0 } else { tot = tot + r }
56 }
57 }
58 sys_close(fd)
59 return tot
60}
61
62// substring present? (n = haystack len)
63func sb_has(buf: *u8, n: i64, needle: *u8) -> i64 {
64 let m: i64 = fa_len(needle)
65 if m == 0 { return 0 }
66 var i: i64 = 0
67 while i + m <= n {
68 var k: i64 = 0
69 var hit: i64 = 1
70 while k < m { if buf[i + k] != needle[k] { hit = 0; k = m } else { k = k + 1 } }
71 if hit == 1 { return 1 }
72 i = i + 1
73 }
74 return 0
75}
76
77// signed decimal at p (within [p,n)); 1 ok / 0 no-digits; value -> vout[0], end -> pend[0]
78func sb_pint(buf: *u8, n: i64, p: i64, vout: *i64, pend: *i64) -> i64 {
79 var i: i64 = p
80 var neg: i64 = 0
81 if i < n { if (buf[i] as i64) == 45 { neg = 1; i = i + 1 } }
82 var v: i64 = 0
83 var d: i64 = 0
84 var go: i64 = 1
85 while go == 1 {
86 if i >= n { go = 0 } else {
87 let c: i64 = buf[i] as i64
88 if c >= 48 { if c <= 57 { v = v * 10 + (c - 48); d = d + 1; i = i + 1 } else { go = 0 } } else { go = 0 }
89 }
90 }
91 if d == 0 { return 0 }
92 if neg == 1 { v = 0 - v }
93 vout[0] = v
94 pend[0] = i
95 return 1
96}
97
98// validate one NODE row of exactly 13 tokens; name span -> nso/nlo, ts (token 10) -> tso. 1 ok / 0 bad.
99func sb_validate(row: *u8, n: i64, nso: *i64, nlo: *i64, tso: *i64) -> i64 {
100 if n < 6 { return 0 }
101 if row[0] != (78 as u8) { return 0 }
102 if row[1] != (79 as u8) { return 0 }
103 if row[2] != (68 as u8) { return 0 }
104 if row[3] != (69 as u8) { return 0 }
105 if row[4] != (32 as u8) { return 0 }
106 var i: i64 = 5
107 let ns: i64 = i
108 var go: i64 = 1
109 while go == 1 {
110 if i >= n { go = 0 } else {
111 if (row[i] as i64) == 32 { go = 0 } else {
112 if (row[i] as i64) == 10 { return 0 }
113 i = i + 1
114 }
115 }
116 }
117 let nl: i64 = i - ns
118 if nl < 1 { return 0 }
119 if nl > 32 { return 0 }
120 let vout: *i64 = sys_mmap(16) as *i64
121 let pend: *i64 = sys_mmap(16) as *i64
122 var t: i64 = 2
123 var ts: i64 = 0 - 1
124 while t <= 12 {
125 if i >= n { return 0 }
126 if (row[i] as i64) != 32 { return 0 }
127 i = i + 1
128 let ok: i64 = sb_pint(row, n, i, vout, pend)
129 if ok == 0 { return 0 }
130 if t == 10 { ts = vout[0] }
131 i = pend[0]
132 t = t + 1
133 }
134 // after the last token: only end / newline allowed (strict 13-token contract)
135 if i < n {
136 if (row[i] as i64) != 10 { return 0 }
137 if i + 1 < n { return 0 }
138 }
139 nso[0] = ns
140 nlo[0] = nl
141 tso[0] = ts
142 return 1
143}
144
145// parse ALL 11 numeric fields of a validated row into f[0..10] = cpu,load,ncpu,lpc,memu,mema,memt,conns,ts,temp,batt.
146// returns 1 ok / 0 bad (same strictness as sb_validate).
147func sb_fields(row: *u8, n: i64, f: *i64) -> i64 {
148 let nso: *i64 = sys_mmap(16) as *i64
149 let nlo: *i64 = sys_mmap(16) as *i64
150 let tso: *i64 = sys_mmap(16) as *i64
151 if sb_validate(row, n, nso, nlo, tso) == 0 { return 0 }
152 let vout: *i64 = sys_mmap(16) as *i64
153 let pend: *i64 = sys_mmap(16) as *i64
154 var i: i64 = nso[0] + nlo[0]
155 var t: i64 = 0
156 while t < 11 {
157 i = i + 1
158 sb_pint(row, n, i, vout, pend)
159 f[t] = vout[0]
160 i = pend[0]
161 t = t + 1
162 }
163 return 1
164}
165
166// pure freshness verdict: 1 FRESH / 0 STALE. Future-forged ts (beyond 60s skew) is STALE too.
167func sb_fresh(ts: i64, now: i64, window_sec: i64) -> i64 {
168 if ts <= 0 { return 0 }
169 if ts > now + K_MAGIC_60000000 { return 0 }
170 if now - ts < window_sec * K_MAGIC_1000000 { return 1 }
171 return 0
172}