Thing lifecycle — five states

Every device (a thing) that a factory onboards moves through a five-state lifecycle defined by ADR-0026 and made concrete on the wire by the DDI-1 contract §5. Understanding these states — and the transitions between them — is the mental model behind every endpoint in the API reference and every error bucket in the error catalog.

Factory thing five-state lifecycle: ROLLEDOFF → PROVISIONED → ACTIVATED → DEACTIVATED → ANNIHILATED, with a re-activation edge from DEACTIVATED back to ACTIVATED and an annihilate edge into the terminal ANNIHILATED state.

Diagram source: _static/diagrams/factory-lifecycle.mmd (mermaid stateDiagram-v2), regenerable with the repo mmdc pattern — see Contributing.

The five states

ROLLEDOFF

The device has rolled off the factory line with bootstrap firmware and a factory-burned attestation token (plus the platform trust-anchor set) — but no platform identity yet. On first boot it calls POST /ddi/v1/identify (DDI-1 §3.1) with the attestation token. A recognized identity advances the thing toward PROVISIONED; an unrecognized serial returns DDI-1-403-NOT-RECOGNIZED, and a blacklisted identity returns DDI-1-403-IDENTITY-BLACKLISTED (jumping straight to the terminal ANNIHILATED view).

PROVISIONED

The device has completed POST /ddi/v1/provision (DDI-1 §3.2) and now holds a signed X.509 device certificate + CA chain. It switches from attestation-token auth to mTLS, opens its MQTT 5.0 connection on port 8883, subscribes to shared-attribute pushes and commands, and publishes its initial client-attribute snapshot. The attestation token is invalidated platform-side. The thing is enrolled but not yet carrying production workload.

ACTIVATED

The cloud-side state after OS / software install completes (delivered via the OTA update surface, DDI-1 §3.5). From the device’s perspective this is simply normal operation: telemetry, events, heartbeat, and logs flow per DDI-1 §4. This is the steady state a healthy fleet device spends almost all of its life in.

DEACTIVATED

The device’s certificate has been revoked. Its next mTLS attempt fails with a TLS alert certificate_revoked (DDI-1-403-CERT-REVOKED). Firmware should halt cloud communication after N consecutive cert failures and back off. A DEACTIVATED thing is not terminal — the cloud can re-issue a certificate via POST /ddi/v1/rotate and re-activate the device, returning it to ACTIVATED (the re-activation edge in the diagram above).

ANNIHILATED

The terminal state. The identity has been blacklisted (ADR-0026). POST /ddi/v1/identify returns DDI-1-403-IDENTITY-BLACKLISTED and firmware MUST stop retrying — log and halt. A thing can be annihilated from any non-terminal state; once annihilated it never returns.

State transitions at a glance

From

To

Trigger

Wire call (DDI-1)

(burn)

ROLLEDOFF

factory burn

ROLLEDOFF

PROVISIONED

identify + provision

§3.1 + §3.2

PROVISIONED

ACTIVATED

activate (OS/software install delivered)

§3.5 (OTA)

ACTIVATED

DEACTIVATED

deactivate (cert revoked)

cert revocation

DEACTIVATED

ACTIVATED

re-activate (cert re-issued)

§3.3 (rotate)

ROLLEDOFF / PROVISIONED / ACTIVATED / DEACTIVATED

ANNIHILATED

annihilate (blacklist)

§3.1 returns 403 (terminal)

Where errors map to states

The lifecycle is also the spine of the error catalog: the thing-state-machine bucket (bucket 4) covers illegal-transition and wrong-state errors (for example, attempting to activate a thing that is still ROLLEDOFF), and the identity / blacklist bucket covers the ANNIHILATED terminal-identity errors. See the error catalog for the full mapping of each THM-* code to the state it guards. The state-change feed that reports these transitions to your backend is documented in the API reference under /api/v1/state-changes.

Trust anchors ride the ACTIVATE edge

Every ACTIVATE (both first-time from PROVISIONED and re-activation from DEACTIVATED) carries the platform trust-anchor envelope to your provision-role backend. See Trust-anchor delivery for the envelope contract and the verb/leg matrix.