Per docs/research/2026-05-05-agent-wave/README.md priority list.
All 7 land together to keep iteration cycles useful — partial fixes
bury the lookahead findings agents already mapped.
## 1. CRITICAL — suspend/resume wifi death (Agent 9, B2)
`veilor-modules-lock.service` runs `kernel.modules_disabled=1` 30s
after graphical.target. iwlwifi/iwlmvm/cfg80211 reload on resume
from S3/S0ix → with modules locked, resume breaks wifi until
reboot. Same architectural class as the LUKS bug — security feature
breaks legitimate kernel state transitions.
The unit already has `ConditionKernelCommandLine=!module.sig_enforce=1`
(self-skip when signed-modules enforcement is on cmdline). Adding
`module.sig_enforce=1` to the kernel cmdline retains the security
property (no unsigned modules) without runtime lock-down → resume
works.
Files: kickstart/veilor-os.ks line 61 + overlay/usr/local/bin/veilor-installer
generated bootloader directive both gain `module.sig_enforce=1`.
## 2. veilor-firstboot.service WantedBy=graphical.target (Agent 2)
Was `WantedBy=multi-user.target` only. Real installs default to
graphical.target so the unit never ran on installed systems — admin
pw stayed at install-time + chage -d 0 expired, SDDM PAM bounced
to chauthtok screen (recoverable but ugly UX).
Now `WantedBy=graphical.target multi-user.target`. Live ISO +
multi-user installs both resolve via this list.
## 3. USBGuard hash → id-based baseline (Agent 9, A3)
Mirrors memory feedback_usbguard_dock.md — onyx had hash+parent-hash
rules that broke on dock replug; we shipped no rules.conf so first
boot blocks the USB keyboard.
Adds overlay/etc/usbguard/rules.conf with HID-class allow rule
(`allow with-interface match-all { 03:*:* }`) — covers every USB
keyboard, mouse, gamepad, fingerprint reader, NFC. Survives dock
replug + kernel-bump vendor renumeration. Mass-storage stays
implicit-block; user explicitly allows post-firstboot via
`ujust veilor-usbguard-enroll` (planned v0.6).
## 4. firewalld trusted zone with tailscale0 pre-bound (Agent 9, D1)
User uses Tailscale daily (memory: project_tailscale_mesh.md).
Default firewalld zone = drop, blocks tailnet traffic on tailscale0.
Adds overlay/etc/firewalld/zones/trusted.xml with
`<interface name="tailscale0"/>`. After `tailscale up` brings the
interface up, NetworkManager dispatcher associates it with the
trusted zone automatically — no user intervention.
Default zone stays drop. Only the tailscale0 interface gets ACCEPT.
## 5. /etc/skel branding (Agent 7)
Was completely empty. Result: per-user KDE config (~/.config/kdeglobals
etc.) pre-empty, so the moment user opened System Settings, KDE wrote
fresh ~/.config/* and silently shadowed our /etc/xdg/kdedefaults/*.
Visual brand evaporated on first click.
Seeds:
/etc/skel/.config/kdeglobals (copy of assets/kde/veilor-default.kdeglobals)
/etc/skel/.config/breezerc (copy of assets/kde/breezerc)
/etc/skel/.config/kwinrc (Plasma 6 wayland defaults: opengl, animspeed=0,
blur off, click-to-focus)
/etc/skel/.config/konsolerc (default profile = Veilor)
/etc/skel/.local/share/konsole/Veilor.profile + .colorscheme
User who opens System Settings now writes against branded baseline,
not against vanilla Breeze.
## 6. KMS modeset args + initramfs keymap (Agents 1 + 9)
Real laptop boot has a 5-15s blank between vt switch and SDDM start
because simpledrm releases before i915/nvidia-drm/amdgpu claim. Plus
non-US users get locked out at LUKS prompt because initramfs ships
en-US keymap by default (RHBZ 1405539, RHBZ 1890085).
Adds to bootloader cmdline (live + installed):
i915.modeset=1 amdgpu.modeset=1 nvidia-drm.modeset=1
rd.vconsole.keymap=us
`rd.vconsole.keymap=us` is a placeholder; the v0.6 firstboot keymap
picker will rewrite it from /etc/vconsole.conf. Until then, en-US
users get correct LUKS keyboard; non-US users still need the v0.6
fix (per Agent 1).
## 7. virtio-9p log capture (Agent 6)
The v0.5.30 virtio-serial wiring depends on rsyslog inside the live
ISO (anaconda's setupVirtio writes a rsyslog forward rule), which
the live ks doesn't install — files were 0-byte across three
install runs.
test/run-vm.sh now adds a `-virtfs local,...,mount_tag=hostlogs`
share pointing at `test/test-runs/<timestamp>/`. veilor-installer
runs `_dump_logs_to_host` via EXIT trap that mounts the share at
/mnt/hostlogs and rsyncs /tmp/{anaconda,program,storage,packaging,dnf}.log
+ /var/log/veilor-installer.log + dmesg + journalctl + the generated
ks. Runs on success AND failure AND ^C.
No-op on real hardware (9p tag absent) — VM-only debug.
## Validate
bash -n overlay/usr/local/bin/veilor-installer # OK
ksvalidator kickstart/veilor-os.ks # clean
## Out-of-scope for v0.5.32 (deferred to v0.6)
Per Agent 1 follow-ups: argon2id retune for slow CPUs, recovery key
generation in firstboot, TPM2/FIDO2 unlock helpers. Per Agent 9
follow-ups: Plasma Wayland fallback X11 install, lid-close handling,
SELinux relabel progress UX. Per Agent 4: AppArmor stack +
nftables preset + audit log shipping CLI.
Per Agent 8 (CI hardening): SHA-pin actions + dependabot + SBOM +
SLSA L3 attestation — separate workflow-only commit.
|
||
|---|---|---|
| .github | ||
| assets | ||
| bluebuild | ||
| 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.
The current install path is an Anaconda kickstart with a custom gum TUI
on top. v0.7+ ships a hybrid path: the kickstart ISO becomes the bootstrap
installer (Anaconda's LUKS UX is mature), but the root filesystem is
populated directly from a cosign-signed bootc OCI image built via BlueBuild
on top of secureblue's
hardened Kinoite variant. Updates from there flow through bootc upgrade
— atomic A/B, instant rollback. v1.0 is bootc-only.
See docs/STRATEGY.md for the full trajectory.
Status
Active development on the install path. Three bug classes have been
worked through (LUKS unlock cmdline, anaconda RPM-6.0 cmdline-mode
brittleness, bootloader install via gen_grub_cfgstub); current focus
is the v0.5.32 blocker list from the
2026-05-05 9-agent research wave.
What is shipping: 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
- SDDM polish, signed ISOs (own MOK + GPG, sigstore/cosign on OCI),
AppArmor + nftables stack,
veilor-update/veilor-doctor/veilor-postinstallhelpers, public docs site, bootc OCI hybrid spike at v0.7, bootc-only at v1.0.
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.