# IoT — Derived Rules > Derives from `domains/edge/first-principles.md`. Applies P3 > (Resources are Constrained and Declared) primarily, with P5 > (command idempotency), P7 (partial degradation when devices drop), > P9 (device identity and provisioning), and P10 (telemetry from > devices). Cross-links `domains/security/secrets` for device > credentials and `domains/messaging/queues` for the MQTT QoS > parallels to delivery semantics. ## What IoT at the Edge Is (P3 Resources are Constrained and Declared) - IoT at the edge is the engineering discipline of operating constrained devices — sensors, actuators, gateways, microcontrollers — as first-class participants in a distributed system. The distinguishing constraint is per-device resource bounds (P3): a battery-powered sensor has kilobytes of RAM, a constrained protocol, and a multi-year sleep budget. These constraints are declared per device class, never assumed infinite. - The boundary is per D-061: edge owns the constrained-device reality; performance owns the generic unbounded-growth-is-a-bug principle (`performance/P4 Resource Bounds`); concurrency owns in-process primitives. IoT is an edge concern because its defining traits are constrained resources (P3), geographic distribution as a fleet (P8), partition-prone operation (P2), and device-scoped identity (P9) — concerns that only arise at the network edge. - See `domains/edge/offline-first.md` for the partition-survival discipline that constrained devices depend on, and `domains/edge/sync.md` for the reconciliation of device state across partitions. ## Device Resource Classes (P3, C8 Economy) - A device resource class declares the bounds for a class of devices: CPU (MHz, cores), memory (KB/MB), power (battery mAh, duty-cycle budget), bandwidth (bytes/sec, latency budget), and storage (KB/MB). Every device in the fleet is assigned to a class; every operation is budgeted against its class. - An undeclared budget is a defect (P3 violation): a sensor that sends telemetry every second without a duty-cycle budget exhausts its battery in days, not years. The budget is the correctness bound (C1) and the economy bound (C8). - A device class implies a protocol choice: a class-0 device (constrained sensor, KB RAM) speaks CoAP; a class-1 device (gateway, MB RAM) speaks MQTT; a class-2 device (edge compute node, GB RAM) speaks HTTP. The protocol follows the constraint, not the reverse. | Class | RAM | Power | Protocol | Typical role | |-------|-----|-------|----------|--------------| | 0 (constrained sensor) | < 10 KB | Battery, multi-year | CoAP, LoRaWAN | Telemetry only, no inbound commands | | 1 (actuator, gateway) | 10 KB – 1 MB | Battery or wired, weeks-months | MQTT, CoAP | Telemetry + commands, queue-and-forward | | 2 (edge compute) | > 1 MB | Wired, continuous | HTTP, MQTT | Local aggregation, gateway, edge inference | ## Constrained Protocols — MQTT and CoAP (P3, P5) - **MQTT** is the canonical pub/sub protocol for constrained devices. It is lightweight (2-byte header), broker-backed, and provides QoS levels (0, 1, 2) that map to delivery semantics. MQTT is the cross-process analog of message-queue delivery — see `domains/messaging/queues` for the general queue/delivery-semantics discipline; the cross-link is one-directional outward (edge → messaging) per D-062 and D-026 extended. - **CoAP** is the REST analog for constrained devices: UDP-based, low-overhead, with confirmable (CON) and non-confirmable (NON) message types. CoAP fits class-0 devices where TCP is too heavy. - A blocking synchronous call on a constrained device with no timeout is the `blocking-call-on-constrained-device` chaos anti-pattern: it blocks the node, has no timeout (= hang), and retries are unsafe without idempotency (P3 + P5 breach). Every device operation must be async with a timeout, and every retried operation must be idempotent. ```json // MQTT publish/subscribe payload with QoS levels (P5 idempotency, // P3 constrained protocol). // QoS 0 — at-most-once: fire-and-forget, no ack. For telemetry // where a dropped sample is acceptable (P3 economy of the // constrained link). { "topic": "devices/sensor-7/temperature", "qos": 0, "payload": { "device": "sensor-7", "ts": 1700000000, "value": 21.4, "unit": "C" } } // QoS 1 — at-least-once: acked, may duplicate. The consumer must // be idempotent (P5) — dedup by (device, ts) or an idempotency key. { "topic": "devices/actuator-3/command", "qos": 1, "payload": { "device": "actuator-3", "idempotencyKey": "cmd-1700000000-1", "command": "set-point", "value": 22.0 } } // QoS 2 — exactly-once: four-step handshake, no duplication. The // heaviest QoS; use only where duplicates are intolerable AND the // device has the budget for the handshake (class-1+ only, P3). { "topic": "devices/actuator-3/irreversible-command", "qos": 2, "payload": { "device": "actuator-3", "idempotencyKey": "cmd-1700000000-2", "command": "calibrate" } } ``` - The QoS choice is a P5 (idempotency) and P3 (resource) decision: QoS 0 is cheapest (no ack) but lossy; QoS 1 requires consumer idempotency (P5); QoS 2 is exactly-once but costs a four-step handshake on a constrained link. The default for telemetry is QoS 0 or 1; the default for commands is QoS 1 with an idempotency key (P5); QoS 2 is reserved for irreversible commands where the device budget permits. ## Device Identity and Provisioning (P9 Identity is Constrained at the Edge) - Every device holds a per-device identity: a unique device ID and a scoped credential (X.509 certificate, API token, or rotating key). No shared fleet credential — one compromise must not equal a fleet compromise (P9). The credential scope is minimal: a device can publish to `devices//+` and subscribe to `devices//commands`, nothing else. - Provisioning is the act of assigning a device identity at enrollment time. The provisioning manifest declares the device, its class, its allowed topics, and its credential. The manifest is the P9 contract — a device operating outside its manifest scope is a violation. - A device that is provisioned with a shared fleet key (the `shared-edge-device-credential` anti-pattern) is a P9 violation: blast radius is unbounded. See `domains/security/secrets` for the general secret-hygiene discipline (per-identity credentials, rotation, minimal scope) that device provisioning builds on. ```yaml # Device provisioning manifest (P9 per-device identity + scoped # credentials). The manifest is the contract; the device operates # only within its declared scope. device: id: sensor-7 class: 0 # P3 resource class model: temp-sensor-v2 firmware: 1.4.2 identity: cert: "sha256-of-device-cert" credentialScope: publish: - "devices/sensor-7/temperature" - "devices/sensor-7/status" subscribe: - "devices/sensor-7/commands" # No wildcard, no fleet-wide topics (P9). provisioning: enrolledAt: 2024-01-15T00:00:00Z rotatesEvery: 90d # Per-device credential; never shared (P9, domains/security/secrets). ``` ## Telemetry from Devices (P10 Edge Observability Survives Partition) - Device telemetry is local-first (P10): the device buffers telemetry on-node and forwards on reconnect. A fire-and-forget telemetry pipeline loses data when the link drops; a buffered pipeline survives. The buffer is bounded by the device class (P3): a class-0 sensor buffers minutes of telemetry, not hours. - Telemetry is observable in aggregate: the operator sees the fleet's behavior, not just per-device. A device that has not reported in its expected interval is itself a signal (a dead device, a partitioned device, a drained battery). See `domains/observability/metrics` for the generic structured-metrics discipline; edge owns the partition-survivable, local-first angle. - Telemetry must not be a secrets channel (P9 analog, see `domains/observability/P6 No Secrets in Observability`): device credentials, PII, and personally-identifying location must not enter telemetry payloads. ## Command Idempotency (P5 Edge Operations are Idempotent) - Device commands are retried by nature (the network is partition-prone). Every command carries an idempotency key so a retried command does not double-apply (P5). A `set-point` command retried with the same idempotency key sets the point once, not twice; an `open-valve` command retried is safe because the valve is already open. - Irreversible commands (a calibration burn-in, a firmware flash) require stronger idempotency: the device tracks applied idempotency keys and refuses re-application. A retried irreversible command without idempotency tracking double-applies the effect (P5 violation, possibly a physical-side-effect bug). - The idempotency key is per-command, not per-device. A device that dedups by device ID alone will drop distinct commands issued in the same window. Use `(device, command-id, ts-window)` or a UUID per command. ## Partial Degradation When Devices Drop (P7 Partial Degradation is Engineered) - A fleet degrades when devices drop (battery exhaustion, partition, hardware failure). The system must continue to operate with the remaining devices; a whole-system crash on one device's failure is a P7 violation. The degraded mode is documented: which aggregations are valid with N-1 devices, which alerts fire, which fallbacks engage. - A device that drops is not an incident by itself — fleets expect churn. The operator-facing signal is the *aggregate* health (X% of devices reporting, Y% partitioned for >Z minutes), not the per-device drop. Per-device drop alerts are noise; aggregate degradation alerts are signal (see `domains/observability/metrics`). - A command to a dropped device must time out (P5 — idempotent retry) and degrade (P7 — the fleet continues without that device). A command that blocks forever waiting for a dropped device is the `blocking-call-on-constrained-device` chaos anti-pattern (P3 + P5 breach). ## Cross-Link to Messaging (P5, cross-link messaging/queues) - MQTT QoS 0/1/2 maps to at-most-once / at-least-once / exactly-once delivery semantics — the same three-way tradeoff documented in `domains/messaging/queues`. The cross-link is one-directional outward (edge → messaging) per D-026 extended: edge owns the constrained-device protocol angle; messaging owns the generic cross-process delivery-semantics angle. - This link dangles until P2 (the messaging domain is authored in P2); it is verified bidirectional in P5 (ATELIER-114 per IDEATE-40). Acceptable per D-053 (vertical-slice integrity — P1 ships the edge domain self-consistent; the messaging cross-link resolves by the P6 ship). - The parallel: a constrained device's QoS 1 publish is the device-flavored instance of an at-least-once queue delivery — the consumer (the broker or the downstream service) must be idempotent (P5 here, `messaging/P3 Consumers are Idempotent` there). The idempotency discipline is the same; the protocol and failure model differ (constrained-device link vs broker-backed network). ## What Violates IoT-at-the-Edge Discipline | Violation | Principle | |-----------|-----------| | Undeclared device resource budget (assumes infinite battery/RAM) | P3 Resources are Constrained and Declared | | Shared fleet credential (one key for all devices) | P9 Identity is Constrained at the Edge | | Non-idempotent device command (retried command doubles the effect) | P5 Edge Operations are Idempotent | | Blocking synchronous call on a constrained device with no timeout | P3, P5 (blocks the node; retry unsafe) | | Fire-and-forget telemetry with no on-device buffer (lost on partition) | P10 Edge Observability Survives Partition | | Whole-system crash on one device's failure (no degradation contract) | P7 Partial Degradation is Engineered | | Device credential scope that includes fleet-wide topics (over-scoped) | P9, `domains/security/secrets` | | QoS 2 used on a class-0 device (no budget for the handshake) | P3 Resources are Constrained and Declared | | Per-device-drop alert (noise; aggregate degradation is the signal) | P7, `domains/observability/metrics` | | Telemetry payload that includes device credentials or PII | P9, `domains/observability/P6 No Secrets in Observability` |