Skip to content

Build one initramfs, not two, in a Limine Mac's re-key - #17

Open
joshuaswarren wants to merge 6 commits into
omacom:mainfrom
joshuaswarren:fix/limine-rekey-one-uki-build
Open

joshuaswarren wants to merge 6 commits into
omacom:mainfrom
joshuaswarren:fix/limine-rekey-one-uki-build

Conversation

@joshuaswarren

Copy link
Copy Markdown
Contributor

On a Limine Mac without GRUB, the owner's re-key builds an initramfs image that nothing boots.

omarchy-provision-owner re-keys the disk and then rebuilds the initramfs with mkinitcpio -P before it calls omarchy-mac-boot-update. On a Limine Mac without GRUB, -P builds /boot's GRUB image, which nothing boots, and then the UKI. On a 14-inch M2 Max's first boot (2026-10-06), each of those builds took 8.6 to 10.0 s. That first boot ran five full initramfs builds in about 46 s. This change removes the GRUB-image one from the re-key.

Now a Limine Mac without GRUB builds only the UKI at that point, as omarchy-mac-encrypt already does. A Limine Mac that keeps GRUB still rebuilds its image.

The change also proves the firmware ordering in the image the Mac actually boots: the initramfs inside the UKI on a Limine Mac, not /boot's image.

test/mac-provision-test.sh has three new cases. A Limine Mac without GRUB tools runs omarchy-mac-boot-update and no mkinitcpio -P. A Limine Mac that keeps GRUB still runs mkinitcpio -P first. On a Limine Mac, the firmware ordering is checked in the UKI's initramfs.

Checked at 4b463ef on main f47d3db (no newer upstream commits at the time), on a Debian host with the runtime CI pins (omacom/omarchy bf659459): .github/scripts/check-scope and check-architecture-gates exit 0. mac-provision-test.sh passes, and so do the other 21 test files that pass on main. Three omarchy-mac-boot test files also fail on main here, so they are not caused by this change: apple-silicon-boot-check-test.sh (exit 127, a missing tool), mac-external-displays-test.sh and update-m1n1-locale-test.sh (exit 1). shellcheck -S warning finds nothing in the files this adds; the two warnings in changed test files are already on main. CI runs the full suites in the Arch container once this is open.

Not measured yet: the first boot with this change. The saving is the one build's time (8.6 to 10.0 s), taken from the first boot above. There is a second, separate saving on the runtime side (a refresh that rebuilds the UKI again, 9 s on the same Mac), which is a separate PR to omacom/omarchy.

@maralcbr maralcbr left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch on the wasted build. The staged key isn't embedded in the initramfs (rd.luks.key=…:UUID=<Boot>), so skipping /boot's image leaves no key material behind. Three changes before merge, all about proving the image that actually boots:

  1. Prove firmware ordering in every image a boot entry uses. boot_image_orders_firmware now checks only the UKI on every Limine Mac, even when apple_rekey_boot has just rebuilt /boot's image because GRUB is kept, and GRUB's entries boot that image. An M2+ booting a GRUB entry could then hit the disk prompt before the keyboard firmware, and nothing checks it before the staged key goes (provision_commit). Rule: on a Limine Mac, always check the UKI, and also /boot's image whenever retained GRUB entries use it. Test a good UKI with a bad GRUB image.
  2. One GRUB decision. grub_tools_present (-x $MAC_BOOT_ROOT/usr/bin/grub-probe and grub-mkconfig) re-implements the decision omarchy-mac-boot-update makes (omarchy-cmd-present grub-probe && … grub-mkconfig, plus OMARCHY_MAC_BOOT_UPDATE_GRUB). Two copies of one decision is exactly what cost #13 five rounds. Share one predicate. One caveat for (1): OMARCHY_MAC_BOOT_UPDATE_GRUB suppresses regeneration, but that doesn't prove existing GRUB entries can't boot, so the firmware check should follow the boot images that are reachable, not that flag.
  3. provision_prepare uses the same rule. It still refuses on initramfs_orders_firmware, /boot's image only, so a UKI-only Mac can be refused because of a stale image nothing boots. Prepare and commit should apply the same "images that boot" rule.

Please also post the first-boot timing once it's run with this change.

@joshuaswarren

joshuaswarren commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor Author

All three are in b5ad429:

  1. Prove firmware ordering in every image a boot entry uses - fixed. On a Limine Mac boot_image_orders_firmware proves the UKI, and also /boot's image whenever GRUB is kept, because GRUB's retained entries boot it. Test row: a Limine Mac that keeps GRUB, a good UKI and a /boot image left without the vendor firmware fails commit (/boot/initramfs-linux-aurora.img does not load the vendor firmware), keeps the staged key for the retry, and commits once the rebuild is good. That row fails on 4b463ef (the commit went through) and passes here.

  2. One GRUB decision - fixed. grub_tools_present now lives once, in omarchy-mac-boot/lib/grub-keep.sh: omarchy-cmd-present grub-probe && omarchy-cmd-present grub-mkconfig. omarchy-mac-boot-update and lib/provision.sh both source it; provision.sh no longer carries its own -x copy. And as you said, OMARCHY_MAC_BOOT_UPDATE_GRUB is not part of the firmware check: it suppresses a rebuild, it does not stop GRUB from booting the image already there, so the check follows the images that are reachable.

  3. provision_prepare uses the same rule - fixed. It now runs boot_image_orders_firmware, the same check commit runs. A UKI-only Mac with a stale /boot image nothing boots is no longer refused, and a Limine Mac that keeps GRUB is held to both images. Test row: one state - good UKI, stale /boot image - passes prepare without the GRUB tools and is refused with them.

The one-build goal holds: a Limine Mac without GRUB still runs omarchy-mac-boot-update alone and builds only the UKI. A Limine Mac that keeps GRUB still builds both images, one build each (mkinitcpio -P for /boot's image, limine-update for the UKI), because both boot. Nothing here adds a build.

Suites: omarchy-mac-boot/test/all 225 ok on b5ad429 (223 ok on 4b463ef, same environment); the two new rows are the difference. First-boot timing needs a run on the test Mac with this change; it is not run yet, and it will be posted here after that run.

The owner's re-key ran mkinitcpio -P before omarchy-mac-boot-update. On a
Limine Mac without GRUB that builds /boot's GRUB image, which nothing boots,
and then the UKI. On a 14-inch M2 Max (2026-10-06 first boot) each build
took 8.6 to 10.0 s. Build only the UKI there, as omarchy-mac-encrypt already
does; a Limine Mac that keeps GRUB still rebuilds its image.

The commit then proves the firmware ordering in the image the Mac boots:
the initramfs inside the UKI on a Limine Mac, not /boot's image.
The re-key proved the ordering in one image: the UKI on a Limine Mac,
/boot's image elsewhere. A Limine Mac that keeps GRUB boots both. GRUB's
retained entries boot /boot's image, so a build that left the vendor
firmware out of it asked for the disk password before the keyboard
firmware worked, and nothing checked it before provision-commit removed
the staged key. provision-prepare refused a UKI-only Mac over a stale
image nothing boots, and never read its UKI.

Prepare and commit now prove the same images: the UKI on a Limine Mac,
plus /boot's image wherever GRUB is kept. One decision names that GRUB
is kept: grub_tools_present in lib/grub-keep.sh, which
omarchy-mac-boot-update and lib/provision.sh both source.
OMARCHY_MAC_BOOT_UPDATE_GRUB stays out of the check: it suppresses a
rebuild, it does not stop GRUB from booting the image already there.
@joshuaswarren
joshuaswarren force-pushed the fix/limine-rekey-one-uki-build branch from b5ad429 to 5f82570 Compare October 8, 2026 23:38
@maralcbr

maralcbr commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator

Thanks @joshuaswarren. My three earlier points are addressed. Two small fixes needed before merge:

  1. omarchy-mac-boot/lib/provision.sh:100: call /usr/bin/mkinitcpio -P explicitly. On root's PATH, mkinitcpio resolves to the limine-mkinitcpio-hook wrapper at /usr/local/bin/mkinitcpio, which runs the real build and then the UKI build, and exits with the UKI build's status. So the keep-GRUB branch builds extra images and hides a failed build of /boot's image. omarchy-mac-encrypt already calls /usr/bin/mkinitcpio for the same reason.
  2. omarchy-mac-boot/bin/omarchy-mac-boot-update:19: make sourcing the helper fail closed, e.g. source "$grub_keep" || exit 1. That script has no set -e, so a missing helper makes grub_tools_present return 127 and the update carries on as if GRUB were absent.

Optional: a test for an updater that fails after rewriting the command line (with a restore that also fails), and a missing-helper test. Please also post the first-boot timing when you have it.

maralcbr and others added 3 commits October 9, 2026 11:32
Without set -e, a missing grub-keep.sh left grub_tools_present returning
127, and the update carried on as if GRUB were absent.
…cpio

On root's PATH, mkinitcpio resolves to the limine-mkinitcpio-hook wrapper
in /usr/local/bin, which builds the real image and then the UKI and exits
with the UKI's status. The keep-GRUB branch of a re-key therefore built
extra images and hid a failed build of /boot's image. Call /usr/bin/mkinitcpio
explicitly, and let the tests redirect it with OMARCHY_MKINITCPIO.
… a missing GRUB helper

An updater that rewrites the command line and then fails, with a restore
that fails too, still leaves the key on the boot partition and the restored
command line in the boot files for the retry. A boot update whose GRUB
helper does not load stops instead of carrying on without it.
@joshuaswarren

Copy link
Copy Markdown
Contributor Author
  1. Re-key image build: 544b8b1 pins the rebuild to ${OMARCHY_MKINITCPIO:-/usr/bin/mkinitcpio}, past the wrapper on root PATH, and the tests redirect it through the override.
  2. Fail-closed helper load: your b8359af already covers it, so I built on it as the new head.
  3. Optional tests: 9183b54 adds both, an updater that fails after rewriting the command line with a restore that fails too, and the missing-helper case.
  4. First-boot timing: still owed, pending the hardware run.

The integration harness runs the staged provision in a fixture root where
only PATH stubs exist, so the absolute /usr/bin/mkinitcpio failed there with
No such file or directory and first-boot setup refused to finish. Resolve it
as every other fixed path in provision.sh does, below MAC_BOOT_ROOT: empty on
a live system, so production still calls /usr/bin/mkinitcpio past the wrapper
on root's PATH, and a fixture root in tests, which now carries the stub. This
replaces yesterday's OMARCHY_MKINITCPIO override, so the tests keep one
convention.

@scottjones scottjones left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Joshua, this does what it says. A Limine Mac without GRUB now builds only the UKI during the re-key, and a Mac that keeps GRUB still rebuilds /boot's image. I checked Marcelo's points at 55d7eb1 and all five look resolved: the three from 10-07 (every boot image is proved, one grub_tools_present, prepare and commit use the same rule) and the two from 10-09 (/usr/bin/mkinitcpio past the wrapper, and the helper load failing closed). Thanks for working through them so carefully.

Three small things, all low and none blocking:

  1. limine-update hides a failed UKI build. limine-mkinitcpio-install has process_uki_kernel || return 0 and process_kernel ... || true, in limine-mkinitcpio-hook 1.40.0-2 on edge (source) and in the 1.39.0-2 that release candidates pin (source). So limine-update exits 0 when the UKI build fails. On a Limine Mac without GRUB, commit then checks the old UKI's .initrd, which passes, and shreds the key, while the old UKI's .cmdline still names rd.luks.key=. Reproduced in a fixture.

    • It's safe at boot: systemd-cryptsetup treats the missing key file as a cue to prompt for the password right away, and the next successful limine-update writes a clean UKI.
    • Two options: refuse in commit when the UKI's .cmdline still holds rd.luks.key=, or, better since every caller is exposed, have omarchy-mac-boot-update confirm the UKI was actually rebuilt.
    • Most of this is on main already. What's new is that main's mkinitcpio -P used to stop a re-key when the mkinitcpio config was broken.
  2. Pre-existing: boot_image_types_layout (omarchy-mac-boot/lib/provision.sh:176, Limine branch at :187) checks only the UKI, even on a Limine Mac that keeps GRUB. The new firmware check, boot_image_orders_firmware (:132), covers every image.

  3. Test gap: the fail-mkinitcpio row (omarchy-mac-boot/test/mac-provision-test.sh:408) passes only because the fixture image can't be extracted. Adding || true to the mkinitcpio -P call at provision.sh:100 still passes the whole suite. A row where the /boot image build really fails would pin that down.

None of these hold the merge from my side. Thanks again!

@iconidentify iconidentify left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Validated 55d7eb1 and its conflict-free merge onto current main d8100ec. The full boot-package suite passes on both; provisioning integration also passes against Omarchy e1b0e5e after the merge retains main's updated platform fixtures.

The rekey path uses the shared GRUB predicate, checks UKI firmware contents and any retained GRUB image, and preserves the staged key on reported failures. No new high/medium finding in this change. Scott's three nonblocking follow-ups remain: a masked UKI build failure can leave the old cmdline, retained-GRUB keymap coverage is still UKI-only, and the mkinitcpio failure test needs a stronger negative control.

This review covers source and controlled tests. It does not supply new cold-boot evidence or first-boot timing.

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.

4 participants