> ## 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.

# Hardware-backed keys

> Bind a device's private key to a TPM, HSM, or Secure Enclave so it can never be copied off the device — the backends, the preference setting, and how the build and the setting must agree.

A device's private key is its identity. By default Omega stores that key in a file in `cert_dir`; anyone who can read that file can clone the device. A **hardware-backed key** is generated inside a secure element (TPM, HSM, or Secure Enclave) and never leaves it — the device can *sign* with the key but can't *export* it, so the key can't be copied to another machine even by someone with full disk access.

This page covers the per-OS backends, the `OMEGA_KEY_PREFERENCE` setting that decides whether hardware is required, and the one rule that keeps them consistent: **the build and the preference must agree.**

## Backends (compile-time, per OS)

A hardware backend is a [Cargo feature](/edge/install#opt-in-features) — you compile in the one that matches the device's OS:

| Feature                   | OS      | Hardware            |
| ------------------------- | ------- | ------------------- |
| `keystore-cng`            | Windows | TPM 2.0 via CNG     |
| `keystore-pkcs11`         | Linux   | TPM / PKCS#11 / HSM |
| `keystore-secure-enclave` | macOS   | Secure Enclave      |

A binary with **no** hardware backend compiled in can only produce software keys. So "can this device use a hardware key?" is answered at build time; "must it?" is answered by the setting below.

## The preference: `OMEGA_KEY_PREFERENCE`

Whether Omega requires, prefers, or skips hardware is set by the **environment variable** `OMEGA_KEY_PREFERENCE`:

| Value              | Behavior                                                                                                                                                                  |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `prefer_hardware`  | **Default.** Use hardware if it's available and provable; otherwise fall back to a software key. The fallback is *honest* — the key then reports its class as `software`. |
| `require_hardware` | Refuse to run without a provable hardware key. Fails **loudly at startup** if hardware isn't available or holds no key.                                                   |
| `software_only`    | Software key only. For CI, containers, and anyone who wants the previous behavior.                                                                                        |

Unrecognized values (and a typo) fall back to `prefer_hardware` with a warning — refusing to start would turn a typo in a deployment script into a bricked fleet, but the warning ensures a misspelled `require_hardware` doesn't silently become `prefer_hardware`, which is the one direction that must never happen quietly.

<Note>
  This is an **environment variable, not a profile field** — deliberately. The device key is provisioned during [enrollment](/edge/jitr), which runs *before* the profile's module sections are loaded. A setting that only existed in the profile couldn't reach the one decision it needs to influence. Set it in the service environment (the systemd unit, the launchd plist, the Windows service environment).
</Note>

## Build and preference must agree

The feature and the env var are two halves of one decision. Get them lined up:

| Build has a backend? | `OMEGA_KEY_PREFERENCE`                 | Result                                                                   |
| -------------------- | -------------------------------------- | ------------------------------------------------------------------------ |
| Yes                  | `prefer_hardware` / `require_hardware` | Hardware key. ✅                                                          |
| Yes                  | `software_only`                        | Software key (hardware ignored).                                         |
| **No**               | `prefer_hardware`                      | Software key, honestly reported. Fine for a device that can't do better. |
| **No**               | `require_hardware`                     | **Refuses to start** — the preference can't be satisfied.                |

The failing row is the important one: asking for `require_hardware` on a binary built without a backend for that OS is a contradiction, and Omega surfaces it at startup rather than limping along on a software key the policy was meant to forbid.

## Why loud-at-startup, not silent-fallback

`require_hardware` fails at boot, not at first authorization, on purpose. A device that quietly enrolls with a software key and is *later* denied by a hardware-only policy looks like a policy bug from every angle — the device is "online," its cert is valid, yet every command is refused. Failing at startup, with the reason in the [startup log](/edge/deploy#when-a-service-wont-start), puts the error where the operator can act on it.

The honest key-class reporting is the other side of this: a `prefer_hardware` device that fell back to software says so, so platform policy can decide what a software key is worth per resource. A fleet can roll out on its weakest platform and tighten later — nothing pretends to be hardware-backed when it isn't.

## Rotation and renewal

Hardware keys participate in [certificate renewal](/edge/jitr#renewal) like any other key — the secure element signs the CSR, the renewed certificate is written to `cert_dir`, and the private key never moves. A `require_hardware` device that somehow holds no hardware key won't silently create a software one to get unstuck; it refuses, because the honest failure is the safer one.

## Checklist for a hardware-key device

* [ ] Built with the backend for its OS (`keystore-cng` / `keystore-pkcs11` / `keystore-secure-enclave`).
* [ ] `OMEGA_KEY_PREFERENCE=require_hardware` in the service environment (or `prefer_hardware` if a software fallback is acceptable).
* [ ] The service account can reach the secure element (TPM device node / PKCS#11 module / Keychain).
* [ ] Verified at first boot that the key class the device reports is what you expect.

## See also

* [Build Omega](/edge/install#opt-in-features) — enabling a keystore backend.
* [Enrollment (JITR)](/edge/jitr) — where the device key is first provisioned.
* [Configure Omega](/edge/configure) — the profile and its security policy.
