Repository navigation
Converge omarchy-mac and omarchy-mx-mac into upstream Omarchy - #14431
Merged
Merged
Conversation
…part aarch64 covers Apple Silicon Macs, Snapdragon laptops, Raspberry Pis and VMs, so the CPU architecture can't pick platform setup. omarchy-hw-platform prints apple-silicon, qualcomm, generic-aarch64 or generic from the vendor prefixes in the device tree's root compatible (/proc/device-tree, then /sys/firmware/devicetree/base) and the CPU, and fails when they contradict each other. Its qualcomm answer uses the same evidence as the dragon branch's omarchy-hw-qualcomm-soc, and the fixtures check the two agree. omarchy-hw-apple-silicon is the Apple predicate on top of it. An image built away from its hardware names its target in a root-owned manifest, /var/lib/omarchy/image/target (format=1, platform=). While the root is being built rather than booted, the detector answers from it and never reads the build host's device tree; a booted system always answers from its hardware. As root it restarts in an empty environment, so no variable picks the platform for privileged setup. Co-authored-by: Birk Skyum <74932975+birkskyum@users.noreply.github.com> Co-authored-by: Jimmy Van Veen <8206925+JimmayVV@users.noreply.github.com>
An architecture check is right for an ABI, a binary or a repository's $arch, not for picking a platform. The lint lists each shipped check after review and fails on a new one, so platform setup goes through omarchy-hw-platform.
omarchy-reinstall-pkgs installed the base list on every machine. A platform's default set is now the base list, then install/omarchy-aarch64.packages on aarch64, then the platform's own list when it has one (install/omarchy-qualcomm.packages adds linux-firmware-qcom). omarchy-pkg-defaults prints the set for the detected platform, or for the one named, and reinstall installs it. x86_64 still gets exactly the base list, and an image build gets its target's set.
linux-aarch64 has no thunderbolt module, and mkinitcpio fails the whole image over a MODULES entry it cannot find. The optional form, thunderbolt?, is not enough: a systemd initramfs copies MODULES as written into modules-load.d, where "thunderbolt?" names no module, and a kernel without the module would still list it there. thunderbolt_module.conf now adds plain thunderbolt only when modinfo finds it as a module for the kernel being built. Co-authored-by: Scott Jones <444693+scottjones@users.noreply.github.com>
omarchy_hooks.conf assigned HOOKS outright, and mkinitcpio sources drop-ins in name order, so it ran after any numbered drop-in (90-*.conf) and threw its hooks away. HOOKS now comes from 00-omarchy-hooks.conf, which sorts first, and later drop-ins add to it. The NVIDIA kms filter and the vconsole handling stay in omarchy_hooks.conf. With the drop-ins Omarchy writes, machines keep the same HOOKS, MODULES and FILES; a user drop-in that sets HOOKS and sorts before omarchy_hooks.conf now takes effect instead of being overwritten. Migration 1786605598 now sources the baseline first, since without it the migration would read empty HOOKS and also rebuild hybrid systems. It skips a machine that lacks the baseline.
lsblk -s prints the ancestor tree with box-drawing prefixes ("└─/dev/nvme0n1p6"), so the crypto_LUKS parent came back as a path that does not exist and first-boot setup stopped with "Could not locate the LUKS device to re-key". -r prints raw names. Factory reset uses the same lookup.
cryptsetup open tries enrolled tokens before the key file, so a TPM2 or FIDO2 token could authorize the re-key with a wrong passphrase.
Exercise raw ancestry output without invoking provisioning or reset. Cover btrfs subvolume roots, absent encryption, failed probes, and explicit cryptdevice specifications.
A first boot killed after the staged install key's slot was retired left the key file behind, and every retry then stopped at "the staged LUKS key no longer unlocks". A lost key file with the boot-time auto-unlock still configured let setup finish with the disk unlocking unattended. The re-key now runs from install/provisioning/luks-rekey.sh and records its phase and slot numbers (never key material) in /var/lib/omarchy/provisioning/luks-rekey.state. It reuses the owner's slot instead of adding a duplicate, retires LUKS1 slots too, and destroys the staged key only after the volume proves it opens nothing. Setup refuses to finish while any part of the provisioning unlock remains. The journal ends at phase=done and leaves together with pending, so a retry after the staged key is retired must use the password the disk already holds. Key checks exclude LUKS2 tokens: cryptsetup open tries an enrolled token (say a TPM2 slot a previous owner left behind) before the key and reports its slot whatever key it was given. Slot listings read only the keyslot section, since a luks2-keyring token looks like a keyslot in luksDump.
Installation gives the login user, root and the disk holding / one password, but omarchy-drive-password changed only the LUKS key, so the three drifted apart. For the disk holding / it now checks the current password opens the disk, changes the LUKS key, confirms the new key opens the disk and the old one no longer does, and only then sets the login and root passwords. Any other encrypted drive changes its LUKS key alone. Every drive now asks for the current password with gum instead of cryptsetup's prompt. A journal in ~/.local/state/omarchy/drive-password.state (disk UUID, key slots and phase, never a password, written with the re-key journal helpers) lets the next run finish an interrupted change. The rerun accepts only the old key (removing the slot the interrupted change added) or the new one (retiring the old slot), so another key such as a recovery passphrase never becomes the login password. luksChangeKey is judged by whether the new key opens the disk, not by its exit status, and key checks exclude LUKS2 tokens, which cryptsetup open would otherwise accept in place of any password. Runs are serialised, cryptsetup calls hold a disk lock, a / whose LUKS partition cannot be resolved fails closed, and passwords are never traced. A new password equal to the current one is refused, and --pbkdf argon2id is passed only to LUKS2, so LUKS1 drives can change their password again.
Provisioning, factory reset and update call a fixed set of operations through it. A platform whose boot chain the generic Limine path can't drive registers a directory of root-owned entrypoints in the dispatcher; every other platform gets a no-op and keeps its generic path. Apple Silicon is registered to omarchy-mac-boot's /usr/lib/omarchy/mac-boot, with its required operations failing clearly when the package or an entrypoint is missing. docs/lifecycle-dispatch.md is the contract, including how a Qualcomm boot package would plug in.
… key slots Owner provisioning asks provision-prepare before the owner form, hands the staged unlock to provision-commit and provision-verify where the platform implements both, and records the kept LUKS slot through luks-slots before the staged key is destroyed; the disk password change records the owner's new slot the same way. On x86, Snapdragon and generic aarch64 every call is a no-op and first boot is unchanged.
Where a platform implements the reset operations, reset-prepare and reset-verify stage and prove the factory root's boot files, the throwaway slot is added only after that, and reset-commit gets the key on standard input once the factory root is active. A failure before the switch rolls the boot state back and revokes the slot. Elsewhere the Limine UKI path is unchanged.
omarchy-update-boot runs update-verify after AUR packages, the last sudo-capable step, so it also covers the initramfs rebuilds they trigger. It follows third-party build code, so like AUR it authenticates through the no-update wrapper. A failed verification finishes the update's remaining steps, then exits non-zero without offering the reboot. Where the platform does not implement it nothing runs and nothing asks for root.
migrations/1790347292.sh resolves the new optional migrate operation and, where the platform's boot package implements it, runs it as root. The entrypoint moves the machine onto the platform's official package set, idempotently and resumably; its failure leaves the migration pending. Once it succeeds, a root-owned marker spares other accounts the migration and its root prompt. Everywhere else, Macs without the boot package included, the migration is a no-op, and an undetermined platform keeps it pending.
A platform package names its platform with groups=('omarchy-platform-<name>'),
<name> being what omarchy-hw-platform prints. omarchy-settings ships a
pre-transaction hook, the guard it runs and a copy of the detector, so the
guard is resident before the runtime and any platform package. The guard
reads tags from the synced databases and refuses a transaction that installs
a package tagged for another platform; a transaction without a tagged
package passes without asking which machine this is. Image builds answer
from the image's root-owned target manifest, as the detector does.
The guard ships on aarch64 only: no platform package is built for x86_64,
so x86 transactions run no hook. Hardware setup starts by proving the guard
is resident and checking the packages an installer placed without it. On
generic aarch64, where no platform packages exist, a missing guard is
logged, not fatal, and on x86 it is expected.
docs/platform-guard.md is the contract.
Co-authored-by: Birk Skyum <74932975+birkskyum@users.noreply.github.com>
Co-authored-by: Jimmy Van Veen <8206925+JimmayVV@users.noreply.github.com>
omarchy-hw-vulkan reports a Vulkan runtime, not whether this voxtype build has a Vulkan backend. The aarch64 voxtype-bin package ships only the CPU and ONNX binaries, so on any aarch64 machine with a Vulkan driver setup ran `voxtype setup gpu --enable` and printed "Vulkan backend not installed" in the middle of an otherwise working install. Require the backend binary too. x86_64 voxtype-bin always ships /usr/lib/voxtype/voxtype-vulkan, so x86 installs still switch to the GPU backend as before. Ported from omacom#483. Co-authored-by: Oliver Lukschander <33756270+oliverlukschander@users.noreply.github.com>
The widevine package registers its CDM for Chromium and Firefox only. Brave looks for the CDM in its own install directory, finds nothing, and DRM sites (Netflix, Apple TV+, Spotify) fail with a blank player and no useful error. This hits machines where Widevine comes from that package, such as aarch64 ones, since no Widevine component is published for them. Link /opt/WidevineCdm/chromium into /opt/brave-bin and /opt/brave-origin-bin through as_root when Brave or Brave Origin is installed, and add a migration that repairs existing installs. Both do nothing when the widevine package is absent or the browser already has a CDM. A standard x86 install has no widevine package, so nothing changes there. Ported from omacom#474, extended to Brave Origin. Co-authored-by: Caleb Marshall <68087341+cajomar@users.noreply.github.com>
With file logging, run_logged sends a failing leaf's output and its "Failed:" marker to the install log only, so the caller sees setup stop with an exit status and nothing else. A missing command (exit 127) was the case that made this hard to diagnose. Print the failed script, its exit status and the log path on stderr. Stdout logging mode already shows the failure marker and is unchanged, and successful steps stay quiet. Ported from omacom#389 (the logging part). Co-authored-by: Scott Jones <444693+scottjones@users.noreply.github.com>
A migration that exits 75 changed nothing and cannot finish yet: omarchy-migrate leaves it pending with its login notification, runs the migrations after it and exits 0, so omarchy update carries on. Any other failure still stops the queue. The platform migration passes a deferred refusal of its entrypoint through, and defers itself where the dispatcher can't resolve its entrypoint (the platform can't be told, or the entrypoint isn't trusted): nothing has run then, so the update goes on to its boot verification, which fails there, as documented.
00-omarchy-hooks.conf set the busybox line on every machine. On a Mac, omarchy-mac-boot's drop-ins then add the Asahi firmware hooks, but its encryption drop-in reads busybox encrypt as a Mac unlocked through cryptdevice= and leaves the line alone. The image came out as "... block encrypt asahi omarchy-vendorfw filesystems ...", with no systemd, sd-encrypt or omarchy-mac-encrypt, so an encrypted Mac could not unlock its disk. Reproduced against an M1 Pro's installed drop-ins. The baseline now asks omarchy-hw-platform. Apple Silicon starts from the systemd line its boot package builds on. A Mac whose own mkinitcpio.conf carries the asahi hook, or busybox encrypt, keeps that line, as a legacy install needs. x86, Snapdragon and generic aarch64 keep today's busybox line, as does a machine with no detector. The detector is the runtime's, or else the copy omarchy-settings ships for its platform guard, so a settings package newer than the runtime still places a Mac. A detector that cannot place the machine stops the build. The HOOKS test now composes the baseline on each platform and on legacy Mac lines. A new test composes it with omarchy-mac-boot 20260926-1.45's drop-ins, kept as fixtures, and checks the exact HOOKS the M1 Pro builds. The NVIDIA test pins its platform.
Not every optional install exists on every architecture: some vendors ship no aarch64 build, and some repositories don't carry a package at all. Each Install row now carries a `when:` guard naming every package its install command adds, checked with omarchy-pkg-available, so a row with nothing to install disappears instead of failing halfway through a pacman transaction. The rows that build from the AUR (Chrome, Edge, Brave, Brave Origin, Zen, NordVPN) are guarded on the CPU architectures their vendors publish for. omarchy-pkg-available asks the sync databases: one `pacman -Slq` per batch, and `pacman -Sp` for a name that list doesn't hold, so a provided name (libappindicator-gtk3) and a version constraint resolve the way `pacman -S` will. The menu's guard prelude sources the same function, so the whole batch still costs one fork. omarchy-reinstall-pkgs reports and skips a default that no configured repository offers instead of failing the transaction. On x86_64 every guarded row resolves against the stable and edge repositories, so the menu is unchanged there. Tests derive each row's packages from its install command on x86_64 and aarch64 and fail when the guard names different ones. Co-authored-by: Ryan Hughes <1630358+ryanrhughes@users.noreply.github.com>
Electron draws a blank window when the machine has no DRM render node: a VM, a headless or passthrough GPU, or a GPU whose driver isn't up yet (Apple M3 and later on Linux today). omarchy-hw-render-gpu tells whether a renderD node exists, and omarchy-cmd-electron-gl-args prints --ozone-platform=wayland --disable-gpu only when it doesn't. omarchy-cmd-electron-gl-wrap installs a marked PATH wrapper in /usr/local/bin that execs the real binary with those flags, so it follows the machine rather than the moment it was installed. It refuses to replace a launcher it didn't write, or to wrap a binary over itself, and stages the write so a failure leaves the old launcher in place. omarchy-cmd-desktop-exec-repair points the user's desktop entry at the wrapper by replacing only the executable token, keeping arguments, other actions and customizations, with a backup. The 1Password installer uses them when the machine has no render node. With one, which is every x86 machine with a working GPU, nothing changes. Co-authored-by: Scott Jones <444693+scottjones@users.noreply.github.com>
gpu-screen-recorder knows NVIDIA, AMD, Intel and Broadcom GPUs and aborts during GL init on anything else, such as Apple Silicon's, before a recording starts. Screen recording now falls back to wf-recorder, when it is installed, if gpu-screen-recorder is missing or exits before the file appears. The picker, file naming, notifications and post-processing are shared. wf-recorder records one audio device, so desktop plus microphone go through a temporary null sink that stop unloads, and its track is pinned to 48 kHz. Portal capture and --resolution stay gpu-screen-recorder only. The finalize pass keeps 48 kHz too: loudnorm declares 192 kHz output, so without -ar the AAC encoder wrote 96 kHz from a 48 kHz recording, which many editors decode as silence, and which on Apple Silicon makes the speaker safety daemon lock the amps. #11092 makes the same one-line change; whichever lands second drops it. Stop and status matched `pgrep -f ^gpu-screen-recorder`, which misses wf-recorder and matches any command line that starts with that path. The script now records the recorder's pid, and the new omarchy-capture-screenrecording-process checks or signals it by its executable, through a pidfd opened before the check so a reused pid can't be signalled. When that pid is no longer a recorder (a recording started before this change, or one that died), every recorder the user runs counts, as it does for the bar indicator and the Stop row, which use the same helper. Where gpu-screen-recorder works, which is every x86 GPU it supports today, it records as before. Co-authored-by: Scott Jones <444693+scottjones@users.noreply.github.com> Co-authored-by: Issac Trotts <15530+ijt@users.noreply.github.com>
systemd-logind treats a closed lid as docked only when an external display is on a connector type it lists (HDMI, DisplayPort, DVI, VGA...). Displays on "USB" connectors, which Apple Silicon uses for USB-C displays, aren't on that list, and a missing comma fuses DVI-A and Composite into one entry. With such a display in use, closing the lid suspended a laptop that was still being used on it. omarchy-system-lid-inhibit holds a handle-lid-switch inhibitor while one of those connectors is connected and enabled in Hyprland, on a laptop where HandleLidSwitchDocked is ignore, which is exactly the case logind would have ignored had it counted the display. It releases the inhibitor when the display goes. A compositor or logind that doesn't answer changes nothing, since releasing then would suspend a closed lid. The inhibitor runs as a transient user unit bound to the monitor watcher's unit, so a watcher that dies can't leave the lid inhibited until logout. The monitor watcher runs it alongside the clamshell sync. On a machine whose external displays are all on connectors logind counts, it does nothing.
When a monitor is unplugged, or the lid turns off the laptop panel, Hyprland first warps focus to whichever monitor it lists first and only then moves the removed monitor's workspaces over. The workspace you were on lands hidden behind that monitor's own (Hyprland 0.56.2, CMonitor::onDisconnect). monitor.focused fires before Hyprland records the new focus, so the monitor still reported as active is the one being left: its workspace is remembered until the event loop next turns, and a monitor.removed in that window focuses it again once the removal has finished. An open special workspace already comes along, and any focus or workspace change made before the restore runs wins.
Laptops with an ambient light sensor and a keyboard backlight LED now light the keys in the dark and dim them as the room brightens, inverse to display auto-brightness. omarchy-brightness-keyboard-auto polls the sensor every five seconds (preferring an IIO device that names itself ALS, and converting a raw channel with its offset and scale) and maps lux onto the LED, with a deadband so it doesn't chase noise. It pauses while the screen is locked or the lid is closed. A level set with the keyboard-brightness keys wins until the light changes enough that it no longer fits. omarchy-brightness-keyboard records each level it sets in the session's runtime directory (a private state directory without one), so the loop can tell keys turned off on purpose from keys left off by the lock screen or a restored zero, which it lights again. The user unit runs it in the graphical session. Its ExecCondition asks --available, so it is a no-op on machines without both a sensor and a keyboard LED. First run enables it, and a migration enables it for existing installs, writing the enable symlink itself when there is no live user manager. Co-authored-by: Scott Jones <444693+scottjones@users.noreply.github.com> Co-authored-by: Gabe Ohlson <3355909+6abe@users.noreply.github.com>
An image is built away from the machine it will run on, so its hardware leaves would probe the builder. While the image manifest the detector already reads (/var/lib/omarchy/image/target) exists, omarchy-apply-hardware queues every leaf of install/hardware/all.sh instead of running it and arms omarchy-provision-hardware.service. On the first boot, before owner setup and the login screen, omarchy-provision-hardware retires the manifest and runs the queue on the real hardware: a failed step stays queued for the next boot (exit 75), and the initramfs is rebuilt once after the last step when a step changed its inputs or asked for it. Bluetooth starts its service on that boot, whose target is already up. Root ignores any environment override of these paths. Without a manifest, as on every ISO install, hardware setup runs exactly as before. Nothing here fetches package databases: an image carries its sync databases and every package its steps install.
A root built from a distribution tarball instead of the ISO (Arch Linux ARM's ships LANG=C) never went through the ISO's locale step, so it ran non-UTF-8. The config phase now selects en_US.UTF-8, generating it if needed, but only when LANG is unset, C or POSIX: any named locale is somebody's choice and stays. The ISO writes the locale before this runs, so an ISO install is unchanged. Co-authored-by: Scott Jones <444693+scottjones@users.noreply.github.com>
This was referenced Oct 11, 2026
This was referenced Oct 11, 2026
oliverlukschander
pushed a commit
to oliverlukschander/omarchy-mac
that referenced
this pull request
Oct 11, 2026
Brings back automatic keyboard lighting, split out of omacom#14431 so it gets its own review: laptops with an ambient light sensor and a keyboard LED light the keys in the dark and dim them as the room brightens, and the brightness keys and firmware keys still choose by hand. (cherry picked from commit 542c91d)
oliverlukschander
pushed a commit
to oliverlukschander/omarchy-mac
that referenced
this pull request
Oct 11, 2026
Brings back automatic keyboard lighting, split out of omacom#14431 so it gets its own review: laptops with an ambient light sensor and a keyboard LED light the keys in the dark and dim them as the room brightens, and the brightness keys and firmware keys still choose by hand. (cherry picked from commit 542c91d)
This was referenced Oct 11, 2026
This was referenced Oct 12, 2026
Contributor
|
For anyone reading the earlier ISO-only closure note: this PR was reopened and merged. It adds AArch64 support paths to Omarchy: The official Omarchy ISO and Apple Silicon's install/boot path are separate questions. Apple Silicon still needs its platform boot package and Asahi-based installer. The earlier closure note (now marked outdated) should not be read as saying Omarchy has no AArch64 support. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Omarchy's Mac forks maintain shared hardware and desktop support separately because upstream assumes PC device names, package repositories and boot ownership. This brings those shared paths into Omarchy while leaving platform-specific boot implementations in their packages.
The platform detector distinguishes Apple Silicon from generic machines; Snapdragon laptops count as generic aarch64. Package defaults compose from base, architecture and platform lists; ARM offers edge only. Fixed lifecycle operations let platform packages own boot setup, provisioning, reset and update verification. Generic machines retain their existing boot paths, and x86 keeps the same default packages and repository templates. The x86-only installer menu entries use the omarchy-hw-x86 predicate.
Hardware helpers recognize batteries, lids and touchpads through their system metadata, preserve Intel Mac quirks, and accept platform display and audio hints. Bars reserve camera cutouts only on matching panels. Virtual default microphone sources get working volume, mute and level controls. Screen recording retains GPU Screen Recorder first and falls back to wf-recorder when available. Combined audio modules are released when the saved recorder exits and status or stop detects it, while active recordings and audio mixes still being started remain intact. Recording changes share a lock, while busy status requests stay read-only so the picker cannot stall the bar. A toggle or stop during a start that is still in its picker or waiting on its recorder cancels that start instead of queueing a new one; the picker stays up until dismissed, and the recording then aborts. A recorder that does not create its file within 15 seconds, or ignores TERM when cancelled, is stopped.
Encrypted owner provisioning resolves raw LUKS parents and journals re-keying so the staged key is retained until the owner's key and boot unlock are verified. Factory reset rolls back failed activation and its temporary key slot. Changing a drive's password changes only its LUKS key; on the system disk the new key must open the disk and the old one must not before the change counts, and the platform records the owner's new key slot. Letting the login and root passwords follow is a separate opt-in change, #14556. The initramfs hook baseline composes through an early drop-in and retains the existing generic hook sequence. The NVIDIA firmware migration includes that baseline so hybrid machines keep their kms hook and skip an unnecessary rebuild.
A root built away from its machine, as the Asahi installer's is for a Mac, names its platform in a manifest and defers hardware setup to the machine's first boot, where it also makes the machine's own pacman keyring. A root still on LANG=C gets a UTF-8 locale, and named locales are kept. Owner setup applies keyboard layout and wireless regulatory domain before completing provisioning. A lid inhibitor covers external connectors logind does not count.
The rest is in separate draft PRs, stacked on this one:
Supersedes #13362 by @maralcbr, whose commits this carries unchanged.