nx_iot_hub_tenant.nx source
↩ module page · 80 lines · 4424 B
1// nx_iot_hub_tenant.nx -- per-HOUSE IoT sharding built ON THE NISHI HR SPINE (killer-feature rung 4).
2//
3// Operator 2026-06-23: "make sure on the tenant and all that this is why we were building into a
4// nishi hr." So the multi-tenancy is NOT a bespoke IoT store -- it composes NISHI HR (the access
5// SSOT). Each HOUSE = an HR REALM; realm isolation is BY CONSTRUCTION (cred_id = sha256(realm|||
6// handle), so the same handle in another house derives a DIFFERENT cred = level 0 here). A viewer's
7// HR level drives their IoT access tier (deny-by-default, exactly like torrent/gallery via HR):
8// level 0 (unknown/suspended) -> DENY (no devices, no controls)
9// level 1 (family) -> FAMILY (see + on/off)
10// level 3 (owner/operator) -> OWNER (+ manage/adopt)
11// Reuses nx_iot_dashboard (zero-JS cards) + nx_hr (hr_cred_id / hr_resolve_level). One running hub
12// serves the right house's dashboard to the right HR-authenticated viewer; sharding is the realm.
13// license_tier: ORIGINAL
14import "nx_hr.nx"
15import "nx_iot_dashboard.nx"
16const IOT_MAGIC_262144: i64 = 262144
17
18// ---- IoT access tiers from HR level (sealed; deny-by-default) ----
19const IOT_DENY: i64 = 0
20const IOT_FAMILY: i64 = 1
21const IOT_OWNER: i64 = 2
22
23func iot_access_from_level(level: i64) -> i64 {
24 if level >= 3 { return IOT_OWNER }
25 if level >= 1 { return IOT_FAMILY }
26 return IOT_DENY
27}
28
29// Resolve a viewer (house realm + handle) to their IoT access tier THROUGH HR. This is the shard
30// key: realm = house, isolation by construction. No login needed to provision (handle-derived cred),
31// and it equals the id a real OPAQUE session resolves to.
32func iot_hub_viewer_access(hr_store: *u8, realm: *u8, realm_n: i64, handle: *u8, hn: i64) -> i64 {
33 let cred: *u8 = sys_mmap(96)
34 let clen: i64 = hr_cred_id(realm, realm_n, handle, hn, cred)
35 if clen <= 0 { return IOT_DENY }
36 let level: i64 = hr_resolve_level(hr_store, cred, clen)
37 return iot_access_from_level(level)
38}
39
40// Render the house-scoped dashboard gated by the viewer's IoT tier. DENY -> banner + denial, NO
41// device cards and NO control forms (can't see or actuate another house's devices). Returns length.
42func iot_tenant_render(h: *u8, house_label: *u8, access: i64) -> i64 {
43 var w: i64 = 0
44 w = cp_head(h, w)
45 w = cp_cat(h, w, "<div class=\"card\"><div class=\"cn\">House: " as *u8)
46 w = cp_cat(h, w, house_label)
47 w = cp_cat(h, w, "</div><div class=\"cr\">" as *u8)
48 if access == IOT_OWNER { w = cp_cat(h, w, "Owner · manage + control · LAN-only" as *u8) }
49 if access == IOT_FAMILY { w = cp_cat(h, w, "Family · control · LAN-only" as *u8) }
50 if access == IOT_DENY { w = cp_cat(h, w, "Not enrolled in this household" as *u8) }
51 w = cp_cat(h, w, "</div></div>\n" as *u8)
52
53 if access == IOT_DENY {
54 w = cp_cat(h, w, "<div class=\"card\">Access denied — you are not a member of this household. No devices shown.</div>\n" as *u8)
55 w = cp_foot(h, w)
56 return w
57 }
58 // family + owner see the device cards with on/off controls (the hub feeds the live per-realm list)
59 w = db_card(h, w, "38:1f:8d:43:9b:22" as *u8, "IoT Plug 1" as *u8, house_label, "Tuya/ESP" as *u8, "192.168.10.166" as *u8, NX_IOT_CLASS_SWITCH, 1, 1, 1)
60 w = db_card(h, w, "38:1f:8d:43:a3:c2" as *u8, "IoT Plug 2" as *u8, house_label, "Tuya/ESP" as *u8, "192.168.10.242" as *u8, NX_IOT_CLASS_SWITCH, 0, 1, 3)
61 // owner-only manage affordance (adopt new devices) -- a distinct POST route, gated by tier
62 if access == IOT_OWNER {
63 w = cp_cat(h, w, "<div class=\"card\"><div class=\"cn\">Manage devices</div><div class=\"row\"><form class=\"w\" method=\"post\" action=\"/home/manage\"><button class=\"b\" name=\"cmd\" value=\"adopt\">Adopt new</button></form></div></div>\n" as *u8)
64 }
65 w = cp_foot(h, w)
66 return w
67}
68
69func main() -> i64 {
70 // demo: render an owner view for the "westhouse" realm
71 let h: *u8 = sys_mmap(IOT_MAGIC_262144)
72 let w: i64 = iot_tenant_render(h, "westhouse" as *u8, IOT_OWNER)
73 let fd: i64 = sys_openat_wr("web_assets/iot_house.html" as *u8, 0x1a4)
74 if fd < 0 { sys_write(2, "FATAL: cannot open web_assets/iot_house.html\n" as *u8, 45); sys_exit(2); return 2 }
75 sys_write(fd, h, w)
76 sys_close(fd)
77 sys_write(1, "[nx_iot_hub_tenant] wrote iot_house.html (owner view)\n" as *u8, 53)
78 sys_exit(0)
79 return 0
80}