code wiki / (root) / nx_telemetry.nx

nx_telemetry.nx source

↩ module page · 212 lines · 10854 B

1// nx_telemetry.nx -- CONSENT-GATED DEVICE TELEMETRY (X-DRV-F1). 2// 3// A spore that has brought up its hardware (X-DRV-W2) can CHOOSE to contribute its device 4// learnings (device-id + working driver-spec + bring-up status) to the federated registry -- 5// but ONLY by explicit opt-in. The CARDINAL property: DEFAULT = LOCAL-ONLY. Nothing leaves the 6// device unless a consent row says SHARED; sharing is per-device-class and REVOCABLE (flip back to 7// LOCAL and the next run shares nothing new). Shared payloads are GENUINELY ed25519-SIGNED with 8// the node's identity key (composing nx_ed25519_signature -- DRY), carrying the node public key as 9// provenance so the federated side can verify authenticity. Privacy-first, sovereign. 10// 11// nx_telemetry [consent_path] [devmap_path] 12// consent_path : telemetry_consent.tsv (deviceid<TAB>LOCAL|SHARED; ABSENT row = LOCAL-ONLY) 13// devmap_path : emu_devmap.tsv (the probed devices) 14// node key : knowledge/registry/node_ed25519_seed.bin (32-byte node identity priv key) 15// LOCAL records -> knowledge/status/telemetry_local.log (retained on-device, NEVER shared) 16// SHARED records -> knowledge/status/telemetry_shared.log (opt-in only; signed + pubkey) 17// Sovereign (syscalls + the ed25519 organ), no gcc/.sh. license_tier: ORIGINAL 18import "nx_syscalls.nx" 19import "nx_ed25519_signature.nx" 20const TM_MAGIC_65536: i64 = 65536 21const TM_MAGIC_1024: i64 = 1024 22 23const TM_REG: *u8 = "knowledge/registry/driver_registry.tsv" 24const TM_SEED: *u8 = "knowledge/registry/node_ed25519_seed.bin" 25const TM_LOCALLOG: *u8 = "knowledge/status/telemetry_local.log" 26const TM_SHAREDLOG:*u8 = "knowledge/status/telemetry_shared.log" 27 28func tm_p(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 29func tm_fp(fd: i64, s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(fd,s,n); return 0 } 30func tm_fn(fd: i64, v: i64) -> i64 { let bb: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m}; let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48;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{bb[i]=t[k-1-i];i=i+1}; sys_write(fd,bb,k); return 0 } 31func tm_n(v: i64) -> i64 { tm_fn(1, v); return 0 } 32 33func tm_read(path: *u8, buf: *u8, cap: i64) -> i64 { 34 let fd: i64 = sys_openat_rd(path) 35 if fd < 0 { return 0 - 1 } 36 var n: i64 = 0 37 var go: i64 = 1 38 while go == 1 { let r: i64 = sys_read(fd, (buf as i64 + n) as *u8, cap - 1 - n); if r <= 0 { go = 0 } else { n = n + r } if n >= cap - 1 { go = 0 } } 39 sys_close(fd) 40 return n 41} 42 43func tm_parse_num(buf: *u8, p: i64, le: i64) -> i64 { 44 var q: i64 = p 45 var val: i64 = 0 46 if q + 1 < le { if buf[q] == (48 as u8) { if buf[q+1] == (120 as u8) { 47 q = q + 2 48 var go: i64 = 1 49 while go == 1 { if q >= le { go = 0 } else { let c: i64 = buf[q] as i64; var d: i64 = 0-1; if c>=48 { if c<=57 { d=c-48 } } if c>=97 { if c<=102 { d=c-87 } } if c>=65 { if c<=70 { d=c-55 } } if d<0 { go=0 } else { val=val*16+d; q=q+1 } } } 50 return val 51 }}} 52 var go2: i64 = 1 53 while go2 == 1 { if q >= le { go2 = 0 } else { let c: i64 = buf[q] as i64; if c>=48 { if c<=57 { val=val*10+(c-48); q=q+1 } else { go2=0 } } else { go2=0 } } } 54 return val 55} 56 57// start offset of the idx-th TAB field in line [ls,le), or -1. 58func tm_field(buf: *u8, ls: i64, le: i64, idx: i64) -> i64 { 59 if idx == 0 { return ls } 60 var f: i64 = 0; var p: i64 = ls 61 while p < le { if buf[p] == (9 as u8) { f = f + 1; if f == idx { return p + 1 } } p = p + 1 } 62 return 0 - 1 63} 64 65// copy field text (until TAB/eol) from start s into out; return length. 66func tm_copy_field(buf: *u8, s: i64, le: i64, out: *u8) -> i64 { 67 var c: i64 = 0; var q: i64 = s 68 while q < le { if buf[q] == (9 as u8) { q = le } else { out[c] = buf[q]; c = c + 1; q = q + 1 } } 69 out[c] = 0 as u8 70 return c 71} 72 73// look up deviceid -> spec in the driver registry buffer; copy into out; return 1 if found. 74func tm_reg_spec(reg: *u8, rn: i64, did: i64, out: *u8) -> i64 { 75 var ls: i64 = 0 76 while ls < rn { 77 var le: i64 = ls 78 var sc: i64 = 1 79 while sc == 1 { if le >= rn { sc = 0 } else { if reg[le] == (10 as u8) { sc = 0 } else { le = le + 1 } } } 80 if reg[ls] != (35 as u8) { 81 let f0: i64 = tm_field(reg, ls, le, 0) 82 let f1: i64 = tm_field(reg, ls, le, 1) 83 if f0 >= 0 { if f1 >= 0 { 84 if tm_parse_num(reg, f0, le) == did { tm_copy_field(reg, f1, le, out); return 1 } 85 } } 86 } 87 ls = le + 1 88 } 89 return 0 90} 91 92// look up deviceid -> consent in the consent buffer; 1 = SHARED, 0 = LOCAL (default if absent). 93func tm_consent(con: *u8, cn: i64, did: i64) -> i64 { 94 var ls: i64 = 0 95 while ls < cn { 96 var le: i64 = ls 97 var sc: i64 = 1 98 while sc == 1 { if le >= cn { sc = 0 } else { if con[le] == (10 as u8) { sc = 0 } else { le = le + 1 } } } 99 if con[ls] != (35 as u8) { 100 let f0: i64 = tm_field(con, ls, le, 0) 101 let f1: i64 = tm_field(con, ls, le, 1) 102 if f0 >= 0 { if f1 >= 0 { 103 if tm_parse_num(con, f0, le) == did { 104 // SHARED iff field1 starts with 'S' 105 if con[f1] == (83 as u8) { return 1 } 106 return 0 107 } 108 } } 109 } 110 ls = le + 1 111 } 112 return 0 // default LOCAL-ONLY (no-share-by-default) 113} 114 115// hex-encode n bytes of src into dst (2*n chars + NUL). 116func tm_hex(src: *u8, n: i64, dst: *u8) -> i64 { 117 var i: i64 = 0 118 while i < n { 119 let b: i64 = src[i] as i64 120 let hi: i64 = (b >> 4) & 0xf 121 let lo: i64 = b & 0xf 122 if hi < 10 { dst[i*2] = (48 + hi) as u8 } else { dst[i*2] = (87 + hi) as u8 } 123 if lo < 10 { dst[i*2+1] = (48 + lo) as u8 } else { dst[i*2+1] = (87 + lo) as u8 } 124 i = i + 1 125 } 126 dst[n*2] = 0 as u8 127 return n * 2 128} 129 130// append NUL-terminated s into dst at off; return new off. 131func tm_cat(dst: *u8, off: i64, s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { dst[off+i] = s[i]; i = i + 1 } return off + i } 132func tm_catn(dst: *u8, off: i64, v: i64) -> i64 { let t: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m}; var k: i64=0; if m==0{t[0]=48;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{dst[off+i]=t[k-1-i];i=i+1}; return off+k } 133 134func main(argc: i64, argv: *i64) -> i64 { 135 var consent_path: *u8 = "knowledge/registry/telemetry_consent.tsv" as *u8 136 var devmap_path: *u8 = "knowledge/registry/emu_devmap.tsv" as *u8 137 if argc >= 2 { consent_path = argv[1] as *u8 } 138 if argc >= 3 { devmap_path = argv[2] as *u8 } 139 140 tm_p("=== nx_telemetry: consent-gated device telemetry (DEFAULT LOCAL-ONLY; opt-in SHARED is ed25519-signed) ===\n" as *u8) 141 142 // node identity key (priv 32B); derive pub. Production generates a random key per node; the 143 // gate writes a fixed test seed for reproducibility -- the SIGNING is real ed25519 either way. 144 let lb: *i64 = sys_mmap(16) as *i64 145 let priv32: *u8 = sys_read_file(TM_SEED, lb) 146 if (priv32 as i64) == 0 { tm_p("TELEMETRY verdict=RED reason=node-key-missing\n" as *u8); sys_exit(1); return 1 } 147 if lb[0] != 32 { tm_p("TELEMETRY verdict=RED reason=node-key-bad-size\n" as *u8); sys_exit(1); return 1 } 148 let pub32: *u8 = sys_mmap(32) 149 if ed25519_pub_from_priv(priv32, pub32) != 0 { tm_p("TELEMETRY verdict=RED reason=pub-derive-fail\n" as *u8); sys_exit(1); return 1 } 150 let pubhex: *u8 = sys_mmap(80) 151 tm_hex(pub32, 32, pubhex) 152 153 let reg: *u8 = sys_mmap(TM_MAGIC_65536); let rn: i64 = tm_read(TM_REG, reg, TM_MAGIC_65536) 154 let con: *u8 = sys_mmap(TM_MAGIC_65536); var cn: i64 = tm_read(consent_path, con, TM_MAGIC_65536) 155 if cn < 0 { cn = 0 } // absent consent file = everything LOCAL-ONLY (no-share-by-default) 156 let dev: *u8 = sys_mmap(TM_MAGIC_65536); let dn: i64 = tm_read(devmap_path, dev, TM_MAGIC_65536) 157 if dn <= 0 { tm_p("TELEMETRY verdict=RED reason=devmap-missing\n" as *u8); sys_exit(1); return 1 } 158 159 let lfd: i64 = sys_openat_append(TM_LOCALLOG, 0x1a4) 160 let sfd: i64 = sys_openat_append(TM_SHAREDLOG, 0x1a4) 161 162 let spec: *u8 = sys_mmap(512) 163 let payload: *u8 = sys_mmap(TM_MAGIC_1024) 164 let sig: *u8 = sys_mmap(64) 165 let sighex: *u8 = sys_mmap(160) 166 167 var shared: i64 = 0 168 var local: i64 = 0 169 170 var ls: i64 = 0 171 while ls < dn { 172 var le: i64 = ls 173 var sc: i64 = 1 174 while sc == 1 { if le >= dn { sc = 0 } else { if dev[le] == (10 as u8) { sc = 0 } else { le = le + 1 } } } 175 if dev[ls] != (35 as u8) { 176 let f0: i64 = tm_field(dev, ls, le, 0) 177 let f1: i64 = tm_field(dev, ls, le, 1) 178 if f0 >= 0 { if f1 >= 0 { 179 let did: i64 = tm_parse_num(dev, f1, le) // emu_devmap: base<TAB>deviceid 180 spec[0] = 0 as u8 181 tm_reg_spec(reg, rn, did, spec) 182 // build the telemetry payload (the learning this node would contribute) 183 var po: i64 = 0 184 po = tm_cat(payload, po, "node-telemetry deviceid=" as *u8); po = tm_catn(payload, po, did) 185 po = tm_cat(payload, po, " spec=" as *u8); po = tm_cat(payload, po, spec) 186 po = tm_cat(payload, po, " status=brought-up-working" as *u8) 187 payload[po] = 0 as u8 188 let consent: i64 = tm_consent(con, cn, did) 189 if consent == 1 { 190 // OPT-IN: SHARE -- ed25519-SIGN the payload with the node identity key. 191 if ed25519_sign_full(priv32, payload, po, sig) == 0 { 192 tm_hex(sig, 64, sighex) 193 if sfd >= 0 { tm_fp(sfd, "SHARED " as *u8); tm_fp(sfd, payload); tm_fp(sfd, " sig=" as *u8); tm_fp(sfd, sighex); tm_fp(sfd, " pub=" as *u8); tm_fp(sfd, pubhex); tm_fp(sfd, "\n" as *u8) } 194 shared = shared + 1 195 tm_p(" deviceid=" as *u8); tm_n(did); tm_p(" consent=SHARED -> signed payload to shared outbox\n" as *u8) 196 } 197 } else { 198 // DEFAULT LOCAL-ONLY: retained on-device, NOTHING leaves. 199 if lfd >= 0 { tm_fp(lfd, "LOCAL " as *u8); tm_fp(lfd, payload); tm_fp(lfd, " (retained on-device, not shared)\n" as *u8) } 200 local = local + 1 201 tm_p(" deviceid=" as *u8); tm_n(did); tm_p(" consent=LOCAL-ONLY -> retained on-device (not shared)\n" as *u8) 202 } 203 } } 204 } 205 ls = le + 1 206 } 207 if lfd >= 0 { sys_close(lfd) } 208 if sfd >= 0 { sys_close(sfd) } 209 tm_p("TELEMETRY summary shared=" as *u8); tm_n(shared); tm_p(" local_only=" as *u8); tm_n(local); tm_p(" (default=LOCAL-ONLY, share requires opt-in consent)\n" as *u8) 210 sys_exit(0) 211 return 0 212}