veilor-os/README.md

199 lines
9.6 KiB
Markdown
Raw Normal View History

# veilor-os
> **Hardened minimal Fedora KDE spin. Black-on-black. Locked down by default.**
[![Build veilor-os ISO](https://git.s8n.ru/veilor-org/veilor-os/badges/workflows/build-iso.yml/badge.svg)](https://git.s8n.ru/veilor-org/veilor-os/actions?workflow=build-iso.yml)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
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.
docs: refine strategy — ostreecontainer install + mesh stack + browser stack Refines docs/STRATEGY.md per parent-operator handoff (2026-05-05). Locks in five things the original draft didn't cover, and corrects one mistake. ## Refinement: ostreecontainer install path The original draft proposed a two-step install: Anaconda partitions + kickstart, then on first boot a `veilor-firstboot-rebase.service` runs `bootc rebase ghcr.io/veilor/veilor-os:43`. This commit drops that step. Anaconda's `ostreecontainer --url=... --transport=registry` directive populates the root filesystem directly from the OCI image during the install pass. No first-boot rebase, no transition window, no second reboot. Same end state, simpler path. Stay on `ostreecontainer` through v0.8. Do NOT migrate to the new `bootc` kickstart command until v1.0 — it blocks multi-disk and authenticated registries. Do NOT use `bootc-image-builder anaconda-iso` output — deprecated in image-builder v44+. Produce the OCI image and the bootstrap ISO as separate artifacts. This compresses the v0.7 BlueBuild spike from 2 days → 1 day. ## Correction: keep Trivalent as default The original strategy.md treated Trivalent (secureblue's hardened Chromium) as an override-and-remove. That was wrong: Trivalent's COPR tracks upstream M147+ within hours, ships hardened_malloc + JIT-less + Drumbrake WASM. Default browser pick. Mullvad Browser layered alongside for anti-fingerprint. Thorium remains opt-in via `ujust install-thorium` only — its CVE lag is months and contradicts the threat model. Never default. ## Mesh stack baked in Three-layer warm-stack documented in STRATEGY.md: - L3a Tailscale + Headscale (Day 1, daily driver) - L3b Yggdrasil-go (Day 1, idle warm-fallback, AllowedPublicKeys mode) - L3c Reticulum/RetiNet AGPL fork (opt-in via ujust install-reticulum) Threat floor table: ISP-DNS-block (i, Day 1), ISP-Tailscale-block (ii, Phase 2 promote Yggdrasil), internet-down (iii, opt-in RetiNet + RNode). Tier model: tag:admin / tag:infra / tag:guest with failsafe pre-auth key on yubikey + paper + Authentik OIDC group. ## Onboarding Token paste / QR (user picks). Misskey signup mints reusable 24h-TTL pre-auth key. NOT auto-OIDC at first boot. ## Iroh seeding daemon stub (v0.8 / Phase 2) `veilor-seed.service` documented but NOT implemented until Iroh hits 1.0 (current 0.96–0.98 RC, Q1 2026 target slipped). BLAKE3 + iroh-gossip per-service topic. Static media only — DEFER DB replication forever. ## External dependency tracked nullstone Traefik `no-guest@file` ACL is currently 0.0.0.0/0 allow-all (XFF chain breakage 2026-05-03). Must be fixed before veilor-os first-public-ISO ships, otherwise tag:guest provisioning leaks the full vhost surface to every veilor user. Parent operator owns the fix; explicitly out of veilor-os scope. ## Files - docs/STRATEGY.md — full refinement - docs/ROADMAP.md — v0.7 spike entry now reflects ostreecontainer + mesh stack + 1-day spike target - README.md — drops the "v0.2.5 pre-release" badge + status box (out of date), adds bootc/atomic trajectory paragraph ## What did NOT change - v0.5.x main branch is untouched. The ostreecontainer swap belongs in the v0.7 spike branch, NOT v0.5.32. - nullstone Traefik config is untouched. Out of scope. - The kickstart and overlay code is untouched.
2026-05-05 15:15:52 +01:00
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](https://github.com/secureblue/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](docs/STRATEGY.md) for the full trajectory.
---
## Status
docs: refine strategy — ostreecontainer install + mesh stack + browser stack Refines docs/STRATEGY.md per parent-operator handoff (2026-05-05). Locks in five things the original draft didn't cover, and corrects one mistake. ## Refinement: ostreecontainer install path The original draft proposed a two-step install: Anaconda partitions + kickstart, then on first boot a `veilor-firstboot-rebase.service` runs `bootc rebase ghcr.io/veilor/veilor-os:43`. This commit drops that step. Anaconda's `ostreecontainer --url=... --transport=registry` directive populates the root filesystem directly from the OCI image during the install pass. No first-boot rebase, no transition window, no second reboot. Same end state, simpler path. Stay on `ostreecontainer` through v0.8. Do NOT migrate to the new `bootc` kickstart command until v1.0 — it blocks multi-disk and authenticated registries. Do NOT use `bootc-image-builder anaconda-iso` output — deprecated in image-builder v44+. Produce the OCI image and the bootstrap ISO as separate artifacts. This compresses the v0.7 BlueBuild spike from 2 days → 1 day. ## Correction: keep Trivalent as default The original strategy.md treated Trivalent (secureblue's hardened Chromium) as an override-and-remove. That was wrong: Trivalent's COPR tracks upstream M147+ within hours, ships hardened_malloc + JIT-less + Drumbrake WASM. Default browser pick. Mullvad Browser layered alongside for anti-fingerprint. Thorium remains opt-in via `ujust install-thorium` only — its CVE lag is months and contradicts the threat model. Never default. ## Mesh stack baked in Three-layer warm-stack documented in STRATEGY.md: - L3a Tailscale + Headscale (Day 1, daily driver) - L3b Yggdrasil-go (Day 1, idle warm-fallback, AllowedPublicKeys mode) - L3c Reticulum/RetiNet AGPL fork (opt-in via ujust install-reticulum) Threat floor table: ISP-DNS-block (i, Day 1), ISP-Tailscale-block (ii, Phase 2 promote Yggdrasil), internet-down (iii, opt-in RetiNet + RNode). Tier model: tag:admin / tag:infra / tag:guest with failsafe pre-auth key on yubikey + paper + Authentik OIDC group. ## Onboarding Token paste / QR (user picks). Misskey signup mints reusable 24h-TTL pre-auth key. NOT auto-OIDC at first boot. ## Iroh seeding daemon stub (v0.8 / Phase 2) `veilor-seed.service` documented but NOT implemented until Iroh hits 1.0 (current 0.96–0.98 RC, Q1 2026 target slipped). BLAKE3 + iroh-gossip per-service topic. Static media only — DEFER DB replication forever. ## External dependency tracked nullstone Traefik `no-guest@file` ACL is currently 0.0.0.0/0 allow-all (XFF chain breakage 2026-05-03). Must be fixed before veilor-os first-public-ISO ships, otherwise tag:guest provisioning leaks the full vhost surface to every veilor user. Parent operator owns the fix; explicitly out of veilor-os scope. ## Files - docs/STRATEGY.md — full refinement - docs/ROADMAP.md — v0.7 spike entry now reflects ostreecontainer + mesh stack + 1-day spike target - README.md — drops the "v0.2.5 pre-release" badge + status box (out of date), adds bootc/atomic trajectory paragraph ## What did NOT change - v0.5.x main branch is untouched. The ostreecontainer swap belongs in the v0.7 spike branch, NOT v0.5.32. - nullstone Traefik config is untouched. Out of scope. - The kickstart and overlay code is untouched.
2026-05-05 15:15:52 +01:00
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](docs/research/2026-05-05-agent-wave/README.md).
Primary git host: <https://git.s8n.ru/veilor-org/veilor-os>. The GitHub
mirror was disabled 2026-05-06; this repo is private-by-default on
Forgejo. ISO builds and CI artifacts are produced by the Forgejo runner
on nullstone — no GitHub Actions involvement.
docs: refine strategy — ostreecontainer install + mesh stack + browser stack Refines docs/STRATEGY.md per parent-operator handoff (2026-05-05). Locks in five things the original draft didn't cover, and corrects one mistake. ## Refinement: ostreecontainer install path The original draft proposed a two-step install: Anaconda partitions + kickstart, then on first boot a `veilor-firstboot-rebase.service` runs `bootc rebase ghcr.io/veilor/veilor-os:43`. This commit drops that step. Anaconda's `ostreecontainer --url=... --transport=registry` directive populates the root filesystem directly from the OCI image during the install pass. No first-boot rebase, no transition window, no second reboot. Same end state, simpler path. Stay on `ostreecontainer` through v0.8. Do NOT migrate to the new `bootc` kickstart command until v1.0 — it blocks multi-disk and authenticated registries. Do NOT use `bootc-image-builder anaconda-iso` output — deprecated in image-builder v44+. Produce the OCI image and the bootstrap ISO as separate artifacts. This compresses the v0.7 BlueBuild spike from 2 days → 1 day. ## Correction: keep Trivalent as default The original strategy.md treated Trivalent (secureblue's hardened Chromium) as an override-and-remove. That was wrong: Trivalent's COPR tracks upstream M147+ within hours, ships hardened_malloc + JIT-less + Drumbrake WASM. Default browser pick. Mullvad Browser layered alongside for anti-fingerprint. Thorium remains opt-in via `ujust install-thorium` only — its CVE lag is months and contradicts the threat model. Never default. ## Mesh stack baked in Three-layer warm-stack documented in STRATEGY.md: - L3a Tailscale + Headscale (Day 1, daily driver) - L3b Yggdrasil-go (Day 1, idle warm-fallback, AllowedPublicKeys mode) - L3c Reticulum/RetiNet AGPL fork (opt-in via ujust install-reticulum) Threat floor table: ISP-DNS-block (i, Day 1), ISP-Tailscale-block (ii, Phase 2 promote Yggdrasil), internet-down (iii, opt-in RetiNet + RNode). Tier model: tag:admin / tag:infra / tag:guest with failsafe pre-auth key on yubikey + paper + Authentik OIDC group. ## Onboarding Token paste / QR (user picks). Misskey signup mints reusable 24h-TTL pre-auth key. NOT auto-OIDC at first boot. ## Iroh seeding daemon stub (v0.8 / Phase 2) `veilor-seed.service` documented but NOT implemented until Iroh hits 1.0 (current 0.96–0.98 RC, Q1 2026 target slipped). BLAKE3 + iroh-gossip per-service topic. Static media only — DEFER DB replication forever. ## External dependency tracked nullstone Traefik `no-guest@file` ACL is currently 0.0.0.0/0 allow-all (XFF chain breakage 2026-05-03). Must be fixed before veilor-os first-public-ISO ships, otherwise tag:guest provisioning leaks the full vhost surface to every veilor user. Parent operator owns the fix; explicitly out of veilor-os scope. ## Files - docs/STRATEGY.md — full refinement - docs/ROADMAP.md — v0.7 spike entry now reflects ostreecontainer + mesh stack + 1-day spike target - README.md — drops the "v0.2.5 pre-release" badge + status box (out of date), adds bootc/atomic trajectory paragraph ## What did NOT change - v0.5.x main branch is untouched. The ostreecontainer swap belongs in the v0.7 spike branch, NOT v0.5.32. - nullstone Traefik config is untouched. Out of scope. - The kickstart and overlay code is untouched.
2026-05-05 15:15:52 +01:00
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](docs/ROADMAP.md)): Plymouth
docs: refine strategy — ostreecontainer install + mesh stack + browser stack Refines docs/STRATEGY.md per parent-operator handoff (2026-05-05). Locks in five things the original draft didn't cover, and corrects one mistake. ## Refinement: ostreecontainer install path The original draft proposed a two-step install: Anaconda partitions + kickstart, then on first boot a `veilor-firstboot-rebase.service` runs `bootc rebase ghcr.io/veilor/veilor-os:43`. This commit drops that step. Anaconda's `ostreecontainer --url=... --transport=registry` directive populates the root filesystem directly from the OCI image during the install pass. No first-boot rebase, no transition window, no second reboot. Same end state, simpler path. Stay on `ostreecontainer` through v0.8. Do NOT migrate to the new `bootc` kickstart command until v1.0 — it blocks multi-disk and authenticated registries. Do NOT use `bootc-image-builder anaconda-iso` output — deprecated in image-builder v44+. Produce the OCI image and the bootstrap ISO as separate artifacts. This compresses the v0.7 BlueBuild spike from 2 days → 1 day. ## Correction: keep Trivalent as default The original strategy.md treated Trivalent (secureblue's hardened Chromium) as an override-and-remove. That was wrong: Trivalent's COPR tracks upstream M147+ within hours, ships hardened_malloc + JIT-less + Drumbrake WASM. Default browser pick. Mullvad Browser layered alongside for anti-fingerprint. Thorium remains opt-in via `ujust install-thorium` only — its CVE lag is months and contradicts the threat model. Never default. ## Mesh stack baked in Three-layer warm-stack documented in STRATEGY.md: - L3a Tailscale + Headscale (Day 1, daily driver) - L3b Yggdrasil-go (Day 1, idle warm-fallback, AllowedPublicKeys mode) - L3c Reticulum/RetiNet AGPL fork (opt-in via ujust install-reticulum) Threat floor table: ISP-DNS-block (i, Day 1), ISP-Tailscale-block (ii, Phase 2 promote Yggdrasil), internet-down (iii, opt-in RetiNet + RNode). Tier model: tag:admin / tag:infra / tag:guest with failsafe pre-auth key on yubikey + paper + Authentik OIDC group. ## Onboarding Token paste / QR (user picks). Misskey signup mints reusable 24h-TTL pre-auth key. NOT auto-OIDC at first boot. ## Iroh seeding daemon stub (v0.8 / Phase 2) `veilor-seed.service` documented but NOT implemented until Iroh hits 1.0 (current 0.96–0.98 RC, Q1 2026 target slipped). BLAKE3 + iroh-gossip per-service topic. Static media only — DEFER DB replication forever. ## External dependency tracked nullstone Traefik `no-guest@file` ACL is currently 0.0.0.0/0 allow-all (XFF chain breakage 2026-05-03). Must be fixed before veilor-os first-public-ISO ships, otherwise tag:guest provisioning leaks the full vhost surface to every veilor user. Parent operator owns the fix; explicitly out of veilor-os scope. ## Files - docs/STRATEGY.md — full refinement - docs/ROADMAP.md — v0.7 spike entry now reflects ostreecontainer + mesh stack + 1-day spike target - README.md — drops the "v0.2.5 pre-release" badge + status box (out of date), adds bootc/atomic trajectory paragraph ## What did NOT change - v0.5.x main branch is untouched. The ostreecontainer swap belongs in the v0.7 spike branch, NOT v0.5.32. - nullstone Traefik config is untouched. Out of scope. - The kickstart and overlay code is untouched.
2026-05-05 15:15:52 +01:00
+ 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
```bash
# 1. Download the ISO from the latest Forgejo release.
# https://git.s8n.ru/veilor-org/veilor-os/releases/tag/ci-latest
# (rolling tag; replaced on each successful build-iso.yml run)
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](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](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 | secureblue |
|---|:-:|:-:|:-:|:-:|
| SELinux enforcing OOTB | yes | yes | yes | yes (custom policy) |
| AppArmor | deferred (post-v0.6 / v0.7 LSM stack) | no | yes | no |
| Secure Boot | yes (Fedora keys) | yes (Fedora keys) | configurable | yes (Fedora keys) |
| LUKS2 with argon2id | default | optional | default | default (Anaconda) |
| Single-prompt install (LUKS only) | yes | no | no | rebase via Anaconda |
| Root account locked by default | yes | no | yes | yes |
| firewalld default zone = drop | yes | no | n/a (nftables) | yes |
| USBGuard default-block | yes | no | yes | yes |
| fail2ban + auditd OOTB | yes | no | partial | partial (auditd) |
| DNS-over-TLS by default | yes | no | yes | yes |
| NTS-authenticated NTP | yes | no | yes | yes |
| `init_on_alloc/free` (post-install) | yes (planned re-enable) | no | yes | yes |
| Telemetry / phone-home | none | minimal | none | none |
| KDE Plasma branded theme | yes (black) | Breeze | n/a (XFCE) | upstream Kinoite |
| Power-profile CLI | yes (3-mode) | partial | no | no |
| Hardened browser (Trivalent / Mullvad) | yes (v0.6+) | no | no | yes (Trivalent shipped) |
| Atomic OCI image + signed base | v0.7 spike (BlueBuild) | no | no | yes (`bootc`) |
| Userns-remap default + module sig enforce | yes | no | partial | yes |
| Base distro | Fedora 43 (KDE) | Fedora 43 | Debian | Fedora atomic (Kinoite/Silverblue) |
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.
### Credit & relationship to secureblue
[secureblue](https://github.com/secureblue/secureblue) (AGPLv3) is an
upstream hardened atomic Fedora build that already solves a long list
of problems we'd otherwise reinvent: Trivalent (hardened Chromium),
custom SELinux policy, sysctl hardening, `module.sig_enforce=1`,
USBGuard defaults, libpam-pwquality config, kernel cmdline hardening,
and a full BlueBuild OCI pipeline with cosign-signed releases. The v0.7
veilor-os spike layers on top of secureblue's
`securecore-kinoite-hardened-userns` image rather than re-deriving the
same hardening from scratch.
Where veilor-os differs is the path, not the destination: a
kickstart-installed flat install for v0.5.x (operator-friendly LUKS
flow, single-prompt install), a hybrid kickstart-bootstrap +
secureblue-OCI image at v0.7, and a fully OCI/`bootc upgrade` path at
v1.0. Branding, theming, the gum installer, the 3-mode power CLI, and
the Forgejo-hosted CI/release plumbing are veilor's own work.
If a chunk of secureblue code, config, or policy ends up in veilor-os
verbatim or near-verbatim, the file carries an upstream-attribution
header and the LICENSE file in this repo records the AGPLv3 obligation
on those files. Anything we ship under MIT is original to this repo.
Thanks to the secureblue maintainers — without their public work the
v0.7 path would be a year of duplicate effort.
---
## 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](docs/BUILD.md).
Roadmap: [docs/ROADMAP.md](docs/ROADMAP.md).
Contributing: [CONTRIBUTING.md](CONTRIBUTING.md).
Changelog: [CHANGELOG.md](CHANGELOG.md).
---
## License
MIT — see [LICENSE](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.