Verified against the actual cached mkinitcpio package (41-4), not assumed: its shipped /etc/mkinitcpio.conf template is HOOKS=(base systemd autodetect microcode modconf kms keyboard sd-vconsole block filesystems fsck) — there's no "udev" token at all on a stock install anymore, systemd is the base hook instead. This silently broke two things: - The existing plymouth-hook insertion (`sed 's/\budev\b/\0 plymouth/'`) has been a no-op on every fresh install using a current mkinitcpio package — the boot splash was never actually getting wired into the initramfs, just failing quietly (the script's best-effort `|| echo WARN` pattern doesn't catch a sed that "succeeds" by matching nothing). - My own new encrypt-hook insertion from the previous commit had the same flaw, and would have used the wrong hook regardless: `encrypt` is the udev-based hook, systemd-based initramfs needs `sd-encrypt` instead. post-install.sh now detects which base hook (systemd or udev) is actually in HOOKS once, and uses the matching hook name for both plymouth (unaffected by the choice, just needed the right anchor to insert after) and LUKS unlocking (encrypt vs sd-encrypt — genuinely different hooks). Also: avoid a redundant pacman -Sy in release-iso.yml's signing step — the job already synced repos earlier in the same container. |
||
|---|---|---|
| .forgejo/workflows | ||
| assets | ||
| bos-settings | ||
| dotfiles | ||
| iso | ||
| packaging | ||
| scripts | ||
| .gitignore | ||
| bakery.toml | ||
| build-local.sh | ||
| Cargo.lock | ||
| Cargo.toml | ||
| DESIGN.md | ||
| KEYS.asc | ||
| LICENSE | ||
| README.md | ||
BOS — Bread Operating System
An Arch-based, Hyprland desktop distribution that ships the bread ecosystem preconfigured. One Calamares install produces a themed, bootable Wayland desktop — no manual Arch bootstrap, no wiring up dotfiles, no per-tool bakery installs.
Design rationale and the btrfs/A-B roadmap live in DESIGN.md. This file is the practical overview: what's in the image, how to build it, and how to test it.
What you get
- Compositor: Hyprland with a native-Lua config (
hyprland.lua), curated keybinds, snappy animations, blur, and pywal-driven colours on a black base. - bread ecosystem, baked into
/etc/skelfrom bakery-managed binaries (no network needed at install time):bread/breadd,breadbar(status bar- notification daemon),
breadbox(launcher),breadcrumbs(Wi-Fi profiles),breadpad(notes/reminders),breadman, and thebakerypackage manager.
- notification daemon),
- bos-settings: a GTK4 control panel that configures every bread* app's config from a GUI (non-destructively), plus snapshot rollback and bakery updates. See below.
- Login: greetd + breadgreet (bread-ecosystem's own greeter, under
cage) → Hyprland session. - Boot splash: Plymouth
bostheme (logo + spinner, black background). - Theming: global dark across GTK3 (Adwaita-dark), GTK4/libadwaita
(
color-scheme: prefer-dark), and Qt (qt5ct/qt6ct Fusion dark); Papirus-Dark icons; Bibata cursor. - Apps: kitty, nautilus (+ gvfs), Zen browser, VLC, loupe, gnome-text-editor,
gnome-calculator, file-roller, with file associations wired in
mimeapps.list.yayships for AUR access beyond bakery's bread ecosystem +[breadway]. - Hardware: pipewire audio, NetworkManager, BlueZ + blueman, CUPS printing with avahi mDNS discovery, TLP power management, fwupd firmware updates.
- Resilience: btrfs + snapper + snap-pac + grub-btrfs snapshots on every pacman transaction; zram swap; ufw firewall (deny-incoming, mDNS allowed).
- Security: full-disk encryption (LUKS, via Calamares' built-in support —
cryptsetup + the matching mkinitcpio/GRUB wiring ship so an encrypted
install actually boots) and self-signed Secure Boot (via
sbctl, enrolled automatically at install time when the firmware is in Setup Mode).
Repo layout
bos/
├── Cargo.toml # workspace (members: bos-settings)
├── bos-settings/ # GTK4 unified settings app (Rust)
│ └── src/
│ ├── config/mod.rs # non-destructive toml_edit config layer
│ └── ui/{widgets,window,sidebar}.rs, ui/views/*.rs
├── iso/ # archiso profile
│ ├── profiledef.sh
│ ├── packages.x86_64 # live + installed package set
│ └── airootfs/ # files overlaid onto the image
│ └── etc/
│ ├── skel/ # default user dotfiles (hypr, kitty, gtk, …)
│ └── calamares/ # installer config + post-install.sh
├── packaging/ # in-house PKGBUILDs for AUR-only deps
│ ├── arch/ # bos-settings
│ ├── calamares/
│ └── bibata/
├── .forgejo/workflows/ # CI: build + publish packages to [breadway]
├── build-local.sh # native ISO build for this machine
└── DESIGN.md
Building the ISO
build-local.sh builds the image natively (no container) and bakes this
machine's bakery-installed bread binaries into /etc/skel:
sudo ./build-local.sh # release-quality (xz squashfs)
sudo FAST_BUILD=1 ./build-local.sh # fast dev iteration (zstd squashfs)
The ISO lands in out/bos-<date>-x86_64.iso. The script pins
SOURCE_DATE_EPOCH (reproducible UUIDs) and rewrites the [breadway] repo URL
to the Tailscale-reachable Forgejo registry for the build.
Why some packages are in-house
calamares, zen-browser-bin, bibata-cursor-theme, and yay-bin are
AUR-only. BOS keeps a PKGBUILD for each under packaging/ and republishes the
built package to the [breadway] repo via a Forgejo Actions workflow (built
on the hestia self-hosted runner, published with a scoped registry token).
bos-settings itself publishes the same way on a v* tag.
Verifying a release
Every tagged release ISO on the Forgejo releases
page ships alongside a
SHA256SUMS file and a detached signature SHA256SUMS.asc, signed by a
dedicated release-signing key (not reused from anything else):
5620 3B86 A110 695A E7F3 1093 4AF3 323D 678E B5E2
The public half is committed at KEYS.asc. To verify a download:
gpg --import KEYS.asc
gpg --verify SHA256SUMS.asc SHA256SUMS
sha256sum -c SHA256SUMS
Testing in a VM
A reusable, GPU-accelerated launcher lives at ~/bos-vm/run.sh:
~/bos-vm/run.sh install # boot the ISO installer (target disk attached)
~/bos-vm/run.sh # boot the installed system from the disk
It uses KVM + -cpu host, 8 GiB / 8 vCPU, and virtio-vga-gl with
-display gtk,gl=on (virgl) — 3D acceleration is essential for a smooth
Hyprland session in QEMU. The disk lives on NVMe (not the tmpfs /tmp) to
avoid memory pressure.
bos-settings
A GTK4 settings app aiming for GNOME-Settings-style parity: not just editing config files, but live system state and control, so day-to-day machine administration doesn't require a terminal.
Bread-ecosystem TOML configs are edited non-destructively: toml_edit
parses the file, changes only the keys a view exposes, and writes it back —
preserving comments and any keys the UI doesn't model (calendar passwords,
saved-network passwords, model paths). Panels with a daemon behind them
(bread, breadbox, breadcrumbs, breadsearch, breadclip) also get live
systemd status + Start/Stop/Restart/Logs via a shared service_control
widget, not just the config file.
| Panel | What it does |
|---|---|
| About | System info (OS/kernel/CPU/GPU/memory/disk/uptime) + hostname |
| Network | Wi-Fi scan/connect, Ethernet status, radio toggle |
| Wi-Fi Profiles (breadcrumbs) | breadcrumbs.toml — settings, saved networks, profiles |
| Firewall | ufw rules: enable/disable, add/remove, view active rules |
| Sound | PipeWire output/input device + volume via pactl |
| Power | Battery status/health, brightness, charge limits (hardware-dependent), TLP profile (read-only) |
| Date & Time | Timezone, NTP sync toggle |
| Display (Hyprland) | Connected monitors + open hyprland.lua in editor |
| Users | Add/remove accounts, change passwords |
| Wallpaper (breadpaper) | Set wallpaper, drives the pywal-derived accent palette |
| Bar (breadbar) | breadbar/style.css override, live-reloads on save |
| Launcher (breadbox) | breadbox/config.toml — launcher contexts |
| Clipboard (breadclip) | breadclipd service control + "open history" |
| Notes (breadpad) | breadpad/breadpad.toml — settings, model + ollama, reminders, calendar |
| File Search (breadsearch) | breadsearch/config.toml — index/search/model + breadmill service |
| Daemon (bread) | breadd.toml — daemon, lua, modules, adapters, events, notifications |
| Packages | bakery installed list + updates, pacman system update |
| AUR | Search via yay; installing opens a terminal (AUR build scripts need review) |
| Firmware | fwupd device list + updates |
| Snapshots | snapper list / boot-into (grub-btrfs) / delete |
Build standalone:
cargo build --release -p bos-settings
cargo test -p bos-settings # includes config round-trip tests
The bread ecosystem at a glance
| Tool | Role | Launch |
|---|---|---|
bread / breadd |
Reactive automation daemon — normalises hardware/compositor signals into events dispatched to Lua modules | runs at login |
breadbar |
Top status bar (workspaces, clock, stats, tray) and the notification daemon | runs at login |
breadbox |
Application launcher | SUPER+Space |
breadpad |
Notes & reminders (AI-classified, optional CalDAV sync) | SUPER+U |
breadman |
Package-manager UI | SUPER+M |
breadcrumbs |
Wi-Fi profile state machine (location-aware) | CLI / BOS Settings |
bakery |
CLI package manager for the ecosystem | bakery |
bos-settings |
Unified GTK4 control panel for all of the above + snapshots + updates | SUPER+, |
Keyboard shortcuts
SUPER is the Windows/Cmd key. Press SUPER+/ at any time for this
cheatsheet in-session; first boot shows a short welcome (once).
| Keys | Action |
|---|---|
SUPER+Return |
Terminal (kitty) |
SUPER+Space |
App launcher (breadbox) |
SUPER+E / SUPER+B |
Files (nautilus) / Browser (zen) |
SUPER+U / SUPER+M |
breadpad / breadman |
SUPER+, / SUPER+/ |
BOS Settings / keybind cheatsheet |
SUPER+L / SUPER+N |
Lock / log out |
SUPER+Backspace |
Close window |
SUPER+F / SUPER+V / SUPER+T |
Fullscreen / float / toggle split |
SUPER+Shift+V |
Clipboard history |
SUPER+Tab |
Last window |
SUPER+Shift+S/C/P |
Screenshot region→file / region→clipboard / screen→file |
SUPER+arrows |
Move focus |
SUPER+Shift+h/j/k/l |
Move window |
SUPER+Shift+arrows |
Resize window |
SUPER+1..0 |
Switch to workspace 1–10 |
SUPER+Shift+1..0 |
Move window to workspace |
SUPER+[ / ] |
Previous / next workspace |
SUPER+left/right-drag |
Move / resize window with the mouse |
Known limitations
- GPUs: ships the generic Mesa stack — AMD and Intel work out of the box.
The NVIDIA proprietary driver is not included; NVIDIA users must install
nvidia/nvidia-utilsand set the usual Hyprland env vars after install. - Virtual machines: Hyprland needs GPU acceleration to be smooth. Use
virtio-vga-gl+-display gtk,gl=on(virgl); plain software rendering is noticeably laggy. - Wayland-first: X11-only apps run through XWayland; a few may misbehave.
- Secure Boot: self-signed only, via
sbctl— BOS can't ship a Microsoft-signed shim (that needs going through Microsoft's own paid UEFI CA process). Post-install enrolls BOS's own keys automatically, but only when the firmware is already in Setup Mode (no vendor keys installed yet); otherwise it's skipped and you can runsudo sbctl enroll-keys --microsoft && sudo sbctl sign-all -gyourself later (after clearing your firmware's existing keys, if any). The installer writes both an NVRAM entry and the removableEFI/BOOT/BOOTX64.EFIfallback either way. - Disk encryption: full-disk LUKS is available on the installer's "Erase
disk" page (Calamares' own checkbox) and on manually-created partitions —
BOS ships the matching
cryptsetup/mkinitcpio/GRUB wiring so an encrypted install actually boots (LUKS1, since GRUB doesn't support LUKS2 + Argon2id). - Snapshots assume btrfs: the snapper/grub-btrfs tooling expects the default btrfs subvolume layout the installer creates.
Recovery
An update broke something (system still boots): open BOS Settings →
Snapshots and roll back, or pick a pre-update snapshot from the GRUB
“snapshots” submenu at boot, then run snapper rollback from the booted
snapshot.
The system won't boot (broken GRUB / lost EFI entry):
- Boot the BOS ISO and open a terminal (
SUPER+Return). - Mount the installed root and EFI, then chroot:
mount -o subvol=@ /dev/sdXN /mnt mount /dev/sdXP /mnt/boot/efi # the EFI partition arch-chroot /mnt - Reinstall the bootloader (the same sequence the installer uses):
grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=BOS --recheck grub-install --target=x86_64-efi --efi-directory=/boot/efi --removable --recheck grub-mkconfig -o /boot/grub/grub.cfg
Firmware shows “no boot device”: select EFI/BOOT/BOOTX64.EFI from the
firmware boot menu — the installer always writes that removable fallback.
Boot architecture notes
archiso keeps the kernel and initramfs outside the squashfs, so the installer
stages them explicitly: a shellprocess@kernel step copies the kernel + ucode
into the target /boot and writes a stock mkinitcpio preset before the native
initcpio module builds the initramfs. GRUB is not installed by Calamares'
bootloader/grubcfg modules (they leave the ESP empty in this layout) —
post-install.sh runs grub-install (NVRAM and --removable) +
grub-mkconfig instead, which is the sequence verified to boot.