From 2a39b849c1c6ca5d5a8674f606c496e780957e16 Mon Sep 17 00:00:00 2001 From: Emir Beganovic Date: Fri, 9 Oct 2026 21:18:54 +0200 Subject: [PATCH] README: a part for users, a part for maintainers The top is for someone who wants Omarchy: where to get the ISO and how to check it, how to try a VM image, and autoinstall. Everything about building, testing and publishing follows under "For maintainers", with what is published where, how the nightly works, the fields of latest.json and the files of a VM image. A read against the code corrected several statements, some of them older: the installer runs omarchy-provision-user; a defer-provisioning marker can stand in for the credentials file; an authorized_keys file fails the install when it holds no keys, not when its keys are bad; the stock firewall also allows Docker's DNS; built_at is when latest.json was written; vm-boot's needs are listed in full. The publish job now fetches the two checksum files back as well, so "every file of the version is compared" is exact. --- .github/workflows/nightly-build.yml | 5 + README.md | 172 +++++++++++++++------------- 2 files changed, 100 insertions(+), 77 deletions(-) diff --git a/.github/workflows/nightly-build.yml b/.github/workflows/nightly-build.yml index 5fc41c7..57e48aa 100644 --- a/.github/workflows/nightly-build.yml +++ b/.github/workflows/nightly-build.yml @@ -261,13 +261,18 @@ jobs: [ "$got" = "$2" ] || { echo "::error::$1 serves $got, expected $2"; exit 1; } echo "ok $1" } + # Every file of the version, the checksum files too. for iso in "$dir"/*.iso; do [[ -f $iso ]] || continue check "$SITE/$RELEASE/${iso##*/}" "$(cut -d" " -f1 "$iso.sha256")" + check "$SITE/$RELEASE/${iso##*/}.sha256" "$(sha256sum "$iso.sha256" | cut -d" " -f1)" done while read -r sum name; do check "$SITE/$RELEASE/vm/$name" "$sum" done <"$dir/vm/SHA256SUMS" + check "$SITE/$RELEASE/vm/SHA256SUMS" "$(sha256sum "$dir/vm/SHA256SUMS" | cut -d" " -f1)" + published=$(find "$dir" -type f | wc -l) + echo "$published files published and checked" - name: Point latest.json at it run: | diff --git a/README.md b/README.md index d85a440..9838d41 100644 --- a/README.md +++ b/README.md @@ -2,77 +2,28 @@ [![Nightly ISO Build](https://github.com/omacom/omarchy-iso/actions/workflows/nightly-build.yml/badge.svg?branch=quattro&event=schedule)](https://github.com/omacom/omarchy-iso/actions/workflows/nightly-build.yml?query=branch%3Aquattro+event%3Aschedule) -The Omarchy ISO is the only supported way to install Omarchy. It ships the Omarchy Configurator, installs Arch Linux, installs the Omarchy packages from the bundled mirror, runs target system setup in the chroot, creates the user, and runs `omarchy-setup-user` for that user. +The Omarchy ISO is the only supported way to install Omarchy. It ships the Omarchy Configurator, installs Arch Linux, installs the Omarchy packages from the bundled mirror, runs target system setup in the chroot, creates the user, and runs `omarchy-provision-user` for that user. -## What is published, and where +## Get Omarchy -| What | Where | When | -| --- | --- | --- | -| Release ISO | `https://iso.omarchy.org/omarchy-.iso`, linked from [omarchy.org](https://omarchy.org) | Each release | -| Edge ISO | `https://nightly.omarchy.org/edge//omarchy-edge-.iso` | Every night | -| VM image of edge | `https://nightly.omarchy.org/edge//vm/` | Every night | -| VM image of the last release | `https://nightly.omarchy.org/stable///vm/` | Once per release | +Download the ISO from [omarchy.org](https://omarchy.org), write it to a USB stick and boot from it. -Nobody has to build those paths: each channel has a `latest.json` that names the newest version and gives the full URLs. - -``` -https://nightly.omarchy.org/edge/latest.json -https://nightly.omarchy.org/stable/latest.json -``` - -A published file is never replaced. Only `latest.json` changes, and it is written last, after everything it points at has been fetched back and compared. - -### Release ISO - -Every release ISO has a `.sha256` and a `.sig` beside it at the same URL. Download the ISO and its `.sha256` into the same directory and check the ISO before writing it to a USB stick: +Check the download first. Every release ISO has a `.sha256` beside it at the same URL; put both in one directory: ```bash sha256sum -c omarchy-4.0.4.iso.sha256 ``` -Corruption anywhere in the ISO is worth catching before the write, and corruption in the bundled package mirror is worth catching most: the mirror lives inside the ISO and the installer reads it straight off the medium, so those bytes surface minutes into the install as a pacman "invalid or corrupted package" error rather than as anything that names the download. Corruption elsewhere is louder and earlier — it stops the medium booting or mounting. The `.sig` is for anyone who wants to verify the ISO against the Omarchy signing key. +A damaged download otherwise shows up minutes into the install, as a pacman "invalid or corrupted package" error. There is a `.sig` beside the ISO too, for verifying it against the Omarchy signing key. -### Nightly builds +## Try it in a VM -The nightly (`.github/workflows/nightly-build.yml`) builds the edge ISO from `quattro` and the edge packages, installs it unattended, boots what it installed, and turns that installed disk into a VM image. It does the same once for each release, from the release ISO. Nothing is published unless the install and the image's checks passed. +A VM image is Omarchy already installed: no installer to go through, and it boots in seconds. There is one of the last release and one of edge, which is rebuilt every night from what is about to become the next release and can be broken on any given day. -### latest.json - -| Field | What it is | -| --- | --- | -| `version` | Edge: the build's date and run number, `2026.10.09.431`. Stable: the release, `4.0.4`. | -| `channel` | `edge` or `stable`. | -| `built_at` | When the image was made, UTC, `2026-10-09T18:02:35Z`. | -| `url` | The ISO that was installed. Edge: on nightly.omarchy.org. Stable: the release ISO on iso.omarchy.org. | -| `sha256`, `size` | The ISO's SHA-256 and its size in bytes. | -| `iso_commit` | The omarchy-iso commit the nightly ran from. | -| `omarchy_version` | The version of the Omarchy package in the image. | -| `omarchy_commit` | Edge: the omarchy commit that package was built from. Stable: empty, the version names it. | -| `kernel` | The kernel release the image boots, as `uname -r` gives it. | -| `vm.url` | The folder the image's files are in, ending in `/`. | -| `vm.disk`, `vm.disk_sha256`, `vm.disk_size` | The disk image, `base.qcow2`: its URL, SHA-256 and size in bytes. | -| `vm.files` | Every file in `vm.url`. | -| `vm.user`, `vm.password` | The account for the desktop and `sudo`. | -| `vm.ssh` | How SSH gets in: keys only, never the password. | - -The version is also in the edge ISO's file name, for a consumer that reads it from there. - -### VM image - -A VM image is Omarchy already installed: it boots to the desktop in a few seconds, with no installer to go through. `vm.url` holds: - -| File | What it is | -| --- | --- | -| `base.qcow2` | The disk, a zstd-compressed qcow2. | -| `vmlinuz`, `initrd`, `cmdline` | The image's own kernel, initramfs and kernel arguments, for booting it directly without firmware or boot loader. | -| `OVMF_VARS.4m.fd` | The firmware variables with the installer's boot entry, for booting it through UEFI instead. | -| `vm-boot` | A script that boots the image with QEMU. | -| `SHA256SUMS` | Checksums of the files above. | - -Every boot is its own machine: a new machine ID and new SSH host keys, and nothing of the build left in the image. To get one and run it, on a machine with QEMU and KVM: +On a Linux machine with QEMU (`qemu-system-x86_64` and `qemu-img`), a `/dev/kvm` you can read and write, `ssh`, `curl` and `jq`: ```bash -latest=https://nightly.omarchy.org/edge/latest.json +latest=https://nightly.omarchy.org/stable/latest.json # or .../edge/latest.json vm=$(curl -fsSL "$latest" | jq -r .vm.url) for file in $(curl -fsSL "$latest" | jq -r '.vm.files[]'); do curl -fsSLO "$vm$file"; done sha256sum -c SHA256SUMS @@ -82,22 +33,14 @@ chmod +x vm-boot ./vm-boot . uname -r # boots, runs the command over SSH, powers off ``` -`vm-boot` runs the machine on a throwaway overlay, so the downloaded image is never written to, and makes an SSH key for each boot. Its header lists what it needs and the settings it takes. +The user is `omarchy` with password `omarchy`. SSH takes keys only, and `vm-boot` makes one for each boot. Every boot is a new machine, and the downloaded image is never written to: the machine runs on an overlay under `/var/tmp` (`VM_STATE_DIR` moves it) that is deleted on exit. The disk is a download of 4 to 5 GB. -## Creating the ISO - -Run `./bin/omarchy-iso-make`; output goes into `./release`. By default the ISO uses the Omarchy packages and tracks the `quattro` branch, from the stable mirror. Pass `--edge` to use `omarchy-dev` and `omarchy-settings-dev` from the edge mirror. - -For local development, build the ISO from sibling checkouts: +The edge ISO itself is published every night as well: ```bash -./bin/omarchy-iso-make --local-source ../omarchy-installer ../omarchy-pkgs +curl -fsSL https://nightly.omarchy.org/edge/latest.json | jq -r .url ``` -Despite the local folder name, the first argument is the Omarchy source checkout (runtime commands, configs, setup scripts, themes, shell, migrations). The installer itself lives in this ISO repo. - -Use `--dev` or `--rc` to build against those package channels. Both `--dev` and `--edge` select the dev packages from the edge mirror. - ## Autoinstall The shipped ISO installs itself with no keyboard when it finds its configuration on a second drive. Attach a drive labeled `cidata` alongside the ISO and the installer copies the config off it and skips the configurator; with no such drive, nothing changes and the wizard runs as usual. No rebuild, no extra boot entry. @@ -118,7 +61,7 @@ These are the configurator's own output files, so the way to get a starting set | `authorized_keys` | No | SSH public keys in sshd's own format, one per line | | `tailscale_authkey` | No | Tailscale auth key; the machine joins your tailnet on first boot | -Both required files must be present or the installer falls back to the configurator. Generate the password hash for `user_credentials.json` with `openssl passwd -6 "yourpassword"`. +Both required files must be present or the installer falls back to the configurator. (A `defer-provisioning` marker file can stand in for `user_credentials.json`: user creation is then deferred to first boot.) Generate the password hash for `user_credentials.json` with `openssl passwd -6 "yourpassword"`. Encryption itself is configured by the `disk_encryption` block inside `user_configuration.json` — which carries the passphrase in plaintext, so treat a drive built from an encrypted install accordingly. The flag file must match it: it drives the encrypted install's SDDM autologin and the final boot validation, not the encryption. @@ -128,7 +71,7 @@ Encryption itself is configured by the `disk_encryption` block inside `user_conf ssh-ed25519 AAAA... you@host ``` -When `authorized_keys` is present, autoinstall installs it as the user's `~/.ssh/authorized_keys`, enables `sshd`, and adds a `ufw allow ssh` rule — a stock Omarchy install ships openssh with the service disabled and its firewall opens neither port 22 nor anything else beyond LocalSend. Networking needs nothing extra; NetworkManager is already enabled with DHCP. Password SSH authentication is left at the distro default. An `authorized_keys` with no usable keys fails the install rather than producing a machine nobody can reach. +When `authorized_keys` is present, autoinstall installs it as the user's `~/.ssh/authorized_keys`, enables `sshd`, and adds a `ufw allow ssh` rule — a stock Omarchy install ships openssh with the service disabled and its firewall opens neither port 22 nor anything else beyond LocalSend and Docker's DNS. Networking needs nothing extra; NetworkManager is already enabled with DHCP. Password SSH authentication is left at the distro default. An `authorized_keys` with no keys in it fails the install rather than producing a machine nobody can reach. When `tailscale_authkey` is present (one key, blank lines and `#` comments ignored), the install adds the `tailscale` package from the ISO's bundled mirror — nothing is fetched from the network at install or boot — and stages the join for first boot: the key lands at `/etc/tailscale/authkey` (root-only), `tailscaled` is enabled, ufw allows traffic in on `tailscale0`, and a background unit runs `tailscale up` once the network is actually up, retrying until it succeeds without holding up the boot. After a successful join the key is deleted and the unit disables itself; until then both survive reboots, so a machine installed offline joins whenever it first gets connectivity. The node appears on the tailnet under the configured hostname. Use a reusable, pre-authorized (tagged) key so one drive image serves many machines — or an ephemeral key for disposable VMs. @@ -160,7 +103,82 @@ Boot order is disk first: the empty disk falls through to the ISO on the first b Encrypted autoinstalls are not fully unattended — the LUKS passphrase prompt still needs someone at the first boot. -## Testing the ISO +## For maintainers + +Everything from here on is about building, testing and publishing the ISO. + +### What is published, and where + +| What | Where | When | +| --- | --- | --- | +| Release ISO, with `.sha256` and `.sig` | `https://iso.omarchy.org/omarchy-.iso` | Each release | +| Edge ISO, with `.sha256` | `https://nightly.omarchy.org/edge//omarchy-edge-.iso` | Every night | +| VM image of edge | `https://nightly.omarchy.org/edge//vm/` | Every night | +| VM image of the last release | `https://nightly.omarchy.org/stable///vm/` | Once per release | +| `latest.json` | `https://nightly.omarchy.org/edge/latest.json` and `.../stable/latest.json` | With each of the three above | + +On nightly.omarchy.org a published file is never replaced. Only `latest.json` changes, and it is written last, after every file of the version has been fetched back through the CDN and compared. + +### The nightly + +`.github/workflows/nightly-build.yml` runs every night and for two channels: + +- `edge`: builds the ISO from the branch the run is on, which is `quattro` for the nightly, with the edge packages. +- `stable`: takes the last release. The latest release of omacom/omarchy names the version, iso.omarchy.org has the ISO, and it is used only if its signature is the release key's. A release already published is skipped on the nightly run; a run started by hand makes it again. + +Each ISO is installed unattended and the installed system booted, by the same install test a pull request gets. `test/vm-image/vm-seal` then turns the installed disk into a VM image and `test/vm-image/vm-verify` boots that image and checks it. Nothing is published unless all of it passed. + +Publishing is a job of its own, the only one with the bucket's key, and it runs on `quattro` only. To try a change, run the workflow by hand on a branch: that builds and checks everything and publishes nothing. + +Which channels a run makes and where they go is decided by `test/vm-image/nightly-plan`; what is laid out and what `latest.json` says by `test/vm-image/nightly-layout`. Both are covered by `test/unit/nightly-test.sh`. + +### latest.json + +| Field | What it is | +| --- | --- | +| `version` | Edge: the build's date and run number, `2026.10.09.431`. Stable: the release, `4.0.4`. | +| `channel` | `edge` or `stable`. | +| `built_at` | When this file was written, once the image had passed its checks. UTC, `2026-10-09T18:02:35Z`. | +| `url` | The ISO that was installed. Edge: on nightly.omarchy.org. Stable: the release ISO on iso.omarchy.org. | +| `sha256`, `size` | The ISO's SHA-256 and its size in bytes. | +| `iso_commit` | The omarchy-iso commit the nightly ran from. | +| `omarchy_version` | The version of the Omarchy package in the image. | +| `omarchy_commit` | Edge: the omarchy commit that package was built from. Stable: empty, the version names it. | +| `kernel` | The kernel release the image boots, as `uname -r` gives it. | +| `vm.url` | The folder the image's files are in, ending in `/`. | +| `vm.disk`, `vm.disk_sha256`, `vm.disk_size` | The disk image, `base.qcow2`: its URL, SHA-256 and size in bytes. | +| `vm.files` | Every file in `vm.url`. | +| `vm.user`, `vm.password` | The account for the desktop and `sudo`. | +| `vm.ssh` | How SSH gets in: keys only, never the password. | + +The version is also in the edge ISO's file name. + +### A VM image's files + +| File | What it is | +| --- | --- | +| `base.qcow2` | The disk, a zstd-compressed qcow2. | +| `vmlinuz`, `initrd`, `cmdline` | The image's own kernel, initramfs and kernel arguments, for booting it directly without firmware or boot loader. | +| `OVMF_VARS.4m.fd` | The firmware variables with the installer's boot entry, for booting it through UEFI instead. | +| `vm-boot` | A script that boots the image with QEMU. | +| `SHA256SUMS` | Checksums of the files above. | + + +### Creating the ISO + +Run `./bin/omarchy-iso-make`; output goes into `./release`. By default the ISO uses the Omarchy packages and tracks the `quattro` branch, from the stable mirror. Pass `--edge` to use `omarchy-dev` and `omarchy-settings-dev` from the edge mirror. + +For local development, build the ISO from sibling checkouts: + +```bash +./bin/omarchy-iso-make --local-source ../omarchy-installer ../omarchy-pkgs +``` + +Despite the local folder name, the first argument is the Omarchy source checkout (runtime commands, configs, setup scripts, themes, shell, migrations). The installer itself lives in this ISO repo. + +Use `--dev` or `--rc` to build against those package channels. Both `--dev` and `--edge` select the dev packages from the edge mirror. + +### Testing the ISO Run `./bin/omarchy-iso-boot [release/omarchy.iso]`. @@ -172,7 +190,7 @@ synthetic disk in `/tmp` with an existing ESP and data partition plus ample unallocated space, then offers to start an interactive installation on it. The fixture exercises Windows partition preservation but does not contain Windows. -## Acceptance testing the ISO +### Acceptance testing the ISO Run `./bin/omarchy-iso-test [release/omarchy.iso]` to install the ISO into a headless VM by driving the real interactive install flow — the harness reads each screen via QMP screendumps + OCR and answers with virtual keystrokes, so the configurator wizard, install dashboard, reboot prompt, and SDDM login are all exercised exactly as a user would. It then boots the installed system, sends real VM keyboard shortcuts for the primary shell and window-management actions, and runs the in-guest acceptance suite (`test/acceptance` in the omarchy repo). The suite checks session and service health, the complete core-package manifest, user defaults, representative applications, menus, panels, live weather, launchers, visual selectors, notifications, clipboard, and other interactive shell behavior. @@ -188,7 +206,7 @@ The harness syncs the acceptance suite from `$OMARCHY_PATH` when it is available Pass `--encrypt` to drive the encrypted install flow (including typing the LUKS passphrase at boot) instead of the unencrypted one. Pass `--no-preview` to collect the same visual artifacts without opening them in `imv` when the run finishes. -## Integration testing the ISO +### Integration testing the ISO Scenarios under `test/integration.d/` boot a real ISO install in QEMU and assert on what the running system actually does. The runner installs the ISO once — unattended, from a generated cidata drive — and saves the result as a reusable base image; every scenario then boots a throwaway overlay of that base with its own copy of the firmware vars, so neither disk nor NVRAM state leaks between runs. Shared machinery (VM lifecycle, QMP screendump + OCR console driving, virtual keystrokes, guest SSH, the cidata build) lives in `test/integration.d/base-test.sh`, so a new scenario is one file. @@ -202,14 +220,14 @@ The first scenario is `factory-reset`: it proves `omarchy-system-factory-reset` Artifacts — screenshots, the fixtured/staged/final `limine.conf`, the reset typescript, and the factory-reset log — land under `test-runs/-integration/runs/-/`, and `--no-preview` skips the `imv` review just like the acceptance harness. -## Signing the ISO +### Signing the ISO Run `./bin/omarchy-iso-sign [release/omarchy.iso]`. The signing key is retrieved from the shared Omarchy vault with the 1Password CLI. -## Uploading the ISO +### Uploading the ISO Run `./bin/omarchy-iso-upload [release/omarchy.iso]`. This requires rclone configuration (`rclone config`). The `.sig` and `.sha256` sidecars go up with the ISO when they exist beside it. -## Full release of the ISO +### Full release of the ISO Run `./bin/omarchy-iso-release VERSION` to create, test, sign, and upload the ISO in one flow. Add `--rc` to release an RC build instead.