> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ilyama.golain.io/llms.txt
> Use this file to discover all available pages before exploring further.

# OTA & self-update

> How Omega delivers updates — signed artifact deployments, and self-updating the agent itself over A/B slots with automatic revert on a bad boot.

OTA is always present in Omega (it's [core](/edge/install#always-compiled-the-core), no feature flag). It does two related jobs:

1. **Deploy artifacts** to the device — files, binaries, whatever a deployment plan specifies — with signature verification.
2. **Self-update the agent binary** over A/B slots, so Omega can replace itself and automatically roll back if the new version fails to come up.

The same subsystem also hosts [inventory](/edge/connect#inventory-sbom-hbom-pbom-core-hosted-by-ota) (SBOM/HBOM/PBOM) and the [node capability](/edge/connect#node-capabilities-core) framework.

## Deploying artifacts

The platform drives a deployment by publishing to the device's `ota/control/*` topics (full list in the [topic reference](/edge/connect#ota-core-always-present)):

| Command  | Topic                 | What it does                                     |
| -------- | --------------------- | ------------------------------------------------ |
| Plan     | `ota/control/plan`    | An ordered set of steps (download, place, run…). |
| URL push | `ota/control/url`     | Fetch a single artifact from a URL.              |
| Release  | `ota/control/release` | Promote / apply a staged update.                 |
| Cancel   | `ota/control/cancel`  | Abort an in-flight deployment.                   |

The device reports back on `ota/report/progress`, `ota/report/success`, and `ota/report/log`, and can pull pending work by publishing to `ota/request`.

### Signature verification

Deployments are **role-bound signed**. Every artifact must carry a valid signature for each required role before Omega will apply it:

* The **`platform`** role is always required — you can't turn it off.
* `required_roles` adds more roles that must also sign (e.g. a second-party approval). `loosen_role` is the escape hatch for a role you want to make optional.
* Signatures are ECDSA P-256; each trusted key is identified by a `key_id` and `fingerprint`.

An artifact whose signatures don't satisfy the required roles is rejected — a compromised URL or broker can't push code the platform didn't sign.

<Warning>
  Artifact URLs must be HTTPS. `allow_insecure_http: true` in the OTA config lifts that for local development only — never ship it, or an on-path attacker can swap the artifact before signature checks even matter.
</Warning>

## Self-update over A/B slots

Omega can update *itself*. To make that safe, the agent binary lives in one of two **slots** — `a` and `b` — under the runtime `data_dir`. The [`omega-launcher`](/edge/overview#two-binaries) supervisor (which is what the [OS service actually runs](/edge/deploy#what-ships-to-the-device)) execs whichever slot is currently active. This indirection is why the service must point at the launcher, not at a fixed agent binary.

Enable it in the OTA config:

```yaml theme={null}
ota:
  ab_enabled: true
  active_slot: a            # the slot in use now
  auto_commit: true         # commit automatically once healthy
  commit_timeout: 5m        # trial-boot window to prove health
  health_check_cmd: ""      # optional command that must exit 0 to confirm
```

### How an update commits — or reverts

The update is staged and then *proven* before it becomes permanent:

<Steps>
  <Step title="Stage">
    Omega writes the new agent binary into the **inactive** slot and records it as `pending`, with a deadline (`commit_timeout` from now), then restarts.
  </Step>

  <Step title="Trial boot">
    The launcher boots the pending slot as a *trial*. This is a probation period, not a commit — the old slot is still intact.
  </Step>

  <Step title="Confirm or revert">
    If the new agent comes up healthy within the deadline (and `health_check_cmd`, if set, exits 0), the update **commits** — the pending slot becomes active. If the deadline passes, the agent keeps crashing, or it fails **`MAX_BOOT_ATTEMPTS` (3)** boots, the launcher **reverts** to the previous slot and restarts into the version that was known good.
  </Step>
</Steps>

The guarantee: **a self-update that doesn't come up healthy cannot brick the device.** The launcher always has a known-good slot to fall back to, and the fallback is automatic — no operator intervention, no remote hands.

With `auto_commit: false`, a healthy trial boot waits for an explicit commit from the platform instead of committing on its own — for fleets that want a human in the loop before a new agent version becomes permanent.

## OTA config reference

The `ota:` section of the [profile](/edge/configure):

| Field                 | Type     | Default  | Meaning                                                        |
| --------------------- | -------- | -------- | -------------------------------------------------------------- |
| `channel`             | string   | `stable` | Update channel this device tracks.                             |
| `version`             | string   | `0.1.0`  | The agent's current version.                                   |
| `ab_enabled`          | bool     | `false`  | Enable A/B self-update.                                        |
| `active_slot`         | string   | `a`      | Which slot is active at first boot.                            |
| `auto_commit`         | bool     | `false`  | Commit automatically after a healthy trial boot.               |
| `commit_timeout`      | duration | —        | Trial-boot window to confirm health (e.g. `5m`).               |
| `health_check_cmd`    | string   | —        | Command that must exit 0 for a trial boot to count as healthy. |
| `allow_insecure_http` | bool     | `false`  | Allow non-HTTPS artifact URLs. **Dev only.**                   |

## See also

* [Connect → topic reference](/edge/connect#ota-core-always-present) — every OTA control/report topic.
* [Deploy Omega](/edge/deploy) — why the service execs `omega-launcher` (the slot indirection).
* [Configure Omega](/edge/configure) — the full profile the `ota:` section lives in.
