Device lifecycle

From first boot to retirement — every state a VelaOS device moves through, who changes it, and what happens at each transition.

The six states

  1. unenrolled — freshly flashed ROM, no tenant yet. Device shows a VelaOS Code on screen.
  2. pending — code entered in console but admin has not clicked Approve. Device is known but receives no policy.
  3. approved — admin approved. Device syncs policy, starts heartbeats, appears in the fleet.
  4. online — normal operating state. Heartbeat within last 90 seconds.
  5. offline — no heartbeat for 90s+. Still enrolled, cached policy still active.
  6. retired — wiped or written off. Enrollment revoked, MQTT cert invalidated.
Only online and offline devices count toward your licensed device limit.unenrolled and retired do not.

Transitions

  • unenrolled → pending — admin types the VelaOS Code in the console
  • pending → approved — admin clicks Approve
  • approved → online — first successful heartbeat received
  • online ↔ offline — based on 90s heartbeat window
  • any → retired — admin wipes the device (DELETE /devices/:id) or factory resets remotely

What happens on each transition

pending → approved

  • Cloud publishes approved MQTT command to device
  • Device shows "Connected to {tenant name}" on screen
  • Cloud pushes effective policy (merged tenant + group + device)
  • Device starts 60s heartbeat loop + subscribes to command topics
  • Compliance engine runs initial evaluation within 5 minutes

online → offline

  • No heartbeat received for 90 seconds
  • Console UI marks device amber immediately
  • After 30 minutes offline, email alert fires (if enabled in Settings → Notifications)
  • Cached policy remains active on device — it keeps working, just can't receive new commands

→ retired

  • Cloud publishes factory_reset or selective_wipe MQTT command
  • Device MQTT certificate is revoked server-side (can't reconnect even if unreset)
  • Device row stays in Postgres for audit, but filtered out of default fleet views

Next steps

Was this helpful?
Updated 2026-04-14Edit on GitHub