Skip to content

OTA Update Mechanism

Why a State Machine

OTA updates are long-running, multi-step operations that can fail at any stage. A finite state machine (FSM) makes the allowed transitions explicit, prevents illegal state combinations, and provides a clear recovery path from every failure mode. The current state is always observable via GetOTAStatus.

State Persistence and Power-Loss Recovery

When entering one of six checkpointable states (update_available, downloading, paused, update_ready, download_failed, update_failed), OTAStateMachine writes an OTASnapshot to a YAML checkpoint file at DEFAULT_CHECKPOINT_FILE (/data/tolomeo/ota-service.yml). The snapshot records the current state, installed firmware info, and pending update info. Entering updated or update_successful instead deletes the checkpoint.

Note

If /data does not exist at runtime (development machines, CI environments), the checkpoint falls back to <tempdir>/tolomeo/ota-service.yml (e.g. /tmp/tolomeo/ota-service.yml on Linux). Production edge devices are expected to have /data mounted.

That path is the default, not a fixture. A deployment relocates it, and the downloaded package, through the plugin's configuration table:

[plugins.swupdate]
download_dir = "/data/firmware"
download_filename = "image.swu"
checkpoint_file = "/data/firmware/ota-state.yml"

OTAPlugin applies checkpoint_file in before_setup, which is the first point where both the plugin id — the table is keyed by it — and the configuration are known. Entering bootstrapping both reads a snapshot and, if one is found, writes another, so the state machine is constructed with bootstrapping deferred and stays in that state until the location is assigned. See the Configuration Reference.

Once its location is known, the FSM enters the bootstrapping state and reads the checkpoint file. It selects one of the six detect_* transitions to move to the correct runtime state:

bootstrapping → updated           (no pending update)
bootstrapping → update_available  (update was announced)
bootstrapping → paused            (download was in progress but paused)
bootstrapping → update_ready      (download completed, not yet installed)
bootstrapping → download_failed   (download was in progress and failed)
bootstrapping → update_failed     (install was in progress and failed)

This means the service survives a power cut at any point in the update lifecycle and resumes from exactly where it left off.

Download Task Synchronisation

OTAPlugin does not drive the FSM directly during a download. Instead, it registers callbacks on TaskManager via _on_download_state_change and _on_download_error. When the download task completes, the callback fires fsm.download_success(); on failure it fires fsm.download_fail(). This decouples the FSM from the asyncio task scheduler.

Error Recovery

Every failure state has exactly one recovery path:

State Recovery command Resulting state
download_failed AbortOTAUpdate update_available
update_failed AbortOTAUpdate update_available
Any state ResetOTAState Returns the FSM to a clean baseline (bootstrapping → re-detect → updated), discarding any pending update

CancelOTAUpdate applies to in-progress states (downloading, paused, update_ready) and returns to update_available without clearing the pending update info, allowing an immediate retry.