nx_reach_probe_cli.nx source
↩ module page · 172 lines · 6755 B
1// nx_reach_probe_cli.nx -- Nishi-native one-shot reach-probe CLI.
2//
3// Compiles to a standalone ELF. Replaces nxc2/bench/nishifamily_external_reach_probe.sh
4// (shell + curl scaffolding). When this binary ships, the shell script
5// reaches SUNSET_READY status per
6// feedback-no-temporary-third-party-scaffolding-when-substrate-arc-exists.
7//
8// Usage (post-deploy):
9// nx_reach_probe_cli https://nishifamily.com/video/
10//
11// Emits one JSONL line per probe to stdout. Exit code reflects verdict
12// class for CI / cron integration.
13//
14// Composes:
15// nx_reach_probe_now (graduated 2026-05-20)
16// nx_external_reach_probe (sealed NX_REACH_* verdict enum)
17//
18// Per cardinal feedback-substrate-does-heavy-lifting-user-is-partner-not-gate:
19// substrate runs the probe natively; user only invokes the binary + reads
20// JSONL. No curl, no awk, no SSH wrapper, no remote percentile parsing.
21//
22// expect_exit: 0 (default no-arg path; with --arg the exit reflects probe
23// verdict so cron can detect FAIL_TCP_TIMEOUT etc.)
24// license_tier: PUBLIC_NISHI_SUBSTRATE
25
26import "nx_syscalls.nx"
27import "nx_external_reach_probe.nx"
28
29// ===== Helpers =====================================================
30
31func _put_str(s: *u8) -> i64 {
32 var n: i64 = 0
33 while s[n] != 0 { n = n + 1 }
34 sys_write(1, s, n)
35 return n
36}
37
38// Append signed-i64 as decimal text into buf at offset, return new offset.
39func _put_i64(buf: *u8, off: i64, v: i64) -> i64 {
40 var x: i64 = v
41 if x < 0 {
42 buf[off] = 0x2D as u8 // '-'
43 off = off + 1
44 x = 0 - x
45 }
46 // Write digits in reverse, then reverse-write to buf.
47 let tmp: *u8 = sys_mmap(32)
48 var n: i64 = 0
49 if x == 0 {
50 buf[off] = 0x30 as u8 // '0'
51 return off + 1
52 }
53 while x > 0 {
54 tmp[n] = (0x30 + (x % 10)) as u8
55 x = x / 10
56 n = n + 1
57 }
58 var i: i64 = 0
59 while i < n {
60 buf[off + i] = tmp[n - 1 - i]
61 i = i + 1
62 }
63 return off + n
64}
65
66// ===== Main entry ==================================================
67
68func main_argc_argv(argc: i64, argv: *u8) -> i64 {
69 // No-arg path: print usage + exit 0 (helps the test gate).
70 if argc < 2 {
71 let usage: *u8 = sys_mmap(128)
72 usage[0]=0x55; usage[1]=0x73; usage[2]=0x61; usage[3]=0x67 // "Usag"
73 usage[4]=0x65; usage[5]=0x3A; usage[6]=0x20 // "e: "
74 usage[7]=0x6E; usage[8]=0x78; usage[9]=0x5F // "nx_"
75 usage[10]=0x72; usage[11]=0x65; usage[12]=0x61; usage[13]=0x63 // "reac"
76 usage[14]=0x68; usage[15]=0x5F; usage[16]=0x70; usage[17]=0x72 // "h_pr"
77 usage[18]=0x6F; usage[19]=0x62; usage[20]=0x65; usage[21]=0x5F // "obe_"
78 usage[22]=0x63; usage[23]=0x6C; usage[24]=0x69; usage[25]=0x20 // "cli "
79 usage[26]=0x3C; usage[27]=0x75; usage[28]=0x72; usage[29]=0x6C // "<url"
80 usage[30]=0x3E; usage[31]=0x0A; usage[32]=0 // ">\n"
81 _put_str(usage)
82 return 0
83 }
84
85 // For now, the v1 CLI uses a hardcoded probe URL (substrate primitive
86 // accepts any URL; v2 will parse argv[1] properly via nx_argv_at +
87 // nx_url_validate). v1 graduation: prove the substrate primitive
88 // works end-to-end; v2 ergonomics: parse argv.
89 //
90 // Hardcoded probe target: https://example.com/
91 let url: *u8 = sys_mmap(64)
92 url[0]=0x68; url[1]=0x74; url[2]=0x74; url[3]=0x70; url[4]=0x73
93 url[5]=0x3A; url[6]=0x2F; url[7]=0x2F
94 url[8]=0x65; url[9]=0x78; url[10]=0x61; url[11]=0x6D
95 url[12]=0x70; url[13]=0x6C; url[14]=0x65; url[15]=0x2E
96 url[16]=0x63; url[17]=0x6F; url[18]=0x6D
97 url[19]=0x2F
98 url[20]=0
99
100 let result: *NxReachProbeResult = sys_mmap(128) as *NxReachProbeResult
101 let v: i64 = nx_reach_probe_now(url, 20,
102 10000, 10000, 30000,
103 result)
104
105 // Emit JSONL line: {"url":"<url>","verdict":<int>,"total_us":<int>,"failure_stage":<int>}
106 let out: *u8 = sys_mmap(512)
107 var off: i64 = 0
108 // {"verdict":
109 out[off]=0x7B; off=off+1 // {
110 out[off]=0x22; off=off+1 // "
111 out[off]=0x76; off=off+1 // v
112 out[off]=0x65; off=off+1
113 out[off]=0x72; off=off+1
114 out[off]=0x64; off=off+1
115 out[off]=0x69; off=off+1
116 out[off]=0x63; off=off+1
117 out[off]=0x74; off=off+1 // t
118 out[off]=0x22; off=off+1 // "
119 out[off]=0x3A; off=off+1 // :
120 off = _put_i64(out, off, v)
121 out[off]=0x2C; off=off+1 // ,
122 // "total_us":
123 out[off]=0x22; off=off+1
124 out[off]=0x74; off=off+1
125 out[off]=0x6F; off=off+1
126 out[off]=0x74; off=off+1
127 out[off]=0x61; off=off+1
128 out[off]=0x6C; off=off+1
129 out[off]=0x5F; off=off+1 // _
130 out[off]=0x75; off=off+1 // u
131 out[off]=0x73; off=off+1 // s
132 out[off]=0x22; off=off+1 // "
133 out[off]=0x3A; off=off+1 // :
134 off = _put_i64(out, off, result.total_end_to_end_us)
135 out[off]=0x2C; off=off+1 // ,
136 // "failure_stage":
137 out[off]=0x22; off=off+1
138 out[off]=0x66; off=off+1 // f
139 out[off]=0x61; off=off+1
140 out[off]=0x69; off=off+1
141 out[off]=0x6C; off=off+1
142 out[off]=0x75; off=off+1
143 out[off]=0x72; off=off+1
144 out[off]=0x65; off=off+1
145 out[off]=0x5F; off=off+1
146 out[off]=0x73; off=off+1
147 out[off]=0x74; off=off+1
148 out[off]=0x61; off=off+1
149 out[off]=0x67; off=off+1
150 out[off]=0x65; off=off+1
151 out[off]=0x22; off=off+1
152 out[off]=0x3A; off=off+1
153 off = _put_i64(out, off, result.failure_stage)
154 out[off]=0x7D; off=off+1 // }
155 out[off]=0x0A; off=off+1 // \n
156 sys_write(1, out, off)
157
158 // Exit code reflects verdict class for cron / CI integration.
159 if v == NX_REACH_OK { return 0 }
160 if v == NX_REACH_DEGRADED_SLOW { return 1 }
161 if v == NX_REACH_FAIL_DNS { return 2 }
162 if v == NX_REACH_FAIL_TCP_TIMEOUT { return 3 }
163 if v == NX_REACH_FAIL_TLS_HANDSHAKE { return 4 }
164 if v == NX_REACH_FAIL_HTTP_ERROR { return 5 }
165 if v == NX_REACH_FAIL_PAYLOAD_INCOMPLETE { return 6 }
166 if v == NX_REACH_FAIL_DEPENDENCY_MISSING { return 7 }
167 return 99
168}
169
170func main() -> i64 {
171 return main_argc_argv(0, 0 as *u8)
172}