nx_app_monitor.nx source
↩ module page · 133 lines · 6879 B
1// nx_app_monitor.nx -- S-class sovereign health monitor for the Nishi web apps.
2// Data-driven: reads web_assets/app_registry.tsv (the single source of truth), and
3// for EVERY registered app does a real HTTPS GET through the Nishi TLS-1.3 client +
4// Mozilla CA chain (no curl), asserting 200 + the app's content marker. This is the
5// watchdog that was missing when sites.elf silently crash-looped. Prints an up/down
6// table; exits 0 iff every app is UP (so it doubles as a gate / cron check).
7// license_tier: ORIGINAL
8import "nx_syscalls.nx"
9import "nx_x509_trust_store.nx"
10import "nx_trust_store_load_from_certdata.nx"
11import "nx_tls13_client_validate_certificate.nx"
12import "nx_tls13_client_session_run.nx"
13import "nx_https_url_for_fetch.nx"
14import "nx_https_url_connect.nx"
15import "nx_https_get.nx"
16import "nx_https_get_complete.nx"
17import "nx_http_response_parse.nx"
18import "nx_app_store.nx"
19const K_MAGIC_2097152: i64 = 2097152
20const K_MAGIC_4194304: i64 = 4194304
21const K_MAGIC_65536: i64 = 65536
22const K_MAGIC_8192: i64 = 8192
23
24func w(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
25func wn(v: i64) -> i64 { let b: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m;sys_write(1,"-" as *u8,1)}; let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=(48 as u8);k=1}; while m>0{t[k]=((48+(m%10)) as u8);m=m/10;k=k+1}; var i: i64=0; while i<k{b[i]=t[k-1-i];i=i+1}; sys_write(1,b,k); return 0 }
26func slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
27func contains(buf: *u8, lo: i64, hi: i64, needle: *u8) -> i64 {
28 let nl: i64=slen(needle); if nl==0 { return 1 }
29 var i: i64=lo
30 while i+nl<=hi { var j: i64=0; var ok: i64=1; while j<nl { if buf[i+j]!=needle[j] { ok=0; j=nl } else { j=j+1 } } if ok==1 { return 1 } i=i+1 }
31 return 0
32}
33// copy the tab-delimited field `fi` of line buf[ls..le) into out (NUL-terminated); returns length
34func field(buf: *u8, ls: i64, le: i64, fi: i64, out: *u8) -> i64 {
35 var f: i64=0; var i: i64=ls
36 while f < fi { if i >= le { out[0]=0 as u8; return 0 } if buf[i]==9 as u8 { f=f+1 } i=i+1 }
37 var o: i64=0
38 while i < le { if buf[i]==9 as u8 { i=le } else { out[o]=buf[i]; o=o+1; i=i+1 } }
39 out[o]=0 as u8
40 return o
41}
42
43// GET https://nishifamily.com<path> ; returns HTTP status (0 if no response), sets mk[0]=1 if marker present
44func check_one(store: *TrustStore, path: *u8, pathlen: i64, marker: *u8, mk: *i64) -> i64 {
45 mk[0]=0
46 let url: *u8 = sys_mmap(512); var u: i64=0
47 let pre: *u8 = "https://nishifamily.com" as *u8; var pi: i64=0
48 while pre[pi]!=(0 as u8) { url[u]=pre[pi]; u=u+1; pi=pi+1 }
49 var qi: i64=0; while qi<pathlen { url[u]=path[qi]; u=u+1; qi=qi+1 }
50 url[u]=0 as u8
51
52 let url_p: *NxUrl = nx_url_new()
53 let target: *NxHttpsTarget = sys_mmap(64) as *NxHttpsTarget
54 target.url = url_p
55 target.port = 0
56 if nx_https_url_for_fetch(url, target) != NX_HTTPS_URL_OK { return 0 }
57 let fd_p: *i64 = sys_mmap(16) as *i64
58 if nx_https_url_connect(target, url, sys_now_realtime_sec(), fd_p) != NX_HTTPS_CONNECT_OK { return 0 }
59 let fd: i64 = fd_p[0]
60 let cr: *u8 = sys_mmap(32); var i: i64=0; while i<32 { cr[i]=(0xC0+i) as u8; i=i+1 }
61 let priv: *u8 = sys_mmap(32); i=0; while i<32 { priv[i]=(0xA0+i) as u8; i=i+1 }
62 let vc: *TlsValidationContext = sys_mmap(64) as *TlsValidationContext
63 vc.store = store
64 vc.sni_host = ((url as i64) + target.url.host_off) as *u8
65 vc.sni_host_len = target.url.host_len
66 vc.now_epoch = sys_now_realtime_sec()
67 let sr: i64 = nx_tls13_client_session_run(fd, ((url as i64)+target.url.host_off) as *u8, target.url.host_len, cr, priv, vc)
68 if sr <= 0 { return 0 }
69 let buf: *u8 = sys_mmap(K_MAGIC_2097152)
70 let session: *Tls13ClientSession = sr as *Tls13ClientSession
71 let gc: i64 = nx_https_get_complete(session, fd, path, pathlen, ((url as i64)+target.url.host_off) as *u8, target.url.host_len, buf, K_MAGIC_2097152)
72 sys_close(fd)
73 if gc <= 0 { return 0 }
74 let pr: *i64 = sys_mmap(128) as *i64
75 var status: i64=0; var body_off: i64=0
76 if nx_http_response_parse(buf, gc, pr)==0 { status=pr[1]; body_off=pr[6] }
77 if contains(buf, body_off, gc, marker)==1 { mk[0]=1 }
78 return status
79}
80
81func main() -> i64 {
82 w("=== NISHI APP MONITOR (sovereign HTTPS health-check of every registered app) ===\n" as *u8)
83 let r: i64 = nx_trust_store_load_from_certdata("data/mozilla_certdata.txt" as *u8, 300, K_MAGIC_4194304)
84 if r <= 0 { w("FATAL: no CA store\n" as *u8); sys_exit(2); return 2 }
85 let store: *TrustStore = r as *TrustStore
86
87 // registry = the sovereign seg_store (no TSV). Iterate __apps__ index -> ss_get each.
88 let idx: *u8 = sys_mmap(K_MAGIC_65536)
89 let idxn: i64 = nx_app_store_index(idx)
90 if idxn <= 0 { w("FATAL: empty app registry (seg_store)\n" as *u8); sys_exit(2); return 2 }
91 let name: *u8 = sys_mmap(128)
92 let path: *u8 = sys_mmap(256)
93 let marker: *u8 = sys_mmap(256)
94 let rec: *u8 = sys_mmap(K_MAGIC_8192)
95 let po: *i64 = sys_mmap(16) as *i64
96 let lo: *i64 = sys_mmap(16) as *i64
97 let mk: *i64 = sys_mmap(16) as *i64
98 var up: i64=0; var total: i64=0
99 var ls: i64=0; var i: i64=0
100 while i <= idxn {
101 var eol: i64=0
102 if i==idxn { eol=1 } else { if idx[i]==10 as u8 { eol=1 } }
103 if eol==1 {
104 if i>ls {
105 var c: i64=0; while c<i-ls { name[c]=idx[ls+c]; c=c+1 } name[i-ls]=0 as u8
106 if nx_app_store_get(name, po, lo) >= 0 {
107 let src: *u8=po[0] as *u8; let rl: i64=lo[0]
108 var d: i64=0; while d<rl { rec[d]=src[d]; d=d+1 }
109 let plen: i64 = field(rec, 0, rl, 0, path) // record field 0 = path
110 field(rec, 0, rl, 4, marker) // record field 4 = content marker
111 if plen > 0 {
112 total = total + 1
113 let st: i64 = check_one(store, path, plen, marker, mk)
114 var reach: i64 = 0
115 if st >= 200 { if st < 400 { reach = 1 } }
116 var badcontent: i64 = 0
117 if st == 200 { if mk[0] == 0 { badcontent = 1 } }
118 w(" " as *u8)
119 if reach == 1 { if badcontent == 0 { w("UP " as *u8); up=up+1 } else { w("WARN " as *u8) } }
120 else { w("DOWN " as *u8) }
121 w(name); w(" " as *u8); w(path); w(" [" as *u8); wn(st); w("]\n" as *u8)
122 }
123 }
124 }
125 ls=i+1
126 }
127 i=i+1
128 }
129 w("---- " as *u8); wn(up); w("/" as *u8); wn(total); w(" apps UP ----\n" as *u8)
130 if up == total { w("ALL APPS HEALTHY\n" as *u8); sys_exit(0); return 0 }
131 w("DEGRADED -- one or more apps DOWN\n" as *u8)
132 sys_exit(1); return 1
133}