veilor-os/test
veilor-org b86b4f9ec3 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-05 15:36:24 +01:00
..
test-runs v0.5.27: rd.luks.uuid via grubby, GRUB rebrand, fbcon=nodefer, ASCII gum cursor 2026-05-05 01:43:00 +01:00
auto-install-keymap.sh v0.5.5: autonomous install test harness (#12) 2026-05-02 22:49:51 +01:00
auto-install.sh test/auto-install.sh: auto-fetch + reassemble chunked ISO from ci-latest 2026-05-02 22:50:37 +01:00
boot-checklist.md veilor-os v0.1 scaffold — kickstart + hardening + 3-mode power + DuckSans-ready KDE black theme 2026-04-30 03:43:33 +01:00
METHOD-CHANGELOG.md v0.5.27: rd.luks.uuid via grubby, GRUB rebrand, fbcon=nodefer, ASCII gum cursor 2026-05-05 01:43:00 +01:00
README.md v0.5.5: autonomous install test harness (#12) 2026-05-02 22:49:51 +01:00
run-vm.sh v0.5.32: ship 7 blockers from 9-agent wave 2026-05-05 15:36:24 +01:00
TESTING.md v0.5.27: rd.luks.uuid via grubby, GRUB rebrand, fbcon=nodefer, ASCII gum cursor 2026-05-05 01:43:00 +01:00

test/

Test harnesses for veilor-os ISO builds.

Files

File Purpose
run-vm.sh Manual smoke test — boot the latest ISO interactively in QEMU/KVM. SSH key injection via cloud-init seed + monitor sendkey fallback for live-image login.
auto-install.sh Autonomous end-to-end install test. Boots ISO, drives the gum installer via QEMU monitor sendkey, waits for anaconda to finish + reboot, SSHs into the installed system, runs validation checklist. Prints PASS/FAIL summary.
auto-install-keymap.sh Sourced helper. Provides km_send_str, km_send_chord, km_send_key, km_screendump, km_wait_socket, etc. Reusable by other automation.
boot-checklist.md Manual post-install checklist (run on a real spare laptop).

Running the autonomous installer test

./test/auto-install.sh build/out/veilor-os-*.iso

Hardcoded inputs (deterministic — do not edit during a test run):

  • Disk: first /dev/vda (the only disk in QEMU)
  • Hostname: veilor (installer hardcoded since v0.5.4)
  • LUKS passphrase: testpass1234
  • Admin password: adminpass1234
  • Locale: en_GB.UTF-8

Expected runtime: 2030 minutes wall clock (anaconda dominates).

Outputs

  • /tmp/veilor-auto-install.log — full driver log
  • /tmp/veilor-auto-install-NN-<step>.png — milestone screenshots
  • /tmp/veilor-auto-install-final-ssh.txt — final SSH session capture (uname/lsblk/cmdline/failed units)

Exit codes

  • 0 — all validation checks passed
  • 1 — any failure (anaconda crashed, SSH never came up, validation check failed)
  • 2 — preflight failure (missing tool, bad ISO arg, missing OVMF)

Prerequisites

  • qemu-system-x86_64, qemu-img, socat, ssh, ssh-keygen
  • edk2-ovmf (OVMF UEFI firmware at /usr/share/edk2/ovmf/OVMF_{CODE,VARS}.fd)
  • mkisofs or xorriso (for cloud-init seed ISO; harness falls back to TTY1 driving if seed cannot be built or cloud-init does not run on the installed system)
  • convert from ImageMagick (optional — converts PPM screendumps to PNG; harness keeps PPM if absent)
  • KVM access (/dev/kvm readable by the user)

What it validates

Post-install on the booted system:

  • /etc/os-releaseNAME=veilor-os
  • hostnamectl --staticveilor
  • systemctl is-activeactive for sshd fail2ban usbguard tuned auditd firewalld chronyd sddm
  • getenforceEnforcing (preferred) or Permissive (acceptable for v0.5.x)
  • lsblk -f shows crypto_LUKS + btrfs
  • /etc/crypttab has a LUKS entry
  • getent passwd admin returns the user
  • /usr/local/bin/{veilor-power,veilor-doctor,veilor-update} are present and executable
  • /proc/cmdline contains init_on_alloc=1

Troubleshooting

  • Stuck at boot banner: ISO didn't autostart veilor-installer on tty1. Check serial.log and auto-install-vm-NN-*.png screenshots. The harness aborts after 5 minutes of identical screen frames.
  • SSH never up: cloud-init may not have run on the installed system (no cidata mount). The harness falls back to TTY1 driving — typing the LUKS passphrase, logging in as admin, and hand-injecting the SSH key. If both paths fail, validation cannot proceed.
  • screendump produces unreadable PPM: install ImageMagick (dnf install ImageMagick) so the harness converts to PNG.