Three-layer fix for the persistent anaconda transaction failure that
killed v0.5.28 (gen_grub_cfgstub) and v0.5.29 (aggregate dnf5 error).
## Layer 1: broad error suppression in transaction_progress.py
dnf5 under RPM 6.0 + cmdline anaconda emits a final aggregate
`error("transaction process has ended with errors..")` at end of
transaction whenever its internal failure counter > 0, regardless of
whether we suppressed individual script_error events. Reproduced
twice. The narrow patch in v0.5.29 suppressed per-package errors but
the aggregate still raised PayloadInstallationError and aborted the
install before the bootloader phase ran.
v0.5.30 patch turns the `elif token == 'error':` branch in
process_transaction_progress into a log.warning. All four producers
(cpio_error, script_error, unpack_error, generic error) now flow
through to a warning + continue. Pattern matches both the original
anaconda layout AND the v0.5.29 narrow-patched layout, so re-applying
on top of either is a no-op.
This brings us back to v0.5.28 broad-suppression behaviour. The
side effect that bit us in v0.5.28 (silent grub2-efi-x64 scriptlet
failure → empty /boot/efi/EFI/fedora/ → gen_grub_cfgstub fails)
is addressed by Layer 2 below.
## Layer 2: bootloader install moved out of anaconda
The generated install kickstart now has `bootloader --location=none`,
which tells anaconda NOT to invoke its own bootloader install code
path (and therefore NOT to call gen_grub_cfgstub). All grub work
moves into the chroot %post block:
1. `dnf reinstall grub2-efi-x64 grub2-pc grub2-tools shim-x64
efibootmgr` — re-runs scriptlets in the chroot with full
PID 1 systemd state, so the systemd-run-style triggers that
anaconda's chroot truncates actually execute.
2. `grub2-install --target=x86_64-efi --efi-directory=/boot/efi
--bootloader-id=fedora --no-nvram` — populates /boot/efi/EFI/fedora/
3. `gen_grub_cfgstub /boot/grub2 /boot/efi/EFI/fedora` (or
`grub2-mkconfig` fallback) — writes /boot/efi/EFI/fedora/grub.cfg.
4. `efibootmgr -c -d <disk> -p <part> -L "veilor-os" -l \EFI\fedora\shimx64.efi`
— registers the NVRAM boot entry pointing at the signed shim.
Each step logs to stdout and continues on failure (`set +e` block);
diagnostics surface in the install log without aborting the whole
%post.
## Layer 3: virtio-serial log capture in run-vm.sh
Anaconda 43.x autodetects `/dev/virtio-ports/org.fedoraproject.anaconda.log.0`
and streams program/packaging/storage/anaconda logs through it in
real time, before any tmpfs / pivot, before networking, surviving
kernel panic. Wiring it into run-vm.sh means the host gets a
tail-able log file at `test/anaconda-vm-YYYYMMDD-HHMMSS.log` for
every VM run.
We've lost logs three times in a row to anaconda failures + tmpfs
reboots. This breaks the loop.
## Diagnostic story
Before this commit: VM aborts → live ISO reboots itself → /tmp/
tmpfs gone → no logs → guess what failed. Three days, two and a
half false fixes.
After this commit: VM aborts → host has /home/admin/ai-lab/_github/veilor-os/test/anaconda-vm-*.log
with the actual scriptlet output, the actual exit codes, the
actual file-trigger failures. Future debug becomes evidence-based.
Files changed:
kickstart/veilor-os.ks — broad error suppression patch
overlay/usr/local/bin/veilor-installer — --location=none + manual grub
test/run-vm.sh — virtio-serial chardev wiring
Verified: bash -n clean, ksvalidator clean.
|
||
|---|---|---|
| .github | ||
| assets | ||
| build | ||
| docs | ||
| kickstart | ||
| overlay | ||
| scripts | ||
| test | ||
| upstream | ||
| .gitignore | ||
| CHANGELOG.md | ||
| CONTRIBUTING.md | ||
| LICENSE | ||
| README.md | ||
veilor-os
Hardened minimal Fedora KDE spin. Black-on-black. Locked down by default.
veilor-os is a Fedora 43 KDE Plasma remix for operators who want a clean, fast, opinionated desktop with serious hardening already wired in. Boot the ISO, set an admin password, work. No installer wizard. No initial-setup screen. No telemetry. No "would you like to enable X" prompts.
Status
Pre-release v0.2.5 — first feature-complete ISO that actually applies
the veilor-os overlay to the installed system. The build pipeline is green
on CI; the live ISO boots to KDE on KVM and bare metal. See
CHANGELOG.md for the full v0.2.0 → v0.2.5 story (it is
worth reading — five real bugs caught and documented).
What is done: hardening (SELinux, sysctl, USBGuard, fail2ban, firewalld), KDE black theme, Fira Code system font, 3-mode power management, single-prompt LUKS install, first-boot admin password flow, reproducible CI build, EFI+BIOS bootable live ISO.
What is planned (see docs/ROADMAP.md): Plymouth black theme, SDDM theme, signed ISOs (own MOK + GPG), AppArmor + nftables, veilor-update / veilor-doctor helpers, public docs site.
Quick install
# 1. Download the ISO (after public release; CI artifact for now)
sha256sum -c veilor-os-43-*.iso.sha256
# 2. Flash to USB. Replace /dev/sdX with your USB device — triple-check.
sudo dd if=veilor-os-43-*.iso of=/dev/sdX bs=4M status=progress conv=fsync
sync
# 3. Boot from USB, pick "Install veilor-os" from the menu.
# 4. Set a strong LUKS passphrase — the only prompt during install.
# 5. Reboot, remove USB.
# 6. On first boot: TTY prompts for an admin password (≥14 chars, mixed case,
# digit, symbol). Once accepted, SDDM starts. Log in as `admin`.
Full install + first-boot walkthrough: docs/INSTALL.md.
What veilor-os ships
| Layer | Hardening |
|---|---|
| Boot | Secure Boot, lockdown=integrity, slab_nomerge, randomize_kstack_offset=on, vsyscall=none. LUKS2 (aes-xts-plain64, argon2id, mem=1GB). zram swap (no disk swap, no cold-boot leak). |
| Kernel | Locked sysctls: ptrace=2, kptr_restrict=2, dmesg_restrict=1, perf_event_paranoid=3, BPF JIT hardening, full ASLR, no SUID core dumps. |
| MAC | SELinux enforcing, targeted policy + custom veilor-systemd module. |
| Network | firewalld zone = drop, ssh only inbound. systemd-resolved with DNS-over-TLS (Cloudflare/Quad9 fallback), LLMNR off. NTS-authenticated chrony time. |
| SSH | password auth off, root login off, single admin user, X11 forwarding off, MaxAuthTries 3. |
| Auth | root locked, single admin user with sudo. pwquality minlen=14, 4 character classes. First-boot password forced via chage -d 0. |
| Audit | auditd rules covering passwd/shadow/sudoers/ssh/cron/sysctl/kernel modules and all privileged binaries. |
| IDS | fail2ban with sshd + pam-generic jails, journal backend, firewalld rich-rule action. |
| USB | USBGuard daemon, default-block, empty allowlist on first boot. |
| Services off | abrt*, cups, geoclue, avahi-daemon, bluetooth, ModemManager, gssproxy, atd, pcscd, kdeconnectd, PackageKit. |
| UX | KDE Plasma minimal, BreezeBlackPure colour scheme, Fira Code system font, veilor-power save | mid | perf with udev AC/battery auto-switch. |
Full reference: docs/HARDENING.md.
60-second tour — what's different from stock Fedora KDE
- No Anaconda Initial Setup wizard after first boot. Single LUKS passphrase prompt is the entire install interaction. Admin user is pre-created; password is set once on TTY1, then SDDM starts.
- Root is locked.
passwd -S rootreportsL. There is nosu -to root, ever. Usesudo. - No PackageKit, no Flatpak by default. Updates happen with
sudo dnf upgradeon your terms, not in the background. - Default firewall zone is
drop, notFedoraWorkstation. The only thing your machine answers is sshd on its assigned port. - USBGuard blocks every USB device by default. First-boot procedure:
plug in everything you trust, run
usbguard generate-policy, done. - Black-on-black KDE. Wallpaper, panel, Konsole all match. No "white flash" anywhere in the session.
veilor-power save | mid | perfswaps the full tuned profile, CPU governor, EPP, battery threshold, and screen-dim policy in one command. Wired to AC/battery udev events too — laptop drops tosavewhen unplugged automatically.
How veilor-os compares
| Feature | veilor-os | Stock Fedora KDE | Kicksecure |
|---|---|---|---|
| SELinux enforcing OOTB | yes | yes | yes |
| AppArmor | planned (v0.5) | no | yes |
| Secure Boot | yes (Fedora keys) | yes (Fedora keys) | configurable |
| LUKS2 with argon2id | default | optional | default |
| Single-prompt install (LUKS only) | yes | no | no |
| Root account locked by default | yes | no | yes |
| firewalld default zone = drop | yes | no | n/a (uses nftables) |
| USBGuard default-block | yes | no | yes |
| fail2ban + auditd OOTB | yes | no | partial |
| DNS-over-TLS by default | yes | no | yes |
| NTS-authenticated NTP | yes | no | yes |
init_on_alloc/free (post-install) |
yes (planned re-enable) | no | yes |
| Telemetry / phone-home | none | minimal | none |
| KDE Plasma branded theme | yes (black) | Breeze | n/a (XFCE) |
| Power-profile CLI | yes (3-mode) | partial | no |
| Reproducible kickstart-built ISO | yes | yes | yes (from Debian) |
| Base distro | Fedora 43 | Fedora 43 | Debian |
veilor-os is not trying to compete with Whonix-style anonymity or Qubes-style isolation. It is a hardened daily-driver desktop — fast, clean, locked down, with no manual post-install hardening required.
Repo layout
kickstart/ veilor-os.ks full kickstart definition
build/ Containerfile + build-iso.sh reproducible ISO builder
overlay/ files dropped into installed root via %post
scripts/ hardening, SELinux policy, theme apply, firstboot
assets/ fonts, KDE colour scheme, branding, plymouth (planned)
docs/ BUILD / INSTALL / HARDENING / POWER / ROADMAP
test/ boot-checklist + KVM runner
.github/ CI workflows + PR template + CODEOWNERS
Build instructions: docs/BUILD.md. Roadmap: docs/ROADMAP.md. Contributing: CONTRIBUTING.md. Changelog: CHANGELOG.md.
License
MIT — see LICENSE. Fira Code ships from Fedora's
fira-code-fonts package under SIL OFL 1.1. Fedora packages remain
under their respective licences. Kickstart, overlay, scripts, and
docs in this repo are MIT.