Skip to content

Post-update boot verification blocks update completion and the reboot - #543

Merged
maralcbr merged 14 commits into
quattro-upstreamfrom
mac/35-update-verify
Sep 25, 2026
Merged

maralcbr merged 14 commits into
quattro-upstreamfrom
mac/35-update-verify

Conversation

@maralcbr

@maralcbr maralcbr commented Sep 25, 2026 •

Copy link
Copy Markdown
Collaborator

Ticket 35 of the Apple Silicon convergence plan: post-update boot verification blocks completion.

Based on quattro-upstream at 4e56f1dd2, which carries #527, #540, #541 and #545. The last merge takes quattro-upstream's owner provisioning and journal test unchanged, and keeps both sides of docs/lifecycle-dispatch.md. boot-rebuild stays optional until ticket 36, because the update path does not call it.

What changed

  • omarchy update calls two lifecycle dispatch operations through a new hidden helper, omarchy-update-boot.
    • update-preflight runs before the keyring and system packages change. A refusal stops the update. Apple ships no preflight, so there it is a no-op.
    • update-verify runs after the last package step: packages, migrations, the post-update hook, AUR, mise and orphans.
    • When verification fails, the update still checks its logs, refreshes the indicator and releases Stay Awake. It then prints "The update is not finished ... Do not reboot until omarchy update completes" and exits 1 without omarchy-update-restart, so no reboot is offered.
    • The helper resolves the operation as the user and uses sudo only when there is an entrypoint to run. On x86, generic aarch64 and Qualcomm both steps are no-ops: the same steps run, nothing extra is printed and nothing asks for root.
  • omarchy-mac-boot ships entrypoints/update-verify, installed as /usr/lib/omarchy/mac-boot/update-verify. It runs omarchy-apple-silicon-boot-check --boot-chain read-only, with the new kernel's reboot allowed to be pending.
    • It checks what the next boot reads: the kernel and initramfs in /boot, the unlock settings (crypttab, rd.luks.name=, sd-encrypt), the device-tree set (order-insensitive, from omarchy-mac-boot: deterministic m1n1 device-tree order, first boot via #528 #541), m1n1 stage 2 and U-Boot in m1n1/boot.bin, and Limine's loader, menu hash and UKI kernel.
    • --boot-chain holds only the kernel image, device trees and m1n1 against their package mtree, and any other pacman -Qkk report still fails it. With both kernels installed it checks the one the boot menu starts first: the first Limine kernel entry, where Omarchy's default_entry points, else GRUB's first entry. A default_entry or GRUB_DEFAULT pointed elsewhere is not followed.
    • It leaves out what the full check keeps but the next boot does not read: other package file drift, the rival-kernel rule, LUKS keyslots and provisioning leftovers, and an m1n1 image its owner took over with M1N1_UPDATE_DISABLED. With that setting, M1N1 and DTBS must still resolve to installed package files.
    • On failure it says not to reboot and how to rebuild (sudo mkinitcpio -P && sudo update-m1n1 && sudo omarchy-mac-boot-update, then omarchy update).
  • The boot check requires a Limine Mac's EFI/BOOT/BOOTAA64.EFI, limine.conf and UKI on ESP_PATH to be the ones on the system ESP the device tree names. U-Boot boots from that ESP.
  • The dispatcher now tells a missing package from an old one:
    • An installed omarchy-mac-boot that lacks a required entrypoint fails with, for example, update-verify on apple-silicon needs /usr/lib/omarchy/mac-boot/update-verify, which omarchy-mac-boot 20260921-10 does not provide; update omarchy-mac-boot (exit 1).
    • A missing package keeps the "not installed" message and exits 3.
  • docs/update-process.md and docs/lifecycle-dispatch.md describe the update caller, the exit codes and the Apple implementation.

The update path does not call boot-rebuild. Package hooks rebuild the boot files, and update-verify catches anything they missed.

Ship order and legacy Macs

  • Publish an omarchy-mac-boot built from this branch (with update-verify) before any runtime carrying this PR. A runtime that arrives first blocks every update on Macs whose omarchy-mac-boot predates it, with the "update omarchy-mac-boot" message. The M2 Max, on the mx stack with omarchy-mac-boot 20260921-10, shows exactly that message today. The recipe pins (Make the Monitor panel's display toggle work under the Lua config #637, Apple Silicon test candidate set pinned to #527, with its signing tool #538) need to move to a commit that includes this PR.
  • A Mac without omarchy-mac-boot at all (a legacy omarchy-mac install) gets a warning that its boot files were not verified, and the update finishes as before. This is deliberate:
    • Those Macs predate the package and boot a chain it does not manage, so the check cannot vouch for them.
    • Blocking them would fail every update, with no fix until ticket 45 migrates them.
    • Converged images always ship the package (install/omarchy-apple.packages), so they are always verified.
  • A Mac that has omarchy-mac-boot and a busybox encrypt root (cryptdevice=, no crypttab) stays blocked until tickets 43/45 migrate it, or until the check's busybox acceptance (mx fix: keep optional recorder failures nonfatal #248, ledger B14) is ported.

Testing

  • New test/shell.d/update-boot-verify-test.sh runs omarchy update with its other steps stubbed, through the real helper, dispatcher and detector on platform fixtures:
    • x86, generic aarch64 and Qualcomm: the steps are unchanged, there is no output and no sudo, even with failing Mac entrypoints on disk and bash locale warnings on stderr.
    • Apple: preflight runs before the keyring, and a refused preflight stops the update.
    • Apple with omarchy-mac-boot's real entrypoint and boot check on a fixture Limine Mac: a coherent chain passes, including a pending reboot. A wrong device tree, a stale m1n1 or a missing UKI fails the update with the reason and remediation, and no reboot is offered.
    • Apple without omarchy-mac-boot: the update warns and finishes.
    • Apple with a package that lacks update-verify: the update fails and names the version.
  • packages/omarchy-mac/boot/test/update-verify-test.sh covers the entrypoint.
    • It fails a wrong device tree, a stale m1n1, a missing or stale UKI, another Limine, a stale initramfs, unlock settings that cannot unlock, and a drifted kernel image, device tree or m1n1.
    • It passes a healthy Mac with a third keyslot, an owner-built m1n1, a second kernel after the booted one, or a drifted module. The full check refuses each of these.
    • With two kernels, it checks the one the menu starts first.
    • The fixture tree is unchanged after every run.
  • Eighteen seeded regressions were all caught.
  • Arch Linux ARM container, non-root, after the quattro-upstream merge: packages/omarchy-mac/boot/test/all passes (125 checks). In runtime ./test/all, every failing file also fails on quattro-upstream in the same container: the suite's container-dependency failures, plus platform-packages-test.sh, which calls omarchy-pkg-defaults without bin/ on PATH. CI's Shell suites, Shellcheck and aarch64 package resolution pass.
  • x86 (Arch amd64 container): the update, dispatcher and new tests pass. As root, both helper steps are silent no-ops even with a failing entrypoint installed.
  • M2 Max, read-only: update-verify passes on its healthy LUKS + Limine chain, both directly as root and through the helper and dispatcher. The full check passes too.
  • Not done:
    • VM acceptance of a normal update (tickets 27/28).
    • The encrypted-install VM on the x86 host omarchy-gpu: the shared x86 harness there has this PR (at 3b1d2e49, same ticket code) queued behind its ISO builds. It is not reported here yet.
  • Follow-up, predating this PR in both modes: U-Boot's u-boot-nodtb.bin is folded into the rebuilt image but never held against its package, so a corrupted copy would pass.

Second review:

  • Round one: an unconditional sudo on x86 and an entrypoint style issue, both fixed.
  • A later review raised three blockers. Verification reused the full check (fixed with --boot-chain). The dispatcher said "not installed" for an old package (fixed). The ship order was undocumented (documented above).
  • The re-check found:
    • The two-kernel choice followed the m1n1 variant rather than the menu (fixed).
    • Stderr noise could trigger sudo (fixed).
    • --boot-chain skipped the mtree check entirely (narrowed instead).
  • The confirming round confirmed all of these fixes. It found one fail-open: on a fresh image, database warnings let any failing pacman -Qkk pass. That is fixed, with a test that fails on the old code. It also found two low gaps, the U-Boot follow-up above and the default_entry limit, now documented.

…erface

Conflicts resolved as #527's quattro-upstream merge (apple package list,
optional availability test) and as ticket 32's merge of #540 (owner
provisioning and its re-key journal test).
U-Boot starts the loader on the ESP the device tree names. The boot check
read Limine's files from ESP_PATH only, so a Mac whose ESP_PATH named another
ESP passed with a loader U-Boot never boots.
The lifecycle dispatch entrypoint runs the boot check on the whole boot chain
after an update, with the new kernel's reboot still to come, and on failure
says not to reboot and how to rebuild the boot files.
omarchy update runs update-preflight before its package changes and
update-verify after them through the lifecycle dispatcher. A failed
verification leaves the update unfinished, exiting non-zero without offering
the reboot. Both are no-ops on platforms without a boot package, x86
included.
The full boot check also fails a Mac that boots fine: package file drift, a
second installed kernel, an extra LUKS keyslot or provisioning leftovers, and
an m1n1 image its owner took over. --boot-chain leaves those to the full
check, and update-verify uses it.
The dispatcher said the package was not installed when an older copy was.
It now names the installed version and asks for its update, and exits 3
only when the package is missing altogether.
Such a Mac predates the package and boots a chain it does not manage, so
verification cannot vouch for it and blocking would fail every update until
its migration installs the package. An omarchy-mac-boot too old to ship
update-verify still blocks.
With two kernels installed, --boot-chain picked the one whose m1n1 was
installed, which is not what boots: it now checks the menu's first kernel.
It also holds the kernel image, device trees and m1n1 against their package
mtree again, leaving out only the rest of the package's files.
A warning on stderr from a successful resolution counted as an entrypoint,
so an x86 update could still call sudo.
A fresh image's database warnings made any failing pacman -Qkk pass once its
own diagnostics were filtered out. Only drift in files the boot files are not
built from is left out now.
@maralcbr
maralcbr changed the base branch from mac/09-mac-boot-source to quattro-upstream September 25, 2026 14:37
Owner provisioning and its journal test take quattro-upstream's version,
which already carries #540 and ticket 32. The dispatch contract keeps both
sides; boot-rebuild stays optional until ticket 36, since the update path
does not call it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant