Offline Updates
This section guides you through the steps of updating a device offline.
Tip
Make sure to check the Offline Update Considerations.
Prerequisites
Enable Offline Update support, i.e. add
aklite-offline
toPACKAGECONFIG
ofaktualizr
. For example:cat meta-subscriber-overrides.git/recipes-sota/aktualizr/aktualizr_%.bbappend PACKAGECONFIG:append = " aklite-offline"
Enable Apps fetching if you would like to update
Compose Apps
along with rootfs (aka ostree). To do so, add the following configuration snippet to thecontainers
section of your Factory definition:containers: ... offline: enabled: <true | false> # `false` implies that apps are disabled for an offline update app_shortlist: <comma separated list of apps> # all target apps are fetched if not specified or empty; if absent then the image preloading app shortlist is applied if set ref_options: refs/heads/<branch>: params: FETCH_APPS: <"1" | "0"> # overrides value set in `containers.offline.enabled` for a given branch FETCH_APPS_SHORTLIST: <comma separated list of apps> # overrides value set in `containers.offline.app_shortlist` for a given branch
Ensure that The Update Framework (TUF) keys are taken offline.
Build an LmP image and flash it onto a target device or update the device with the image via OTA.
Obtaining Offline Update Content
First download the offline update content from your Factory to an install medium which can be attached to the target device. The offline update content, further referred to as “bundle”, consists of:
- TUF metadata;
- OSTree repo containing a device’s rootfs;
- Compose Apps.
Use the command fioctl targets offline-update <target-name> <dst> <--tag <tag> [--prod] | --wave <wave-name>> [--expires-in-days <days>]
to download the bundle.
<target-name>
- denotes the Target to update a device to<dst>
- defines a path to download the update content to<tag>
- specifies the Target tag and the tag that the device is on--prod
- indicates that this is an update for a production device and<target-name>
refers to Production Target (see the note below)--wave
- indicates that this is an update for a production device and<target-name>
refers to Wave Target (see the note below)<days>
- Offline artifact validity period in days
Note
Use fioctl waves init
command to generate Wave Target.
Use fioctl waves complete
command to turn Wave Target into Production Target.
Ensure that the target device is a Production device, see Manufacturing Process for Device Registration for more details.
Note
In order to download all artifacts, fioctl
requires token with scopes: targets:read
, ci:read
.
Signing the Offline Update Bundle
It is essential to sign the bundle using one or more TUF targets role keys. This ensures the authenticity of an offline update bundle during the update process on a device.
If the bundle contains CI targets, it is signed by the OTA Lite service using the online TUF targets role key. Users do not need to take any action in this scenario.
If the bundle contains production or wave targets, it should be signed using one or more TUF targets offline keys.
Use fioctl targets offline-update sign <bundle-path> --keys <path-to-targets-keys-file>
command to sign the bundle.
The number of required signatures is determined by the threshold set in the latest TUF root role metadata,
which is printed as part of the overall output of any of the fioctl targets offline-update
sub-commands.
Additionally, you can find out the signature threshold by running fioctl targets offline-update show <bundle-path>
,
as well as by using fioctl keys tuf show-root --prod
command (look for the targets role threshold).
The “aklite-offline” utility verifies the bundle signature(s) before initiating the update process to ensure the authenticity of the bundle. If the signature check fails, the utility will not start the update process.
Performing the Offline Update
Before doing the offline update, ensure the bundle is accessible on a device, e.g., attach and mount the USB drive.
Use the aklite-offline
CLI utility to perform an offline update.
Run
aklite-offline install [--config <config dir or file>] --src-dir <path to a bundle>
.Run one of the post installation actions depending on the
aklite-offline install
result:- code 100: reboot device and invoke
aklite-offline run [--config <config dir or file>]
to finalize an ostree installation and start Apps if both ostree/rootfs and Apps are updated; - code 10: invoke
aklite-offline run [--config <config dir or file>]
to start updated Apps. - code 90: reboot device to finalize the previous boot firmware update and go to the step #1 to start the update.
- code 100: reboot device and invoke
Reboot a device after running
aklite-offline run [--config <config dir or file>]
command if:- code 100: Apps failed to start after update, you must reboot the device and re-execute
aklite-offline run
to complete the rollback; - code 5: the update includes a boot firmware, you can optionally reboot a device to finalize the boot firmware upgrade.
- code 100: Apps failed to start after update, you must reboot the device and re-execute
Usage Details
The CLI utility supports two commands:
aklite-offline install [--config <config file/dir>] --src-dir <update-content-dir>
aklite-offline run [--config <config file/dir>]
``--config`` - Path to a directory that contains one of more ``*.toml`` configuration snippets or a path to a ``*.toml`` file. It may be omitted at all so the command collects config from the snippets found in the default directories/files, as ``aktualizr-lite`` does:
/usr/lib/sota/conf.d
/var/sota/sota.toml
/etc/sota/conf.d/
``--src-dir`` - Path to a directory that contains the bundle downloaded by ``fioctl targets offline-update`` command.
Post Install and Run Actions
The install
and run
commands sets exit codes (echo $?
) to instruct which of the post install actions you should perform.
The install
command sets the following exit codes:
- 0: Installation was not performed.
- Device already runs the specified target, no update is needed.
- 4: Installation was not performed.
- Failed to update TUF metadata.
- 6: Installation was not performed.
- Failed to find Targets in the device TUF repo that matches a device tag and/or hardware ID.
- 8: Installation was not performed.
- Failed to find the ostree commit and/or all Apps of the Target to be installed in the provided source bundle.
- 10: Installation succeeded.
aklite-offline run
must be invoked to start the updated Apps.
- 11: Installation was not performed.
- Provided TUF metadata is invalid.
- 12: Installation was not performed.
- Provided TUF metadata is expired.
- 14: Installation was not performed.
- TUF metadata not found in the provided path.
- 15: Installation was not performed.
- The bundle metadata is invalid. There are a few reasons why the metadata might be invalid:
- One or more bundle signatures is/are invalid.
- The bundle targets’ type, whether CI or production, differs from the device’s type.
- The bundle targets’ tag differs from the device’s tag.
- 30: Installation was not performed.
- Could not start a new update because there is an ongoing installation that requires finalization.
- 50: Installation was not performed.
- Failed to pull Target content.
- 70: Installation was not performed.
- The pulled Target content is invalid, specifically App compose file is invalid.
- 90: Installation was not performed.
- Reboot is required to complete the previous boot firmware update. After reboot a client should repeat the update attempt from the beginning.
- 100: Installation succeeded.
- Reboot is required to complete installation. After reboot
aklite-offline run
must be invoked.
- Reboot is required to complete installation. After reboot
- 102: Downgrade attempt.
- The Target to be installed has a version lower than the one running. Use the
--force
option to force its installation.
- The Target to be installed has a version lower than the one running. Use the
The run
command sets the following exit codes:
- 0: Update succeeded.
- Device is booted on the updated rootfs and running the updated Apps.
- 5: Update succeeded.
- The boot firmware was updated too. Optionally, a reboot to confirm its update can be performed.
- 40: The
run
command was not executed - Could not start the command because there is no pending installation. Make sure you ran the
install
command before.
- Could not start the command because there is no pending installation. Make sure you ran the
- 40: The
- 90: Update succeeded.
- Device is booted on the updated rootfs and running the updated Apps.
- Bootloader is updated too, optionally, a reboot to confirm its update can be performed.
- 99: Update failed.
- Device failed to boot on the updated rootfs and rolled back to the previous version.
- 100: Update failed.
- Device successfully booted on the updated rootfs but failed to start the updated Apps after the reboot.
- Device is rolling back to the previous version, reboot followed by
aklite-offline run
is required to complete the rollback.
- 110: Update failed.
- Device failed to boot on the updated rootfs and rolled back to the previous version.
- Device failed to start the previous version’s Apps since they are unknown.
- 120: Update and rollback failed.
- Device successfully booted on the updated rootfs but failed to start the updated Apps after the reboot.
- Device cannot perform rollback because the Target/version to rollback to is unknown.
Configuration Details
The minimum required configuration is:
[provision]
primary_ecu_hardware_id = <>
The command can digest the default device config consisting of:
*toml
files added into LmP during bitbaking (usually just/usr/lib/sota/conf.d/40-hardware-id.toml
);sota.toml
generated bylmp-device-register
.
If a device needs to support offline and online updating, then the configuration needs to be shared with aktualizr-lite
.
Normally, each LmP image includes a configuration file /usr/lib/sota/conf.d/40-hardware-id.toml
which defines a hardware ID.
Therefore, by default, an LmP image includes the minimum required configuration, hence NO device registration is required for aklite-offline
to work.
If you register a device and sota.toml
is generated, then the offline update command can either work alone or alone with aktualizr-lite
.
In the later case, you must stop the aktualizr-lite
systemd service before running the offline update command.
Offline Update Considerations
Offline Update Bundle Packaging
The content provided by
fioctl targets offline-update
command should be packaged by you, and verified by the client service.Offline Update Bundle Delivery
Related to the bullet above, Foundries.io™ cannot provide secure delivery of an update bundle since you should do the packaging and delivery.
Offline Update for Unregistered Devices
When dealing with devices not registered with the FoundriesFactory™ service or a custom registration server, several considerations arise:
- Production Status: The distinction between production and non-production status of the device remains undetermined.
- Device Tag: The specific tag associated with the device is not configured.
As a consequence, during the initial offline update, users can install both production and non-production targets on unregistered devices. However, subsequent updates are constrained by the type of targets installed during the initial update. Therefore, the target type chosen during the first update dictates the supported targets’ type for future updates.
Additionally, due to the absence of a defined tag in the configuration of unregistered devices, users can install targets associated with any tag. This issue can be addressed by incorporating a configuration snippet (a
*.toml
file) into either/usr/lib/sota/conf.d
or/etc/sota/conf.d
. We recommend implementing this solution through a new recipe in the factory’smeta-subscriber-overrides.git
repository. The snippet should contain the following content:
[pacman]
tags = "<tag>"
Online/Offline Mixed Updates (aka hybrid mode)
There are a few points to take into account by the custom client application:
- Offline or Online downgrade fails by default.
Therefore, if the latest target is installed on a device through an online update,
then an offline update for the outdated bundle fails, unless a user explicitly specifies the
--force
parameter. - Offline Update fails if its input TUF metadata are outdated. For example, it fails if an online update upgrades the device’s TUF root/timestamp/snapshot/targets metadata to version N while the bundle contains version N-1 of the metadata.
- Running offline and online update simultaneously leads to undefined behaviour.
It is impossible to run two or more update agents of the same or different types simultaneously,
for example
aktualizr-lite daemon
andaklite-offline
. However, since the API supports both types of the update, a user may develop a custom sota client that does these two types of update in parallel by mistake.
- Offline or Online downgrade fails by default.
Therefore, if the latest target is installed on a device through an online update,
then an offline update for the outdated bundle fails, unless a user explicitly specifies the
Controlling the Expiration Time of the Offline Update Bundle
The bundle obtained through the fioctl targets offline-update
command comes with an expiration time.
If the expiration time of the bundle has passed, the offline update will fail.
Use the --expires-in-days
parameter of the fioctl targets offline-update
command to set the desired expiration time of the bundle.
If the command fails with the one of the errors below, then it means the root or
targets metadata expires sooner than the date specified in the parameter.
Getting CI Target details; target: intel-corei7-64-lmp-2377, tag: master...
Refreshing and downloading TUF metadata for Target intel-corei7-64-lmp-2377 to 2377/tuf...
ERROR: Failed to download TUF metadata: HTTP error during POST 'https://api.foundries.io/ota/factories/<factory>/targets/intel-corei7-64-lmp-2377/meta/': 400 BAD REQUEST
= Root metadata expire (2024-07-06T07:56:57Z) before the specified expiration time (2025-02-11T09:17:39Z)
Getting production Target details; target: intel-corei7-64-lmp-2356, tag: master...
Refreshing and downloading TUF metadata for Target intel-corei7-64-lmp-2356 to 2356/tuf...
ERROR: Failed to download TUF metadata: HTTP error during POST 'https://api.foundries.io/ota/factories/<factory>/targets/intel-corei7-64-lmp-2356/meta/': 400 BAD REQUEST
= Targets metadata expire (2025-01-28T16:38:23Z) before the specified expiration time (2025-02-06T09:27:35Z)
To fix the issue, either decrease the parameter value or refresh the root/targets metadata accordingly and then re-run the command.
To refresh root metadata you should rotate TUF root role key. The expiration time is set to one year since the moment of the latest root key rotation.
To refresh targets role metadata use one of the following depending on targets type, CI or wave/production.
- CI targets — Trigger a new CI build, it will create a new target and update CI targets role metadata expiration time to 1 year since the moment of creation.
- Wave or production targets — Create a new wave for the given target version.
Use
--expires-days
or--expires-at
parameters of thefioctl waves init
command to set a desired expiration time. By default, if none of the parameters above are specified, the expiration of a wave’s targets role metadata is set to one year.
Therefore, the --expires-in-days
parameter of the fioctl targets offline-update
command is the primary knob
to tune the bundle’s expiration time up to 1 year (the maximum validity period of TUF root metadata).
Effectively, this parameter sets the expiration time for the bundle’s copy of the TUF timestamp role metadata, and does not affect the factory’s metadata.
Root and/or CI/wave/production targets refreshing serves as the secondary mechanism. It should be applied if the desired expiration time occurs later than the root’s and/or the targets’ expiration, respectively.
More details on FoundriesFactory TUF metadata expiration time can be found in the following section.
Understanding the Math Behind the Offline Update Bundle Expiration Time
The expiration time of the bundle is determined by the expiration times of the TUF metadata it encompasses. Specifically, it equals the minimum value among the expiration times across all TUF roles’ metadata.
- CI/Wave/Production root role metadata
- The expiration time is set to 1 year from the moment when of the latest TUF root key was added or rotated. The other commands that modify the TUF root metadata do not extend its expiration. It is possible to set the TUF root expiration time to any value through the API.
- CI timestamp, snapshot, and targets roles metadata
- The default expiration time is set to 1 year since the last successful CI build. If there are no builds for a year, the expiration is automatically extended by one month every month. A user can overwrite the default value using the factory config parameter tuf.targets_expire_after.
- Wave/Production timestamp roles metadata
- The expiration time is set to 7 days. The TUF specification recommends setting a short expiration date for the TUF timestamp metadata and re-signing it frequently. This allows clients to quickly detect if they are being prevented from obtaining the most recent metadata (“indefinite freeze attacks”). The FoundriesFactory automatically refreshes the metadata for an additional 7 days just before expiration.
- Wave/Production snapshot and targets roles metadata
- The default expiration time is set to 1 year.
A user can overwrite the default value using the
--expires-days
or--expires-at
parameter of thefioctl wave init
command.