hardened Fedora KDE; primary on Forgejo
Find a file
veilor-org dab0990e46 v0.5.32: ship 7 blockers from 9-agent wave
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.
2026-05-06 16:10:03 +01:00
.github ci(bluebuild): pin actions to node20-safe tags 2026-05-06 13:54:12 +01:00
assets v0.5.27: rd.luks.uuid via grubby, GRUB rebrand, fbcon=nodefer, ASCII gum cursor 2026-05-05 01:43:00 +01:00
bluebuild v0.7 spike: BlueBuild recipe + ostreecontainer kickstart + cosign workflow 2026-05-05 15:30:04 +01:00
build ci: switch refs from veilorveilor-org (GH org slug); domain veilor.org 2026-04-30 13:59:20 +01:00
docs docs: refine strategy — ostreecontainer install + mesh stack + browser stack 2026-05-05 15:15:52 +01:00
kickstart v0.5.32: ship 7 blockers from 9-agent wave 2026-05-06 16:10:03 +01:00
overlay v0.5.32: ship 7 blockers from 9-agent wave 2026-05-06 16:10:03 +01:00
scripts v0.5.2: move veilor-installer + veilor-firstboot to /usr/local/bin 2026-05-02 05:33:22 +01:00
test v0.5.32: ship 7 blockers from 9-agent wave 2026-05-06 16:10:03 +01:00
upstream v0.3 theme: match onyx exactly — solid black wallpaper, Linux Konsole scheme, Breeze_Light cursor 2026-04-30 17:18:14 +01:00
.gitignore chore: gitignore agent worktrees + un-track accidental embedded repos 2026-05-02 01:08:14 +01:00
CHANGELOG.md docs: CHANGELOG v0.2.0-v0.2.5, README rewrite, ROADMAP, release notes update (#5) 2026-05-02 03:42:39 +01:00
CONTRIBUTING.md ci: switch refs from veilorveilor-org (GH org slug); domain veilor.org 2026-04-30 13:59:20 +01:00
LICENSE veilor-os v0.1 scaffold — kickstart + hardening + 3-mode power + DuckSans-ready KDE black theme 2026-04-30 03:43:33 +01:00
README.md docs: refine strategy — ostreecontainer install + mesh stack + browser stack 2026-05-05 15:15:52 +01:00

veilor-os

Hardened minimal Fedora KDE spin. Black-on-black. Locked down by default.

Build veilor-os ISO License: MIT

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-postinstall helpers, 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 root reports L. There is no su - to root, ever. Use sudo.
  • No PackageKit, no Flatpak by default. Updates happen with sudo dnf upgrade on your terms, not in the background.
  • Default firewall zone is drop, not FedoraWorkstation. 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 | perf swaps 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 to save when 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.