Skip to content

USER GUIDE

User guide: CLI and configuration reference

A single-page reference for the otactl operator CLI (zyvor-ota is the same binary), the zyvor-otad daemon, and every agent.json field. For a narrated first run, see the Tutorial. For what each step in the lifecycle actually does, see Architecture; for incident procedures, see Operations.

otactl CLI

Global flags: -socket PATH (default /run/zyvor-ota/agent.sock) and the demo-only -simulation-url http://127.0.0.1:PORT (used by make demo, never in production — see cmd/zyvor-ota/main.go). zyvor-ota accepts the same commands.

Command Effect
version Prints the build version. No socket needed.
keygen DIR Generates an Ed25519 keypair. DIR must not already exist; writes DIR/release.key (0600) and DIR/release.pub (0644).
sign RELEASE KEY KEY_ID OUT Signs the release JSON at RELEASE with the private key at KEY, tagged KEY_ID, and writes the signed envelope to OUT.
campaign-export ASSIGNMENT ARTIFACT_DIR OUT_DIR Copies a signed assignment and its digest-named artifacts into OUT_DIR for removable media. Does not rewrite the signed URLs.
campaign-import CAMPAIGN_DIR MEDIA_DIR Copies that campaign into MEDIA_DIR and prints the assignment path to submit.
status Current version, backend, device ID, the active job (or null), and event/sequence counters. status json prints the raw agent JSON.
job ID The full job record for ID — state, old/target slot, boot ID, timestamps.
submit ASSIGNMENT Submits a signed assignment. Returns the created (or already-accepted) job. Rejects a replayed or downgraded release sequence.
events Unacknowledged state-transition events, oldest first. null when there are none.
ack SEQUENCE Acknowledges every event up to and including SEQUENCE, removing them locally. Rejects an ack past the latest event.
reboot Requests reboot into the newly installed slot. Only valid while a job is awaiting_reboot and inside its assignment window.
recover-abort Operator-only: marks an ambiguous install target bad and restores the original slot as next boot target. Only valid in needs_recovery, with the backend idle on the original slot.
gc Deletes cached/partial artifact files. Refused while a job is active.
backup DEST Writes a VACUUM INTO copy of the journal to an absolute path that must not already exist. Stop the agent before replacing ota.db with that copy.
archive Prints terminal jobs that have left the hot journal. Hot jobs are omitted.

zyvor-otad daemon flags

Flag Default Notes
-config PATH /etc/zyvor-ota/agent.json The Config described below.
-simulation-listen ADDR (unset) Demo-only loopback HTTP listener; requires backend: simulator and a 127.0.0.1 address. Never set this in production.

Job states

accepted → downloading → verified → installing → awaiting_reboot → checking_health → committing → committed, with rollback_pending → rolled_back when health fails or times out, and needs_recovery when a crash makes the outcome ambiguous (see Operations §NeedsRecovery). committed, rolled_back, and failed are terminal — gc only runs when no job is in a non-terminal state.

Configuration reference (agent.json)

Every field below is validated by Config.Validate() in internal/ota/model.go at daemon startup; an invalid config refuses to start rather than run with a relaxed setting.

Field Type Required Constraint
device_id string yes ^[a-zA-Z0-9][a-zA-Z0-9._-]{0,95}$; must match the assignment's device_id.
compatible string yes Same pattern; must match the release's compatible and the backend's actual board/image compatibility string.
state_dir string yes Absolute path. Single-writer journal directory — never tmpfs/NFS, never shared with another agent instance.
socket string yes Absolute path to the operator Unix socket.
backend string yes simulator or rauc.
allow_device_writes bool conditionally Must be true when backend: rauc.
trust_keys map[string]string yes, ≥1 entry key_id → base64 Ed25519 public key. A release's envelope key_id must match an entry here.
download_hosts []string yes, ≥1 entry host:port allowlist, no scheme — the artifact URL's host must match exactly.
max_artifact_bytes int64 yes 1 – 1<<40 (1 TiB).
reserve_bytes uint64 no Disk space kept free during download; ≤ 1<<50.
health_timeout_seconds int yes 1 – 3600.
health_stable_seconds int yes 0 – (health_timeout_seconds − 1): how long health must hold continuously before commit.
checks []Check conditionally {"kind": "...", "target": "..."}; required (≥1) when backend: rauc. See below.
bandwidth_bytes_per_sec int64 no 0 means no cap. A positive value limits the artifact read.
download_jitter_seconds int no 0–3600. 0 starts the download on the next engine step. Otherwise the device waits FNV(device_id) % (n+1) seconds.
download_window_start / download_window_end string no Both empty, or both HH:MM local time. Outside the window the job stays deferred.
local_media_dir string no Absolute directory of {sha256}.raucb files for offline install.
relay_url string no Site relay base URL. Empty skips the relay. HTTPS, or HTTP on 127.0.0.1 / ::1, with an empty path. The agent asks the relay for the signed digest before the artifact URL. A miss falls through. A digest mismatch does not.
relay_token_file string conditionally Bearer token for relay_url. Required when the URL is set.
trust_dir string no Absolute directory of root.json, timestamp.json, snapshot.json, and targets.json. Empty keeps the single pinned release key.
root_sha256 string conditionally SHA-256 of the initial root payload. Required with trust_dir. Later root versions must meet the previous root's signature threshold.
require_sbom_signature bool no Off unless set. A release must then carry sbom_key_id and sbom_signature over the SBOM bytes.
allow_adaptive bool no Off unless set. Refuses a release that sets adaptive when this is false.
capabilities []string no Capability names a schema 2 target may list in requires.
fleet_url string no Plain HTTPS base URL — no userinfo, query string, or fragment. HTTP is rejected at config validation. Use fleet_ca (or the system trust store) for lab self-signed Fleet certs.
fleet_token_file string conditionally Required when fleet_url is set, unless both fleet_cert and fleet_key are given (mTLS). Mode 0600/640; readable by the agent user.
fleet_ca string no Custom CA bundle path for the Fleet connection.
fleet_cert / fleet_key string conditionally mTLS client cert/key pair; both required together.
otlp_endpoint string no OpenTelemetry collector base URL. Empty disables traces. HTTPS, or HTTP on 127.0.0.1 / ::1. Path must be empty or /v1/traces. Prometheus on /metrics stays available either way.
otlp_token_file string no Bearer token for the collector. Requires otlp_endpoint.

For a multi-product lab (Fleet TLS + simulator OTA), see LAB.md. For bring-up without Fleet, run zyvor-fleet-ref (see QUALIFICATION.md).

Health check kinds

kind target must be
file An absolute path; the check passes while the file exists.
systemd A unit name matching the same identifier pattern as device_id.
http A URL parsed as http://127.0.0.1 or http://[::1], no userinfo — loopback only.

Examples

examples/agent.simulator.json and examples/agent.rauc.json are complete, runnable templates for each backend; examples/release.json and examples/assignment.json are minimal release/assignment shapes. All contain placeholder values — see the Tutorial for a config built and run end to end.