Align ISO bake and docs with bakery/Tauri product story

Derive BREAD_BINS from iso/bread-lockfile.toml and fail the bake when a
listed binary or breadhelp content is missing. Bake bakery share files
and drop breadcast from the copied installed.json. Add WebKitGTK 4.1 for
Tauri bos-settings, remove rustup, and rewrite README/DESIGN to match the
ISO+skel tree.
This commit is contained in:
Breadway 2026-08-15 21:40:55 +08:00
parent 881ac41cbb
commit a21e81476f
11 changed files with 427 additions and 212 deletions

163
DESIGN.md
View file

@ -1,4 +1,24 @@
# BOS — Bread Operating System Plan # BOS — historical design plan
## Current architecture
**Read [README.md](README.md) for how this repo actually ships.** This file
is the original plan. Several sections below are historical and must not be
taken as current:
| Plan said | What the tree does now |
|-----------|------------------------|
| Cargo workspace with a `bos-settings/` member | This repo is ISO + Calamares + skel only. No Cargo workspace. |
| `bos-settings` as an in-tree GTK4 app | Standalone bakery product, **Tauri 2 + Svelte**. |
| bakery install in Calamares post-install | bakery binaries + breadhelp content are **baked into `/etc/skel` at ISO build time** from `iso/bread-lockfile.toml`. Missing bins fail the bake. |
| `dotfiles/` is the live skel | Live defaults are `iso/airootfs/etc/skel`. `dotfiles/` is stale. |
| A/B root swapping | **Future.** Today: btrfs + snapper + **grub-btrfs**. GRUB pins `rootflags=subvol=@`, so `snapper rollback` is not the user-facing recovery path. |
| Work on `dev`; origin = GitHub | Single-trunk `main`; `stable` is a CI marker. `origin` = Forgejo, `github` = GitHub. |
| `[breadway]` provides bakery/breadbar/bos-settings | `[breadway]` is breadlock + AUR republishes. Desktop apps are bakery. **Not shipped:** breadcast, breadarr. |
---
# Original plan (kept for history)
## Context ## Context
@ -7,51 +27,25 @@ The bread ecosystem (bread, breadbar, breadbox, breadcrumbs, breadpad/breadman,
Goals: Goals:
- **Install and be done**: Calamares GUI installer → reboot → working Hyprland + full bread stack - **Install and be done**: Calamares GUI installer → reboot → working Hyprland + full bread stack
- **Rollback safety**: Btrfs subvolumes + snapper + snap-pac; every pacman transaction is snapshotted - **Rollback safety**: Btrfs subvolumes + snapper + snap-pac; every pacman transaction is snapshotted
- **Unified config**: `bos-settings` GTK4 app surfaces all app configs + snapshot management + bakery updates - **Unified config**: `bos-settings` surfaces all app configs + snapshot management + bakery updates
- **Future-compatible**: Btrfs layout is designed to allow A/B partition migration later (SteamOS model) - **Future-compatible**: Btrfs layout is designed to allow A/B partition migration later (SteamOS model)
--- ---
## Repo Structure ## Repo Structure
Single new repo: `Breadway/bos` — a Cargo workspace. Single new repo: `Breadway/bos`*planned as* a Cargo workspace. **That is
not what landed**; see Current architecture.
``` ```
bos/ bos/
├── Cargo.toml # Workspace (members: [bos-settings]) ├── Cargo.toml # Workspace (members: [bos-settings]) — NOT in tree
├── bos-settings/ # GTK4 unified settings app ├── bos-settings/ # planned GTK4 app — now its own bakery repo
│ ├── Cargo.toml ├── iso/ # archiso profile (this is the repo)
│ └── src/
│ ├── main.rs
│ ├── state.rs
│ ├── theme.rs
│ ├── ui/
│ │ ├── window.rs # Sidebar + content shell (port breadman pattern)
│ │ ├── sidebar.rs
│ │ └── views/
│ │ ├── bread.rs
│ │ ├── breadbar.rs
│ │ ├── breadbox.rs
│ │ ├── breadcrumbs.rs
│ │ ├── breadpad.rs
│ │ ├── snapshots.rs
│ │ ├── packages.rs
│ │ └── hyprland.rs
│ └── config/
│ └── mod.rs # Per-app config loaders
├── iso/ # archiso profile
│ ├── profiledef.sh │ ├── profiledef.sh
│ ├── packages.x86_64 # Live ISO + installed system package list │ ├── packages.x86_64
│ ├── airootfs/ # Files overlaid onto live ISO root │ └── airootfs/
│ │ └── etc/ └── dotfiles/ # planned install-time configs — NOT the live skel
│ │ ├── calamares/ # Calamares YAML configuration
│ │ └── skel/ # Default user dotfiles
└── dotfiles/ # Default configs deployed at install time
├── hyprland/ # hyprland.conf, keybinds, autostart
├── bread/ # breadd.toml, init.lua, devices.lua
├── breadbar/ # (no config needed; zero-config by default)
├── breadbox/ # config.toml with default context priorities
└── breadcrumbs/ # breadcrumbs.toml with default home profile
``` ```
--- ---
@ -70,7 +64,11 @@ bos/
Mount options: `noatime,compress=zstd,space_cache=v2` on all subvolumes. Mount options: `noatime,compress=zstd,space_cache=v2` on all subvolumes.
**A/B compatibility note:** The `@` subvolume is self-contained and can be swapped atomically — this is the design property needed for a future A/B upgrade path. The layout does not need to change to adopt it. **A/B compatibility note (future):** The `@` subvolume is self-contained and
could be swapped atomically. This is a design property for a later upgrade
path. It is **not** implemented. Recovery today is reboot into a grub-btrfs
snapshot; GRUB's `rootflags=subvol=@` means a raw `snapper rollback` is the
wrong instruction to give users.
### Snapshot tooling (installed + configured during post-install) ### Snapshot tooling (installed + configured during post-install)
@ -96,100 +94,79 @@ No user-facing CLI needed for this component — `bos-settings` is the interface
### archiso profile (`iso/`) ### archiso profile (`iso/`)
- Derives from `/usr/share/archiso/configs/releng/` (the standard baseline) - Derives from `/usr/share/archiso/configs/releng/` (the standard baseline)
- `packages.x86_64` includes: base, linux, grub, btrfs-progs, snapper, snap-pac, grub-btrfs, hyprland, pipewire, wireplumber, networkmanager, gtk4, gtk4-layer-shell, iw, librsvg, libpulse, bluez, bluez-utils, calamares, calamares-qt6 - `packages.x86_64` is the live + installed pacman set (Hyprland, Calamares,
- `airootfs/etc/skel/` contains the default dotfiles (symlinked from `dotfiles/`) breadlock, WebKitGTK 4.1 for Tauri bos-settings, …). bakery apps are not
listed here.
- `airootfs/etc/skel/` contains the default user configs (this is the live
skel — not `dotfiles/`).
- Live session autologs into a `liveuser` and launches Calamares automatically - Live session autologs into a `liveuser` and launches Calamares automatically
### Calamares modules (in order) ### Calamares modules (in order)
The historical list below included a post-install `bakery install` and
Calamares `bootloader`/`grubcfg` installing GRUB. What shipped instead:
binaries are already in skel; `post-install.sh` runs `grub-install` +
`grub-mkconfig` (Calamares' bootloader modules leave the ESP empty here).
1. **welcome** — system checks (RAM ≥ 2GB, internet, disk space) 1. **welcome** — system checks (RAM ≥ 2GB, internet, disk space)
2. **locale** — timezone + locale selection 2. **locale** — timezone + locale selection
3. **keyboard** — layout selection 3. **keyboard** — layout selection
4. **partition** — custom `btrfs` mode: creates EFI partition + single btrfs pool with the subvolume layout above 4. **partition** — custom `btrfs` mode: creates EFI partition + single btrfs pool with the subvolume layout above
5. **users** — create main user, set password 5. **users** — create main user, set password
6. **packages** — install package list (reuses `packages.x86_64`) 6. **packages** — install package list (reuses `packages.x86_64`)
7. **bootloader** — install GRUB to EFI, `grub-mkconfig` with grub-btrfs hook 7. **bootloader***planned*; actual GRUB install is in `post-install.sh`
8. **shellprocess (post-install)** — runs `iso/post-install.sh`: 8. **shellprocess (post-install)** — snapper, services, copy skel; does **not** run bakery
- Configures snapper root config
- Enables services: `NetworkManager`, `bluetooth`, `breadd` (user), `breadbox-sync` (user)
- Runs `bakery install bread breadbar breadbox breadcrumbs breadpad` (or `bakery install --all`)
- Copies `dotfiles/` into `/home/$USER/.config/` (skips any file that already exists)
9. **finished** — reboot prompt 9. **finished** — reboot prompt
--- ---
## Component 3: `bos-settings` GTK4 App ## Component 3: `bos-settings` (planned as GTK4)
### Tech choices (original)
### Tech choices
- **gtk4-rs** (v0.11, v4_12 feature), no relm4 — plain GTK4 following breadman's pattern - **gtk4-rs** (v0.11, v4_12 feature), no relm4 — plain GTK4 following breadman's pattern
- **bread-theme** for palette + CSS (git dep: `github.com/Breadway/bread-ecosystem`)
- Reads/writes each tool's own config file directly (no unified intermediate config) **What shipped:** Tauri 2 + Svelte in its own repo
- Window: 960×640, sidebar 190px, `gtk4::Stack` for view switching — identical structure to breadman (`git.breadway.dev/Breadway/bos-settings`), distributed by bakery. This
repo does not build it.
### Sidebar sections + views ### Sidebar sections + views
| Section | View | What it does | The panel list is still roughly accurate; see README. Snapshots recovery
|---------|------|--------------| should send users through **grub-btrfs reboot**, not `snapper rollback N`.
| **Apps** | bread | Edit `~/.config/bread/breadd.toml` |
| | breadbar | Edit `~/.config/breadbar/` (style.css override, no TOML needed) |
| | breadbox | Edit `~/.config/breadbox/config.toml` (context priority lists) |
| | breadcrumbs | Edit `~/.config/breadcrumbs/breadcrumbs.toml` (profiles, networks) |
| | breadpad | Edit `~/.config/breadpad/breadpad.toml` (model, reminders, calendar) |
| **System** | Snapshots | `snapper list` output; rollback button calls `snapper rollback N` |
| | Packages | `bakery list --installed`; update buttons call `bakery update <pkg>` |
| | Hyprland | "Open config in editor" + monitor list from `bread.state.monitors()` |
### Config loading pattern
Each view has a dedicated `load_config(path) -> Result<T>` and `save_config(path, T) -> Result<()>` using `toml` crate. Config structs mirror each app's existing types (no duplication — import the `*-shared` crate where it exists, e.g. `breadpad-shared`). For apps without a shared crate (breadbox, breadcrumbs), define minimal local structs.
### Snapshots view specifics
- On open: runs `snapper list --output-cols number,date,description,pre-post` via `std::process::Command`, parses into table rows
- Rollback: confirmation dialog → `snapper rollback <N>` → notify user to reboot
- Delete: `snapper delete <N>`
- No write access to `/` needed for list/rollback since snapper is configured with `ALLOW_USERS` for the main user
### Packages view specifics
- On open: reads `~/.local/state/bakery/installed.json` directly (no network)
- "Check for updates": runs `bakery list` (triggers index refresh), compares versions
- "Update all": runs `bakery update --all` in a subprocess, streams stdout to a log TextView
### Distribution ### Distribution
`bos-settings` gets a `bakery.toml` and is added to the `bread-ecosystem` registry — installable standalone on any Arch/Hyprland system via `bakery install bos-settings`, not only as part of a BOS install. `bos-settings` has its own `bakery.toml` and is installable via
`bakery install bos-settings` on any Arch/Hyprland system, not only as part
of a BOS install.
--- ---
## Component 4: Default Dotfiles ## Component 4: Default Dotfiles
Minimal but functional defaults deployed at install time. These are opinionated starting points, not locked configs — users edit freely after install. Minimal but functional defaults. These live in `iso/airootfs/etc/skel`
(`hyprland.lua` + JSON binds, not `dotfiles/hyprland/*.conf`).
| File | Key content | Zero-config bakery apps survive with no extra skel files. breadcrumbs
|------|-------------| networks are user-filled after install — do not invent a full
| `dotfiles/hyprland/hyprland.conf` | Monitor auto-detect, default keybinds, `exec-once` for breadd/breadbar/breadbox-sync | `breadcrumbs.toml` in-tree.
| `dotfiles/hyprland/keybinds.conf` | `$mod+Space` → breadbox, `$mod+N` → breadpad, `$mod+M` → breadman, `$mod+S` → bos-settings |
| `dotfiles/bread/breadd.toml` | All adapters enabled, log_level=info |
| `dotfiles/bread/init.lua` | Minimal: activates "default" profile on startup |
| `dotfiles/breadbox/config.toml` | Single default context with common apps |
| `dotfiles/breadcrumbs/breadcrumbs.toml` | Placeholder home profile (user fills in SSIDs) |
--- ---
## Build Order ## Build Order
1. **Dotfiles** — write default configs; these unblock installer testing immediately Historical. The ISO profile + skel + Calamares path is what this repo
2. **Btrfs + snapper config** — write `post-install.sh`; test in a VM with `archiso` livecdbase iterates on. bos-settings is developed in its own repo.
3. **ISO profile** — archiso profiledef + package list + Calamares YAML; iterate in a VM
4. **bos-settings** — start with Snapshots and Packages views (highest value, no app-specific config parsing needed), then add per-app views one at a time
--- ---
## Verification ## Verification
- **ISO**: Build with `mkarchiso -v -w /tmp/bos-work -o /tmp/bos-out iso/`; boot in QEMU (`qemu-system-x86_64 -cdrom bos.iso -m 4G -enable-kvm`); complete install; reboot into installed system; confirm all services running and bakery packages present - **ISO**: `sudo ./build-local.sh` (not a raw `mkarchiso iso/` — the bake
step is required). Boot in QEMU; complete install; confirm bakery bins and
`~/.local/share/breadhelp/content`.
- **btrfs layout**: `btrfs subvolume list /` after install; confirm `@`, `@home`, `@snapshots`, `@log`, `@cache` exist - **btrfs layout**: `btrfs subvolume list /` after install; confirm `@`, `@home`, `@snapshots`, `@log`, `@cache` exist
- **snapper**: `snapper list`; run `pacman -Syu` and confirm two new snapshots appear - **snapper**: `snapper list`; run `pacman -Syu` and confirm two new snapshots appear
- **grub-btrfs**: Reboot and confirm snapshot submenu in GRUB - **grub-btrfs**: Reboot and confirm snapshot submenu in GRUB
- **bos-settings**: `cargo build --release`; launch; confirm each view loads its config file; edit a value, save, re-open and confirm persistence; test rollback button in Snapshots view - **bos-settings**: built and tested in the bos-settings repo, not here

168
README.md
View file

@ -5,36 +5,40 @@ ecosystem](https://git.breadway.dev/Breadway) preconfigured. One Calamares insta
produces a themed, bootable Wayland desktop — no manual Arch bootstrap, no produces a themed, bootable Wayland desktop — no manual Arch bootstrap, no
wiring up dotfiles, no per-tool bakery installs. wiring up dotfiles, no per-tool bakery installs.
> Design rationale and the btrfs/A-B roadmap live in [DESIGN.md](DESIGN.md). > This file is the product as the tree ships it. [DESIGN.md](DESIGN.md) is the
> This file is the practical overview: what's in the image, how to build it, > original plan, kept as history — several of its sections (in-tree GTK
> and how to test it. > bos-settings, bakery-at-post-install, A/B as if it were current) are not
> how the ISO works today.
## What you get ## What you get
- **Compositor**: Hyprland with a native-Lua config (`hyprland.lua`), curated - **Compositor**: Hyprland with a native-Lua config (`hyprland.lua`), curated
keybinds, snappy animations, blur, and pywal-driven colours on a black base. keybinds, snappy animations, blur, and pywal-driven colours on a black base.
- **bread ecosystem**, baked into `/etc/skel` from bakery-managed binaries - **bread ecosystem**, baked into `/etc/skel` from bakery-managed binaries
(no network needed at install time): the `bread`/`breadd` automation daemon, (no network needed at install time): the `bread`/`breadd` automation daemon
`breadbar` (status bar + notifications), `breadbox` (launcher), `breadclip` plus `bread-emit` / `bread-module-host`, `breadbar` (status bar +
(clipboard history), `breadcrumbs` (Wi-Fi profiles), `breadpad`/`breadman` notifications), `breadbox` (launcher), `breadclip` (clipboard history),
(notes), `breadpaper` (wallpaper + theme), `breadsearch` (system search), `breadcrumbs` (Wi-Fi profiles), `breadpad`/`breadman` (notes), `breadpaper`
`breadmon` (monitor layout TUI), `breadshot` (screenshots), `bread-theme` (wallpaper + theme), `breadsearch` (system search), `breadmon` (monitor
(the shared palette engine), and the `bakery` package manager. `breadlock` layout TUI), `breadshot` (screenshots), `bread-theme` (the shared palette
(lock screen + greeter) ships as its own pacman package alongside engine), `breadhelp` (onboarding + cheatsheet), `bos-settings` (control
`bos-settings`, not through bakery. See [below](#the-bread-ecosystem) for panel), and the `bakery` package manager. Most of those apps are
what each one actually does. zero-config on first boot; breadcrumbs networks are user-filled after
- **bos-settings**: a GTK4 control panel that configures every bread\* app's install. See [below](#the-bread-ecosystem).
config from a GUI (non-destructively), plus snapshot rollback and bakery - **breadlock** (lock screen + greeter) is the one bread\* app that ships as
updates. See below. **pacman**, not bakery — it needs a root-owned PAM service.
- **Login**: greetd + breadgreet (bread-ecosystem's own greeter, under `cage`) - **bos-settings**: a **Tauri 2 + Svelte** control panel (standalone bakery
→ Hyprland session. product, not a member of this repo). Configures every bread\* app
non-destructively, plus snapshots, bakery/pacman updates, and day-to-day
machine administration.
- **Login**: greetd + breadgreet (under `cage`) → Hyprland session.
- **Boot splash**: Plymouth `bos` theme (logo + spinner, black background). - **Boot splash**: Plymouth `bos` theme (logo + spinner, black background).
- **Theming**: global dark across GTK3 (Adwaita-dark), GTK4/libadwaita - **Theming**: global dark across GTK3 (Adwaita-dark), GTK4/libadwaita
(`color-scheme: prefer-dark`), and Qt (qt5ct/qt6ct Fusion dark); Papirus-Dark (`color-scheme: prefer-dark`), and Qt (qt5ct/qt6ct Fusion dark); Papirus-Dark
icons; Bibata cursor. icons; Bibata cursor.
- **Apps**: kitty, nautilus (+ gvfs), Zen browser, VLC, loupe, gnome-text-editor, - **Apps**: kitty, nautilus (+ gvfs), Zen browser, VLC, loupe, gnome-text-editor,
gnome-calculator, file-roller, with file associations wired in `mimeapps.list`. gnome-calculator, file-roller, with file associations wired in `mimeapps.list`.
`yay` ships for AUR access beyond bakery's bread ecosystem + `[breadway]`. `yay` ships for AUR access beyond bakery + `[breadway]`.
- **Hardware**: pipewire audio, NetworkManager, BlueZ + blueman, CUPS printing - **Hardware**: pipewire audio, NetworkManager, BlueZ + blueman, CUPS printing
with avahi mDNS discovery, TLP power management, fwupd firmware updates. with avahi mDNS discovery, TLP power management, fwupd firmware updates.
- **Resilience**: btrfs + snapper + snap-pac + grub-btrfs snapshots on every - **Resilience**: btrfs + snapper + snap-pac + grub-btrfs snapshots on every
@ -44,35 +48,69 @@ wiring up dotfiles, no per-tool bakery installs.
install actually boots) and self-signed Secure Boot (via `sbctl`, enrolled install actually boots) and self-signed Secure Boot (via `sbctl`, enrolled
automatically at install time when the firmware is in Setup Mode). automatically at install time when the firmware is in Setup Mode).
## What ships vs what does not
| Channel | What |
|---------|------|
| **Bakery, baked into skel** | `bakery`, `bread` / `breadd` / `bread-emit` / `bread-module-host`, `breadbar`, `breadbox` / `breadbox-sync`, `breadcrumbs`, `breadpad` / `breadman`, `breadpaper`, `bread-theme`, `breadmon`, `breadsearch` / `breadmill`, `breadclip` / `breadclipd`, `breadshot`, `bos-settings`, `breadhelp` (+ breadhelp content under `~/.local/share/breadhelp/`) |
| **pacman (`packages.x86_64`)** | `breadlock`, plus the rest of the distro (Hyprland, Calamares, Zen, …) |
| **Not shipped** | `breadcast`, `breadarr` |
The baked name list is [`iso/bread-lockfile.toml`](iso/bread-lockfile.toml).
`build-local.sh` fails if any listed binary is missing on the builder.
## Repo layout ## Repo layout
This is an **ISO + Calamares + skel** repo. There is no Cargo workspace and
no `bos-settings/` member — bos-settings and breadhelp live in their own
repos and arrive via bakery.
``` ```
bos/ 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 ├── iso/ # archiso profile
│ ├── bread-lockfile.toml # bakery bins that MUST be baked
│ ├── profiledef.sh │ ├── profiledef.sh
│ ├── packages.x86_64 # live + installed package set │ ├── packages.x86_64 # live + installed pacman set
│ └── airootfs/ # files overlaid onto the image │ └── airootfs/ # files overlaid onto the image
│ └── etc/ │ └── etc/
│ ├── skel/ # default user dotfiles (hypr, kitty, gtk, …) │ ├── skel/ # live user defaults (hypr, kitty, gtk, …)
│ └── calamares/ # installer config + post-install.sh │ └── calamares/ # installer config + post-install.sh
├── packaging/ # in-house PKGBUILDs for AUR-only deps ├── packaging/ # in-house PKGBUILDs for AUR-only deps
│ ├── arch/ # bos-settings
│ ├── calamares/ │ ├── calamares/
│ └── bibata/ │ ├── bibata/
├── .forgejo/workflows/ # CI: build + publish packages to [breadway] │ ├── powerlevel10k/
│ └── yay-bin/
├── dotfiles/ # STALE — not the live skel; see its README
├── scripts/smoke-test.sh
├── .forgejo/workflows/ # CI: AUR republish + tagged ISO release
├── build-local.sh # native ISO build for this machine ├── build-local.sh # native ISO build for this machine
└── DESIGN.md ├── README.md
└── DESIGN.md # historical plan
``` ```
Live binds are `iso/airootfs/etc/skel/.config/hypr/binds.json` (`Super+L`
`loginctl lock-session`, breadshot on `Super+Shift+S/C/P`, `Super+U`
breadpad). Do not treat `dotfiles/hypr/keybinds.conf` as current.
## Branches and remotes
Single-trunk: work on **`main`** via short-lived `feature/*` / `fix/*`
branches. **`stable`** is a marker branch CI fast-forwards to the latest
non-RC release tag — do not land work there by hand.
Dual remotes:
- **`origin`** — Forgejo (`ssh://git@100.66.238.26:2222/Breadway/bos.git`),
authoritative
- **`github`** — GitHub (`https://github.com/Breadway/bos.git`) mirror
Push `origin` (and `github` when mirroring). Do not treat origin as GitHub.
## Building the ISO ## Building the ISO
`build-local.sh` builds the image natively (no container) and bakes this `build-local.sh` builds the image natively (no container) and bakes this
machine's bakery-installed bread binaries into `/etc/skel`: machine's bakery-installed bread binaries + breadhelp content into
`/etc/skel`:
```sh ```sh
sudo ./build-local.sh # release-quality (xz squashfs) sudo ./build-local.sh # release-quality (xz squashfs)
@ -80,16 +118,20 @@ 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 The ISO lands in `out/bos-<date>-x86_64.iso`. The script pins
`SOURCE_DATE_EPOCH` (reproducible UUIDs) and rewrites the `[breadway]` repo URL `SOURCE_DATE_EPOCH` (reproducible UUIDs), rewrites the `[breadway]` repo URL
to the Tailscale-reachable Forgejo registry for the build. to the Tailscale-reachable Forgejo registry for the build, and **exits
non-zero** if any lockfile binary (or breadhelp content) is missing.
CI should populate the builder from the **stable** bakery index; local
builds still snapshot the builder. The lockfile is names only.
### Why some packages are in-house ### Why some packages are in-house
`calamares`, `zen-browser-bin`, `bibata-cursor-theme`, and `yay-bin` are `calamares`, `zen-browser-bin`, `bibata-cursor-theme`, and `yay-bin` are
AUR-only. BOS keeps a PKGBUILD for each under `packaging/` and republishes the AUR-only. BOS keeps a PKGBUILD for each under `packaging/` and republishes
built package to the `[breadway]` repo via a Forgejo Actions workflow (built the built package to the `[breadway]` repo via a Forgejo Actions workflow
on the hestia self-hosted runner, published with a scoped registry token). (built on the hestia self-hosted runner, published with a scoped registry
`bos-settings` itself publishes the same way on a `v*` tag. token). `[breadway]` is **not** where bakery/breadbar/bos-settings live.
### Verifying a release ### Verifying a release
@ -124,19 +166,21 @@ It uses KVM + `-cpu host`, 8 GiB / 8 vCPU, and `virtio-vga-gl` with
Hyprland session in QEMU. The disk lives on NVMe (not the tmpfs `/tmp`) to Hyprland session in QEMU. The disk lives on NVMe (not the tmpfs `/tmp`) to
avoid memory pressure. avoid memory pressure.
Post-install, `scripts/smoke-test.sh` (run as the installed user) checks
subvolumes, services, bakery bins, and breadhelp content under
`~/.local/share/breadhelp/content`.
## bos-settings ## bos-settings
A GTK4 settings app aiming for GNOME-Settings-style parity: not just editing Standalone bakery product: **Tauri 2 + Svelte**, not GTK4, and not built
config files, but live system state and control, so day-to-day machine from this repo. Install/update with `bakery`; the ISO just bakes whatever
administration doesn't require a terminal. binary the builder has.
Bread-ecosystem TOML configs are edited **non-destructively**: `toml_edit` It aims for GNOME-Settings-style parity: live system state and control, so
parses the file, changes only the keys a view exposes, and writes it back — day-to-day administration doesn't require a terminal. Bread-ecosystem
preserving comments and any keys the UI doesn't model (calendar passwords, configs are edited **non-destructively** (comments and unmodeled keys stay).
saved-network passwords, model paths). Panels with a daemon behind them Panels with a daemon (bread, breadbox, breadcrumbs, breadsearch, breadclip)
(bread, breadbox, breadcrumbs, breadsearch, breadclip) also get live also get live systemd status + Start/Stop/Restart/Logs.
systemd status + Start/Stop/Restart/Logs via a shared `service_control`
widget, not just the config file.
| Panel | What it does | | Panel | What it does |
|-------|--------------| |-------|--------------|
@ -161,12 +205,8 @@ widget, not just the config file.
| Firmware | `fwupd` device list + updates | | Firmware | `fwupd` device list + updates |
| Snapshots | `snapper` list / boot-into (grub-btrfs) / delete | | Snapshots | `snapper` list / boot-into (grub-btrfs) / delete |
Build standalone: Source and build live in the [bos-settings](https://git.breadway.dev/Breadway/bos-settings)
repo, not here.
```sh
cargo build --release -p bos-settings
cargo test -p bos-settings # includes config round-trip tests
```
## The bread ecosystem ## The bread ecosystem
@ -174,17 +214,17 @@ Everything below is a separate bakery-distributed project with its own repo
and release cadence, baked into `/etc/skel` at ISO build time so a fresh and release cadence, baked into `/etc/skel` at ISO build time so a fresh
install has them all with no network round-trip. Some ship more than one install has them all with no network round-trip. Some ship more than one
binary from a single package — that's noted where it applies. Most have a binary from a single package — that's noted where it applies. Most have a
corresponding **bos-settings** panel for configuration; this table is about corresponding **bos-settings** panel; this table is about *using* the app
*using* the app directly. directly.
**Desktop shell** **Desktop shell**
| Tool | Role | Launch | | Tool | Role | Launch |
|------|------|--------| |------|------|--------|
| `bread` / `breadd` | Reactive automation daemon — normalises hardware/compositor/power/network signals into events dispatched to Lua modules (`~/.config/bread/`). Everything else in the ecosystem can subscribe to its events. | runs at login (`breadd.service`) | | `bread` / `breadd` | Reactive automation daemon — normalises hardware/compositor/power/network signals into events dispatched to Lua modules (`~/.config/bread/`). `bread-emit` is the fire-and-forget helper hooks/CLIs use; `bread-module-host` is the sandboxed out-of-process module runtime breadd spawns. | runs at login (`breadd.service`) |
| `breadbar` | Top status bar: workspaces, clock, system stats, tray, **and** the notification daemon — one process, not two | runs at login | | `breadbar` | Top status bar: workspaces, clock, system stats, tray, **and** the notification daemon — one process, not two | runs at login |
| `breadbox` | Application launcher (fuzzy search, per-context results via `breadbox-sync`) | `SUPER+Space` | | `breadbox` | Application launcher (fuzzy search, per-context results via `breadbox-sync`) | `SUPER+Space` |
| `breadlock` | Idle lock screen. Also provides `breadgreet`, the login greeter hosted under `cage` via greetd — same project, two binaries, one visual identity from login to lock | `SUPER+L` (via `loginctl lock-session`, picked up by `hypridle`); `breadgreet` runs automatically at boot | | `breadlock` | Idle lock screen. Also provides `breadgreet`, the login greeter hosted under `cage` via greetd — same project, two binaries, one visual identity from login to lock. **pacman**, not bakery. | `SUPER+L` (via `loginctl lock-session`, picked up by `hypridle`); `breadgreet` runs automatically at boot |
| `bread-theme` | The shared palette engine every bread app renders through: fixed dark base colors, with only the accent slots following the current wallpaper's pywal palette. `bread-theme generate` regenerates the stylesheet; hyprland.lua calls it automatically on wallpaper change. | invoked automatically, rarely run by hand | | `bread-theme` | The shared palette engine every bread app renders through: fixed dark base colors, with only the accent slots following the current wallpaper's pywal palette. `bread-theme generate` regenerates the stylesheet; hyprland.lua calls it automatically on wallpaper change. | invoked automatically, rarely run by hand |
**Productivity** **Productivity**
@ -195,6 +235,7 @@ corresponding **bos-settings** panel for configuration; this table is about
| `breadman` | The fuller notes manager view (browse/organize) — ships from the same `breadpad` package as a second binary | `SUPER+M` | | `breadman` | The fuller notes manager view (browse/organize) — ships from the same `breadpad` package as a second binary | `SUPER+M` |
| `breadclip` | Clipboard history. `breadclipd` is the background daemon that actually records history; `breadclip` is the GTK4 popup that browses it | `SUPER+V` / `SUPER+Shift+V` | | `breadclip` | Clipboard history. `breadclipd` is the background daemon that actually records history; `breadclip` is the GTK4 popup that browses it | `SUPER+V` / `SUPER+Shift+V` |
| `breadsearch` | Semantic system-wide search (indexes files/notes, embeds locally — CPU/ROCm/CUDA backend configurable). `breadmill` is its indexing daemon. | via breadbox, or BOS Settings → File Search | | `breadsearch` | Semantic system-wide search (indexes files/notes, embeds locally — CPU/ROCm/CUDA backend configurable). `breadmill` is its indexing daemon. | via breadbox, or BOS Settings → File Search |
| `breadhelp` | Onboarding + in-session help/cheatsheet. Content lives at `~/.local/share/breadhelp/content` (bakery `content.tar.gz`, baked into skel). | `SUPER+/` |
**System** **System**
@ -210,7 +251,7 @@ corresponding **bos-settings** panel for configuration; this table is about
| Tool | Role | Launch | | Tool | Role | Launch |
|------|------|--------| |------|------|--------|
| `bakery` | CLI package manager for the whole ecosystem — install/update/list, tracks installed binaries + versions independently of pacman | `bakery` | | `bakery` | CLI package manager for the whole ecosystem — install/update/list, tracks installed binaries + versions independently of pacman | `bakery` |
| `bos-settings` | Unified GTK4 control panel: live system state + control (network, power, firewall, users, packages, firmware, AUR, snapshots) plus non-destructive config editing for every app above | `SUPER+,` | | `bos-settings` | Unified Tauri 2 + Svelte control panel: live system state + control (network, power, firewall, users, packages, firmware, AUR, snapshots) plus non-destructive config editing for every app above | `SUPER+,` |
## Keyboard shortcuts ## Keyboard shortcuts
@ -274,9 +315,16 @@ cheatsheet in-session; first boot shows a short welcome (once).
## Recovery ## Recovery
**An update broke something (system still boots):** open BOS Settings → **An update broke something (system still boots):** open BOS Settings →
Snapshots and roll back, or pick a pre-update snapshot from the **GRUB Snapshots and pick a snapshot to boot, **or** choose one from the **GRUB
“snapshots” submenu** at boot, then run `snapper rollback` from the booted “snapshots” submenu** (grub-btrfs) at boot, then reboot into it.
snapshot.
Do **not** run `snapper rollback` as the default recovery step. BOS GRUB
pins `rootflags=subvol=@`, so a snapper-swapped default subvolume is not
what the installed grub.cfg will boot next. Use the grub-btrfs entry so the
kernel command line matches the snapshot you want.
A/B root swapping (SteamOS-style) is a **future** idea in DESIGN.md — it is
not shipped.
**The system won't boot (broken GRUB / lost EFI entry):** **The system won't boot (broken GRUB / lost EFI entry):**

View file

@ -41,66 +41,210 @@ if [ "${FAST_BUILD:-0}" = "1" ]; then
fi fi
grep airootfs_image_tool_options "$STAGE/profiledef.sh" grep airootfs_image_tool_options "$STAGE/profiledef.sh"
# --- Bake this laptop's bakery-installed bread ecosystem into /etc/skel ------- # --- Bake this machine's bakery-installed bread ecosystem into /etc/skel ------
# The bread apps are managed by bakery (which fetches release binaries from # The bread desktop apps are bakery-managed (release binaries from
# GitHub), not pacman. bakery needs DNS at install time, which the live/installed # dl.breadway.dev / GitHub), not pacman. bakery needs DNS at install time,
# image doesn't have — so instead of running bakery on the target, we copy the # which the live/installed image doesn't have — so instead of running bakery
# exact binaries + bakery manifest this laptop already has into skel. Every user # on the target, we copy the binaries + bakery manifest this builder already
# created from skel (the live user and the installed user) then gets the same # has into skel. Every user created from skel then gets those versions fully
# versions `bakery list` reports here, fully offline. Copied at build time so the # offline. Copied at build time so the binaries never bloat the git repo.
# binaries never bloat the git repo and always track the current bakery state. #
BREAD_BINS=(bakery bread breadd breadman breadbar breadbox breadbox-sync breadcrumbs breadpad breadpaper bread-theme breadmon breadsearch breadmill breadclip breadclipd breadshot bos-settings breadhelp) # CI should prefer the stable bakery index when populating the builder home.
# Local builds still snapshot the builder. The lockfile is the name list;
# missing bins fail the bake (a hollow ISO is worse than a failed build).
LOCKFILE="$REPO/iso/bread-lockfile.toml"
if [[ ! -f "$LOCKFILE" ]]; then
echo "ERROR: bakery lockfile missing: $LOCKFILE" >&2
exit 1
fi
mapfile -t BREAD_BINS < <(python3 - "$LOCKFILE" <<'PY'
import sys, tomllib
path = sys.argv[1]
with open(path, "rb") as f:
data = tomllib.load(f)
bins = data.get("bins") or data.get("binaries")
if not isinstance(bins, list) or not bins:
sys.exit(f"{path}: missing non-empty bins list")
for b in bins:
if not isinstance(b, str) or not b or "/" in b or b in (".", ".."):
sys.exit(f"{path}: invalid bin name {b!r}")
print(b)
PY
)
if [[ ${#BREAD_BINS[@]} -eq 0 ]]; then
echo "ERROR: $LOCKFILE produced an empty bins list" >&2
exit 1
fi
LAPTOP_HOME="${LAPTOP_HOME:-$(getent passwd "${SUDO_USER:-$USER}" | cut -d: -f6)}" LAPTOP_HOME="${LAPTOP_HOME:-$(getent passwd "${SUDO_USER:-$USER}" | cut -d: -f6)}"
BAKERY_BIN="$LAPTOP_HOME/.local/bin" BAKERY_BIN="$LAPTOP_HOME/.local/bin"
BAKERY_STATE="$LAPTOP_HOME/.local/state/bakery" BAKERY_STATE="$LAPTOP_HOME/.local/state/bakery"
BAKERY_CACHE="$LAPTOP_HOME/.cache/bakery" BAKERY_CACHE="$LAPTOP_HOME/.cache/bakery"
BAKERY_SHARE="$LAPTOP_HOME/.local/share"
SKEL="$STAGE/airootfs/etc/skel" SKEL="$STAGE/airootfs/etc/skel"
echo "=== baking bakery bread ecosystem from $LAPTOP_HOME ===" echo "=== baking bakery bread ecosystem from $LAPTOP_HOME ==="
echo "lockfile: $LOCKFILE (${#BREAD_BINS[@]} bins)"
missing=()
for b in "${BREAD_BINS[@]}"; do
if [[ ! -x "$BAKERY_BIN/$b" ]]; then
missing+=("$BAKERY_BIN/$b")
fi
done
if [[ ${#missing[@]} -gt 0 ]]; then
echo "ERROR: bakery lockfile requires binaries that are missing on the builder:" >&2
printf ' %s\n' "${missing[@]}" >&2
echo "Install them with bakery (or stage them under $BAKERY_BIN) before baking." >&2
echo "A hollow ISO is worse than a failed build." >&2
exit 1
fi
install -d -m 0755 "$SKEL/.local/bin" "$SKEL/.local/state/bakery" "$SKEL/.cache/bakery" install -d -m 0755 "$SKEL/.local/bin" "$SKEL/.local/state/bakery" "$SKEL/.cache/bakery"
for b in "${BREAD_BINS[@]}"; do for b in "${BREAD_BINS[@]}"; do
install -m 0755 "$BAKERY_BIN/$b" "$SKEL/.local/bin/$b" install -m 0755 "$BAKERY_BIN/$b" "$SKEL/.local/bin/$b"
done done
install -m 0644 "$BAKERY_STATE/installed.json" "$SKEL/.local/state/bakery/installed.json"
# Drop packages that are not in the lockfile (breadcast/breadarr must not
# appear installed when their binaries were deliberately left out).
python3 - "$BAKERY_STATE/installed.json" "$SKEL/.local/state/bakery/installed.json" "${BREAD_BINS[@]}" <<'PY'
import json, sys
src, dest, *bins = sys.argv[1:]
wanted = set(bins)
with open(src) as f:
data = json.load(f)
pkgs = data.get("packages", data)
if not isinstance(pkgs, dict):
sys.exit(f"{src}: expected packages object")
kept = {}
for name, pkg in pkgs.items():
pbins = pkg.get("binaries") or []
if name in wanted or any(b in wanted for b in pbins):
kept[name] = pkg
out = {"packages": kept}
if "track" in data:
out["track"] = data["track"]
with open(dest, "w") as f:
json.dump(out, f, indent=2)
f.write("\n")
print("installed.json packages:", ", ".join(sorted(kept)) or "(none)")
PY
# bakery fetches its package index from dl.breadway.dev (then a GitHub fallback), # bakery fetches its package index from dl.breadway.dev (then a GitHub fallback),
# but falls back to a cached index when both are unreachable. With no network/DNS # but falls back to a cached index when both are unreachable. With no network/DNS
# in the live/installed image, even `bakery list` errors unless that cache exists, # in the live/installed image, even `bakery list` errors unless that cache exists,
# so bake it in too — then bakery works fully offline (list/info from cache; # so bake it in too — then bakery works fully offline (list/info from cache;
# install/update still need network, as expected). # install/update still need network, as expected).
if [[ ! -f "$BAKERY_CACHE/index.json" ]]; then
echo "ERROR: bakery index cache missing: $BAKERY_CACHE/index.json" >&2
exit 1
fi
install -m 0644 "$BAKERY_CACHE/index.json" "$SKEL/.cache/bakery/index.json" install -m 0644 "$BAKERY_CACHE/index.json" "$SKEL/.cache/bakery/index.json"
echo "baked: $(ls "$SKEL/.local/bin")" echo "baked bins: $(ls "$SKEL/.local/bin")"
# --- Bake bakery data dirs the apps need offline ------------------------------
# bakery extracts data_archive (breadhelp's content.tar.gz) to
# ~/.local/share/<pkg>/ and writes desktop entries + licenses next to it.
# Copy those — never laptop-local state (clipboard history, WebKit cache,
# bread sync-repo, models).
echo "=== baking bakery share/data into skel ==="
BREADHELP_CONTENT="$BAKERY_SHARE/breadhelp/content"
if [[ ! -d "$BREADHELP_CONTENT" ]]; then
echo "ERROR: breadhelp content missing: $BREADHELP_CONTENT" >&2
echo "bakery installs this from content.tar.gz into ~/.local/share/breadhelp/content" >&2
echo "A breadhelp binary without content is a hollow ISO." >&2
exit 1
fi
install -d -m 0755 "$SKEL/.local/share"
cp -a "$BAKERY_SHARE/breadhelp" "$SKEL/.local/share/breadhelp"
echo " baked $SKEL/.local/share/breadhelp/content"
python3 - "$BAKERY_CACHE/index.json" "$BAKERY_SHARE" "$SKEL/.local/share" "${BREAD_BINS[@]}" <<'PY'
import json, os, shutil, sys
index_path, src_share, dest_share, *bins = sys.argv[1:]
wanted = set(bins)
try:
with open(index_path) as f:
idx = json.load(f)
packages = idx.get("packages", {})
except (OSError, json.JSONDecodeError):
packages = {}
# Package names we ship: lockfile bin names plus index packages that
# publish at least one of those bins.
pkg_names = set(wanted)
for name, pkg in packages.items():
pbins = []
for b in pkg.get("binaries") or []:
n = b["name"] if isinstance(b, dict) else b
pbins.append(str(n).removesuffix("-x86_64"))
if name in wanted or any(b in wanted for b in pbins):
pkg_names.add(name)
os.makedirs(os.path.join(dest_share, "applications"), exist_ok=True)
os.makedirs(os.path.join(dest_share, "licenses"), exist_ok=True)
for name in sorted(pkg_names):
pkg = packages.get(name) or {}
if pkg.get("data_archive"):
src = os.path.join(src_share, name)
dest = os.path.join(dest_share, name)
if name == "breadhelp":
continue # already copied above, required
if os.path.isdir(src):
if os.path.exists(dest):
shutil.rmtree(dest)
shutil.copytree(src, dest, symlinks=True)
print(f" baked data dir {dest}")
else:
sys.exit(f"ERROR: bakery data_archive for {name} missing at {src}")
desktop_src = os.path.join(src_share, "applications", f"{name}.desktop")
desktop_dest = os.path.join(dest_share, "applications", f"{name}.desktop")
if os.path.isfile(desktop_src) and not os.path.isfile(desktop_dest):
shutil.copy2(desktop_src, desktop_dest)
print(f" baked desktop {desktop_dest}")
lic_src = os.path.join(src_share, "licenses", name)
lic_dest = os.path.join(dest_share, "licenses", name)
if os.path.isdir(lic_src) and not os.path.isdir(lic_dest):
shutil.copytree(lic_src, lic_dest, symlinks=True)
print(f" baked license {lic_dest}")
PY
# --- Bake systemd user services for bakery-managed bread packages ----------- # --- Bake systemd user services for bakery-managed bread packages -----------
# Historically only breadd.service was hand-committed to skel; every other # Historically only breadd.service was hand-committed to skel; every other
# bakery package's service (breadbox-sync, breadmill, breadclipd, ...) was # bakery package's service (breadbox-sync, breadmill, breadclipd, ...) was
# silently left out, so those daemons never start on a fresh install/live # silently left out, so those daemons never start on a fresh install/live
# boot until the user re-runs `bakery install` (which needs network). # boot until the user re-runs `bakery install` (which needs network).
# Generalize from the same source of truth as the binary bake above: read # Source of truth is the *filtered* installed.json we just wrote: only
# the services this laptop's bakery actually installed, copy each unit file # lockfile packages. Copy each unit with ExecStart rewritten from this
# into skel with ExecStart rewritten from this laptop's literal home path to # laptop's literal home path to the portable `%h` specifier, and recreate
# the portable `%h` specifier, and recreate whichever *.target.wants enable # whichever *.target.wants enable symlink bakery created locally. Units
# symlink bakery created locally. Units already committed by hand (breadd.service # already committed by hand (breadd.service carries a
# carries a RuntimeDirectoryPreserve=yes fix not yet upstreamed — see bread-release-build # RuntimeDirectoryPreserve=yes fix not yet upstreamed) are left alone.
# notes) are left alone rather than overwritten.
echo "=== baking bakery service units into skel ===" echo "=== baking bakery service units into skel ==="
SYSTEMD_USER_DIR="$LAPTOP_HOME/.config/systemd/user" SYSTEMD_USER_DIR="$LAPTOP_HOME/.config/systemd/user"
SKEL_SYSTEMD="$SKEL/.config/systemd/user" SKEL_SYSTEMD="$SKEL/.config/systemd/user"
mapfile -t SERVICE_UNITS < <(python3 -c " mapfile -t SERVICE_UNITS < <(python3 - "$SKEL/.local/state/bakery/installed.json" <<'PY'
import json import json, sys
with open('$BAKERY_STATE/installed.json') as f: with open(sys.argv[1]) as f:
d = json.load(f) d = json.load(f)
for pkg in d.get('packages', d).values(): for pkg in d.get("packages", d).values():
for s in pkg.get('services', []): for s in pkg.get("services", []):
print(s) print(s["unit"] if isinstance(s, dict) else s)
") PY
)
for unit in "${SERVICE_UNITS[@]}"; do for unit in "${SERVICE_UNITS[@]}"; do
[[ -n "$unit" ]] || continue
if [[ -f "$SKEL_SYSTEMD/$unit" ]]; then if [[ -f "$SKEL_SYSTEMD/$unit" ]]; then
echo " $unit already committed in skel, leaving as-is" echo " $unit already committed in skel, leaving as-is"
continue continue
fi fi
src="$SYSTEMD_USER_DIR/$unit" src="$SYSTEMD_USER_DIR/$unit"
if [[ ! -f "$src" ]]; then if [[ ! -f "$src" ]]; then
echo " warning: $unit not found at $src, skipping" echo "ERROR: $unit listed in bakery installed.json but not found at $src" >&2
continue echo "Refusing to bake a skel whose daemons will never start." >&2
exit 1
fi fi
install -d -m 0755 "$SKEL_SYSTEMD" install -d -m 0755 "$SKEL_SYSTEMD"
sed "s#ExecStart=$LAPTOP_HOME/.local/bin/#ExecStart=%h/.local/bin/#" "$src" > "$SKEL_SYSTEMD/$unit" sed "s#ExecStart=$LAPTOP_HOME/.local/bin/#ExecStart=%h/.local/bin/#" "$src" > "$SKEL_SYSTEMD/$unit"

9
dotfiles/README.md Normal file
View file

@ -0,0 +1,9 @@
# `dotfiles/` is not the live skel
These files are a leftover from an earlier design (Hyprland `.conf` binds,
including grimblast). They are **not** copied into the ISO or the installed
system.
Live user defaults live in [`iso/airootfs/etc/skel`](../iso/airootfs/etc/skel)
(`hyprland.lua` + `binds.json`, breadlock/`loginctl lock-session`, breadshot,
breadpad, …). Edit that tree.

View file

@ -374,9 +374,8 @@ fi
# The whole bread ecosystem (bakery, bread, breadbar, breadbox, breadcrumbs, # The whole bread ecosystem (bakery, bread, breadbar, breadbox, breadcrumbs,
# breadpad, bos-settings, breadhelp, ...) is bakery-managed, not pacman: the # breadpad, bos-settings, breadhelp, ...) is bakery-managed, not pacman: the
# binaries and bakery manifest live in /etc/skel/.local (baked in at ISO # binaries and bakery manifest live in /etc/skel/.local (baked in at ISO
# build time via build-local.sh's BREAD_BINS array) and are copied into the # build time from iso/bread-lockfile.toml) and are copied into the user's
# user's home below, so the install works fully offline with no DNS for # home below, so the install works fully offline with no DNS for bakery.
# bakery/GitHub.
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Deploy dotfiles + the bakery bread ecosystem into the user's home (Calamares # Deploy dotfiles + the bakery bread ecosystem into the user's home (Calamares

View file

@ -26,12 +26,13 @@ Include = /etc/pacman.d/mirrorlist
Include = /etc/pacman.d/mirrorlist Include = /etc/pacman.d/mirrorlist
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
# Breadway custom repo — provides: bakery and the bread ecosystem packages # Breadway custom repo — breadlock plus AUR republishes the ISO needs
# (bread, breadbar, breadbox, breadcrumbs, breadpad, bos-settings). # (calamares, zen-browser-bin, bibata-cursor-theme-bin, yay-bin,
# (calamares comes from the official extra repo, not here.) # zsh-theme-powerlevel10k). bakery / breadbar / bos-settings / breadhelp
# are NOT here; they are bakery-baked into /etc/skel at ISO build time.
# #
# Packages are published to the Forgejo Arch registry (group "os") by the # Packages are published to the Forgejo Arch registry (group "os") by the
# .forgejo/workflows/package.yml workflow in each repo, on tag push. # .forgejo/workflows/*.yml workflows in this repo (and breadlock's).
# #
# Forgejo signs the repo db with a key pacman can't look up, so TrustAll # Forgejo signs the repo db with a key pacman can't look up, so TrustAll
# fails. SigLevel = Never skips verification (acceptable for this private # fails. SigLevel = Never skips verification (acceptable for this private

36
iso/bread-lockfile.toml Normal file
View file

@ -0,0 +1,36 @@
# Bakery binaries that MUST be baked into the live/installed skel.
#
# build-local.sh derives BREAD_BINS from `bins` — this is the name list, not a
# second hardcoded array. A missing binary fails the bake: a hollow ISO is
# worse than a failed build.
#
# CI should populate the builder from the stable bakery index
# (https://dl.breadway.dev/index.json). Local builds still snapshot whatever
# is installed on the builder; this file only names what must be present.
#
# Not shipped (even if present on the builder): breadcast, breadarr.
# breadlock is pacman (see packages.x86_64), not bakery.
bins = [
"bakery",
"bread",
"breadd",
"bread-emit",
"bread-module-host",
"breadman",
"breadbar",
"breadbox",
"breadbox-sync",
"breadcrumbs",
"breadpad",
"breadpaper",
"bread-theme",
"breadmon",
"breadsearch",
"breadmill",
"breadclip",
"breadclipd",
"breadshot",
"bos-settings",
"breadhelp",
]

View file

@ -99,12 +99,17 @@ bluez-utils
# blueman: GUI Bluetooth manager (pair/connect devices; breadbar shows status only). # blueman: GUI Bluetooth manager (pair/connect devices; breadbar shows status only).
blueman blueman
# GTK4 runtime # GTK4 runtime (breadbar, breadbox, breadclip, breadhelp, and other bakery apps)
gtk4 gtk4
gtk4-layer-shell gtk4-layer-shell
librsvg librsvg
libpulse libpulse
hicolor-icon-theme hicolor-icon-theme
# Tauri 2 runtime for bakery-baked bos-settings. Arch's WebKitGTK 4.1 package
# is webkit2gtk-4.1 (libwebkit2gtk-4.1.so); libsoup3 and JavaScriptCore 4.1
# are pulled in as its dependencies. xdg-desktop-portal comes from the
# Hyprland/GTK portal packages listed above.
webkit2gtk-4.1
# GTK3 dark theme (Adwaita-dark); without this package the gtk-theme-name in # GTK3 dark theme (Adwaita-dark); without this package the gtk-theme-name in
# skel settings.ini silently falls back to the light theme for GTK3 apps. # skel settings.ini silently falls back to the light theme for GTK3 apps.
gnome-themes-extra gnome-themes-extra
@ -179,19 +184,16 @@ yay-bin
# Bread ecosystem. # Bread ecosystem.
# #
# None of the bread apps (bakery, bread, breadbar, breadbox, breadcrumbs, # breadlock is the only bread* pacman package here (it needs a root-owned
# breadpad, bos-settings, breadhelp, ...) are pacman packages here anymore — # /etc/pam.d/breadlock). Everything else — bakery, bread/breadd/bread-emit/
# they are all bakery-managed binaries baked into /etc/skel/.local/bin at # bread-module-host, breadbar, breadbox, breadcrumbs, breadpad, breadpaper,
# build time (see build-local.sh's BREAD_BINS array), so every user gets the # bread-theme, breadmon, breadsearch, breadclip, breadshot, bos-settings,
# exact versions from this laptop's bakery install with no network/DNS # breadhelp — is bakery-managed and baked into /etc/skel/.local at ISO build
# needed at install or runtime. bos-settings/breadhelp's desktop entries are # time from iso/bread-lockfile.toml (see build-local.sh). breadcast and
# hand-committed static files at # breadarr are not shipped. bos-settings/breadhelp desktop entries are
# iso/airootfs/etc/skel/.local/share/applications/, matching the pattern # committed under iso/airootfs/etc/skel/.local/share/applications/. Runtime
# already used for breadclip/breadman/breadmon/breadsearch. Their runtime # deps stay listed even though no bread package depends on them via pacman
# system deps are pulled in elsewhere in this list (gtk4, gtk4-layer-shell, # (gtk4, gtk4-layer-shell, webkit2gtk-4.1, iw, libpulse, librsvg, …).
# iw, libpulse, librsvg, networkmanager, openssl, zlib, systemd-libs,
# hicolor-icon-theme) — keep those even though no bread package depends on
# them via pacman.
# Input / screen utilities # Input / screen utilities
brightnessctl brightnessctl
@ -324,6 +326,3 @@ qt6ct
# hyprland.lua) needs these or Qt apps fall back to (blurry) XWayland. # hyprland.lua) needs these or Qt apps fall back to (blurry) XWayland.
qt5-wayland qt5-wayland
qt6-wayland qt6-wayland
# Dev tools (for bos-settings standalone install)
rustup

View file

@ -26,12 +26,13 @@ Include = /etc/pacman.d/mirrorlist
Include = /etc/pacman.d/mirrorlist Include = /etc/pacman.d/mirrorlist
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
# Breadway custom repo — provides: bakery and the bread ecosystem packages # Breadway custom repo — breadlock plus AUR republishes the ISO needs
# (bread, breadbar, breadbox, breadcrumbs, breadpad, bos-settings). # (calamares, zen-browser-bin, bibata-cursor-theme-bin, yay-bin,
# (calamares comes from the official extra repo, not here.) # zsh-theme-powerlevel10k). bakery / breadbar / bos-settings / breadhelp
# are NOT here; they are bakery-baked into /etc/skel at ISO build time.
# #
# Packages are published to the Forgejo Arch registry (group "os") by the # Packages are published to the Forgejo Arch registry (group "os") by the
# .forgejo/workflows/package.yml workflow in each repo, on tag push. # .forgejo/workflows/*.yml workflows in this repo (and breadlock's).
# #
# Forgejo signs the repo db with a key pacman can't look up, so TrustAll # Forgejo signs the repo db with a key pacman can't look up, so TrustAll
# fails. SigLevel = Never skips verification (acceptable for this private # fails. SigLevel = Never skips verification (acceptable for this private

View file

@ -10,8 +10,8 @@ one publishes on a push to `packaging/<name>/**`.
Every bread-ecosystem app (bakery, bread, breadbar, breadbox, breadcrumbs, Every bread-ecosystem app (bakery, bread, breadbar, breadbox, breadcrumbs,
breadpad, breadpaper, breadmon, breadsearch, breadclip, breadshot, breadpad, breadpaper, breadmon, breadsearch, breadclip, breadshot,
bos-settings, breadhelp, ...) is bakery-managed, not pacman-packaged — see bos-settings, breadhelp, ...) is bakery-managed, not pacman-packaged — see
`build-local.sh`'s `BREAD_BINS` array, which bakes this laptop's `iso/bread-lockfile.toml`, which `build-local.sh` uses as the name list
bakery-installed binaries into the ISO's `/etc/skel` at build time. when baking this machine's bakery install into the ISO's `/etc/skel`.
`breadlock` is the sole deliberate exception (it needs a root-owned `breadlock` is the sole deliberate exception (it needs a root-owned
`/etc/pam.d/breadlock` PAM service file, which bakery — by design — has no `/etc/pam.d/breadlock` PAM service file, which bakery — by design — has no
privileged-install path for) and stays on pacman only; see privileged-install path for) and stays on pacman only; see

View file

@ -46,7 +46,7 @@ done
check "graphical.target is default" "[ \"\$(systemctl get-default)\" = graphical.target ]" check "graphical.target is default" "[ \"\$(systemctl get-default)\" = graphical.target ]"
echo "== bread ecosystem on PATH ==" echo "== bread ecosystem on PATH =="
for bin in bakery bread breadd breadbar breadbox breadbox-sync breadcrumbs breadpad breadman; do for bin in bakery bread breadd bread-emit bread-module-host breadbar breadbox breadbox-sync breadcrumbs breadpad breadman; do
check "$bin found" "command -v $bin" check "$bin found" "command -v $bin"
done done
@ -55,7 +55,8 @@ check "bos-settings installed" "command -v bos-settings"
echo "== breadhelp ==" echo "== breadhelp =="
check "breadhelp installed" "command -v breadhelp" check "breadhelp installed" "command -v breadhelp"
check "breadhelp content installed" "[ -d /usr/share/breadhelp/content ]" check "breadhelp content installed" \
"[ -d \"\$HOME/.local/share/breadhelp/content\" ] || [ -d /etc/skel/.local/share/breadhelp/content ]"
check "bos-netcheck present" "command -v bos-netcheck" check "bos-netcheck present" "command -v bos-netcheck"
echo "== default dotfiles ==" echo "== default dotfiles =="