The first screen offers English or Simplified Chinese. Chinese reruns
the configurator in cage + foot, which draw Chinese, with Noto Sans
Mono CJK unpacked from the offline mirror's noto-fonts-cjk; no font
joins the live image. There the keyboard list keeps US-keymap layouts
and preselects Chinese (Simplified, Pinyin), every later screen and
the install dashboard read in Chinese, and the target gets
LANG=zh_CN.UTF-8 through locale_config. Without a display or the
font, Chinese continues in English on the VT. The summary shows the
keyboard by its label instead of its keymap.
Needs omarchy's omarchy_prompt_language and omarchy_text in
install/provisioning/setup-form.sh.
The release script computed the ISO's sha256 and echoed it to the releaser's terminal, where nobody downloading the ISO could ever see it, and the uploader shipped the ISO and its signature but nothing to check the download against. So "verify your download" was not advice anyone could act on.
That matters more for this ISO than for most. The offline pacman mirror lives inside the airootfs and is stored uncompressed, and the installer bind-mounts it onto the target's package cache, so pacstrap reads packages straight off the medium. A flipped block there meets no decompressor and no squashfs block checksum on the way; pacman's integrity check is the first thing in the chain to notice, minutes into the install, as "invalid or corrupted package" against a package name that says nothing about the download.
The sidecar names the ISO alone rather than the build path, so it verifies from whatever directory it was downloaded into. A release that cannot write it stops, because the sidecar left over from the previous release reads as a checksum for this one. The uploader now tallies every copy instead of letting the last one decide the exit status, and warns when there is no checksum to publish.
Co-authored-by: Codex XHigh <codex@openai.com>
* Move the unit tests under test/unit
test/ is growing an integration suite; the fast, VM-free tests get their
own directory so the two kinds cannot be confused. test/all keeps its
role as the aggregate runner for everything that needs no built ISO.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Add an integration suite that boots ISO scenarios in QEMU
test/integration installs an ISO once — unattended, from a generated
cidata drive — and saves a reusable base image; each scenario in
test/integration.d/ then boots a throwaway overlay with its own copy of
the firmware vars. Shared machinery (VM lifecycle, QMP screendump + OCR
console driving, virtual keystrokes, guest SSH, the cidata build) lives
in base-test.sh, so a new scenario is one file.
The first scenario proves omarchy-system-factory-reset hands a machine
on without destroying a shared ESP: a Windows entry and a foreign Linux
with its own machine-id, boot directory, and UKIs must survive both the
staged reset and the first-boot provisioning that follows.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Let omarchy-iso-make keep the host package cache
Purging /var/cache/pacman/pkg needs interactive sudo, which blocks
unattended builds. --keep-pkg-cache skips the purge so test rigs and
agents can build an ISO without a prompt.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* 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>
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>
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>
Installs an ISO into a headless QEMU VM by driving the real interactive
install flow: each screen is read via QMP screendumps + OCR and answered
with virtual keystrokes, so the configurator wizard, install dashboard,
reboot prompt, and SDDM login are exercised exactly as a user would.
SSH access is bootstrapped by logging into a console TTY, the installed
disk is snapshotted for fast reruns, and a throwaway overlay then runs
the omarchy repo's in-guest acceptance suite over SSH.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>