Commit Graph
482 Commits
Author SHA1 Message Date
David Heinemeier HanssonandClaude Fable 5 8aee09cb29 Second review pass: strip account fields, clear stale cidata inputs, await OEM finalization
Codex xhigh round 2:

- Strip account authentication fields (!users, !root-password,
  root_enc_password, auth_config account entries) from the archinstall config
  in OEM mode, not just user_credentials — a combined/legacy config could
  otherwise have archinstall create a provisioning account before OOBE.
- Clear stale OEM inputs (oem marker, credentials, optional files) from /root
  before copying a cidata drive, so a retried install in the same live session
  can't force a fresh drive back into OEM mode or reuse an old LUKS passphrase.
- The test harness waits for the OEM pending flag to clear before saving the
  base image, so it never snapshots a half-finalized first boot; it also
  handles the deliberately skipped mode picker on blank disks built against a
  pre-OEM runtime.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-07 19:03:49 -07:00
David Heinemeier HanssonandClaude Fable 5 e49ea2bc96 Keep provisioning credentials out of OEM installs and factory snapshots
Review findings from codex and Copilot:

- A rig-supplied credentials file in OEM mode now contributes only its LUKS
  passphrase: preserved users/root_enc_password fields would have created a
  hidden provisioning account despite OEM promising no user until first boot.
- The factory snapshot is taken writable, scrubbed of deployment credentials
  (staged authorized_keys, tailscale auth key and join unit, the dead OEM
  LUKS key and its initramfs drop-ins), then set read-only — a reset years
  later must not authorize the original deployment key or rejoin its tailnet.
- The missing-Node-tarball case is a hard install error on every install,
  not just OEM: the stash is what lets a factory reset finalize offline.
- The configurator's OEM gate checks the runtime package for the service
  unit as well as the setup binary.
- The test harness handles the deliberately skipped mode picker on blank
  disks when the bundled runtime lacks OEM support.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-07 18:09:10 -07:00
David Heinemeier HanssonandClaude Fable 5 445db32792 Make OEM state world-readable for user-context finalization
The Node tarball in /var/lib/omarchy/oem/packages is read by
omarchy-finalize-user running as the freshly created user; a 0700 directory
forced the offline install onto its network fallback. The only secret inside
(luks-key) is itself 0600 root.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-07 16:51:01 -07:00
David Heinemeier HanssonandClaude Fable 5 fbee4ba437 Isolate the test harness SSH identity from the local ssh-agent
A loaded agent offers its keys before the harness identity and can exhaust
the server's MaxAuthTries before the right key is ever tried; IdentitiesOnly
pins authentication to the per-run key.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-07 16:03:15 -07:00
David Heinemeier HanssonandClaude Fable 5 b5f7e96c70 Offer OEM mode only when the bundled runtime supports it
The configurator now peeks into the offline mirror's runtime package for
omarchy-oem-setup before offering the OEM install mode, so an ISO built
against a pre-OEM runtime can't start an install doomed to fail at the
staging phase (which still hard-checks as the backstop). Blank disks with no
OEM choice to offer skip the mode picker entirely, like the pre-OEM flow.

Also keep the test harness QMP socket in /tmp — unix socket paths cap at
~108 bytes, which nested test-run directories can exceed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-07 15:52:55 -07:00
David Heinemeier HanssonandClaude Fable 5 679edce2e3 Add OEM install mode and factory snapshots
OEM mode installs the entire system but defers user creation to first boot,
where the runtime's omarchy-oem-setup asks for the owner and lands them in the
already-installed system — and the same machinery arms omarchy-reset-computer.

The configurator's install-mode step gains an OEM choice, which required
moving the user step after mode selection (OEM skips it entirely). Autoinstall
rigs select OEM with an `oem` marker file on the cidata drive, which also
replaces the user_credentials.json requirement.

In the orchestrator, OEM swaps the user phases for a stage_oem_state phase:
it stashes the bundled Node tarball (every install, for offline factory
resets), arms omarchy-oem-setup.service in the target, and on encrypted
installs stages the throwaway LUKS passphrase — generated by InstallContext
when the input carries none — with a keyfile embedded in the UKI so boot
auto-unlocks until first-boot setup re-keys to the owner's password. The
install fails with a clear error if the bundled runtime lacks OEM support.

Every install now also ends with a read-only @factory snapshot of @ at the
btrfs top level — outside snapper's reach — which is what makes
omarchy-reset-computer a true factory reset.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-07 15:41:04 -07:00
David Heinemeier Hansson 6004e80485 New plan 2026-08-08 00:08:46 +02:00
David Heinemeier HanssonandClaude Fable 5 0339b13d12 Join the tailnet from a cidata tailscale_authkey (#97)
* Join the tailnet from a cidata tailscale_authkey

A tailscale_authkey file on the autoinstall drive gets the installed
machine onto the tailnet on its own: the tailscale package is bundled in
the offline mirror and installed only when the key is present, the key is
staged root-only at /etc/tailscale/authkey, and a oneshot first-boot unit
runs tailscale up once the network is really there. On success the key is
deleted and the unit disables itself; on failure both survive to retry on
the next boot, so an offline install joins whenever it first gets
connectivity. ufw allows traffic in on tailscale0 via the same
write-the-rule-then-ignore-the-chroot-exit-code dance as the SSH phase.

Bundling rather than fetching at first boot is deliberate: pacman -Sy
tailscale on a fresh machine is a partial upgrade, and pacman -Syu means
an unattended full system upgrade racing the user's first login. The
offline mirror is a coherent snapshot; installing from it keeps the
target consistent and the install offline-capable. See plans/tailscale.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xc74h4WSYpuYPGuYbFAR1q

* Do not hold boot hostage for the tailnet join

Review caught that the join unit was Type=oneshot wanted by
multi-user.target: target units implicitly gain After= for their Wants=,
so an offline machine's boot -- SDDM included -- stalled for the full
10-minute timeout, and once timed out the join never retried within that
boot. Type=simple counts as started the moment it forks, so boot proceeds
while the join retries in the background for as long as the boot lasts.
Cleanup moves inside the script, sequenced after tailscale up succeeds,
which preserves the retry-across-boots behavior: key and unit survive a
boot with no connectivity, and the key never outlives a successful join.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xc74h4WSYpuYPGuYbFAR1q

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-07 15:10:30 +02:00
Axel Fontaine 4d10233371 Update boot command to use full path (#95) 2026-08-04 13:40:21 -05:00
David Heinemeier Hansson c9d445cf33 Merge branch 'autoinstall' into quattro 2026-08-04 10:00:30 -07:00
David Heinemeier HanssonandClaude Fable 5 f11a70c6ba Take authorized_keys on the cidata drive instead of ssh.json
The JSON wrapper was the one piece of autoinstall input that wasn't an
existing format: the other files are the configurator's own output, and
the keys end up in sshd's authorized_keys anyway. So take that file
directly -- one key per line, comments and key options work as they do
everywhere else -- and drop the parsing in between. The only thing JSON
bought was parse-error detection, and the phase still fails loudly when
the file carries no usable keys.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-04 06:08:33 -07:00
David Heinemeier HanssonandClaude Fable 5 be68a76a44 True up the autoinstall docs
user_encrypt_installation.txt does not encrypt anything: encryption is
driven by the disk_encryption block inside user_configuration.json,
which carries the passphrase in plaintext. The flag must match that
block, since it drives the encrypted install's autologin and the final
boot validation. The plan also claimed sshd ships enabled -- it ships
disabled, which is the whole reason the SSH phase exists -- and now
reflects the extracted loader and the unit tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-04 05:56:33 -07:00
David Heinemeier HanssonandClaude Fable 5 69ec1b4b8e Add unit tests for cidata loading and the SSH access phase
./test/all runs every suite under test/: the shell tests exercise
omarchy-cidata-load against a sandbox with mount/umount/udevadm stubbed,
and test_configure_ssh_access.py runs the orchestrator phase against a
temp target with subprocess.run recorded and ufw's
write-the-rule-then-fail chroot behavior simulated. The Python tests go
through unittest discovery, which also picks up the repo's existing
test_*.py suites that previously had no runner.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-04 05:56:33 -07:00
David Heinemeier HanssonandClaude Fable 5 dd5c53cd3c Trust the target to have ufw when opening SSH access
ufw is part of Omarchy's default package set, so the presence check was
guarding a machine that cannot exist -- and if it somehow did, skipping
the firewall would produce an unreachable install, which is exactly what
this phase fails loudly to prevent. The user.rules assertion already
raises in that case. With the check gone, the helper is small enough to
live inline in the phase.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-04 05:56:33 -07:00
David Heinemeier HanssonandClaude Fable 5 e8a7e3cfce Extract cidata loading into a standalone omarchy-cidata-load command
The loader now settles udev before probing, so a fast boot cannot miss
the drive and silently drop a headless install into the wizard. All the
files come over in a single cp, and a failed copy removes anything it
already copied and falls back to the wizard: the wizard cleans up only
what it writes itself, so a half-copied ssh.json would otherwise leak
into the interactive install that follows. The command takes an optional
path prefix so tests can run it against a sandbox instead of the live
system.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-04 05:56:33 -07:00
Jeffrey Guenther 7e782bb7cf Document autoinstall
Claude-Session: https://claude.ai/code/session_01Sy3HeoBqb13ahLuaiP2UkM
2026-08-04 05:54:00 -07:00
Jeffrey Guenther 5f601f2c54 Make the installed machine reachable over SSH
An autoinstall drive can carry an ssh.json of public keys. Install them
for the user, enable sshd, and open port 22.

All three are needed: Omarchy ships openssh with the service disabled,
and install/config/firewall.sh opens LocalSend and docker DNS but not
22, so keys alone leave the machine pingable with SSH timing out.

The ufw call exits non-zero from a chroot -- it cannot reach netfilter
-- but writes the rule to user.rules before failing, and that file is
what ufw.service loads on first boot. Check the file rather than the
exit status, since the file is the thing that has to be right.

Unusable key files fail the phase. A machine that installs
"successfully" and then can't be logged into is worse than one that
stops with the reason on screen.

Claude-Session: https://claude.ai/code/session_01Sy3HeoBqb13ahLuaiP2UkM
2026-08-04 05:54:00 -07:00
Jeffrey Guenther b323a4cf08 Skip the dashboard's prompts when nobody is at the keyboard
The dashboard owns the two prompts that outlive an install -- the reboot
confirm and the failure menu -- and both read from the TTY, so both hang
forever on a headless VM. Gate them on OMARCHY_UI_INTERACTIVE here, in
the one process that owns the visible UI, rather than patching prompts
out of the scripts they live in.

The existing OMARCHY_UI_AUTO_REBOOT check still guards the reboot
itself, so setting both stops on the finish screen without a keyboard.

Claude-Session: https://claude.ai/code/session_01Sy3HeoBqb13ahLuaiP2UkM
2026-08-04 05:54:00 -07:00
Jeffrey Guenther a4ec97d174 Autoinstall from a cidata drive instead of the configurator
Look for a drive labeled cidata at startup. If it carries the
configurator's own output files, copy them into /root and skip the
wizard -- everything downstream then runs the ordinary path against
ordinary inputs. No drive, no change.

The required pair matches what the orchestrator treats as mandatory, so
a drive with less than that falls back to the wizard rather than failing
the boot.

Claude-Session: https://claude.ai/code/session_01Sy3HeoBqb13ahLuaiP2UkM
2026-08-04 05:54:00 -07:00
Jeffrey Guenther f5aa88b06c Plan autoinstall against the quattro install pipeline
PR #72 targeted the pre-quattro tree and can't be rebased -- the bash
seams it hooked into are gone. This plans the same feature against the
configurator/dashboard/orchestrator split, and drops the three parts of
that PR that were flagged as questionable.

Claude-Session: https://claude.ai/code/session_01Sy3HeoBqb13ahLuaiP2UkM
2026-08-04 05:54:00 -07:00
David Heinemeier Hansson f66b6fc28e Prune unneeded packages from the ISO mirror cache 2026-08-03 12:22:49 -07:00
17532793fd [INSTALL SPEED] Configure target keyboard without temporary boot (#93)
* Configure target keyboard without temporary boot

* Slim the offline keyboard configuration to what Omarchy consumes

Drop the Xorg 00-keyboard.conf writer: the installed system only reads
the XKB* settings in vconsole.conf (via detect-keyboard-layout.sh, which
copies XKBLAYOUT/XKBVARIANT into Hyprland's kb_layout), and nothing on
an Omarchy system reads xorg.conf.d. systemd-firstboot already writes
those vconsole settings.

Drop the set_keyboard_language monkey-patch: passing an empty kb_layout
to minimal_installation takes archinstall's documented skip path (no
nspawn boot), and the keymap is configured offline right after instead.

Append the preserved FONT= line instead of reproducing localectl's exact
file layout, and slim the tests to the behavior that matters: XKB
settings present, font preserved, every configurator keymap valid, and
archinstall-compatible invalid/empty handling.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: David Heinemeier Hansson <david@hey.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-01 20:00:56 -05:00
N1R4 0b4d74f6af Filter phantom entries when rewriting EFI BootOrder (#94)
The Limine registration helper rebuilds the UEFI BootOrder from the
pre-state read, but passes every entry through verbatim. Some firmware
(listed in the BootOrder but with no matching BootXXXX variable) causes
efibootmgr --bootorder to fail with exit status 8, aborting the whole
install at the bootloader step.

Only carry forward entries that still exist as boot variables.
2026-08-01 13:07:09 -05:00
David Heinemeier HanssonandClaude Opus 5 a76f599eaa Drop the Qt5 stack from the recorded manifests
basecamp/omarchy#6431 takes kvantum-qt5 and qt5-wayland out of
omarchy-base.packages, and the rest of the stack follows them out of a
fresh install: qt5-base, qt5-declarative, qt5-svg, qt5-translations and
qt5-x11extras. kvantum stays put, moving from dependency to explicit now
that the package list names it directly instead of reaching it through
kvantum-qt5.

Hand-applied rather than re-recorded, so everything else in both files
still reflects the 2026-05-25 capture.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 11:14:40 -07:00
Ryan Hughes 64152dda3f Always makes sure the docker container is updated 2026-07-29 12:25:53 -04:00
Ryan Hughes 56eb451b92 Merge pull request #86 from omacom-io/fix-t2-kernel
Boot the T2 kernel the ISO says it boots
2026-07-28 15:52:12 -04:00
d64ffb0e00 Fix the nightly ISO build
The workflow has been running ./build_iso.sh, which has not existed for a long
time — the build moved to bin/omarchy-iso-make. Every scheduled run in the last
100 has failed.

Run the real build driver instead, with NO_BOOT_OFFER=1 so it skips the
interactive "Boot this ISO?" prompt at the end, and collect from ./release/
where the ISO actually lands. Drop the outer archlinux container: omarchy-iso-make
starts its own privileged container, so the job only needs Docker on the runner.
Add a disk cleanup step, since a ~6.5GB ISO plus a ~4.5GB offline mirror does not
fit in a stock runner's free space.

Taken from #67 by @karthikkalidas, which was opened against main. main is 143
commits behind and is no longer the default branch, and scheduled workflows only
run from the default branch, so merging it there would have fixed nothing.

Co-Authored-By: Karthik Kalidas <karthik.kalidas@gmail.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 08:50:02 -07:00
5b626d2a92 Update the copyright year
From #61 by @LukaMarinkic, applied to quattro; it was opened against main.

Co-Authored-By: LukaMarinkic <147918748+LukaMarinkic@users.noreply.github.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 08:43:43 -07:00
3efdb5c94b Reject reserved system usernames in the configurator
The username regex accepted names that already exist on the target, so
useradd failed inside arch_install_system — after the disk had been
partitioned and formatted, which is the worst point to discover it.

From #59 by @wolfgangw, applied to quattro; it was opened against main.

Co-Authored-By: Wolfgang Woehl <tito@online.de>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 08:43:43 -07:00
b3cf0c6f29 Run the live CPUs at the performance governor during install (#89)
archiso boots on whatever governor the kernel defaults to, which on most
laptops ramps conservatively. Package extraction and the UKI build are both
CPU-bound, so pin performance for the whole install and put it back afterwards.

Reduced from #87 by @davydotcom, which also disabled every non-POLL cpuidle
state. That half is left out deliberately: C1 wake latency is 1us on current
hardware, and disabling it leaves POLL as the only idle state, so idle cores
busy-spin instead of halting. Package extraction is single-threaded, so on an
8-core laptop that is 7 cores burning power for no work — on a thermally
limited machine that costs the working core its turbo headroom, which is a
plausible way to make the install slower. #87 measured the two tweaks together
with a page-cache prefetch and an updatedb deferral, so the C-state
contribution was never isolated.

Scoped to the whole install rather than just the pacstrap window, which #87
used: finalize_limine_boot runs mkinitcpio and compresses a UKI, and is the
most purely CPU-bound phase there is.

Restoring only matters when an install fails and the user keeps using the live
environment; a successful one reboots out of it.

Co-authored-by: David Estes <davydotcom@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 08:24:13 -07:00
David Heinemeier HanssonandClaude Opus 5 4f60a18c87 Drive the install bar from packages, not from a stopwatch
install_progress() mapped each phase to a start%, an end% and a hard-coded
"expected seconds", then interpolated on elapsed time. Those seconds were
calibrated on one machine, so the bar was only ever honest on that machine.
Measured against a real 69s install on an XPS 13, the table expected 145s: 95s
for a phase that took 58.6s, 10s for one that took 2.6s. On slow media it is
wrong the other way — the bar reaches each phase's ceiling in a fraction of the
real time and then sits there. Today's clamp turns that into a 386-second freeze
at 85% on a USB2 install.

Phases do not even scale together: package installation is bound by media read
and disk write, the UKI rebuild is CPU and compression bound. No single seconds
table can be right for both.

So stop predicting durations. Position is now max(floor, work), both
non-decreasing:

  floor  an asymptote over the phase's band, on time since this process first
         saw the phase. tau is a shape constant, not a prediction — wrong by 3x
         it changes the pace of a crawl, never the correctness.
  work   only for "Installing Arch + Omarchy", which is ~85% of the install.
         libalpm creates one directory per package under
         <target>/var/lib/pacman/local, so counting them counts finished work.
         The denominator is resolved at ISO build time by pacman's own resolver
         against the offline mirror, so there is no second package list to
         drift. phases.py records expected and actual in the timing JSON on
         every install, so drift shows up in acceptance runs.

There is deliberately no give-up predicate: two discontinuous curves selected by
a toggle, behind a monotonic clamp, is a freeze generator. If the signal never
appears — no denominator, wrong target, implausible count — the floor alone
still walks the band to its end.

Position moves to per-mille. That is load-bearing, not precision theatre: these
are asymptotes, and in whole percent an asymptote over a 2-point band has two
reachable values and stops moving within seconds.

Time now comes from /proc/uptime. The live ISO runs tzupdate and
systemd-timesyncd, and the old code read EPOCHSECONDS, so a clock step
mid-install jumped every band straight to its ceiling.

Also fixes drift the old table had accumulated: it still weighted five phases
that no longer exist and had no entry at all for "Preparing install target",
which fell through to a 1->99 sweep. Unknown names now derive an even band from
the index the orchestrator already publishes.

bin/omarchy-iso-installer fakes a pacman database so the preview exercises the
package path instead of the fallback; PREVIEW_PACKAGES tunes the count.

Verified by simulation against the real logic: a 69s NVMe install, a 6-minute
USB2 install with a 70s pre-extraction stall, an implausible denominator, an
unknown phase name, and a target that already holds packages. All monotonic, all
reach 100%. Not yet run on real hardware.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 07:54:05 -07:00
David Heinemeier HanssonandClaude Opus 5 0631c0530a Stop shipping a second kernel nobody boots
The live ISO boots one kernel for every machine, and after the preceding commit
that kernel is linux-t2. Stock linux is then a second kernel nobody boots, plus
its own archiso initramfs, copied into both the ISO tree and the
size-constrained FAT EFI image — about 147MB.

It could not simply be deleted: releng's broadcom-wl hard-depends on linux and
is the only releng package that does, so pacman would pull the kernel back in.

broadcom-wl is a prebuilt module for stock linux and cannot load on linux-t2.
It has kept working until now only because of the bug the preceding commit
fixes — the ISO has been booting stock all along — so it is booting T2 that
takes it away, not this commit. Dropping the package only stops shipping
something that can no longer load. Nothing in the offline install needs Wi-Fi
in the live environment.

Both packages stay in the offline mirror and this changes nothing about
installed systems: linux comes from builder/archinstall.packages and remains
what configurator's detect_kernel picks for every machine that is not a T2 Mac,
and broadcom-wl comes from omarchy-other.packages, so install/hardware/
fix-bcm43xx.sh can still install it offline on BCM4360/BCM4331 MacBooks.

The removal is anchored so linux-t2, linux-firmware, linux-firmware-marvell and
linux-atm are untouched; verified against releng's real list.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 22:27:53 -07:00
David Heinemeier HanssonandClaude Opus 5 ed710a3b41 Boot the T2 kernel the ISO says it boots
Every boot entry has named vmlinuz-linux-t2 since 0638855, but the file has
contained stock Arch. Verified against the shipped ISO:

  arch/boot/x86_64/vmlinuz-linux-t2:
    bzImage, version 7.1.5-arch1-1 (linux@archlinux)

while the mirror carries linux-t2-7.1.4-2. T2 MacBooks have been installing
with no keyboard or trackpad in the live environment.

Kernel packages ship nothing in /boot — only usr/lib/modules/<kver>/vmlinuz.
install_kernel() in /usr/share/libalpm/scripts/mkinitcpio sources
/etc/mkinitcpio.d/<pkgbase>.preset and copies THAT package's kernel to whatever
ALL_kver names, whenever is_kernelcopy() sees an unowned absolute path. Our file
was named linux.preset, so it overrode releng's copy of the same name and
pointed the STOCK kernel at the T2 filenames. linux-t2's own generated preset
writes those same two paths, and stock won.

c23e0e7 introduced it a week after 0638855 added T2 support, so the feature
worked for exactly seven days: before the preset existed, releng's linux.preset
kept the stock kernel on its own filenames and linux-t2's generated preset
produced the real thing.

The fix is the filename. releng's linux.preset takes over the stock kernel
again, which now writes /boot/vmlinuz-linux and initramfs-linux.img instead of
squatting on the T2 names — so the ISO grows by one kernel and one archiso
initramfs. That cost is not free and is worth revisiting: the stock kernel is
in the live root only because releng's broadcom-wl hard-depends on it, and
broadcom-wl is a prebuilt module for that kernel which cannot load on the T2
one we now boot. Dropping both is ~147MB and old Broadcom Macs lose live Wi-Fi.

Boot the ISO on a T2 Mac before trusting this; the version string is only
evidence that the right kernel ships, not that it works.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 22:20:15 -07:00
David Heinemeier HanssonandClaude Opus 5 89245f44ec Warm the offline mirror while the user answers the wizard
The install reads ~3GB out of the offline mirror, and pacman consumes it at
only ~33MB/s (3.14GB of packages across the 95s the package phase is calibrated
at), so on any medium slower than that the install is read-bound. The ISO medium
is idle for the minutes the wizard takes, and the target disk written to later
is a different device, so reading ahead costs nothing that is otherwise in use.

Warms largest-first under a MemAvailable/2 budget. Deliberately no dependency
resolution: asking pacman exactly which files the install will read would copy
the package-set knowledge in phases_impl.py — the early list, the runtime
target, the kernel from the wizard's JSON — into a shell script that would drift
silently. The install reads roughly 69% of the mirror, so warming blindly is
close enough for a cache hint.

The page cache this fills is clean and reclaimable, so the failure mode under
memory pressure is wasted bandwidth rather than OOM. The budget stops small
machines evicting what was just warmed. The job ends when that budget is spent
rather than when the wizard exits, because the partitioning and format window
afterwards is also free medium time.

OMARCHY_NO_PREFETCH=1 turns it off so a single ISO can measure both arms against
the Installing Arch + Omarchy entry in /var/log/omarchy-install-timing.json.
Not yet measured on real USB media, and expected to be worth nothing in the VM
harness, where the ISO is backed by the host's page cache.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 16:16:16 -07:00
David Heinemeier HanssonandClaude Opus 5 e47ce379ad Drop gum from the orchestrator's progress lines
The dashboard owns the visible UI: it captures the orchestrator's stdout into
the support log and strips CSI sequences on the way in, so the colouring was
never seen by anyone. gum charged a Go process start per line for it, on every
phase and every package group. Plain writes keep the same indented shape.

Flushing stays: pacstrap and arch-chroot inherit this fd, so buffering would
reorder our lines against theirs in the log.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 15:24:35 -07:00
David Heinemeier HanssonandClaude Opus 5 a8c3a671ea Compress the live root with zstd instead of xz
Squashfs decompresses on the page-fault path through a single stream
(CONFIG_SQUASHFS_DECOMP_SINGLE), where xz manages roughly a tenth of zstd's
throughput, and the live root is read cold on every boot: kernel, plymouth,
systemd, python, archinstall, gum. The offline mirror is already stored
uncompressed, so this only covers the part that still gets decompressed.

Costs well under a percent of ISO size. Dropping the x86 BCJ filter also
removes one of the blockers listed in plans/aarch64-support.md.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 15:24:35 -07:00
David Heinemeier HanssonandClaude Opus 5 c2b93afe01 Defer the target's UKI rebuild until the final Limine build
The existing hook mask only covers pacstrap. pacman.conf(5) HookDir is
absolute, so it never applied to the pacman runs inside arch-chroot, and during
omarchy-setup-system the target's own hooks are live. limine-entry-tool's
90-mkinitcpio-install.hook triggers on usr/lib/firmware/*, usr/src/*/dkms.conf
and usr/lib/modules/*/pkgbase, and collect_pkgbase_files sets rebuild_all for
any trigger outside usr/lib/modules — so it rebuilds the initramfs and UKI for
every installed kernel. The hardware scripts hit that routinely: sof-firmware
on Intel audio, nvidia-open-dkms, linux-ptl on Panther Lake, linux-t2 on Macs.

Every one of those builds is discarded. finalize_limine_boot runs limine-update
immediately after, which pipes "rebuild" into the same script and rebuilds
unconditionally. They are stale as well as redundant: hardware/nvidia.sh writes
its mkinitcpio drop-in after installing the driver.

Mask that one hook inside the target for the duration of the phase. The
kernel-removal hooks stay live — they only prune Limine entries, which is what
ptl-kernel.sh's "pacman -Rdd linux" needs.

A mask that survived would be invisible until a later kernel update shipped a
UKI-less boot, so it is restored in a finally, again in main()'s finally, and
validate_boot repairs and then insists the real hook is back before reboot.
limine-mkinitcpio-hook is a hard dependency of the Omarchy runtime package, so
that file is always on disk before the mask goes up.

Worth several seconds per avoided build on real hardware. It is invisible in
the VM harness, which installs no hardware packages, so measure it on an Intel
laptop rather than in CI.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 15:24:26 -07:00
David Heinemeier HanssonandClaude Opus 5 436f37f926 Explain why archinstall's zram-generator.conf gets dropped
omarchy-settings now ships the tuning as a vendor drop-in that outranks the
main config file, so setup_swap's /etc copy decides nothing and is removed only
to keep /etc free for the local admin.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LjeyQZsNBxqyYy9z8KaKm7
2026-07-25 14:00:52 -07:00
David Heinemeier HanssonandClaude Opus 5 529001d566 Validate the UKI that was actually built
Hardware packages like omarchy-hw-intel-ptl swap the kernel mid-install,
so the UKI lands under a name the configurator never asked for and boot
validation halted a perfectly good install. Read the kernels back off
/usr/lib/modules/*/pkgbase instead of trusting the requested name.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 13:05:22 -07:00
David Heinemeier HanssonandClaude Opus 4.8 78824fe084 Skip archinstall's pre-format countdown
archinstall's FilesystemHandler.perform_filesystem_operations defaults to
show_countdown=True, which runs a ~4.5s "5...4...3...2...1" warning
(_final_warning) before wiping the disk. That prompt is meant for
archinstall's interactive TUI; in our orchestrated/dashboard install it
renders nowhere and just sleeps silently on every full-disk install.

Pass show_countdown=False, guarded by an introspection check so it stays
safe on newer archinstall releases that dropped both the countdown and the
parameter. Generalize _method_accepts_users into _method_accepts for the
check.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-25 09:41:45 -07:00
David Heinemeier HanssonandClaude Opus 4.8 8653d07e06 Drop archinstall's zram-generator.conf before pacstrap
archinstall's setup_swap writes /etc/systemd/zram-generator.conf directly
into the target, but omarchy-settings now ships that file as a packaged
config. pacstrap then aborts the transaction on a file conflict
("zram-generator.conf exists in filesystem"), failing the install.

Delete archinstall's copy after setup_swap so omarchy-settings' packaged
config lands cleanly. We still keep the zram-generator package and service
that setup_swap installs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-25 07:54:04 -07:00
David Heinemeier HanssonandClaude Fable 5 5e80c917ea Skip signature checks for the offline mirror
Fresh installs on real hardware failed pacstrap with "keyring is not
writable" and "required key missing from keyring". The offline repo's
SigLevel = Optional TrustAll was not the bypass it appeared to be:
Optional verifies whenever a .sig file exists — and every package in the
offline mirror ships one — while pacstrap's pacman -r relocates the
install root but not GpgDir, so verification runs against the LIVE
keyring. That silently made every install depend on archiso's boot-time
pacman-init.service having finished: fast in QEMU, minutes-or-never on
real hardware, where pacstrap then raced it and lost.

The mirror ships inside the ISO, whose integrity is already covered by
the airootfs sha512 and the ISO signature, so pacman-level signature
verification adds nothing. Set SigLevel = Never so installs never touch
the live keyring. The target still gets its own keyring via pacstrap -K
plus the archlinux-keyring/omarchy-keyring package hooks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 15:49:29 -07:00
David Heinemeier HanssonandClaude Fable 5 f47785a196 Drop the emulated serial port from omarchy-iso-boot
QEMU's default COM1 is advertised to the guest via ACPI SPCR, the boot
stub appends console=uart,io,0x3f8 to the kernel cmdline, and Plymouth
deliberately forces its plain-text details mode whenever a serial console
is active. That made every omarchy-iso-boot test show an unthemed LUKS
prompt that real hardware (no serial port) never shows. Remove the serial
device so manual QEMU tests match real-hardware appearance.

omarchy-iso-test keeps its -serial file: logging on purpose — the harness
reads the console log and types blindly, so details mode is harmless there.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 12:18:39 -07:00
David Heinemeier HanssonandClaude Fable 5 6ac95226af Stop blocking installs on the live pacman keyring
prepare_live waited on archiso's pacman-init.service, stalling real-hardware
installs at 5% for minutes: the boot-time keyring init (gpg key generation
plus populating every keyring) is slow when reading from USB, and as a
Type=oneshot unit with no start timeout it can block systemctl start
indefinitely.

Nothing in the install consults the live keyring. The offline repo is
SigLevel = Optional TrustAll and archinstall bootstraps with pacstrap -K,
which gives the target its own independently initialized keyring. Drop the
wait and let pacman-init finish in the background for anyone using the live
environment interactively.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 11:41:23 -07:00
David Heinemeier HanssonandClaude Fable 5 f97a664ea3 Unpack the live initramfs synchronously
Kernel 7.1 unpacks the initramfs asynchronously, which races /init: the
early /proc, /sys, /dev, and /run mounts fail with EPERM while unpacking
settles. In the live environment that kills Plymouth and leaves early
archiso mounts to luck. Force synchronous unpacking on every boot entry,
matching the fix shipped for installed systems in omarchy-defaults.conf.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 11:30:55 -07:00
David Heinemeier Hansson 338e687f32 Explain Super 2026-07-23 19:56:35 -07:00
David Heinemeier Hansson 4e8ee7da9b Switch to using tips 2026-07-23 19:39:54 -07:00
David Heinemeier Hansson ca79d0f4dc Prefer local packages in local-source ISOs 2026-07-23 17:41:15 -07:00
David Heinemeier Hansson 463f862b6c Speed up offline ISO installs 2026-07-23 17:21:13 -07:00
David Heinemeier Hansson 3544261e8c Retry stale offline package downloads 2026-07-23 16:20:36 -07:00