20992883ff
---ci--- project: atelier phase: 1 milestone: v0.4 status: complete phase_role: execution phase_tag: v0.3.1 requirements: covered: [ATELIER-92, ATELIER-93, ATELIER-94, ATELIER-95, ATELIER-96] partial: [] ---/ci---
12 KiB
12 KiB
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-linksdomains/security/secretsfor device credentials anddomains/messaging/queuesfor 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.mdfor the partition-survival discipline that constrained devices depend on, anddomains/edge/sync.mdfor 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/queuesfor 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-devicechaos 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.
// 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/<its-id>/+and subscribe todevices/<its-id>/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-credentialanti-pattern) is a P9 violation: blast radius is unbounded. Seedomains/security/secretsfor the general secret-hygiene discipline (per-identity credentials, rotation, minimal scope) that device provisioning builds on.
# 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/metricsfor 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-pointcommand retried with the same idempotency key sets the point once, not twice; anopen-valvecommand 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-devicechaos 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 Idempotentthere). 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 |