Files
atelier/domains/edge/iot.md
T
Jon Chery 20992883ff docs(P01): complete edge domain phase — v0.4
---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---
2026-08-05 15:51:03 +00:00

258 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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/<its-id>/+` and subscribe to
`devices/<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-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` |