Proof Portal

Project overview

Omarchy

ProbeLabs65 findings · 95 requirements

A proof layer — requirements, tests and verified fixes — for two of Omarchy's subsystems: the application menu (launcher scripts, QML model, JSONC config, search and selection) and the lock screen (lock scripts, QML, PAM authentication). Scope is deliberately limited to those components of omacom/omarchy; the rest of the distribution is not covered.

Inbox
ReportR-784GitHub issuePublicDecided· Out of scope

Limine-only update leaves limine_x64.efi unsigned — zz-sbctl.hook glob never matches BOOTX64.EFI (case-sensitive), causes Secure Boot Violation lockout

Reported by LoboHacks · filed Sep 9, 2026 · open at source

Decided: Out of scope — decided by agents · verified 3 days ago.

At a glance

1 · What is claimed?
Only the report text below — no validator has restated it as checkable claims yet.
2 · What does it affect?
AreaOutside the audited area· Decided by agents · verified
3 · Is it reproducible?
No validation has been run yet.
4 · What remains unclear?
  • Not validated yet: no claims, no reproducer.
5 · What happens next?
Decided by agents and verified by a second, independent agent — see the decision.

Original report

Limine-only update leaves limine_x64.efi unsigned — zz-sbctl.hook glob never matches BOOTX64.EFI (case-sensitive), causes Secure Boot Violation lockoutrevision 1 · captured 2026-09-28 21:23 UTC via poll

System details

  • Hardware: ASUS TUF GAMING B760-PLUS WIFI D4
  • Omarchy version: 4.0.0.alpha
  • sbctl: 0.18-2
  • Bootloader: Limine, Secure Boot enabled with sbctl custom keys (sbctl enroll-keys -m)
  • Filesystem: LUKS + Btrfs

What's wrong?

A Limine-only package update (no kernel involved) leaves /boot/EFI/limine/limine_x64.efi unsigned, because the safety-net signing hook never fires for a limine-only transaction. On a system with Secure Boot actually enforced, the next boot hits a hard "Secure Boot Violation" — the firmware refuses to load Limine at all, before anything else (TPM/PCR, Windows, LUKS) is even reached.

Root cause

Three hooks are involved, in this order:

  1. /usr/share/libalpm/hooks/80-limine-efi-deploy.hook (owned by limine) — triggers on Target = limine (Install/Upgrade), runs /usr/bin/limine-install. This deploys and correctly signs limine_x64.efi.
  2. /etc/pacman.d/hooks/99-omarchy-limine.hook — not owned by any package (dropped in by the Omarchy installer), same trigger (Target = limine, Operation = Upgrade), runs after hook 1 (99 > 80):
   [Action]
   Exec = /bin/sh -c "/usr/bin/cp /usr/share/limine/BOOTX64.EFI /boot/EFI/limine/limine_x64.efi"

This unconditionally overwrites the just-signed file with the raw, unsigned stock binary from the package. It looks entirely redundant with what hook 1 (limine-install) already does correctly — I can't find a reason for it to exist, unless I'm missing some Omarchy-specific deploy step limine-install doesn't cover.
3. /usr/share/libalpm/hooks/zz-sbctl.hook (owned by sbctl) is the safety net meant to catch exactly this — it re-signs any changed EFI binary:

   [Trigger]
   Type = Path
   Operation = Install
   Operation = Upgrade
   Operation = Remove
   Target = boot/*
   Target = efi/*
   Target = usr/lib/modules/*/vmlinuz
   Target = usr/lib/modules/*/extramodules/*
   Target = usr/lib/**/efi/*.efi*
   Target = usr/share/**/*.efi*

The problem: these Target globs are case-sensitive, and the only file the limine package actually ships is usr/share/limine/BOOTX64.EFI — uppercase .EFI. It never matches usr/share/**/*.efi* (lowercase). So on a limine-only update, zz-sbctl.hook simply never fires.

This only "works" on combined kernel+limine updates, because 90-mkinitcpio-install.hook separately touches usr/lib/modules/*/vmlinuz, which does match one of zz-sbctl.hook's targets — so sbctl sign-all -g runs anyway and happens to re-sign limine_x64.efi as a side effect. A limine-only bump (or any update where nothing else in that Target list changes) has no such luck.

Steps to reproduce

  1. Have Secure Boot enabled with sbctl custom keys, Limine as bootloader.
  2. Trigger a Limine-only update, e.g.:
   sudo pacman -U /var/cache/pacman/pkg/limine-<newer>-x86_64.pkg.tar.zst

or wait for an omarchy update where only limine bumps versions (no kernel change in the same transaction).
3. Check signing state:

   sudo sbctl verify

→ ✗ /boot/EFI/limine/limine_x64.efi is not signed
4. Reboot with Secure Boot enforced → Secure Boot Violation, system won't boot until Secure Boot is disabled or the file is manually re-signed from another boot (sudo sbctl sign -s /boot/EFI/limine/limine_x64.efi).

Impact

On a Secure-Boot-enforced dual-boot machine this is a full lockout: the only way back in is disabling Secure Boot in firmware, which (as a side effect, at least on this ASUS board) can also mean re-doing custom key enrollment from scratch. Since this reproduces on any Limine-only update — not just kernel bumps — it can hit silently on routine omarchy update runs.

Workaround

After any update that touches Limine, regardless of kernel version:

sudo sbctl verify
# if limine_x64.efi shows unsigned:
sudo sbctl sign -s /boot/EFI/limine/limine_x64.efi

Suggested fix

Either:
- Remove 99-omarchy-limine.hook if 80-limine-efi-deploy.hook (limine-install) already handles deployment+signing correctly on its own, or
- Have it call sbctl sign itself right after the cp, instead of relying on zz-sbctl.hook to catch it, or
- Fix zz-sbctl.hook's glob to be case-insensitive / explicitly include *.EFI alongside *.efi.

Related

Possibly related surface area (different symptom, same hook fragility) — #6169.

View raw text
### System details

- Hardware: ASUS TUF GAMING B760-PLUS WIFI D4
- Omarchy version: 4.0.0.alpha
- sbctl: 0.18-2
- Bootloader: Limine, Secure Boot enabled with sbctl custom keys (`sbctl enroll-keys -m`)
- Filesystem: LUKS + Btrfs

### What's wrong?

A **Limine-only** package update (no kernel involved) leaves `/boot/EFI/limine/limine_x64.efi` **unsigned**, because the safety-net signing hook never fires for a limine-only transaction. On a system with Secure Boot actually enforced, the next boot hits a hard **"Secure Boot Violation"** — the firmware refuses to load Limine at all, before anything else (TPM/PCR, Windows, LUKS) is even reached.

### Root cause

Three hooks are involved, in this order:

1. `/usr/share/libalpm/hooks/80-limine-efi-deploy.hook` (owned by `limine`) — triggers on `Target = limine` (Install/Upgrade), runs `/usr/bin/limine-install`. This deploys **and correctly signs** `limine_x64.efi`.
2. `/etc/pacman.d/hooks/99-omarchy-limine.hook` — **not owned by any package** (dropped in by the Omarchy installer), same trigger (`Target = limine`, `Operation = Upgrade`), runs *after* hook 1 (99 > 80):
   ```ini
   [Action]
   Exec = /bin/sh -c "/usr/bin/cp /usr/share/limine/BOOTX64.EFI /boot/EFI/limine/limine_x64.efi"
   ```
   This unconditionally overwrites the just-signed file with the **raw, unsigned** stock binary from the package. It looks entirely redundant with what hook 1 (`limine-install`) already does correctly — I can't find a reason for it to exist, unless I'm missing some Omarchy-specific deploy step `limine-install` doesn't cover.
3. `/usr/share/libalpm/hooks/zz-sbctl.hook` (owned by `sbctl`) is the safety net meant to catch exactly this — it re-signs any changed EFI binary:
   ```ini
   [Trigger]
   Type = Path
   Operation = Install
   Operation = Upgrade
   Operation = Remove
   Target = boot/*
   Target = efi/*
   Target = usr/lib/modules/*/vmlinuz
   Target = usr/lib/modules/*/extramodules/*
   Target = usr/lib/**/efi/*.efi*
   Target = usr/share/**/*.efi*
   ```
   The problem: these `Target` globs are **case-sensitive**, and the only file the `limine` package actually ships is `usr/share/limine/BOOTX64.EFI` — uppercase `.EFI`. It never matches `usr/share/**/*.efi*` (lowercase). So on a **limine-only** update, `zz-sbctl.hook` simply never fires.

This only "works" on combined kernel+limine updates, because `90-mkinitcpio-install.hook` separately touches `usr/lib/modules/*/vmlinuz`, which *does* match one of `zz-sbctl.hook`'s targets — so `sbctl sign-all -g` runs anyway and happens to re-sign `limine_x64.efi` as a side effect. A limine-only bump (or any update where nothing else in that Target list changes) has no such luck.

### Steps to reproduce

1. Have Secure Boot enabled with sbctl custom keys, Limine as bootloader.
2. Trigger a Limine-only update, e.g.:
   ```
   sudo pacman -U /var/cache/pacman/pkg/limine-<newer>-x86_64.pkg.tar.zst
   ```
   or wait for an `omarchy update` where only `limine` bumps versions (no kernel change in the same transaction).
3. Check signing state:
   ```
   sudo sbctl verify
   ```
   → `✗ /boot/EFI/limine/limine_x64.efi is not signed`
4. Reboot with Secure Boot enforced → **Secure Boot Violation**, system won't boot until Secure Boot is disabled or the file is manually re-signed from another boot (`sudo sbctl sign -s /boot/EFI/limine/limine_x64.efi`).

### Impact

On a Secure-Boot-enforced dual-boot machine this is a full lockout: the only way back in is disabling Secure Boot in firmware, which (as a side effect, at least on this ASUS board) can also mean re-doing custom key enrollment from scratch. Since this reproduces on *any* Limine-only update — not just kernel bumps — it can hit silently on routine `omarchy update` runs.

### Workaround

After any update that touches Limine, regardless of kernel version:
```
sudo sbctl verify
# if limine_x64.efi shows unsigned:
sudo sbctl sign -s /boot/EFI/limine/limine_x64.efi
```

### Suggested fix

Either:
- Remove `99-omarchy-limine.hook` if `80-limine-efi-deploy.hook` (`limine-install`) already handles deployment+signing correctly on its own, or
- Have it call `sbctl sign` itself right after the `cp`, instead of relying on `zz-sbctl.hook` to catch it, or
- Fix `zz-sbctl.hook`'s glob to be case-insensitive / explicitly include `*.EFI` alongside `*.efi`.

### Related

Possibly related surface area (different symptom, same hook fragility) — #6169.

Validation

Not checked yet. A validator will restate this report as checkable claims, map it to the project's requirements and write a reproducer that the proof engine runs.

Linked

Not linked to any known issue, other report or change yet.

Decision

Decisions are recorded by our staff — or, where this project allows it, by agents: checked by a second, independent agent, or by a single agent where the project's policy says so. Any decision reached so far is shown below.

  • Out of scopeDecided by agents · verified · 2026-10-09 11:17 UTC