code wiki / _hdl_build / nx_iot_ctl_lib.nx
nx_iot_ctl_lib.nx
buildroot/runtime/_hdl_build/nx_iot_ctl_lib.nx
about
nx_iot_ctl_lib.nx -- sovereign HOME-ELECTRONICS identification LIB (no main).
THE MEASURED DEFECT THIS CLOSES (2026-07-25, measured on the real LAN before
a line was written): the live `nx_printer_ctl survey` classifies EVERY host
on 192.168.8 as "web-device" because its port profile is four hardcoded
printer/web ports (7125/10088/80/443). It cannot tell a smart plug from a
NAS. Meanwhile a full vendor-routing brain -- nx_iot_classify, built and
gate-proven back in June (fused reply > OUI > port > SSID over Kasa / Tuya /
WiZ / MagicHome / Yeelight) -- has been sitting STRANDED in the tree, never
wired to anything live. This lib is the wiring, not a re-derivation.
TWO DESIGN LAWS ARE MECHANISED HERE:
1. DATA-DRIVEN, NOT HARDCODED (rule 11). The service table is a MANIFEST:
port / proto / vendor / kind / label rows, loaded from `iot_services.conf`
with a compiled-in fail-safe default set (rule 14: a missing/unreadable
conf degrades to the defaults, it never leaves the organ blind). Adding a
vendor's port is a conf edit, not a rebuild. MANIFEST ORDER IS PRECEDENCE:
the first matching row decides a host's kind, so specificity is expressed
as data too.
2. AN INSTRUMENT MUST DECLARE WHAT IT CANNOT SEE (the cynical-instruments
law: unproven == absent; a synthetic pass is a stub). A TCP connect sweep
STRUCTURALLY CANNOT find a UDP-only service -- WiZ bulbs live on UDP 38899
and would be silently reported as "not there", which reads identically to
"no WiZ bulbs exist". So every emitted scan carries `udp_ports_unswept`
plus an explicit note that absence there is not evidence of absence. The
manifest tracks proto per row precisely so this stays honest as it grows.
NEVER-BRICK (rule 26, ABSOLUTE): this lib is IDENTIFICATION ONLY. Its single
active probe is Kasa `get_sysinfo` -- a READ. There is no write verb, no
set_stainfo credential push, no firmware/OTA path, no raw command
passthrough. A device cannot be altered through any code path in this file;
nx_iot_ctl_gate proves it mechanically.
license_tier: ORIGINAL No hw writes (Rule 26).
dependencies 5 imports · 2 importers
imports: nx_syscalls.nxnx_lan_scan.nxnx_iot_classify.nxnx_iot_local_kasa.nxnx_moonraker_io.nx
imported by: nx_iot_ctl.nxnx_iot_ctl_gate.nx
structs
| none |
consts
| 42 | const IOTC_MAGIC_65535: i64 = 65535 |
| 43 | const IOTC_MAGIC_9999: i64 = 9999 |
| 44 | const IOTC_MAGIC_6668: i64 = 6668 |
| 45 | const IOTC_MAGIC_6667: i64 = 6667 |
| 46 | const IOTC_MAGIC_55443: i64 = 55443 |
| 47 | const IOTC_MAGIC_5577: i64 = 5577 |
| 48 | const IOTC_MAGIC_38899: i64 = 38899 |
| 49 | const IOTC_MAGIC_7125: i64 = 7125 |
| 50 | const IOTC_MAGIC_10088: i64 = 10088 |
| 51 | const IOTC_MAGIC_1883: i64 = 1883 |
| 52 | const IOTC_MAGIC_8123: i64 = 8123 |
| 55 | const IOTC_OUT: i64 = 32768 |
| 56 | const IOTC_RESP: i64 = 8192 |
| 57 | const IOTC_SCRATCH: i64 = 4096 |
| 60 | const IOTC_V_BAD: i64 = 0 |
| 61 | const IOTC_V_SERVICES: i64 = 1 |
| 62 | const IOTC_V_SCAN: i64 = 2 |
| 63 | const IOTC_V_PROBE: i64 = 3 |
| 64 | const IOTC_V_CONTRACT: i64 = 4 |
| 67 | const IOTC_KIND_UNKNOWN: i64 = 0 |
| 68 | const IOTC_KIND_PLUG: i64 = 1 |
| 69 | const IOTC_KIND_BULB: i64 = 2 |
| 70 | const IOTC_KIND_PRINTER: i64 = 3 |
| 71 | const IOTC_KIND_HUB: i64 = 4 |
| 72 | const IOTC_KIND_WEB: i64 = 5 |
| 73 | const IOTC_KIND_N: i64 = 6 |
| 76 | const IOTC_PROTO_TCP: i64 = 1 |
| 77 | const IOTC_PROTO_UDP: i64 = 2 |
| 82 | const IOTM_BASE: i64 = 8 |
| 83 | const IOTM_ROW: i64 = 64 |
| 84 | const IOTM_LABEL: i64 = 32 |
| 85 | const IOTM_MAXLBL: i64 = 31 |
| 86 | const IOTM_MAX: i64 = 32 |
| 87 | const IOTM_BYTES: i64 = 4096 |
functions
| 89 | func iotm_new() -> *u8 |
| 95 | func iotm_count(m: *u8) -> i64 { let hdr: *i64 = m as *i64; return hdr[0] } |
| 96 | func iotm_row(m: *u8, i: i64) -> *u8 |
| 100 | func iotm_port(m: *u8, i: i64) -> i64 { let r: *i64 = iotm_row(m, i) as *i64; return r[0] } |
| 101 | func iotm_vendor(m: *u8, i: i64) -> i64 { let r: *i64 = iotm_row(m, i) as *i64; return r[1] } |
| 102 | func iotm_kind(m: *u8, i: i64) -> i64 { let r: *i64 = iotm_row(m, i) as *i64; return r[2] } |
| 103 | func iotm_proto(m: *u8, i: i64) -> i64 { let r: *i64 = iotm_row(m, i) as *i64; return r[3] } |
| 104 | func iotm_label(m: *u8, i: i64) -> *u8 |
| 110 | func iotm_add(m: *u8, port: i64, vendor: i64, kind: i64, proto: i64, label: *u8) -> i64 |
| 142 | func iotm_defaults(m: *u8) -> i64 |
| 164 | func iotc_is_space(c: i64) -> i64 called by 1: iotm_parse |
| 170 | func iotc_word_eq(buf: *u8, s: i64, e: i64, lit: *u8) -> i64 |
| 190 | func iotc_word_num(buf: *u8, s: i64, e: i64) -> i64 called by 1: iotm_parse |
| 203 | func iotc_vendor_of(buf: *u8, s: i64, e: i64) -> i64 |
| 211 | func iotc_kind_of(buf: *u8, s: i64, e: i64) -> i64 |
| 222 | func iotm_parse(m: *u8, buf: *u8, n: i64) -> i64 called by 2: mainiotm_load calls 7: iotc_is_spaceiotc_word_numiotc_word_eqiotc_vendor_ofiotc_kind_ofsys_mmap+1 |
| 306 | func iotm_load(m: *u8, path: *u8) -> i64 |
| 320 | func iotc_kind_name(k: i64) -> *u8 |
| 328 | func iotc_vendor_name(v: i64) -> *u8 |
| 336 | func iotc_proto_name(p: i64) -> *u8 called by 1: iotc_emit_services |
| 342 | func iotc_streq(a: *u8, b: *u8) -> i64 |
| 350 | func iotc_verb_id(v: *u8) -> i64 |
| 359 | func iotc_cat(d: *u8, o: i64, s: *u8) -> i64 |
| 364 | func iotc_catn(d: *u8, o: i64, v: i64) -> i64 called by 5: iotc_cat_ipiotc_emit_servicesiotc_emit_scaniotc_emit_probeiotc_emit_contract calls 1: sys_mmap |
| 375 | func iotc_cat_ip(d: *u8, o: i64, ipv4: i64) -> i64 |
| 387 | func iotc_emit_services(out: *u8, m: *u8, from_conf: i64) -> i64 |
| 423 | func iotc_scan(m: *u8, base24: i64, budget_per_port: i64, mask: *i64) -> i64 |
| 445 | func iotc_mask_row(m: *u8, bits: i64) -> i64 |
| 457 | func iotc_mask_vendor(m: *u8, bits: i64) -> i64 called by 2: mainiotc_emit_scan calls 4: iotm_countnx_iot_classify_by_portiotm_portnx_iot_classify_fuse |
| 473 | func iotc_emit_scan(out: *u8, m: *u8, prefix: *u8, base24: i64, mask: *i64, from_conf: i64) -> i64 |
| 543 | func iotc_kasa_probe(ipv4: i64, port: i64, plain: *u8, plain_cap: i64) -> i64 called by 2: mainmain calls 8: sys_mmapnx_iot_kasa_pt_sysinfonx_iot_kasa_frame_tcpnx_mr_tcp_connect_ipv4sys_writesys_close+2 |
| 565 | func iotc_probe_err(code: i64) -> *u8 called by 1: iotc_emit_probe |
| 572 | func iotc_emit_probe(out: *u8, ipv4: i64, port: i64, pn: i64, plain: *u8) -> i64 called by 1: main calls 9: iotc_catiotc_cat_ipiotc_catniotc_probe_errnx_iot_classify_by_replynx_iot_classify_by_port+3 |
| 601 | func iotc_emit_contract(out: *u8, m: *u8, from_conf: i64) -> i64 |