oh-my-zsh's install script, even with RUNZSH=no/CHSH=no, still prompts
"Do you want to overwrite it with the Oh My Zsh template? [Y/n]" in interactive
mode — which hangs an unattended/answerfile install at the shell-setup module.
Pass --unattended to skip all prompts, and KEEP_ZSHRC=yes so it preserves the
dotfiles ~/.zshrc symlink (created earlier in this module) instead of replacing
it with the oh-my-zsh template.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R5kHioUMK3mtf2eiLEozCM
Audited every module for prompts that would hang an answerfile/unattended run.
The EWW form-factor question and several interactive-config modules were still
unguarded (only hyprlua had been fixed):
- hyprland.sh, niri.sh: skip the EWW form-factor `read` when MARCHY_UNATTENDED=1
or stdin is not a TTY, defaulting to the desktop/no-battery bar (matches hyprlua).
- mail-notmuch.sh, caldav-sync.sh: install the tools, then exit cleanly in
unattended mode instead of blocking on the account/server credential prompts —
the user configures the account after first boot.
- freeipa-server.sh: bail out early in unattended mode; FreeIPA server provisioning
is interactive and must run on a booted system (ipa-server-install needs running
services), so it can never run during the install.
freeipa-client.sh is left as-is: it has a genuine --unattended enrolment path.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R5kHioUMK3mtf2eiLEozCM
The temporary setup drop-in granted `NOPASSWD: ALL`, which covers `sudo <cmd>`
but NOT `sudo -v`. Installers run by the TUI (starship, rustup, …) call `sudo -v`
to pre-authorise, and that check still demands a password whenever the user has
any password-required sudoers entry — which they do, via the wheel rule in
10-wheel. The result was a hidden `[sudo] password for <user>:` prompt that
stalled the otherwise-unattended module install.
Add `Defaults:<user> !authenticate` to the 99-setup-nopasswd drop-in (in both
the auto and guided installers) so the auth check is skipped entirely for the
setup user; `sudo -v` and `sudo <cmd>` are now both passwordless during setup.
Verified live in a VM: `sudo -nv` for the user went from "a password is required"
to rc=0 after adding the line.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R5kHioUMK3mtf2eiLEozCM
Previously the user password (and the LUKS passphrase for encrypted installs)
were always prompted interactively, so an answerfile install could never be
fully hands-free. Add optional "password" and "luks_password" answerfile fields:
- arch-autoinstall.sh: read both via af_get; when present use them (chpasswd /
cryptsetup --key-file=- with --batch-mode and stdin-piped luksAddKey auth),
otherwise fall back to the interactive prompt. Empty/null/absent => prompt.
- generate-answerfile.sh: replace the "passwords are never stored" notice with
an optional confirmed-entry password prompt (and a LUKS one when encryption is
enabled); emit both as JSON (null when declined).
Secrets stored this way are plain text in the file (and world-readable once
embedded in an ISO) — documented in the header; decline to keep prompting.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R5kHioUMK3mtf2eiLEozCM
An answerfile/PXE install runs the TUI modules with no operator present, so any
interactive prompt or confirmation hangs the deployment forever.
- tui-install.sh: run each module in the FOREGROUND with a background `tail -f`
mirroring its log, instead of backgrounding the module. Backgrounding made
interactive module reads (e.g. the EWW form-factor question) trigger SIGTTIN
and stop; the file-based log still avoids the `| tee` pipe-inherit hang that
paused installs after a module spawned a daemon. Export MARCHY_UNATTENDED=1
in answerfile mode so modules can detect it.
- hyprlua.sh: skip the EWW form-factor `read` when MARCHY_UNATTENDED=1 or stdin
is not a TTY, defaulting to the desktop/no-battery bar; add --noconfirm to the
tablet-branch yay call.
- python.sh: add --noconfirm --needed to its `pacman -Syu` so it no longer waits
on the "Proceed?" prompt during unattended installs.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R5kHioUMK3mtf2eiLEozCM
When the TUI modules run inside the archiso installer chroot, the new
system's systemd is not the running init and there is no user session bus.
Operations like `systemctl start`, `enable --now`, `systemctl --user`, and
`gsettings` fail there and, under `set -e`, abort the whole module.
- logging.sh: add in_chroot/have_user_bus/enable_service/start_service
helpers. enable_service warns instead of aborting; start_service skips in
a chroot (unit starts on first boot via its enable symlink).
- core.sh, hyprland.sh, hyprlua.sh, niri.sh: route enables through
enable_service, starts through start_service, and guard gsettings behind
have_user_bus. Fixes the cronie-enable failure and the ly/DM setup abort.
- app modules (tlp, timeshift, open-webui, mysql, qemu, ollama, cockpit,
docker, ssh-server): convert `enable --now`/plain enables to
enable_service + start_service so they no longer abort during chroot install.
- tui-install.sh: run modules with output to a file plus a pid-bound tail,
waiting on the module PID, so a daemon child inheriting the pipe can no
longer hang the installer after a module (e.g. flatpak) finishes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R5kHioUMK3mtf2eiLEozCM
The base installers granted wheel sudo with `echo '%wheel ... ALL' >> /etc/sudoers`.
Because the stock sudoers ends with `@includedir /etc/sudoers.d`, that appended
rule is parsed AFTER the drop-ins, and since sudo applies the last matching rule,
it overrode the temporary 99-setup-nopasswd NOPASSWD rule — so the user had to
re-enter their password on every pacman/yay/flatpak call while the TUI installed
modules.
Grant wheel via /etc/sudoers.d/10-wheel instead, which sorts before
99-setup-nopasswd so NOPASSWD wins during the TUI run and password auth resumes
once the temp file is removed. Also guard that @includedir is present (so the
drop-ins are always read) and set both drop-ins to the canonical 0440 mode.
Applied to both archbaseos-guided-install.sh and arch-autoinstall.sh.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Flip their default field to "on" in modules.conf and regenerate the checklist
blocks in tui-install.sh and generate-answerfile.sh so they come pre-selected
in both the TUI installer and the answerfile generator.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Without oflag=direct, dd copies the ISO into the page cache at high speed, the
status=progress counter races to ~100%, then appears to hang at the end while
the slow USB drains and conv=fsync flushes — looking frozen when it is still
writing. write-usb.sh now writes with oflag=direct (honest device-level
progress) and falls back to a cached write if the bridge rejects O_DIRECT;
build.sh's dd hint and explanation are updated to match.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
"failed to load ldlinux.c32" when booting from USB is caused by writing the
isohybrid ISO at file level (drag-and-drop, Rufus "ISO" mode, UNetbootin), which
destroys the MBR/isolinux layout. The ISO build itself is correct — archiso's
bios.syslinux mode installs isolinux.bin + isohdpfx.bin + the c32 modules and
applies the isohybrid MBR, and the profile's bootmodes match upstream releng.
Add archiso/write-usb.sh: a safe block-level (dd) writer that lists removable
disks, refuses partitions and the system/root disk, requires an all-caps YES,
unmounts the target, then writes with conv=fsync. build.sh's completion output
now points at it and warns that file-level copies cause exactly this error.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
shellcheck passes (default severity) on the base installer and the whole
answerfile chain: generate-answerfile.sh → build.sh → .automated_script.sh →
launch.sh → arch-autoinstall.sh → tui-install.sh. Verified the generator's
JSON schema (drive, kernel, keymap, hostname, username, encrypt, fido2_root,
fido2_user, run_tui, components, desktop_environment, apps, shell_rc, colors)
parses correctly through both the auto base installer (af_get/af_bool) and the
TUI installer (load_answerfile), for full and minimal/empty answerfiles.
Fixed the three legitimate findings surfaced along the way:
- generate-answerfile.sh: drop unused AVAIL_DRIVES (SC2034); the drive list is
rendered inline in the dialog prompt.
- tui-install.sh: drop unused C_RULE (SC2034); write log truncation as
': > "$LOG"' so the redirection has an explicit command (SC2188).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
In answerfile mode the hostname now always gets the machine's MAC appended as
"<name>-<mac>" with the MAC stripped of all separators (colons/dots/dashes), so
fleet deployments from one answerfile stay unique. The default "arch" name now
also receives the suffix, and the dash is only added when a MAC is actually
found (no trailing "-" on NIC-less machines).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Flatpaks now install globally again: ensure_flatpak adds the Flathub remote at
--system and all 19 app modules use `sudo flatpak install --system`. Running
via sudo (root) performs the system op directly, avoiding the SystemHelper/
polkit D-Bus path that caused "The name is not activatable" for non-root users.
- tui-install.sh no longer prompts for or sets the hostname — the base installer
already configures it. Removed the Hostname section, the MAC-suffix helper, the
AF_HOSTNAME field and the summary line.
- archbaseos-guided-install.sh now gathers ALL input up front, including
passwords. New ask_password() prompts in clear text (by request) and requires a
confirmation entry, looping until the two match — so each password is typed
exactly twice and never again. The LUKS passphrase is captured once and fed to
luksFormat/open/luksAddKey (--key-file=-) and cryptenroll ($PASSWORD), instead
of cryptsetup prompting repeatedly. After all input, a single all-caps "type
YES" gate replaces the old per-step confirmations (answerfile mode keeps its
5-second abort window). The run-TUI choice is also asked up front.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Diagnosed from a full guided-install log plus a Hyprland startup log. Three
distinct remaining failures:
1. ~/.config left root-owned. The FIDO/Yubico setup runs `mkdir -p
~/.config/Yubico` as root (creating ~/.config itself), then chowned only
Yubico/. ~/.config stayed root-owned, so every later user step failed with
EACCES: shell-setup symlinks (starship.toml), the mail/caldav systemd --user
timers, and Hyprland creating ~/.config/hypr at startup. Chown the whole
~/.config in both Yubico spots, and defensively reclaim it in shell-setup.
2. python/wprs/plymouth/zfs sourced ../lib/logging.sh, but apps/ modules need
../../lib — so they aborted with "No such file or directory". Corrected.
3. Flatpak app modules ran `flatpak install -y` at system scope, which needs the
Flatpak SystemHelper D-Bus service + polkit (unavailable in a chroot/TTY
install) — the "The name is not activatable" failures (wireshark, xournal,
rnote, firefox-browser, …). Switch ensure_flatpak and all 19 main-flow
installs to --user scope, matching apply_flatpak_theme's --user overrides.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The core, hyprlua and hyprland package installs used backslash-continued
`pacman` commands with inline `#` comments. Bash treats the first inline
comment as the end of the command, so pacman ran with only the packages
before it and every later name (7zip, cronie, nwg-dock-hyprland, …) was
executed as a shell command — failing under `set -e`. Move all three lists
into arrays, where per-item comments are valid, and install with `--`.
Also:
- himalaya: install the official `himalaya` package (AUR `himalaya-bin` is gone).
- mail-notmuch / caldav-sync: make the systemd *user* timer setup and the
initial sync best-effort. A bare TTY/chroot install has no user session bus
(and ~/.config may not be writable yet), so `systemctl --user` and the mkdir
could abort the module; warn and continue instead.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The applications menu (~80 entries) overflowed the terminal. Render
the checklist one terminal-height page at a time, clearing and homing
the cursor each round for a stable scrollable view, with n/p paging.
Item numbering stays global so any entry can be toggled from any page.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Drop the dialog dependency entirely so the installer runs on a bare
console with only bash + coreutils. Reimplement the needed widgets
(msgbox, yesno, input, menu, checklist, form) as ui_* helpers using
read, preserving the cyberqueer magenta/cyan palette via ANSI codes
and the stdout/stderr fd convention so existing capture sites work
unchanged. Update generate-modules.sh to emit the ui_checklist form.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
opendeck is not in the official repos, so install the native AUR package
with yay (matching the repo's other AUR modules) and drop the Flatpak
path. Wire `opendeck` into Hyprland autostart instead of
`flatpak run com.mairtech.OpenDeck`, and update the idempotency guard.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Target ~/.config/hypr/usr/monitors.lua (the config Hyprland actually
loads) so `hyprctl reload` applies layouts immediately; the Dotfiles
repo copy stays the deploy source and is no longer written.
Add overlap geometry helpers and integrate them into the apply flow:
- block moves that would drive a monitor into a neighbor (TUI coords)
- snap positions to the MOVE_STEP_FINE grid to avoid frozen digits
- auto-resolve snap-induced collisions by re-reading the live layout
and nudging the moved monitor clear, up to MAX_RESOLVE_ITERS
- warn on residual overlap after apply and after save/reload
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the machine-specific DP-1/DP-3/HDMI-A-1 layout with a wildcard
catch-all rule (preferred mode, auto position, auto scale) that brings up
any connected output. Per-machine layout stays the job of monitor-manager,
which overwrites this file.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
#2 — Word-boundary match for all module patterns
Generator now pads SELECTED_APPS with spaces and uses *" id "* in
counters, summary, and dispatch, matching the conflict fix from #1.
plymouth-custom no longer false-triggers any plymouth check.
#3 — Guided installer now runs tui-install.sh
archbaseos-guided-install.sh was calling simple-install.sh; both
paths now use the full TUI (sentinel-managed, modules.conf-driven).
#4 — EFI/boot partition size unified at 10 GiB
arch-autoinstall.sh was 15 GiB, archbaseos-guided-install.sh was
5 GiB. Both now use 10 GiB.
#5 — Interactive retry for dotfiles clone (guided installer)
Clone moved outside the chroot heredoc so read() reaches the terminal.
Loops until success or the user skips; AF_MODE warns and continues.
#6 — PAM target unified on system-local-login
archbaseos-guided-install.sh was writing to system-auth (affects
sudo). Both installers now target system-local-login only.
#7 — Redundant second clone removed from autoinstaller
arch-autoinstall.sh had a second git clone inside the chroot as a
fallback that collided with the skel copy and printed a spurious
warning. Removed; skel-only approach matches the guided installer
(last updated). Also removed the individual .zshrc/.bashrc/.vimrc
cp block; aligned to the guided installer's cleaner skel structure.
#8 — Docs: remove stale plymouth core-module section
docs/md/modules.md still described plymouth under Core Modules.
Section removed; plymouth appears in Optional Applications (system
category) via the generated sentinel.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two bugs in the module-conflicts system:
1. warn() was called by the generated conflict block but never defined —
any conflict would crash with 'warn: command not found'.
Added warn() to helpers: dialog msgbox in interactive mode, logged
printf in answerfile mode.
2. Conflict patterns used substring globs (*"id"*) which caused
plymouth-custom to match the plymouth check — selecting only
plymouth-custom would trigger the conflict block, call the missing
warn(), and then remove plymouth-custom from SELECTED_APPS, leaving
no boot splash running at all.
Fixed by padding SELECTED_APPS with spaces and using *" id "* word-
boundary patterns in both the condition and the removal substitution.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Both plymouth and plymouth-custom are now optional — neither is strictly
required, so removing plymouth from the core COMPONENTS checklist and
treating it identically to plymouth-custom.
- Remove plymouth from COMPONENTS checklist, counter, summary, and dispatch
- Add plymouth back to modules.conf (default=on, excludes=plymouth-custom)
- Regenerate all sentinel regions; plymouth now appears in optional apps
checklist/summary/conflicts/dispatch alongside plymouth-custom
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
plymouth is a core component (COMPONENTS checklist), not an optional app.
Moving plymouth.sh to apps/ left the core dispatch pointing at the deleted
path; also incorrectly added it to modules.conf, duplicating it in the
optional apps checklist.
- Fix core dispatch: $MODULES/optional-Modules/plymouth.sh → $APPS/plymouth.sh
- Remove plymouth from modules.conf (plymouth-custom remains as optional app)
- Regenerate all sentinel regions; conflict block now only has plymouth-custom
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
sync-modules.sh and generate-modules.sh are developer tooling, not part of
the installer runtime — same rationale as freeipa-image.sh. Update SETUP_DIR
paths in both scripts to resolve correctly from the new location.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
apps/ is for modules that install software during setup. freeipa-image is
support tooling for the ansipa controller, run manually before installation,
so it has no place in the TUI module picker.
- git mv optional-Modules/plymouth.sh → apps/plymouth.sh
- git mv apps/freeipa-image.sh → setup/tools/freeipa-image.sh
- modules.conf: add plymouth (default=on, excludes=plymouth-custom); remove freeipa-image
- generate-modules.sh: regenerate all sentinel regions (81 → 81 active modules,
freeipa-image dropped from checklist/summary/dispatch, plymouth added with on default,
conflict block gains plymouth ↔ plymouth-custom pair)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Installs io.github.suchnsuch.Tangent via Flatpak with cyberqueer theme
applied. Registered in TUI installer, answerfile generator, and docs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Installs md.obsidian.Obsidian via Flatpak with cyberqueer theme applied.
Registered in TUI installer, answerfile generator, and docs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Installs com.github.flxzt.rnote via Flatpak with cyberqueer theme
applied. Registered in TUI installer, answerfile generator, and docs
alongside xournal++ in the Productivity section.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Installs ydotool via pacman and OpenDeck via Flatpak, wires ydotoold
and OpenDeck into the Hyprland autostart. Registers the module in the
TUI installer, answerfile generator, and docs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- plymouth.sh: accepts PLYMOUTH_LOGO_SRC env var; PNG used as-is, SVG
converted via rsvg-convert (librsvg only installed when needed)
- apps/plymouth-custom.sh: thin wrapper that validates the caller-supplied
path and delegates to plymouth.sh with PLYMOUTH_LOGO_SRC exported
- install-modules.sh: adds 'Plymouth (custom)' checklist entry; prompts
for image path via inputbox before the confirmation dialog; exports
PLYMOUTH_LOGO_SRC into the module run
- generate-answerfile.sh: adds 'plymouth' (on by default) to the
components checklist to match tui-install.sh
- docs: installation.md and modules.md updated with Plymouth component,
answerfile schema, mkinitcpio note, and custom-logo module entry
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SyBNiWy3wpawrWb9ryVk7p
Move the splash logo into resources/bg-skull.svg so it's tracked in git
and always available alongside the dotfiles. build.sh now copies
resources/ into /root/installer/resources/ on the ISO. The Plymouth
module resolves the SVG from the repo copy first, ISO copy second —
no user intervention or ~/Pictures setup required.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SyBNiWy3wpawrWb9ryVk7p
Installs a custom Plymouth theme (m-archy) with bg-skull.svg converted
to PNG (Plymouth is PNG-only via libpng — no SVG support) and a 12-dot
magenta spinner animation. Enabled by default in tui-install.sh; also
available as an optional module in install-modules.sh. Archiso image
remains Plymouth-free.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SyBNiWy3wpawrWb9ryVk7p
Steps taken:
- Added overlay/airootfs/etc/os-release with NAME="M-Archy", ID=m-archy,
ID_LIKE=arch so fastfetch and other tools show the correct distro name while
keeping pacman/AUR helpers happy via ID_LIKE.
- Added overlay/airootfs/etc/fastfetch/config.jsonc — system-wide fastfetch
config equivalent to:
--logo-type file --logo /etc/fastfetch/m-archy-SPC.txt
--logo-color-1 red --logo-color-2 red --color red
(keys + title colored red, custom file logo, no other defaults changed).
- Added overlay/airootfs/etc/fastfetch/m-archy-SPC.txt — copy of the pin.txt
ASCII logo; lands on the live ISO via the existing cp -r overlay/airootfs/
step in build.sh, no build.sh or profiledef.sh changes needed.
- Renamed resources/pin.txt → resources/m-archy-SPC.txt and updated all
references in .bashrc, .zshrc, and both kitty/bash-remoteconf files.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a dialog (interactive + answerfile) letting the user choose whether
to copy the dotfiles' .zshrc / .bashrc / .vimrc into /etc/skel, or leave
system defaults in place. The choice is persisted as shell_rc in the
answerfile JSON and respected by both the TUI installer and the generator.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
logical_width/height now divide physical pixels by scale so the canvas
correctly represents the compositor's coordinate space (e.g. 3840px @1.5x
is 2560 logical px wide, not 3840).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The merge agent reverted the switch from usr.monitors to root-level monitors
(managed by hyprmoncfg). Restore main's version and update the surrounding
comment to explain the new arrangement.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>