Selectively trigger aktualizr for offline updates - check and install subcommands

Hello,

I’m trying to run selective aktualizr commands to see if there is an update available in the “offline” mode, e.g. do not run aktualizr in a cycle as a service, but as a shell command.
From the aktualizr documentation I can see there is a way to do that (sorry for the wayback machine link, the HERE site is unavailable at the moment) Selectively trigger aktualizr | OTA Connect Documentation

According the docs there is a check command and there is a download and install commands that should respectively check, download an update and install it. When I run aktualizr-torizon check with USB drive with fresh update inserted, in the logs - No updates response, but with debug logs it seems like it is searching only for online updates even if I disable online updates in the aktualizr config. But when I run aktualizr-torizon once command, the board is updated correctly and sees an update, installs it etc… I would like to understand how should I run those two commands in order to tell if there is a valid update available from USB and be able to install it via two separate aktualizr commands.

Here are the logs from aktualizr-torizon check command run

Aktualizr version tdx-d71f18a1 starting
Reading config: "/usr/lib/sota/conf.d/20-sota-device-cred.toml"
Reading config: "/usr/lib/sota/conf.d/30-rollback.toml"
Reading config: "/usr/lib/sota/conf.d/40-hardware-id.toml"
Reading config: "/usr/lib/sota/conf.d/50-secondaries.toml"
Reading config: "/usr/lib/sota/conf.d/60-polling-interval.toml"
Reading config: "/usr/lib/sota/conf.d/70-reboot.toml"
Reading config: "/etc/sota/conf.d/99-offline-updates.toml"
Final configuration that will be used: 
[logger]
loglevel = 0

[p11]
module = ""
pass = ""
uptane_key_id = ""
tls_ca_id = ""
tls_pkey_id = ""
tls_clientcert_id = ""

[tls]
server = "https://dgw.torizon.io"
server_url_path = "/usr/lib/sota/gateway.url"
ca_source = "file"
pkey_source = "file"
cert_source = "file"

[provision]
server = "https://dgw.torizon.io"
p12_password = ""
expiry_days = "36000"
provision_path = ""
device_id = ""
primary_ecu_serial = ""
primary_ecu_hardware_id = "verdin-imx8mp"
ecu_registration_endpoint = "https://dgw.torizon.io/director/ecus"
mode = "DeviceCred"

[uptane]
polling_sec = 300
director_server = "https://dgw.torizon.io/director"
repo_server = "https://dgw.torizon.io/repo"
key_source = "file"
key_type = "RSA2048"
force_install_completion = true
secondary_config_file = "/usr/lib/sota/secondaries.json"
secondary_preinstall_wait_sec = 600
enable_online_updates = false
enable_offline_updates = true
offline_updates_source = "/tmp/update"

[pacman]
type = "ostree"
os = ""
sysroot = ""
ostree_server = "https://dgw.torizon.io/treehub"
images_path = "/var/sota/images"
packages_file = "/usr/package.manifest"
fake_need_reboot = false
booted = "booted"

[storage]
type = "sqlite"
path = "/var/sota"
sqldb_path = "sql.db"
uptane_metadata_path = "metadata"
uptane_private_key_path = "ecukey.der"
uptane_public_key_path = "ecukey.pub"
tls_cacert_path = "root.crt"
tls_pkey_path = "pkey.pem"
tls_clientcert_path = "client.pem"

[import]
base_path = "/var/sota/import"
uptane_private_key_path = ""
uptane_public_key_path = ""
tls_cacert_path = "/usr/lib/sota/root.crt"
tls_pkey_path = "pkey.pem"
tls_clientcert_path = "client.pem"

[telemetry]
report_network = true
report_config = true

[bootloader]
rollback_mode = "uboot_masked"
reboot_sentinel_dir = "/var/run/aktualizr-session"
reboot_sentinel_name = "need_reboot"
reboot_command = "/usr/bin/touch /run/need-reboot"

Current directory: /var/rootdirs/home/root
Use existing SQL storage: "/var/sota/sql.db"
Couldn`t import data: empty path received
Root for image already present, not importing
Root for director already present, not importing
Initializing docker-compose Secondaries...
Use existing SQL storage: "/var/sota/storage/docker-compose/sql.db"
targets metadata not found in database
No valid metadata found in storage.
Adding Secondary with ECU serial: ba6bfd426bc29eed36fc66209e673bc6733828ebb4a9082bf540c88fae2c45a4 with hardware ID: docker-compose
Initializing torizon-generic Secondaries...
Use existing SQL storage: "/var/sota/storage/bootloader/sql.db"
Root metadata not found in database
No valid metadata found in storage.
Adding Secondary with ECU serial: d1972929a5d04fcf666ad3a2acd13d38e44ea1f89afe10152dd5b487f5cf24c3 with hardware ID: verdin-imx8mp-bootloader
Stashing ECU serials for hwid
No pending updates, continuing with initialization
Stashing ECU serials for hwid
All ECUs are already registered with the server.
Primary ECU serial: f7575a3b4e456f3ef820a90ff0a274ebe9fb56d42bcc09c04c8e28d87f6fd85f with hardware ID: verdin-imx8mp
Device ID: 1f840ace-8d5b-4c6a-a0e5-40f0e49c8f63
Device Gateway URL: https://dgw.torizon.io
Certificate subject: CN=1f840ace-8d5b-4c6a-a0e5-40f0e49c8f63
Certificate issuer: CN=ota-devices-CA
Certificate valid from: May  2 14:54:54 2024 GMT until: May  2 14:54:54 2124 GMT
... provisioned OK
Offline Updates are enabled
Not reporting default hardware information because it has already been reported
Not reporting installed packages because they have not changed
Not reporting network information because it has not changed
Reporting libaktualizr configuration
* STATE: INIT => CONNECT handle 0xffff8803d070; line 1834 (connection #-5000)
* Added connection 0. The cache now contains 1 members
* STATE: CONNECT => RESOLVING handle 0xffff8803d070; line 1880 (connection #0)
* family0 == v4, family1 == v6
*   Trying 3.72.32.174:443...
* STATE: RESOLVING => CONNECTING handle 0xffff8803d070; line 1964 (connection #0)
* Connected to dgw.torizon.io (3.72.32.174) port 443 (#0)
* STATE: CONNECTING => PROTOCONNECT handle 0xffff8803d070; line 2027 (connection #0)
* ALPN, offering http/1.1
*  CAfile: /tmp/aktualizr-a5f3-212f-4cbe-ea19/94ae-8669-tls-ca
*  CApath: /etc/ssl/certs
* Didn't find Session ID in cache for host HTTPS://dgw.torizon.io:443
* STATE: PROTOCONNECT => PROTOCONNECTING handle 0xffff8803d070; line 2047 (connection #0)
* Didn't find Session ID in cache for host HTTPS://dgw.torizon.io:443
* Added Session ID to cache for HTTPS://dgw.torizon.io:443 [server]
* SSL connection using TLSv1.2 / ECDHE-ECDSA-AES256-GCM-SHA384
* ALPN, server accepted to use http/1.1
* Server certificate:
*  subject: CN=ota-gateway
*  start date: Jun  9 15:36:25 2022 GMT
*  expire date: Jun  6 15:36:25 2032 GMT
*  subjectAltName: host "dgw.torizon.io" matched cert's "dgw.torizon.io"
*  issuer: CN=ota-server-CA
*  SSL certificate verify ok.
* STATE: PROTOCONNECTING => DO handle 0xffff8803d070; line 2066 (connection #0)
> POST /system_info/config HTTP/1.1
Host: dgw.torizon.io
User-Agent: Aktualizr/tdx-d71f18a1
Accept: */*
Content-Type: application/toml
Content-Length: 1863

* STATE: DO => DID handle 0xffff8803d070; line 2146 (connection #0)
* STATE: DID => PERFORMING handle 0xffff8803d070; line 2265 (connection #0)
* Mark bundle as not supporting multiuse
* HTTP 1.1 or later with persistent connection
< HTTP/1.1 204 No Content
< Server: openresty/1.21.4.1
< Date: Thu, 02 May 2024 16:48:05 GMT
< Connection: keep-alive
< x-ats-version: director-v2/d8c42b8c606c2156ff5d8926beae3ca143ea56a0
< X-B3-Sampled: 1
< X-B3-TraceId: 90ac7fc173616b5c
< X-B3-SpanId: 90ac7fc173616b5c
< 
* STATE: PERFORMING => DONE handle 0xffff8803d070; line 2464 (connection #0)
* multi_done: status: 0 prem: 0 done: 0
* Connection #0 to host dgw.torizon.io left intact
* Expire cleared (transfer 0xffff8803d070)
response http code: 204
response: 
Event: SendDeviceDataComplete
Not reporting network information because it has not changed
Failed to get current installed version: no more rows available
Current versions in storage and reported by OSTree do not match
Read compose-file: 1105 chars
DockerComposeSecondary::getFirmwareInfo: hash=71d6393dbc56dea5058d0b779d06bfeb569eef9ecb18a551a6601f6fe86d2efb
Action-handler "/usr/bin/bl_actions.sh" (action=get-firmware-info) started
Action-handler "/usr/bin/bl_actions.sh" (action=get-firmware-info) finished with exit code 64
Device installation result not found in database
No installation result to report in manifest
put request body:{
	"signatures" : 
	[
		{
			"keyid" : "f7575a3b4e456f3ef820a90ff0a274ebe9fb56d42bcc09c04c8e28d87f6fd85f",
			"method" : "rsassa-pss",
			"sig" : "m+Og/6X3c+yuBdxpIl15SNEmYm9LcmcgAHMmQYtBB6ttoZFGcMvh3dPKLgSy/q6buGzfESvGQ9vwy/BM70zH10OgjYnAbiMKcJI1uMN7C0owURYmJWVY93N0Hy98zOKc2bmcFqTAUz4hWOVau9hr+JP3b/qeyEOvQrhSEi0qhICY1kkeGYJBwaR0tcb/KYnWrp9EkZePyC2oNODqLFy0TzaWPlLjtv1FT06uV7wEmyD9SBCNJPRy35yOKK1D/M19POnBshqn38L4r+zDuN1YRA6r/XImV6rZ3QOVRj/x6PeUECJZuom3f3mY2+Gi3NfSY61brx1rsJIL4mc95dkGyA=="
		}
	],
	"signed" : 
	{
		"ecu_version_manifests" : 
		{
			"ba6bfd426bc29eed36fc66209e673bc6733828ebb4a9082bf540c88fae2c45a4" : 
			{
				"signatures" : 
				[
					{
						"keyid" : "ba6bfd426bc29eed36fc66209e673bc6733828ebb4a9082bf540c88fae2c45a4",
						"method" : "rsassa-pss",
						"sig" : "gUApZttYUEwP9khBYug2iSUo5g3C5UBWxetJGFKmSOV1Un7Fen/qIvVg5XScKYO9hXnm+XtBqh/BxejS3wlaybOmAKUAUSGmcjUlSzrgaN9UOj+LaVgottOrQhX7Wy6WE32eth3n54IGcpjRMywgWhIJ+Uq4gyq4MwnHE0UdfIgrSni7KIcdjzBPTdhknQT2a9YByg/WzOZPEplum4jMb2Z4QwFPfI1VJXMPHg+tjZVZ9fPzDUAKU8nqlVd6Yz57VFWQxxlQOSGlK3jAYTtnFfIMtBb6xYHN8KsYJ7yyM7Uc+WNZg3kWpZrVAJYRYG89NdlQi9CBJTh3FOXq5YP6Wg=="
					}
				],
				"signed" : 
				{
					"attacks_detected" : "",
					"ecu_serial" : "ba6bfd426bc29eed36fc66209e673bc6733828ebb4a9082bf540c88fae2c45a4",
					"installed_image" : 
					{
						"fileinfo" : 
						{
							"hashes" : 
							{
								"sha256" : "71d6393dbc56dea5058d0b779d06bfeb569eef9ecb18a551a6601f6fe86d2efb"
							},
							"length" : 874
						},
						"filepath" : "aktina-torizon-spirometry-docker-apps-3"
					},
					"previous_timeserver_time" : "1970-01-01T00:00:00Z",
					"timeserver_time" : "1970-01-01T00:00:00Z"
				}
			},
			"d1972929a5d04fcf666ad3a2acd13d38e44ea1f89afe10152dd5b487f5cf24c3" : 
			{
				"signatures" : 
				[
					{
						"keyid" : "d1972929a5d04fcf666ad3a2acd13d38e44ea1f89afe10152dd5b487f5cf24c3",
						"method" : "rsassa-pss",
						"sig" : "irt95QVmIOgf7Dd4gRcy4+N40RFl74hjl8DU3IljY5VNU5sI+TNBuUPY6EumI55yjezVNiGMJU+A//c6O1VAzExpXQaquFK3wb0m+qepVeCW2yFkRy/gzpmMXs4boyrpsrD8PJBPw/sOT3CuVKRg91tThPt48pOkrdmqnvZTIZmfSUvmEcXUGoyjnd9Vx9jYDqY+K3YEBi52L2PHSDCmy/UDDm0f+cqIRQB8Nx+IqS3jVlK86G+589SekjKgnxQdV5/yzclghqg2kf1egTkOThaZEalbzGi+ZzlRvRQwI9FnuShLWRSu3wzT3Mj7WO7riJCT8ctYofQnF0bq3Q8HiA=="
					}
				],
				"signed" : 
				{
					"attacks_detected" : "",
					"ecu_serial" : "d1972929a5d04fcf666ad3a2acd13d38e44ea1f89afe10152dd5b487f5cf24c3",
					"installed_image" : 
					{
						"fileinfo" : 
						{
							"hashes" : 
							{
								"sha256" : "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
							},
							"length" : 0
						},
						"filepath" : "noimage"
					},
					"previous_timeserver_time" : "1970-01-01T00:00:00Z",
					"timeserver_time" : "1970-01-01T00:00:00Z"
				}
			},
			"f7575a3b4e456f3ef820a90ff0a274ebe9fb56d42bcc09c04c8e28d87f6fd85f" : 
			{
				"signatures" : 
				[
					{
						"keyid" : "f7575a3b4e456f3ef820a90ff0a274ebe9fb56d42bcc09c04c8e28d87f6fd85f",
						"method" : "rsassa-pss",
						"sig" : "po2E7NI/1q/y5TkjwWjoTsqUnnJA2jjISzBQqAR3U6SBOcAM/9wM4XRLK0UJNCmKVq6N/XiYe0MlQ3JlsFkszaRtIGZAJWthdIBpHr1PGpeBqg6jpjXXvZwkkPQvjS6MzGO4eFqfqW9ejp91HuHrkMv++fTM51/SeHrv7ds7w9a6Nu5ix8xsxwqk9tx830WqTOUBOlA2dPBKnpHkgA67482SMOc/HG4co6G8UgCxhbg2UV0lk0F9+TK+WLSl7I0H1eMt5A1oT37G/KwMmH3aA5W1scZYo2s89Nj11ypLRgto/mfpLhgix7tewW4rneCPZ9TzAWyolFSzUZdjVQ4Hvw=="
					}
				],
				"signed" : 
				{
					"attacks_detected" : "",
					"ecu_serial" : "f7575a3b4e456f3ef820a90ff0a274ebe9fb56d42bcc09c04c8e28d87f6fd85f",
					"installed_image" : 
					{
						"fileinfo" : 
						{
							"hashes" : 
							{
								"sha256" : "79f52bb87ebc098e9e58a4f97e0a929cf8d241d6bf0662b45283df08237f1329"
							},
							"length" : 0
						},
						"filepath" : "unknown"
					},
					"previous_timeserver_time" : "1970-01-01T00:00:00Z",
					"report_counter" : "17",
					"timeserver_time" : "1970-01-01T00:00:00Z"
				}
			}
		},
		"primary_ecu_serial" : "f7575a3b4e456f3ef820a90ff0a274ebe9fb56d42bcc09c04c8e28d87f6fd85f"
	}
}
* STATE: INIT => CONNECT handle 0xffff8805ed70; line 1834 (connection #-5000)
* Added connection 0. The cache now contains 1 members
* STATE: CONNECT => RESOLVING handle 0xffff8805ed70; line 1880 (connection #0)
* family0 == v4, family1 == v6
*   Trying 3.120.54.180:443...
* STATE: RESOLVING => CONNECTING handle 0xffff8805ed70; line 1964 (connection #0)
* Connected to dgw.torizon.io (3.120.54.180) port 443 (#0)
* STATE: CONNECTING => PROTOCONNECT handle 0xffff8805ed70; line 2027 (connection #0)
* ALPN, offering http/1.1
*  CAfile: /tmp/aktualizr-a5f3-212f-4cbe-ea19/94ae-8669-tls-ca
*  CApath: /etc/ssl/certs
* Didn't find Session ID in cache for host HTTPS://dgw.torizon.io:443
* STATE: PROTOCONNECT => PROTOCONNECTING handle 0xffff8805ed70; line 2047 (connection #0)
* Didn't find Session ID in cache for host HTTPS://dgw.torizon.io:443
* Added Session ID to cache for HTTPS://dgw.torizon.io:443 [server]
* SSL connection using TLSv1.2 / ECDHE-ECDSA-AES256-GCM-SHA384
* ALPN, server accepted to use http/1.1
* Server certificate:
*  subject: CN=ota-gateway
*  start date: Jun  9 15:36:25 2022 GMT
*  expire date: Jun  6 15:36:25 2032 GMT
*  subjectAltName: host "dgw.torizon.io" matched cert's "dgw.torizon.io"
*  issuer: CN=ota-server-CA
*  SSL certificate verify ok.
* STATE: PROTOCONNECTING => DO handle 0xffff8805ed70; line 2066 (connection #0)
> PUT /director/manifest HTTP/1.1
Host: dgw.torizon.io
User-Agent: Aktualizr/tdx-d71f18a1
Accept: */*
Content-Type: application/json
Content-Length: 3325

* STATE: DO => DID handle 0xffff8805ed70; line 2146 (connection #0)
* STATE: DID => PERFORMING handle 0xffff8805ed70; line 2265 (connection #0)
* Mark bundle as not supporting multiuse
* HTTP 1.1 or later with persistent connection
< HTTP/1.1 200 OK
< Server: openresty/1.21.4.1
< Date: Thu, 02 May 2024 16:48:05 GMT
< Content-Type: text/plain; charset=UTF-8
< Content-Length: 2
< Connection: keep-alive
< x-ats-version: director-v2/d8c42b8c606c2156ff5d8926beae3ca143ea56a0
< X-B3-Sampled: 1
< X-B3-TraceId: d5444497ce860157
< X-B3-SpanId: d5444497ce860157
< 
* STATE: PERFORMING => DONE handle 0xffff8805ed70; line 2464 (connection #0)
* multi_done: status: 0 prem: 0 done: 0
* Connection #0 to host dgw.torizon.io left intact
* Expire cleared (transfer 0xffff8805ed70)
response http code: 200
response: OK
GET https://dgw.torizon.io/director/3.root.json
* STATE: INIT => CONNECT handle 0xffff88077920; line 1834 (connection #-5000)
* Added connection 0. The cache now contains 1 members
* STATE: CONNECT => RESOLVING handle 0xffff88077920; line 1880 (connection #0)
* family0 == v4, family1 == v6
*   Trying 3.72.32.174:443...
* STATE: RESOLVING => CONNECTING handle 0xffff88077920; line 1964 (connection #0)
* Connected to dgw.torizon.io (3.72.32.174) port 443 (#0)
* STATE: CONNECTING => PROTOCONNECT handle 0xffff88077920; line 2027 (connection #0)
* ALPN, offering http/1.1
*  CAfile: /tmp/aktualizr-a5f3-212f-4cbe-ea19/94ae-8669-tls-ca
*  CApath: /etc/ssl/certs
* Didn't find Session ID in cache for host HTTPS://dgw.torizon.io:443
* STATE: PROTOCONNECT => PROTOCONNECTING handle 0xffff88077920; line 2047 (connection #0)
* Didn't find Session ID in cache for host HTTPS://dgw.torizon.io:443
* Added Session ID to cache for HTTPS://dgw.torizon.io:443 [server]
* SSL connection using TLSv1.2 / ECDHE-ECDSA-AES256-GCM-SHA384
* ALPN, server accepted to use http/1.1
* Server certificate:
*  subject: CN=ota-gateway
*  start date: Jun  9 15:36:25 2022 GMT
*  expire date: Jun  6 15:36:25 2032 GMT
*  subjectAltName: host "dgw.torizon.io" matched cert's "dgw.torizon.io"
*  issuer: CN=ota-server-CA
*  SSL certificate verify ok.
* STATE: PROTOCONNECTING => DO handle 0xffff88077920; line 2066 (connection #0)
> GET /director/3.root.json HTTP/1.1
Host: dgw.torizon.io
User-Agent: Aktualizr/tdx-d71f18a1
Accept: */*

* STATE: DO => DID handle 0xffff88077920; line 2146 (connection #0)
* STATE: DID => PERFORMING handle 0xffff88077920; line 2265 (connection #0)
* Mark bundle as not supporting multiuse
* HTTP 1.1 or later with persistent connection
< HTTP/1.1 424 Failed Dependency
< Server: openresty/1.21.4.1
< Date: Thu, 02 May 2024 16:48:06 GMT
< Content-Type: application/json
< Content-Length: 154
< Connection: keep-alive
< x-ats-version: director-v2/d8c42b8c606c2156ff5d8926beae3ca143ea56a0
< X-B3-Sampled: 1
< X-B3-TraceId: 3dfb952ddfa0d738
< X-B3-SpanId: 3dfb952ddfa0d738
< 
* STATE: PERFORMING => DONE handle 0xffff88077920; line 2464 (connection #0)
* multi_done: status: 0 prem: 0 done: 0
* Connection #0 to host dgw.torizon.io left intact
* Expire cleared (transfer 0xffff88077920)
response http code: 424
response: {"code":"root_role_not_found","description":"root role was not found in upstream key store","cause":null,"errorId":"ee65f4ab-863d-44b1-8a58-7f57568d4936"}
GET https://dgw.torizon.io/director/targets.json
* STATE: INIT => CONNECT handle 0xffff88077920; line 1834 (connection #-5000)
* Added connection 0. The cache now contains 1 members
* STATE: CONNECT => RESOLVING handle 0xffff88077920; line 1880 (connection #0)
* family0 == v4, family1 == v6
*   Trying 3.120.54.180:443...
* STATE: RESOLVING => CONNECTING handle 0xffff88077920; line 1964 (connection #0)
* Connected to dgw.torizon.io (3.120.54.180) port 443 (#0)
* STATE: CONNECTING => PROTOCONNECT handle 0xffff88077920; line 2027 (connection #0)
* ALPN, offering http/1.1
*  CAfile: /tmp/aktualizr-a5f3-212f-4cbe-ea19/94ae-8669-tls-ca
*  CApath: /etc/ssl/certs
* Didn't find Session ID in cache for host HTTPS://dgw.torizon.io:443
* STATE: PROTOCONNECT => PROTOCONNECTING handle 0xffff88077920; line 2047 (connection #0)
* Didn't find Session ID in cache for host HTTPS://dgw.torizon.io:443
* Added Session ID to cache for HTTPS://dgw.torizon.io:443 [server]
* SSL connection using TLSv1.2 / ECDHE-ECDSA-AES256-GCM-SHA384
* ALPN, server accepted to use http/1.1
* Server certificate:
*  subject: CN=ota-gateway
*  start date: Jun  9 15:36:25 2022 GMT
*  expire date: Jun  6 15:36:25 2032 GMT
*  subjectAltName: host "dgw.torizon.io" matched cert's "dgw.torizon.io"
*  issuer: CN=ota-server-CA
*  SSL certificate verify ok.
* STATE: PROTOCONNECTING => DO handle 0xffff88077920; line 2066 (connection #0)
> GET /director/targets.json HTTP/1.1
Host: dgw.torizon.io
User-Agent: Aktualizr/tdx-d71f18a1
Accept: */*

* STATE: DO => DID handle 0xffff88077920; line 2146 (connection #0)
* STATE: DID => PERFORMING handle 0xffff88077920; line 2265 (connection #0)
* Mark bundle as not supporting multiuse
* HTTP 1.1 or later with persistent connection
< HTTP/1.1 200 OK
< Server: openresty/1.21.4.1
< Date: Thu, 02 May 2024 16:48:06 GMT
< Content-Type: application/json
< Content-Length: 296
< Connection: keep-alive
< x-ats-version: director-v2/d8c42b8c606c2156ff5d8926beae3ca143ea56a0
< X-B3-Sampled: 1
< X-B3-TraceId: 1a14a503fbd7333c
< X-B3-SpanId: 1a14a503fbd7333c
< 
* STATE: PERFORMING => DONE handle 0xffff88077920; line 2464 (connection #0)
* multi_done: status: 0 prem: 0 done: 0
* Connection #0 to host dgw.torizon.io left intact
* Expire cleared (transfer 0xffff88077920)
response http code: 200
response: {"signatures":[{"keyid":"415d9ede7666d29b67499557f0c66af8e2520785d0971bf85e999b04da581295","method":"ed25519","sig":"5pBU4SW+csC6LekhbEQzVUEyNSwbb/n7m52x0DPKEj7N4z9Ii30DdeHU49UUDqAcAp5tVTGGm+NpeyBjnZ4dAQ=="}],"signed":{"_type":"Targets","expires":"2024-06-02T14:55:05Z","targets":{},"version":1}}
No new updates found in Uptane metadata.
Event: UpdateCheckComplete, Result - No updates available
Flushing report queue

Thank you.

Greetings @ollamh,

The individual sub-commands for Aktualizr have not been validated to work with offline updates. I just double-checked and can confirm that aktualizr-torizon check does not do anything for offline updates at all.

Could you describe in detail what your goal here is. Perhaps we can have a discussion on ways to achieve this at the moment, and it would be useful feedback for our team.

Best Regards,
Jeremias

Nothing really specific from what is already present in the online updates version, just to be able to check the offline directory path and report if the update is available and be able to install that update. Those should be two separate commands.

Ideally I am trying to do it via making python bindings for the libaktualizr c shared .so file but for me it would work even by calling shell commands.

Let me know if it is something that can be achieved easily or we cannot rely on the selective commands for aktualizr-torizon for offline update in the nearest future.

If I understand correctly then, you want to detect when an update is available on your system and then manually choose when to install this update instead of relying on the automatic mechanisms of Aktualizr, correct? Or is your use-case different than this?

I assume your application then will control the update process?

At the moment, we don’t have any plans to update all the selective commands to be functional with offline updates. I can give the feedback now to our team and put this request into our planning. But, of course this would still take some time before you get any benefit.

In the meantime, your application/software could manually monitor the for the presence of the Lockbox files. Since they should be in a known predictable location. Basically acting as the check function. Then when you want to actually install the offline update you could invoke the once function since that actually works properly as you said (small note once is currently broken on the 6.6.1 release, known issue currently working on this).

Would something like this work for you?

Best Regards,
Jeremias

Thank you for the clarification @jeremias.tx !

It may work for us, if I could not only check for the update files presence, but also be able to see if it is a valid and correctly signed Lockbox before running once command.

but also be able to see if it is a valid and correctly signed Lockbox before running once command.

I see, yes this would be something Aktualizr needs to do then. I have another idea then. You can apply a lock file to prevent Aktualizr from going through with an update: Aktualizr - Modifying the Settings of Torizon Update Client | Toradex Developer Center

When the lock file is in place I believe Aktualizr will still check the update and validate the files. But it won’t go forward with installing the update. In theory then you can have the a lock file in place and use once, in this scenario it will effectively act like check. Since it will check the update but not install due to the presence of the lock file. Then, when you actually want to install you can remove the lock file and call once.

This could be a possible idea for the near-term. Let me know what you think.

Best Regards,
Jeremias

I’ve just tried to run the commands and it looks good. Is there a way to retrieve more information from aktualizr about the versions etc rather than getting only the message that there is an update available? Anyway it seems to be a nice workaround, thank you very much!

Here is the aktualizr-torizon once command run logs (the flock is running in another opened shell and it has locked the aktualizr-lock file):

aktualizr-torizon once --loglevel 0
Aktualizr version tdx-d71f18a1 starting
Reading config: "/usr/lib/sota/conf.d/20-sota-device-cred.toml"
Reading config: "/usr/lib/sota/conf.d/30-rollback.toml"
Reading config: "/usr/lib/sota/conf.d/40-hardware-id.toml"
Reading config: "/usr/lib/sota/conf.d/50-secondaries.toml"
Reading config: "/usr/lib/sota/conf.d/60-polling-interval.toml"
Reading config: "/usr/lib/sota/conf.d/70-reboot.toml"
Reading config: "/etc/sota/conf.d/99-offline-updates.toml"
Final configuration that will be used: 
[logger]
loglevel = 0

[p11]
module = ""
pass = ""
uptane_key_id = ""
tls_ca_id = ""
tls_pkey_id = ""
tls_clientcert_id = ""

[tls]
server = "https://dgw.torizon.io"
server_url_path = "/usr/lib/sota/gateway.url"
ca_source = "file"
pkey_source = "file"
cert_source = "file"

[provision]
server = "https://dgw.torizon.io"
p12_password = ""
expiry_days = "36000"
provision_path = ""
device_id = ""
primary_ecu_serial = ""
primary_ecu_hardware_id = "verdin-imx8mp"
ecu_registration_endpoint = "https://dgw.torizon.io/director/ecus"
mode = "DeviceCred"

[uptane]
polling_sec = 300
director_server = "https://dgw.torizon.io/director"
repo_server = "https://dgw.torizon.io/repo"
key_source = "file"
key_type = "RSA2048"
force_install_completion = true
secondary_config_file = "/usr/lib/sota/secondaries.json"
secondary_preinstall_wait_sec = 600
enable_online_updates = false
enable_offline_updates = true
offline_updates_source = "/tmp/update"

[pacman]
type = "ostree"
os = ""
sysroot = ""
ostree_server = "https://dgw.torizon.io/treehub"
images_path = "/var/sota/images"
packages_file = "/usr/package.manifest"
fake_need_reboot = false
booted = "booted"

[storage]
type = "sqlite"
path = "/var/sota"
sqldb_path = "sql.db"
uptane_metadata_path = "metadata"
uptane_private_key_path = "ecukey.der"
uptane_public_key_path = "ecukey.pub"
tls_cacert_path = "root.crt"
tls_pkey_path = "pkey.pem"
tls_clientcert_path = "client.pem"

[import]
base_path = "/var/sota/import"
uptane_private_key_path = ""
uptane_public_key_path = ""
tls_cacert_path = "/usr/lib/sota/root.crt"
tls_pkey_path = "pkey.pem"
tls_clientcert_path = "client.pem"

[telemetry]
report_network = true
report_config = true

[bootloader]
rollback_mode = "uboot_masked"
reboot_sentinel_dir = "/var/run/aktualizr-session"
reboot_sentinel_name = "need_reboot"
reboot_command = "/usr/bin/touch /run/need-reboot"

Current directory: /var/rootdirs/home/root
Use existing SQL storage: "/var/sota/sql.db"
Couldn`t import data: empty path received
Root for image already present, not importing
Root for director already present, not importing
Initializing docker-compose Secondaries...
Use existing SQL storage: "/var/sota/storage/docker-compose/sql.db"
targets metadata not found in database
No valid metadata found in storage.
Adding Secondary with ECU serial: ba6bfd426bc29eed36fc66209e673bc6733828ebb4a9082bf540c88fae2c45a4 with hardware ID: docker-compose
Initializing torizon-generic Secondaries...
Use existing SQL storage: "/var/sota/storage/bootloader/sql.db"
Root metadata not found in database
No valid metadata found in storage.
Adding Secondary with ECU serial: d1972929a5d04fcf666ad3a2acd13d38e44ea1f89afe10152dd5b487f5cf24c3 with hardware ID: verdin-imx8mp-bootloader
Stashing ECU serials for hwid
No pending updates, continuing with initialization
Stashing ECU serials for hwid
All ECUs are already registered with the server.
Primary ECU serial: f7575a3b4e456f3ef820a90ff0a274ebe9fb56d42bcc09c04c8e28d87f6fd85f with hardware ID: verdin-imx8mp
Device ID: 1f840ace-8d5b-4c6a-a0e5-40f0e49c8f63
Device Gateway URL: https://dgw.torizon.io
Certificate subject: CN=1f840ace-8d5b-4c6a-a0e5-40f0e49c8f63
Certificate issuer: CN=ota-devices-CA
Certificate valid from: May  2 14:54:54 2024 GMT until: May  2 14:54:54 2124 GMT
... provisioned OK
Offline Updates are enabled
CheckAndInstallOffline: call CheckUpdatesOffline
fetchMetaOffUpd() called with source_path: "/tmp/update"
Getting ECU serials for hwid: LEN=3
New updates found in Director metadata. Checking Image repo metadata...
1 new update found in both Director and Image repo metadata.
Event: UpdateCheckComplete, Result - Updates available
Update available. Acquiring the update lock...
Unable to acquire lock: "/run/lock/aktualizr-lock"
Flushing report queue

Is there a way to retrieve more information from aktualizr about the versions etc rather than getting only the message that there is an update available?

You want information on the update package that is available in the Lockbox?

In that case you’d want to parse the metadata in the Lockbox files itself. In your Lockbox there should be a file with something like metadata/director/<NAME OF YOUR LOCKBOX.json. If you look at this file it contains information on the packages that are available in this Lockbox. As an example you would see something like this:

  "signed": {
    "_type": "Offline-Updates",
    "expires": "2025-04-11T19:54:18Z",
    "targets": {
      "hello-world.lock.yml-v2": {
        "custom": {
          "hardwareIds": [
            "docker-compose"
          ]
        },
        "hashes": {
          "sha256": "c2d124d3daf7d496411a6d89bfd83e07e020d9851b811c904f72ebf84a4e0d14"
        },
        "length": 166
      }
    },
    "version": 15
  }
}

So from this I can see this Lockbox contains a single docker-compose package. It has an identifier of hello-world.lock.yml-v2. This means it contains the package hello-world.lock.yml from my package lists and is using version v2 of my package.

Would this the information you are looking for?

Best Regards,
Jeremias

I tried it and I believe it’ll do, at least for the nearest future. Thank you @jeremias.tx !

Perfect, glad to help come up with creative solutions. I’ll provide this as feedback to our product team to see if one day we can come up with something more integrated into our solution.

Best Regards,
Jeremias