Compare commits

..

41 commits
main ... v0.2.0

Author SHA1 Message Date
Breadway
1f53377914 bos-settings: full, non-destructive control of every bread* config
All checks were successful
Mirror to GitHub / mirror (push) Successful in 6s
Build and publish package / package (push) Successful in 1m35s
The bread/breadpad/breadcrumbs/breadbox views wrote invented schemas
(e.g. top-level log_level, [[profile]] name/ssids) that did not match the
apps' real TOML, so they showed empty and — worse — clobbered the real
config on Save, since the old config::save serialized only the keys it
modelled.

Rework the config layer onto toml_edit: parse each file into a
DocumentMut, mutate only the specific keys a view exposes, and write it
back preserving comments and any unmodelled keys (calendar password,
saved-network passwords, model paths). Unit-tested.

Add ui/widgets.rs (switch/entry/password/dropdown/spin/float/csv rows +
view scaffold + save button) bound to the shared document, then rewrite
the four views against the real schemas with far more coverage:

- bread: [daemon], [lua], [modules], all five [adapters.*] with their
  sub-options, [events], [notifications]
- breadpad: [settings], [model] + [model.ollama], [reminders], [calendar]
- breadcrumbs: [settings] (7 keys), [[networks]] editor, [profiles.*] editor
- breadbox: fixed to real [[contexts]] name/priority array editor

Goal: configure everything from the GUI rather than hand-editing TOML.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 14:26:49 +08:00
Breadway
c46e348d6a Fill desktop gaps: GUI apps, printing, media, Qt/portal integration
Add the packages a general desktop is expected to ship, chosen to stay
opinionated but average-user friendly:

- Editors: neovim (+ ripgrep, fd for a usable nvim/fzf experience)
- GUI basics: gnome-text-editor, gnome-calculator, file-roller, loupe
- Media: vlc (BOS had codecs but no player)
- Hardware: cups + cups-pk-helper + system-config-printer (enable
  cups.socket in post-install), blueman, seahorse
- Platform: qt5-wayland + qt6-wayland (native Wayland for Qt apps under
  the QT_QPA_PLATFORM=wayland we set), xdg-desktop-portal-gtk (file
  dialogs/screenshare for Flatpak/Electron/Zen), flatpak

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 14:26:49 +08:00
Breadway
31d0875791 Restore Bibata cursor now that it's published to [breadway]
All checks were successful
Mirror to GitHub / mirror (push) Successful in 5s
bibata-cursor-theme-bin 2.0.7-1 is now in the [breadway] repo, so add it
back to the package list and re-enable the Bibata-Modern-Ice cursor in the
Hyprland env, GTK settings, and gsettings autostart.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 13:03:11 +08:00
Breadway
3dd53f3fe6 Republish bibata-cursor-theme to [breadway] (AUR-only upstream)
All checks were successful
Mirror to GitHub / mirror (push) Successful in 6s
Build and publish bibata-cursor-theme / bibata (push) Successful in 34s
Bibata is the chosen BOS default cursor but is AUR-only, so mirror the
prebuilt -bin package into the [breadway] repo the same way calamares and
zen-browser-bin are. The workflow clones the triggering branch (not the
default branch) so it can build from iso-boot-fix, and uses the scoped
REGISTRY_TOKEN for publishing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 13:01:38 +08:00
Breadway
4a16e18cf3 Drop bibata-cursor-theme (AUR-only, not in repos)
Use Hyprland default cursor instead. All other theming changes from the
previous commit are unaffected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 10:34:10 +08:00
Breadway
d484c8e933 Polish BOS: dark theme, shell QoL, icons, media, clipboard
All checks were successful
Mirror to GitHub / mirror (push) Successful in 5s
- Global dark theme: gnome-themes-extra (GTK3 Adwaita-dark), qt5ct/qt6ct
  with Fusion dark skel config, QT_QPA_PLATFORMTHEME=qt5ct env
- Icons/cursor: papirus-icon-theme (Papirus-Dark) + bibata-cursor-theme
  (Bibata-Modern-Ice), set via gsettings autostart + GTK settings.ini + env
- gnome-keyring: credential storage for browsers and apps
- gvfs + gvfs-mtp: nautilus trash, phone access, network shares
- zsh: default user shell (users.conf userShell=/bin/zsh) + skel .zshrc
  with eza/bat/fzf/zoxide aliases, git prompt, fzf history search
- Shell QoL packages: eza, bat, fzf, zoxide
- Media: gst-plugins-good/bad/ugly, pavucontrol
- cliphist: clipboard history daemon (autostart) + SUPER+SHIFT+V bind
- Fonts: noto-fonts-cjk, ttf-jetbrains-mono-nerd

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 10:31:18 +08:00
Breadway
a4af3aa938 Make Plymouth splash black to match the black-base theme
All checks were successful
Mirror to GitHub / mirror (push) Successful in 4s
The boot splash still used the old bread-brown background (#230b00)
after the rest of the theme moved to a black base (#0c0c0c). Switch
bos.script's background to black so the boot splash is consistent with
the wallpaper/pywal palette and breadbar.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 09:17:10 +08:00
Breadway
9ea57d87c0 Make BOS a complete, bootable, themed desktop OS
Some checks failed
Mirror to GitHub / mirror (push) Failing after 7s
Install/boot reliability:
- Use native Calamares initcpiocfg/initcpio + explicit grub-install (nvram +
  --removable) in post-install; drop the flaky native bootloader/grubcfg modules.
- mount.conf: bind /proc /sys /dev (devtmpfs) /run + efivars into the chroot.
- bos-copy-kernel: stage kernel + write a stock mkinitcpio preset (replace the
  archiso preset). Per-service systemctl enable (fixes NetworkManager et al.
  silently not enabling due to the all-or-nothing grub-btrfs.path name).

System completeness:
- greetd + tuigreet graphical login; installed pacman.conf + working mirrorlist;
  base CLI tools (nano, micro, vim, htop, …); amd/intel-ucode; tlp + hypridle
  power management; systemd-timesyncd, fstrim.timer; wpa_supplicant wifi; Zen
  browser (republished to the [Breadway] repo).

Desktop + theming:
- Native Lua Hyprland config (hyprland.lua) with curated standard binds; kitty
  (blur) replaces foot; awww wallpaper + pywal palette (tamed to a black base
  with warm accents); GTK dark mode.
- Plymouth boot splash (bos theme: logo + spinner + status) via plymouthcfg.
- Varela Round font; Calamares bread-palette sidebar (logo/black-region polish
  still pending).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 09:09:34 +08:00
Breadway
2a866144f4 Fix breadd skel config schema; remove temp live diagnostic
All checks were successful
Mirror to GitHub / mirror (push) Successful in 6s
- breadd.toml: the shipped skel used a stale [adapters] schema
  (keyboard/mouse/touchpad/gamepad booleans); breadd 0.6.4 expects
  hyprland/udev/power/network/bluetooth structs. `bluetooth = true` collided
  with the real AdapterToggle field and aborted the daemon at startup.
- Drop the temporary bos-live-diag serial diagnostic now that the live-session
  failures are diagnosed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 19:38:06 +08:00
Breadway
122cd39cb1 TEMP: route live diag to serial via sudo (revert after)
All checks were successful
Mirror to GitHub / mirror (push) Successful in 10s
2026-06-14 19:17:46 +08:00
Breadway
356cc08dfe TEMP: live-session diagnostic to serial (revert after)
Some checks failed
Mirror to GitHub / mirror (push) Failing after 9s
2026-06-14 19:08:42 +08:00
Breadway
3a30cd004f Drop removed Hyprland dwindle:pseudotile option
All checks were successful
Mirror to GitHub / mirror (push) Successful in 11s
Current Hyprland no longer accepts dwindle:pseudotile (it's a dispatcher now),
which threw a non-fatal config-error banner on both the live and installed
desktop. preserve_split is still valid and kept.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 18:54:06 +08:00
Breadway
23e60dffe0 Bake bread ecosystem into the ISO + full live desktop; fix installer timeout
All checks were successful
Mirror to GitHub / mirror (push) Successful in 16s
- packages.x86_64: add bread, breadbar, breadbox, breadcrumbs, breadpad,
  bos-settings so they ship in the squashfs and reach the target via unpackfs
  (no network needed; install works fully offline)
- shellprocess.conf: set timeout 1800 — Calamares' 10s default was killing
  post-install.sh partway (the real cause of the empty /boot + ESP); the "-"
  prefix had been masking the kill as success
- bos-live-setup: live user now boots the real BOS desktop from /etc/skel
  (breadd + breadbar + breadbox) with the installer layered on top
  (auto-launch + Super+I), instead of an installer-only kiosk
- post-install.sh: drop the now-redundant networked `bakery install`

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 18:41:59 +08:00
Breadway
b9544d517b Fix unbootable installs: lay the kernel into the target and own GRUB
All checks were successful
Mirror to GitHub / mirror (push) Successful in 9s
archiso keeps vmlinuz/initramfs in the ISO boot dir, not the squashfs, so
unpackfs lays down an empty /boot. The chroot's mkinitcpio/grub-mkconfig had
nothing to work with and the ESP ended up empty (firmware found no bootloader).

- shellprocess@kernel (dontChroot) copies the live kernel into the target
  /boot before the bootloader step
- post-install.sh now runs grub-install itself, including a --removable pass
  so firmware with no NVRAM entry still boots via EFI/BOOT/BOOTX64.EFI

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 17:57:50 +08:00
Breadway
12dbec5f32 Add rsync and make the installed system bootable/clean
All checks were successful
Mirror to GitHub / mirror (push) Successful in 4s
unpackfs runs unsquashfs then rsync to copy the rootfs onto the target;
rsync was missing (error code 127), so add it alongside squashfs-tools.

unpackfs also copies the live filesystem verbatim, so the installed
system would inherit the archiso initramfs hooks (booting into the live
path) plus the live autologin/user/sudoers. Rework post-install.sh to run
in the target chroot as a resilient best-effort script that:
- removes the live autologin drop-in, bos-live-setup service/scripts and
  the liveuser sudoers file, and locks root (sudo model; the live medium
  left root passwordless),
- drops the archiso mkinitcpio config, installs the stock linux.preset and
  regenerates the initramfs, then refreshes grub.cfg,
- keeps the snapper/services/dotfiles setup, with the network-dependent
  bakery install made non-fatal so offline installs still complete.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 13:29:49 +08:00
Breadway
de4e3b09ba Add squashfs-tools so Calamares can unpack the rootfs
Some checks failed
Mirror to GitHub / mirror (push) Failing after 4s
Calamares' unpackfs module shells out to unsquashfs to extract
airootfs.sfs onto the target. squashfs-tools wasn't in the live package
list, so installs failed at the Finish step with "Failed to find
unsquashfs ... Bad unpackfs configuration". Add it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 13:15:27 +08:00
Breadway
2cbf46a836 Log the live Hyprland session to a user-writable path
All checks were successful
Mirror to GitHub / mirror (push) Successful in 4s
liveuser can't write /var/log, so the .bash_profile redirect
(Hyprland &>/var/log/hyprland-live.log) failed and bash aborted the line
without ever launching the compositor. Log to /tmp/hyprland-live.log,
which the live user can write.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 04:24:52 +08:00
Breadway
6c3c33e4ae Run the live session as an unprivileged user (Hyprland won't run as root)
All checks were successful
Mirror to GitHub / mirror (push) Successful in 4s
The live medium autologged root on tty1 and exec'd Hyprland, but Hyprland
refuses to start with superuser privileges ("launched with superuser
privileges, but the privileges check is not omitted") and exited before
even creating a log — leaving tty1 at a blank blinking cursor. (Boot,
switch-root, firstboot suppression and the bos login on other ttys were
all already working.)

Adopt the standard live-ISO pattern:
- bos-live-setup.service (oneshot, gated on the archisobasedir cmdline so
  it only runs on the live medium) creates an unprivileged `liveuser`,
  adds it to the usual hardware groups, clears its password, and drops in
  a minimal live Hyprland config that auto-launches the installer.
- tty1 autologin now targets liveuser instead of root.
- Calamares needs root, so bos-launch-calamares runs it via passwordless
  sudo (/etc/sudoers.d/99-bos-live) with the Wayland env preserved, so the
  root installer renders on the live user's compositor.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 04:13:10 +08:00
Breadway
a7b3f70930 Capture live-session Hyprland output and fall back to a shell
All checks were successful
Mirror to GitHub / mirror (push) Successful in 5s
Redirect the live autologin compositor's stdout/stderr to
/var/log/hyprland-live.log, and on exit drop to an interactive shell
showing the return code instead of letting the getty autologin
respawn-loop hide any startup failure behind a blank blinking cursor.
Makes a failed live boot diagnosable and leaves the medium usable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 03:57:27 +08:00
Breadway
cbdeccd03e Let the live Hyprland session fall back to software rendering
All checks were successful
Mirror to GitHub / mirror (push) Successful in 4s
On GPU-less targets (VMs, headless, exotic hardware) wlroots refuses to
initialise without a hardware renderer, so the autologin session exec'd
Hyprland on tty1 and it died immediately — leaving a blinking cursor and
no desktop, while tty2 still showed the (correct) `bos` login.

Export WLR_RENDERER_ALLOW_SOFTWARE=1 before exec Hyprland in root's
.bash_profile so wlroots may use the llvmpipe software renderer when no
GPU renderer exists. On real hardware the hardware renderer is still
chosen; this is purely a fallback. Also set WLR_NO_HARDWARE_CURSORS=1 so
the pointer isn't invisible in VMs. Both must be real env vars (read at
wlroots init), not Hyprland `env=` lines, which apply too late.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 03:35:23 +08:00
Breadway
337d280f2b Add live-environment config so the ISO boots straight to the session
All checks were successful
Mirror to GitHub / mirror (push) Successful in 4s
The fixed initramfs boots into userspace, but systemd-firstboot
(ConditionFirstBoot=yes, --prompt-locale --prompt-keymap-auto
--prompt-timezone --prompt-root-password) then blocked the console
waiting for interactive input, and root was locked (no /etc/shadow),
so the live medium never reached the autologin getty + Hyprland.

Ship the same base files releng uses to satisfy firstboot and unlock
root for autologin:
- etc/locale.conf  (LANG=C.UTF-8)        -> no locale prompt
- etc/localtime    (-> UTC)              -> no timezone prompt
- etc/vconsole.conf (KEYMAP=us)          -> no keymap prompt
- etc/hostname     (bos)
- etc/shadow       (root unlocked, empty pw, perms 0400 via profiledef)
- etc/passwd       (root shell = bash; system users are appended by the
                    systemd-sysusers pacman hook during pacstrap)

The overlay is applied before pacstrap (mkarchiso _make_custom_airootfs
precedes _make_packages) and these are pacman backup files, so the
static passwd/shadow act as the base and package scriptlets add the
rest — no clobbering of polkitd/pipewire/etc. users.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 03:13:54 +08:00
Breadway
6135b2215d Add archiso initramfs hooks so the live ISO can switch root
All checks were successful
Mirror to GitHub / mirror (push) Successful in 9s
The profile shipped boot configs and the package list but lacked the
mkinitcpio archiso configuration, so mkarchiso built a stock initramfs
with no archiso hook. At boot the kernel honoured archisosearchuuid/
archisobasedir but nothing knew how to find and mount airootfs.sfs, so
switch-root failed and the live medium dropped to emergency mode.

Add the canonical releng pieces:
- airootfs/etc/mkinitcpio.conf.d/archiso.conf (HOOKS incl. archiso)
- airootfs/etc/mkinitcpio.d/linux.preset (builds initramfs-linux.img)
- mkinitcpio{,-archiso,-nfs-utils} in packages.x86_64

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 02:55:53 +08:00
Breadway
5a14288025 Add in-house Calamares package (AUR-only upstream)
All checks were successful
Mirror to GitHub / mirror (push) Successful in 4s
Build and publish calamares / calamares (push) Successful in 2m36s
Calamares isn't in Arch's official repos, so BOS vendors the PKGBUILD and
publishes a built package to the [breadway] repo. All its deps are official
(kpmcore, qt6-*, yaml-cpp). Also drop the nonexistent calamares-qt6 from the
package list (calamares 3.4.x is already Qt6).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 23:39:39 +08:00
Breadway
82c63bc4c4 Set [breadway] SigLevel=Never (Forgejo db key unavailable to pacman)
All checks were successful
Mirror to GitHub / mirror (push) Successful in 4s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 23:34:51 +08:00
Breadway
0d550a1bda Fix archiso bootmodes and add syslinux to package list
All checks were successful
Mirror to GitHub / mirror (push) Successful in 4s
mkarchiso validation: bios.syslinux.mbr/eltorito and uefi-x64.* bootmodes
are deprecated -> use bios.syslinux + uefi.systemd-boot. syslinux must be
in the package list for the BIOS bootmode; add memtest86+/edk2-shell too.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 23:32:25 +08:00
Breadway
ed0eea3cb1 Add bootloader configs to archiso profile (syslinux/efiboot/grub)
All checks were successful
Mirror to GitHub / mirror (push) Successful in 11s
The profile declared syslinux + systemd-boot bootmodes but lacked the
required config directories, so mkarchiso would fail. Added from the
official releng profile, rebranded to Bread OS; %PLACEHOLDER% tokens are
substituted by mkarchiso at build time.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 23:03:54 +08:00
Breadway
8e41d9fc2b Disable debug package so the main package publishes correctly
All checks were successful
Mirror to GitHub / mirror (push) Successful in 9s
Build and publish package / package (push) Successful in 2m10s
makepkg's debug split produced a -debug pkg; the upload's head -1 could
grab it instead of the main package. !debug yields a single package.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 23:00:48 +08:00
Breadway
30d94aa286 Remove accidentally-committed .claude agent state; gitignore it
All checks were successful
Mirror to GitHub / mirror (push) Successful in 9s
Build and publish package / package (push) Successful in 1m51s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 22:54:47 +08:00
Breadway
1bcd9588de Fix bos-settings compile errors and use REGISTRY_TOKEN for publishing
All checks were successful
Mirror to GitHub / mirror (push) Successful in 4s
bos-settings was scaffolded but never compiled. Fixes:
- main.rs: import gtk4::prelude (connect_activate/run)
- window.rs: disambiguate WidgetExt::display(); drop unused GBox import
- hyprland.rs: Label has no set_monospace -> use the monospace CSS class
- theme.rs: drop unused prelude import

Also switch package.yml to secrets.REGISTRY_TOKEN (scoped write:package),
since the auto Actions token is not authorized for the owner registry.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 22:54:27 +08:00
Breadway
12a8fa00bb Disable LTO in PKGBUILD (vendored ring/mlua static libs vs makepkg -flto)
All checks were successful
Mirror to GitHub / mirror (push) Successful in 10s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 17:06:53 +08:00
Breadway
0ee174e16e Regenerate Cargo.lock for bos-settings
Some checks are pending
Mirror to GitHub / mirror (push) Waiting to run
The scaffolded lockfile was stale, so packaging builds with --locked failed.
Regenerated against current Cargo.toml (88 packages).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 16:59:30 +08:00
Breadway
eda2c44c48 Add Calamares branding images from bread logo
Some checks are pending
Mirror to GitHub / mirror (push) Waiting to run
- logo.png (productLogo/productIcon): rasterised from the bread logo, transparent
- languages.png (productWelcome): logo centred on a light Nord canvas
- logo.svg / bread_white.svg: source vector

Resolves the missing-branding-asset blocker so Calamares can render.
Colour scheme can be refined when final SVGs land.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 16:53:25 +08:00
Breadway
f98f21bbdd Source calamares from official extra, not [breadway]
All checks were successful
Mirror to GitHub / mirror (push) Successful in 5s
calamares and calamares-qt6 are in Arch's extra repo; no custom PKGBUILD
needed. Update packages.x86_64 and the pacman.conf comment accordingly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 16:40:59 +08:00
Breadway
7ef51e8722 Clone from public URL, not GITHUB_SERVER_URL (resolves to localhost in runner)
All checks were successful
Mirror to GitHub / mirror (push) Successful in 4s
The Forgejo runner injects GITHUB_SERVER_URL as http://localhost:3002, which
is unreachable from inside the job container. Use the public URL instead.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 16:14:12 +08:00
Breadway
c259aa9e93 Rename mirror secret to MIRROR_TOKEN (GITHUB_ prefix is reserved)
Some checks failed
Mirror to GitHub / mirror (push) Failing after 2s
Forgejo/gitea rejects user secret names starting with GITHUB_.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 16:10:39 +08:00
Breadway
a2973b91eb Use Forgejo-prescribed pacman section name for the Arch registry
Some checks failed
Mirror to GitHub / mirror (push) Failing after 1s
Forgejo serves the repo db as {owner}.{group}.{domain}.db, and pacman
fetches "<section>.db" from Server — so the section name must match.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 16:03:55 +08:00
Breadway
769b6283e0 Fix Forgejo workflows for the actual server capabilities
Some checks failed
Mirror to GitHub / mirror (push) Failing after 2s
- package.yml: use correct Arch registry upload (octet-stream + binary body
  + PUT /api/packages/Breadway/arch/os), drop --privileged, remove
  actions/checkout (archlinux image has no Node) in favour of a manual
  shell clone, use the built-in Actions token instead of a stored secret,
  and --nocheck (tests belong in CI, not packaging)
- mirror.yml: clone --mirror + explicit refs/heads + refs/tags push with
  --prune, instead of pushing refs/remotes pollution from a checkout
- pacman.conf: correct Server URL to the Forgejo Arch registry format

Requires only the GITHUB_MIRROR_TOKEN secret (GitHub PAT, repo scope) for
the mirror job; package publishing uses the automatic per-run token.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 16:01:50 +08:00
Breadway
2c6feb4ea0 Add Forgejo Actions workflows and fix [breadway] repo URL
Some checks failed
Mirror to GitHub / mirror (push) Failing after 2s
- .forgejo/workflows/mirror.yml: mirrors every push/tag to GitHub
- .forgejo/workflows/package.yml: builds PKGBUILD on tag and publishes
  bos-settings to the Forgejo Arch package registry (distrib=breadway)
- iso/pacman.conf: replace placeholder repo.breadway.dev with the actual
  Forgejo package registry URL

Requires two Forgejo secrets:
  GITHUB_MIRROR_TOKEN — GitHub PAT with repo push scope
  FORGEJO_TOKEN       — Forgejo token with package:write scope

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 11:42:00 +08:00
Breadway
6f148e9a06 Add bakery.toml and packaging/arch to match bread ecosystem
Mirrors the build/distribution pattern used by the bread project:
- bakery.toml describes bos-settings as a bakery-managed package
- packaging/arch/PKGBUILD builds and installs the binary via cargo
- packaging/arch/bos-settings.desktop for app launchers
- LICENSE (MIT) required by PKGBUILD

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 11:32:40 +08:00
Breadway
8682698402 Fix prod-readiness issues flagged in audit
- Fix XDG config dir logic in config/mod.rs (was double-nesting and had /home/user hardcode)
- Replace /home/user hardcodes in breadbar.rs and hyprland.rs with config::config_dir()
- Fix /home/user hardcode in packages.rs (uses /root fallback for .local/state path)
- Remove eprintln! from GTK callback in packages.rs (no stderr at runtime)
- Fix YAML parse error in branding.desc (missing space after sidebarTextHighlight key)
- Add .gitignore (Rust target/, ISO artifacts, editor/OS junk, secrets)
- Delete state.rs (dead code — never mod'd in main.rs)
- Add brightnessctl, grim, slurp to packages.x86_64 (used by keybinds)
- Rename can-you-begin-a-composed-beacon.md → DESIGN.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 11:29:53 +08:00
Breadway
c744e45c90
Merge pull request #1 from Breadway/scaffold/bos-initial
Scaffold/bos initial
2026-06-13 11:15:38 +08:00
41 changed files with 186 additions and 3121 deletions

View file

@ -1,35 +0,0 @@
name: Build and publish powerlevel10k
# Powerlevel10k (the BOS default zsh prompt) is AUR-only, so BOS maintains an
# in-house PKGBUILD and publishes the built package to the [breadway] repo.
# Builds gitstatus + libgit2 from source, so it needs cmake + zsh beyond base-devel.
on:
push:
paths:
- 'packaging/powerlevel10k/**'
workflow_dispatch:
jobs:
powerlevel10k:
runs-on: [self-hosted, hestia]
container:
image: archlinux:latest
steps:
- name: Build and publish
env:
PUBLISH_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
run: |
set -euo pipefail
pacman -Syu --noconfirm base-devel git cmake zsh
useradd -m builder
git config --global --add safe.directory '*'
git clone --depth 1 --branch "${GITHUB_REF_NAME}" \
"https://git.breadway.dev/${GITHUB_REPOSITORY}.git" /home/builder/src
chown -R builder:builder /home/builder/src
su builder -c "cd /home/builder/src/packaging/powerlevel10k && makepkg -f --noconfirm --nocheck"
PKG=$(find /home/builder/src/packaging/powerlevel10k -name '*.pkg.tar.zst' | head -1)
curl -fsS -X PUT \
-H "Authorization: token ${PUBLISH_TOKEN}" \
-H "Content-Type: application/octet-stream" \
--data-binary "@${PKG}" \
"https://git.breadway.dev/api/packages/Breadway/arch/os"

View file

@ -1,207 +0,0 @@
name: Build and release ISO
# Builds the BOS ISO on the hestia self-hosted runner (native Arch container),
# downloads all bakery ecosystem binaries from their GitHub releases, compiles
# bread-theme from source, and uploads the resulting ISO to a Forgejo pre-release.
# A matching GitHub release is created that points to Forgejo for the download
# (GitHub releases cannot host files larger than 2 GB).
#
# Required secrets:
# RELEASE_TOKEN — Forgejo API token with write:repository scope
# MIRROR_TOKEN — GitHub personal access token with repo scope (already used by mirror.yml)
on:
push:
tags: ['v*']
workflow_dispatch:
inputs:
tag:
description: 'Git tag to build (e.g. v0.4.0)'
required: true
jobs:
release-iso:
runs-on: [self-hosted, hestia]
container:
image: archlinux:latest
# --privileged: mkarchiso needs CAP_SYS_ADMIN for loop mounts + mknod
# --network=host: gives localhost:3002 access to Forgejo (avoids the
# public git.breadway.dev → Aegis → Tailscale round-trip for pacman)
options: --privileged --network=host
steps:
- name: Install build dependencies
run: |
pacman -Syu --noconfirm archiso curl python git rust
- name: Determine tag and version
id: vars
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
TAG="${{ github.event.inputs.tag }}"
else
TAG="${{ github.ref_name }}"
fi
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
echo "version=${TAG#v}" >> "$GITHUB_OUTPUT"
- name: Clone repository at tag
run: |
git clone --branch "${{ steps.vars.outputs.tag }}" --depth 1 \
"https://git.breadway.dev/${GITHUB_REPOSITORY}.git" /bos
- name: Download bakery ecosystem binaries
run: |
set -euo pipefail
mkdir -p /build-home/.local/bin \
/build-home/.local/state/bakery \
/build-home/.cache/bakery
# Fetch the canonical bakery index
curl -fsSL "https://dl.breadway.dev/index.json" \
-o /build-home/.cache/bakery/index.json
# Download each binary from dl.breadway.dev (canonical source; github_url
# is not always published for dev/patch releases) and generate the
# installed.json that bakery expects in ~/.local/state.
python3 << 'PYEOF'
import json, urllib.request, os
with open('/build-home/.cache/bakery/index.json') as f:
idx = json.load(f)
BIN_DIR = '/build-home/.local/bin'
installed = {}
for pkg_name, pkg in idx['packages'].items():
bins = []
for b in pkg['binaries']:
dest_name = b['name'].removesuffix('-x86_64')
dest = os.path.join(BIN_DIR, dest_name)
url = b['dl_url']
print(f' {dest_name} <- {url}', flush=True)
urllib.request.urlretrieve(url, dest)
os.chmod(dest, 0o755)
bins.append(dest_name)
# installed.json services field is a flat list of unit-name strings
services = [
(s['unit'] if isinstance(s, dict) else s)
for s in pkg.get('services', [])
]
installed[pkg_name] = {
'name': pkg_name,
'version': pkg['version'],
'binaries': bins,
'services': services,
'installed_at': '2024-01-01T00:00:00+00:00',
}
with open('/build-home/.local/state/bakery/installed.json', 'w') as f:
json.dump({'packages': installed}, f, indent=2)
print('installed.json written', flush=True)
PYEOF
- name: Build bread-theme from source
run: |
set -euo pipefail
# bread-theme is not in the bakery index; build it at the tag pinned
# in bos-settings/Cargo.toml so the CLI matches the library version.
THEME_TAG=$(grep 'bread-theme.*tag' /bos/bos-settings/Cargo.toml \
| grep -oP '"v[^"]+"' | tr -d '"')
echo "Building bread-theme @ $THEME_TAG"
git clone --branch "$THEME_TAG" --depth 1 \
https://github.com/Breadway/bread-ecosystem /bread-ecosystem
cd /bread-ecosystem
cargo build --release -p bread-theme
install -m 755 target/release/bread-theme /build-home/.local/bin/bread-theme
echo "bread-theme built OK"
- name: Build ISO
run: |
set -euo pipefail
mkdir -p /bos-work /bos-out
cd /bos
LAPTOP_HOME=/build-home \
WORK=/bos-work \
OUT=/bos-out \
CI_BUILD=1 \
bash build-local.sh
ls -lh /bos-out/*.iso
- name: Create Forgejo release and upload ISO
env:
FORGEJO_TOKEN: ${{ secrets.RELEASE_TOKEN }}
run: |
set -euo pipefail
TAG="${{ steps.vars.outputs.tag }}"
VERSION="${{ steps.vars.outputs.version }}"
ISO=$(ls /bos-out/*.iso | head -1)
ISO_NAME="bos-${VERSION}-x86_64.iso"
# Use an existing release for this tag if one exists (e.g. created
# manually or by a prior re-run), otherwise create a fresh one.
EXISTING=$(curl -sf \
-H "Authorization: token ${FORGEJO_TOKEN}" \
"http://localhost:3002/api/v1/repos/${GITHUB_REPOSITORY}/releases/tags/${TAG}" \
2>/dev/null || true)
RELEASE_ID=$(echo "${EXISTING}" | python3 -c \
"import json,sys; d=json.load(sys.stdin); print(d.get('id',''))" 2>/dev/null || true)
if [ -z "${RELEASE_ID}" ]; then
RELEASE=$(curl -fsS -X POST \
-H "Authorization: token ${FORGEJO_TOKEN}" \
-H "Content-Type: application/json" \
"http://localhost:3002/api/v1/repos/${GITHUB_REPOSITORY}/releases" \
-d "{
\"tag_name\": \"${TAG}\",
\"name\": \"BOS ${TAG}\",
\"prerelease\": false,
\"body\": \"ISO image attached below.\\n\\nSee the [README](https://github.com/Breadway/bos#testing-in-a-vm) for VM testing instructions.\"
}")
RELEASE_ID=$(echo "${RELEASE}" | python3 -c "import json,sys; print(json.load(sys.stdin)['id'])")
fi
echo "Using release ID: ${RELEASE_ID}"
# Remove any existing asset with the same name before uploading
ASSET_ID=$(curl -sf \
-H "Authorization: token ${FORGEJO_TOKEN}" \
"http://localhost:3002/api/v1/repos/${GITHUB_REPOSITORY}/releases/${RELEASE_ID}/assets" \
| python3 -c "
import json,sys
assets=json.load(sys.stdin)
match=[a['id'] for a in assets if a['name']=='${ISO_NAME}']
print(match[0] if match else '')
" 2>/dev/null || true)
if [ -n "${ASSET_ID}" ]; then
curl -fsS -X DELETE \
-H "Authorization: token ${FORGEJO_TOKEN}" \
"http://localhost:3002/api/v1/repos/${GITHUB_REPOSITORY}/releases/${RELEASE_ID}/assets/${ASSET_ID}"
echo "Removed existing ${ISO_NAME} asset"
fi
curl -fsS -X POST \
-H "Authorization: token ${FORGEJO_TOKEN}" \
-F "attachment=@${ISO};filename=${ISO_NAME}" \
"http://localhost:3002/api/v1/repos/${GITHUB_REPOSITORY}/releases/${RELEASE_ID}/assets"
echo "Uploaded: ${ISO_NAME}"
- name: Create GitHub release
env:
GH_TOKEN: ${{ secrets.MIRROR_TOKEN }}
run: |
set -euo pipefail
TAG="${{ steps.vars.outputs.tag }}"
VERSION="${{ steps.vars.outputs.version }}"
FORGEJO_URL="https://git.breadway.dev/${GITHUB_REPOSITORY}/releases/tag/${TAG}"
printf '**Download ISO:** %s\n\nGitHub releases cannot host files >2 GB; the `bos-%s-x86_64.iso` (~2.5 GB) is on Forgejo.\n\nSee the [README](https://github.com/Breadway/bos#testing-in-a-vm) for VM testing instructions.' \
"${FORGEJO_URL}" "${VERSION}" > /tmp/gh-release-notes.md
gh release create "${TAG}" \
--repo "Breadway/bos" \
--title "BOS ${TAG}" \
\
--notes-file /tmp/gh-release-notes.md \
2>/dev/null || echo "GitHub release already exists — skipping"

1
.gitignore vendored
View file

@ -27,7 +27,6 @@ secrets/
# archiso build artifacts (these are large and reproducible)
/iso-build/
/iso-out/
/out/
*.iso
*.img

273
Cargo.lock generated
View file

@ -28,10 +28,9 @@ checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
[[package]]
name = "bos-settings"
version = "0.4.0"
version = "0.2.0"
dependencies = [
"async-channel",
"bread-theme",
"glib",
"gtk4",
"serde",
@ -40,22 +39,11 @@ dependencies = [
"toml_edit 0.22.27",
]
[[package]]
name = "bread-theme"
version = "0.2.3"
source = "git+https://github.com/Breadway/bread-ecosystem?tag=v0.2.8#77417d552130281ff787e07d52541eb25e9d533b"
dependencies = [
"dirs",
"gtk4",
"serde",
"serde_json",
]
[[package]]
name = "cairo-rs"
version = "0.22.0"
version = "0.20.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cc8d9aa793480744cd9a0524fef1a2e197d9eaa0f739cde19d16aba530dcb95"
checksum = "91e3bd0f4e25afa9cabc157908d14eeef9067d6448c49414d17b3fb55f0eadd0"
dependencies = [
"bitflags",
"cairo-sys-rs",
@ -65,9 +53,9 @@ dependencies = [
[[package]]
name = "cairo-sys-rs"
version = "0.22.0"
version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8b4985713047f5faee02b8db6a6ef32bbb50269ff53c1aee716d1d195b76d54"
checksum = "059cc746549898cbfd9a47754288e5a958756650ef4652bbb6c5f71a6bda4f8b"
dependencies = [
"glib-sys",
"libc",
@ -84,12 +72,6 @@ dependencies = [
"target-lexicon",
]
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "concurrent-queue"
version = "2.5.0"
@ -105,27 +87,6 @@ version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "dirs"
version = "5.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
dependencies = [
"dirs-sys",
]
[[package]]
name = "dirs-sys"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
dependencies = [
"libc",
"option-ext",
"redox_users",
"windows-sys 0.48.0",
]
[[package]]
name = "equivalent"
version = "1.0.2"
@ -227,9 +188,9 @@ dependencies = [
[[package]]
name = "gdk-pixbuf"
version = "0.22.0"
version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25f420376dbee041b2db374ce4573892a36222bb3f6c0c43e24f0d67eae9b646"
checksum = "2fd242894c084f4beed508a56952750bce3e96e85eb68fdc153637daa163e10c"
dependencies = [
"gdk-pixbuf-sys",
"gio",
@ -239,9 +200,9 @@ dependencies = [
[[package]]
name = "gdk-pixbuf-sys"
version = "0.22.0"
version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48f31b37b1fc4b48b54f6b91b7ef04c18e00b4585d98359dd7b998774bbd91fb"
checksum = "5b34f3b580c988bd217e9543a2de59823fafae369d1a055555e5f95a8b130b96"
dependencies = [
"gio-sys",
"glib-sys",
@ -252,9 +213,9 @@ dependencies = [
[[package]]
name = "gdk4"
version = "0.11.2"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd42fdbbf48612c6e8f47c65fb92d2e8f39c25aecd6af047e83897c1a22d2a4e"
checksum = "4850c9d9c1aecd1a3eb14fadc1cdb0ac0a2298037e116264c7473e1740a32d60"
dependencies = [
"cairo-rs",
"gdk-pixbuf",
@ -267,9 +228,9 @@ dependencies = [
[[package]]
name = "gdk4-sys"
version = "0.11.2"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d974ac4f15e67472c3a9728daf612590b4a5762a4b33f0edd298df0b80d043c"
checksum = "6f6eb95798e2b46f279cf59005daf297d5b69555428f185650d71974a910473a"
dependencies = [
"cairo-sys-rs",
"gdk-pixbuf-sys",
@ -282,22 +243,11 @@ dependencies = [
"system-deps",
]
[[package]]
name = "getrandom"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "gio"
version = "0.22.6"
version = "0.20.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3848bcba3a35cc0a71df8ba8ecfd799d6bfb862342a53a4a915fb62213aa4e6"
checksum = "8e27e276e7b6b8d50f6376ee7769a71133e80d093bdc363bd0af71664228b831"
dependencies = [
"futures-channel",
"futures-core",
@ -312,22 +262,22 @@ dependencies = [
[[package]]
name = "gio-sys"
version = "0.22.0"
version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64729ba2772c080448f9f966dba8f4456beeb100d8c28a865ef8a0f2ef4987e1"
checksum = "521e93a7e56fc89e84aea9a52cfc9436816a4b363b030260b699950ff1336c83"
dependencies = [
"glib-sys",
"gobject-sys",
"libc",
"system-deps",
"windows-sys 0.59.0",
"windows-sys",
]
[[package]]
name = "glib"
version = "0.22.7"
version = "0.20.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c207e04e51605dcf7b2924c41591b3a10e1438eaac5bcf448fb91f325381104a"
checksum = "ffc4b6e352d4716d84d7dde562dd9aee2a7d48beb872dd9ece7f2d1515b2d683"
dependencies = [
"bitflags",
"futures-channel",
@ -346,11 +296,12 @@ dependencies = [
[[package]]
name = "glib-macros"
version = "0.22.6"
version = "0.20.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "506d23499707c7142898429757e8d9a3871d965239a2cb66dfa05052be6d6f19"
checksum = "e8084af62f09475a3f529b1629c10c429d7600ee1398ae12dd3bf175d74e7145"
dependencies = [
"heck",
"proc-macro-crate",
"proc-macro2",
"quote",
"syn",
@ -358,9 +309,9 @@ dependencies = [
[[package]]
name = "glib-sys"
version = "0.22.6"
version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f7fbac234ed5bc2a28359b7bde8e1b9cdf1441cc2d7f068e4824672d7db9445"
checksum = "8ab79e1ed126803a8fb827e3de0e2ff95191912b8db65cee467edb56fc4cc215"
dependencies = [
"libc",
"system-deps",
@ -368,9 +319,9 @@ dependencies = [
[[package]]
name = "gobject-sys"
version = "0.22.6"
version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22a861859b887a79cf461359c192c97a57d8fb0229dd291232e57aa11f6fa72c"
checksum = "ec9aca94bb73989e3cfdbf8f2e0f1f6da04db4d291c431f444838925c4c63eda"
dependencies = [
"glib-sys",
"libc",
@ -379,9 +330,9 @@ dependencies = [
[[package]]
name = "graphene-rs"
version = "0.22.0"
version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7d1b7881f96869f49808b6adfe906a93a57a34204952253444d68c3208d71f1"
checksum = "6b86dfad7d14251c9acaf1de63bc8754b7e3b4e5b16777b6f5a748208fe9519b"
dependencies = [
"glib",
"graphene-sys",
@ -390,9 +341,9 @@ dependencies = [
[[package]]
name = "graphene-sys"
version = "0.22.0"
version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "517f062f3fd6b7fd3e57a3f038a74b3c23ca32f51199ff028aa704609943f79c"
checksum = "df583a85ba2d5e15e1797e40d666057b28bc2f60a67c9c24145e6db2cc3861ea"
dependencies = [
"glib-sys",
"libc",
@ -402,9 +353,9 @@ dependencies = [
[[package]]
name = "gsk4"
version = "0.11.1"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53c912dfcbd28acace5fc99c40bb9f25e1dcb73efb1f2608327f66a99acdcb62"
checksum = "61f5e72f931c8c9f65fbfc89fe0ddc7746f147f822f127a53a9854666ac1f855"
dependencies = [
"cairo-rs",
"gdk4",
@ -417,9 +368,9 @@ dependencies = [
[[package]]
name = "gsk4-sys"
version = "0.11.1"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7d54bbc7a9d8b6ffe4f0c95eede15ccfb365c8bf521275abe6bcfb57b18fb8a"
checksum = "755059de55fa6f85a46bde8caf03e2184c96bfda1f6206163c72fb0ea12436dc"
dependencies = [
"cairo-sys-rs",
"gdk4-sys",
@ -433,9 +384,9 @@ dependencies = [
[[package]]
name = "gtk4"
version = "0.11.3"
version = "0.9.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7181b837f04cbe93f79441475f7a00560a92cba7a72e38cc1a68b6f8b78eaae2"
checksum = "f274dd0102c21c47bbfa8ebcb92d0464fab794a22fad6c3f3d5f165139a326d6"
dependencies = [
"cairo-rs",
"field-offset",
@ -454,9 +405,9 @@ dependencies = [
[[package]]
name = "gtk4-macros"
version = "0.11.0"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3581b242ba62fdff122ebb626ea641582ec326031622bd19d60f85029c804a87"
checksum = "0ed1786c4703dd196baf7e103525ce0cf579b3a63a0570fe653b7ee6bac33999"
dependencies = [
"proc-macro-crate",
"proc-macro2",
@ -466,9 +417,9 @@ dependencies = [
[[package]]
name = "gtk4-sys"
version = "0.11.3"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20ba8e695e2640455561274e65e45f0a151619e450746007667f4b23ceae4e1b"
checksum = "41e03b01e54d77c310e1d98647d73f996d04b2f29b9121fe493ea525a7ec03d6"
dependencies = [
"cairo-sys-rs",
"gdk-pixbuf-sys",
@ -517,15 +468,6 @@ version = "0.2.186"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
[[package]]
name = "libredox"
version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f02ab6bace2054fb888a3c16f990117b579d14a3088e472d63c6011fa185c9d3"
dependencies = [
"libc",
]
[[package]]
name = "memchr"
version = "2.8.2"
@ -541,17 +483,11 @@ dependencies = [
"autocfg",
]
[[package]]
name = "option-ext"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "pango"
version = "0.22.6"
version = "0.20.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "251bdc6e6487b811be0e406a21e301e07e45c0aa8fa39e00c0c8e12a91752438"
checksum = "6576b311f6df659397043a5fa8a021da8f72e34af180b44f7d57348de691ab5c"
dependencies = [
"gio",
"glib",
@ -561,9 +497,9 @@ dependencies = [
[[package]]
name = "pango-sys"
version = "0.22.0"
version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd111a20ca90fedf03e09c59783c679c00900f1d8491cca5399f5e33609d5d6"
checksum = "186909673fc09be354555c302c0b3dcf753cd9fa08dcb8077fa663c80fb243fa"
dependencies = [
"glib-sys",
"gobject-sys",
@ -616,17 +552,6 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "redox_users"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
dependencies = [
"getrandom",
"libredox",
"thiserror",
]
[[package]]
name = "rustc_version"
version = "0.4.1"
@ -745,26 +670,6 @@ version = "0.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
[[package]]
name = "thiserror"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "toml"
version = "0.8.23"
@ -869,43 +774,13 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03c2856837ef78f57382f06b2b8563a2f512f7185d732608fd9176cb3b8edf0e"
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets 0.48.5",
]
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm 0.48.5",
"windows_aarch64_msvc 0.48.5",
"windows_i686_gnu 0.48.5",
"windows_i686_msvc 0.48.5",
"windows_x86_64_gnu 0.48.5",
"windows_x86_64_gnullvm 0.48.5",
"windows_x86_64_msvc 0.48.5",
"windows-targets",
]
[[package]]
@ -914,46 +789,28 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.6",
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc 0.52.6",
"windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm 0.52.6",
"windows_x86_64_msvc 0.52.6",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
@ -966,48 +823,24 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"

209
README.md
View file

@ -1,209 +0,0 @@
# BOS — Bread Operating System
An Arch-based, Hyprland desktop distribution that ships the [bread
ecosystem](https://github.com/Breadway) 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](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/skel` from 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 the `bakery` package manager.
- **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 + tuigreet → Hyprland session.
- **Boot splash**: Plymouth `bos` theme (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`.
- **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).
## 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`:
```sh
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`, and `bibata-cursor-theme` 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.
## Testing in a VM
A reusable, GPU-accelerated launcher lives at `~/bos-vm/run.sh`:
```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
`bos-settings` edits each bread\* app's TOML **non-destructively**: it parses
the file with `toml_edit`, 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). Views:
| View | Config |
|------|--------|
| bread | `bread/breadd.toml` — daemon, lua, modules, all adapters, events, notifications |
| breadbar | `breadbar/style.css` override |
| breadbox | `breadbox/config.toml` — launcher contexts |
| breadcrumbs | `breadcrumbs/breadcrumbs.toml` — settings, saved networks, profiles |
| breadpad | `breadpad/breadpad.toml` — settings, model + ollama, reminders, calendar |
| Snapshots | `snapper` list / rollback / delete |
| Packages | `bakery` installed list + updates |
| Hyprland | open config in editor + monitor list |
Build standalone:
```sh
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 110 |
| `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-utils` and 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**: not configured. Boot with Secure Boot disabled, or enroll
your own keys. The installer writes both an NVRAM entry and the removable
`EFI/BOOT/BOOTX64.EFI` fallback.
- **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):**
1. Boot the BOS ISO and open a terminal (`SUPER+Return`).
2. Mount the installed root and EFI, then chroot:
```sh
mount -o subvol=@ /dev/sdXN /mnt
mount /dev/sdXP /mnt/boot/efi # the EFI partition
arch-chroot /mnt
```
3. Reinstall the bootloader (the same sequence the installer uses):
```sh
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.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

View file

@ -1,14 +1,11 @@
[package]
name = "bos-settings"
version = "0.4.0"
version = "0.2.0"
edition = "2021"
[dependencies]
gtk4 = { version = "0.11", features = ["v4_12"] }
glib = "0.22"
# Shared ecosystem theming — bos-settings loads the same generated stylesheet as
# breadbar/breadbox/breadpad so the whole desktop looks consistent.
bread-theme = { git = "https://github.com/Breadway/bread-ecosystem", tag = "v0.2.8", features = ["gtk"] }
gtk4 = { version = "0.9", features = ["v4_12"] }
glib = "0.20"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
toml = "0.8"

View file

@ -1,30 +1,87 @@
//! Theming for bos-settings.
//!
//! bos-settings deliberately owns almost no styling: it loads the ecosystem's
//! shared stylesheet (the same one breadbar/breadbox/breadpad use, generated by
//! `bread-theme` from the pywal palette) and adds only the few layout rules
//! specific to this app's sidebar + content shell. This keeps it visually
//! identical to the rest of the bread desktop and live-recolouring for free.
use gtk4::CssProvider;
use std::cell::RefCell;
// App-specific layout only — everything visual (colours, buttons, entries,
// switches, sidebar/row styling, cards, scrollbars) comes from the shared sheet.
const APP_CSS: &str = "\
.view-content { padding: 24px; }\n\
.view-content > label.title { margin-bottom: 16px; }\n\
";
thread_local! {
static APP_PROVIDER: RefCell<Option<CssProvider>> = const { RefCell::new(None) };
const CSS: &str = r#"
window {
background-color: #2e3440;
color: #eceff4;
}
pub fn load(_display: &gtk4::gdk::Display) {
// Shared ecosystem stylesheet (loads the generated file or a rendered
// fallback, and live-reloads when the palette changes).
bread_theme::gtk::apply_shared();
// bos-settings layout, layered on top at APPLICATION priority.
APP_PROVIDER.with(|cell| bread_theme::gtk::apply_css(APP_CSS, cell));
.sidebar {
background-color: #3b4252;
border-right: 1px solid #434c5e;
}
.sidebar row {
padding: 8px 12px;
color: #d8dee9;
}
.sidebar row:selected {
background-color: #5e81ac;
color: #eceff4;
}
.sidebar .section-header {
padding: 12px 12px 4px 12px;
font-size: 0.75em;
font-weight: bold;
color: #616e88;
text-transform: uppercase;
letter-spacing: 1px;
}
.view-content {
padding: 24px;
}
.view-content label.title {
font-size: 1.4em;
font-weight: bold;
color: #eceff4;
margin-bottom: 16px;
}
button {
background-color: #5e81ac;
color: #eceff4;
border: none;
border-radius: 4px;
padding: 6px 16px;
}
button:hover {
background-color: #81a1c1;
}
button.destructive-action {
background-color: #bf616a;
}
button.destructive-action:hover {
background-color: #d08770;
}
entry {
background-color: #434c5e;
color: #eceff4;
border: 1px solid #4c566a;
border-radius: 4px;
}
textview {
background-color: #272c36;
color: #a3be8c;
font-family: monospace;
padding: 8px;
}
"#;
pub fn load(display: &gtk4::gdk::Display) {
let provider = CssProvider::new();
provider.load_from_string(CSS);
gtk4::style_context_add_provider_for_display(
display,
&provider,
gtk4::STYLE_PROVIDER_PRIORITY_APPLICATION,
);
}

View file

@ -6,6 +6,7 @@ fn css_path() -> PathBuf {
crate::config::config_dir().join("breadbar/style.css")
}
pub fn build() -> GBox {
let path = css_path();
let existing_css = std::fs::read_to_string(&path).unwrap_or_default();

View file

@ -50,10 +50,9 @@ fn stream_command(args: &[&str], log_buf: gtk4::TextBuffer) {
}
};
// Merge stderr into the channel too.
// Both are Some because we spawned with Stdio::piped() above.
let stdout = child.stdout.take().expect("stdout piped");
let stderr = child.stderr.take().expect("stderr piped");
// Merge stderr into the channel too
let stdout = child.stdout.take().unwrap();
let stderr = child.stderr.take().unwrap();
let tx2 = sender.clone();
std::thread::spawn(move || {

View file

Before

Width:  |  Height:  |  Size: 4 KiB

After

Width:  |  Height:  |  Size: 4 KiB

Before After
Before After

View file

@ -14,10 +14,7 @@
set -euo pipefail
REPO="$(cd "$(dirname "$0")" && pwd)"
# WORK defaults to /tmp, but on hermes /tmp is a 16 GB tmpfs — a full xz build
# (uncompressed rootfs + squashfs + work copies) can exhaust it mid-build. Allow
# pointing it at the NVMe instead: WORK=/home/.../bos-work sudo ./build-local.sh
WORK="${WORK:-/tmp/bos-work}"
WORK=/tmp/bos-work
OUT="${OUT:-$REPO/out}"
# Build against a throwaway copy of the profile so the working tree stays clean
@ -25,15 +22,10 @@ OUT="${OUT:-$REPO/out}"
STAGE=/tmp/bos-iso-stage
rm -rf "$STAGE" && cp -a "$REPO/iso" "$STAGE"
# Rewrite the [breadway] pacman repo URL to the fastest reachable address.
# CI_BUILD=1 — container runs on hestia with --network=host; localhost:3002 is direct
# default — building on hermes; git.breadway.dev is flaky from there, use Tailscale
# Only ever rewrites the staged copy, never the committed pacman.conf.
if [ "${CI_BUILD:-0}" = "1" ]; then
sed -i 's#https://git.breadway.dev/api/packages/Breadway/arch/os#http://localhost:3002/api/packages/Breadway/arch/os#' "$STAGE/pacman.conf"
else
sed -i 's#https://git.breadway.dev/api/packages/Breadway/arch/os#http://100.66.238.26:3002/api/packages/Breadway/arch/os#' "$STAGE/pacman.conf"
fi
# The public git.breadway.dev URL is flaky/unreachable from hermes; Forgejo is
# directly reachable over Tailscale (hestia 100.66.238.26:3002). Only rewrites
# the staged copy, never the committed pacman.conf.
sed -i 's#https://git.breadway.dev/api/packages/Breadway/arch/os#http://100.66.238.26:3002/api/packages/Breadway/arch/os#' "$STAGE/pacman.conf"
if [ "${FAST_BUILD:-0}" = "1" ]; then
echo "=== FAST_BUILD: squashfs -> zstd level 6 ==="
@ -49,7 +41,7 @@ grep airootfs_image_tool_options "$STAGE/profiledef.sh"
# created from skel (the live user and the installed user) then gets the same
# versions `bakery list` reports here, fully offline. Copied at build time so the
# 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)
BREAD_BINS=(bakery bread breadd breadman breadbar breadbox breadbox-sync breadcrumbs breadpad)
LAPTOP_HOME="${LAPTOP_HOME:-$(getent passwd "${SUDO_USER:-$USER}" | cut -d: -f6)}"
BAKERY_BIN="$LAPTOP_HOME/.local/bin"
BAKERY_STATE="$LAPTOP_HOME/.local/state/bakery"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 9 KiB

Before After
Before After

View file

@ -24,46 +24,23 @@ userdel -r liveuser 2>/dev/null || true
passwd -l root || true
# ---------------------------------------------------------------------------
# Pacman keyring. The live medium's /etc/pacman.d/gnupg doesn't reliably carry
# over to the target (unpackfs may skip it / perms differ), leaving the installed
# system unable to verify package signatures — the first `pacman -Syu` then dies
# with "keyring is not writable / required key missing". Initialise it here so a
# fresh install can update out of the box. archlinux-keyring is already present;
# [breadway] is SigLevel=Never so it needs no key.
# ---------------------------------------------------------------------------
if command -v pacman-key &>/dev/null; then
pacman-key --init || echo "WARN: pacman-key --init failed"
pacman-key --populate archlinux || echo "WARN: pacman-key --populate failed"
fi
# ---------------------------------------------------------------------------
# Initramfs HOOKS: microcode + plymouth. Edit HOOKS first, rebuild once below.
# microcode — embeds the (autodetect-pruned) CPU microcode into the initramfs
# so it loads at early boot. The live ISO embeds ucode the same way, so the
# ISO /boot carries no separate ucode image and bos-copy-kernel stages none
# onto the target — the installed initramfs must therefore carry it itself.
# Must sit AFTER `autodetect` so it's pruned to the running CPU's microcode.
# plymouth — the BOS boot splash. Only the udev `plymouth` hook exists (there
# is NO `sd-plymouth`), so always insert it after `udev`.
# All best-effort: a failure here still leaves a bootable initramfs.
# ---------------------------------------------------------------------------
if [[ -f /etc/mkinitcpio.conf ]]; then
if ! grep -qE '^HOOKS=.*\bmicrocode\b' /etc/mkinitcpio.conf; then
sed -i 's/^\(HOOKS=.*\bautodetect\b\)/\1 microcode/' /etc/mkinitcpio.conf \
|| echo "WARN: adding microcode hook failed"
fi
if command -v plymouth-set-default-theme &>/dev/null \
&& ! grep -qE '^HOOKS=.*\bplymouth\b' /etc/mkinitcpio.conf; then
sed -i 's/^\(HOOKS=.*\budev\b\)/\1 plymouth/' /etc/mkinitcpio.conf \
|| echo "WARN: adding plymouth hook failed"
fi
fi
# ---------------------------------------------------------------------------
# Boot splash (Plymouth) — BOS logo + spinner instead of kernel text. Set the
# theme + cmdline BEFORE grub so grub.cfg picks up the new cmdline.
# Boot splash (Plymouth) — BOS logo + spinner instead of kernel text. Done
# BEFORE grub so grub.cfg picks up the new cmdline and the rebuilt initramfs.
# All best-effort: if anything here fails the system still boots (just without
# the splash) — the initramfs the initcpio module already built stays valid.
# ---------------------------------------------------------------------------
if command -v plymouth-set-default-theme &>/dev/null; then
# Ensure the plymouth hook is in HOOKS (plymouthcfg/initcpiocfg usually add it;
# this is the belt). Handle both the udev and systemd initramfs styles.
if ! grep -q 'plymouth' /etc/mkinitcpio.conf 2>/dev/null; then
if grep -qE '^HOOKS=.*\bsystemd\b' /etc/mkinitcpio.conf; then
sed -i 's/^\(HOOKS=.*\bsystemd\b\)/\1 sd-plymouth/' /etc/mkinitcpio.conf \
|| echo "WARN: adding sd-plymouth hook failed"
else
sed -i 's/^\(HOOKS=.*\budev\b\)/\1 plymouth/' /etc/mkinitcpio.conf \
|| echo "WARN: adding plymouth hook failed"
fi
fi
# Clean boot: splash activates plymouth; hiding systemd status removes the
# "[ OK ] Started ..." text (what looked like kernel output) even if the
# splash itself doesn't grab the display (e.g. in some VMs).
@ -71,13 +48,10 @@ if command -v plymouth-set-default-theme &>/dev/null; then
sed -i 's/^\(GRUB_CMDLINE_LINUX_DEFAULT="\)/\1splash quiet vt.global_cursor_default=0 systemd.show_status=false rd.systemd.show_status=false rd.udev.log_level=3 /' \
/etc/default/grub || echo "WARN: adding splash cmdline failed"
fi
plymouth-set-default-theme bos || echo "WARN: plymouth-set-default-theme failed"
# Set the BOS theme and rebuild the initramfs (-R) with the plymouth hook.
plymouth-set-default-theme -R bos || echo "WARN: plymouth-set-default-theme failed"
fi
# Rebuild every preset (default + fallback that bos-copy-kernel wrote) so the
# microcode + plymouth HOOKS above are actually baked into the initramfs.
mkinitcpio -P || echo "WARN: mkinitcpio -P failed"
# ---------------------------------------------------------------------------
# Install GRUB (UEFI). /boot now has the kernel + initramfs, and the mount
# module has bind-mounted /proc /sys /dev /run + efivars into this chroot, so
@ -126,33 +100,11 @@ fi
# ---------------------------------------------------------------------------
for unit in NetworkManager.service bluetooth.service systemd-timesyncd.service \
tlp.service greetd.service snapper-cleanup.timer grub-btrfsd.service \
fstrim.timer cups.socket avahi-daemon.service ufw.service \
fwupd-refresh.timer reflector.timer; do
fstrim.timer cups.socket; do
systemctl enable "$unit" || echo "WARN: failed to enable $unit"
done
systemctl set-default graphical.target || echo "WARN: set-default graphical failed"
# ---------------------------------------------------------------------------
# mDNS resolution (nss-mdns): insert mdns_minimal into the hosts: line so the
# resolver answers *.local (network printers, other hosts) via avahi. Idempotent.
# ---------------------------------------------------------------------------
if [[ -f /etc/nsswitch.conf ]] && ! grep -q 'mdns_minimal' /etc/nsswitch.conf; then
sed -i 's/^\(hosts:[[:space:]]*\)/\1mdns_minimal [NOTFOUND=return] /' \
/etc/nsswitch.conf || echo "WARN: wiring nss-mdns failed"
fi
# ---------------------------------------------------------------------------
# Firewall: deny inbound by default, allow outbound, and permit inbound mDNS so
# avahi printer/service discovery keeps working. Best-effort — rule application
# happens at boot; here we only persist the policy + enable the unit.
# ---------------------------------------------------------------------------
if command -v ufw &>/dev/null; then
ufw default deny incoming || echo "WARN: ufw default deny incoming failed"
ufw default allow outgoing || echo "WARN: ufw default allow outgoing failed"
ufw allow 5353/udp || echo "WARN: ufw allow mDNS failed"
ufw --force enable || echo "WARN: ufw enable failed"
fi
# The bread ecosystem (bakery + bread, breadbar, breadbox, breadcrumbs, breadpad)
# is bakery-managed, not pacman: the binaries and bakery manifest live in
# /etc/skel/.local (baked in at ISO build time) and are copied into the user's

View file

@ -1,7 +0,0 @@
SHELL=/usr/bin/zsh
GROUP=users
HOME=/home
INACTIVE=-1
EXPIRE=
SKEL=/etc/skel
CREATE_MAIL_SPOOL=no

View file

@ -35,8 +35,7 @@ Include = /etc/pacman.d/mirrorlist
#
# Forgejo signs the repo db with a key pacman can't look up, so TrustAll
# fails. SigLevel = Never skips verification (acceptable for this private
# repo over TLS). Future improvement: import Forgejo's signing key and
# switch to SigLevel = Required for full package verification.
# repo over TLS). TODO: import Forgejo's signing key + SigLevel = Required.
# -----------------------------------------------------------------------
# The section name must match Forgejo's served db filename
# ({owner}.{group}.{domain}.db) — pacman fetches "<section>.db" from Server.

View file

@ -1,26 +0,0 @@
-- low-battery-warning — notify once when the battery runs low (zero-config).
-- Shipped active in BOS; auto-discovered by breadd. Safe on desktops too
-- (simply never fires without a battery).
local M = bread.module({ name = "low-battery-warning", version = "1.0.0" })
local warned = false
function M.on_load()
bread.on("bread.power.battery.low", function(event)
if warned then return end
warned = true
local pct = event.data.battery_percent or "?"
bread.notify("Battery low (" .. pct .. "%). Plug in soon.", {
urgency = "critical",
title = "Battery",
timeout = 10000,
})
end)
bread.on("bread.power.ac.connected", function()
warned = false
end)
end
return M

View file

@ -57,41 +57,6 @@ hl.config({
},
})
-- ---------------------------------------------------------------------------
-- Animations — snappy curves + per-leaf speeds (matches the reference laptop;
-- the hl.config default above is much slower).
-- ---------------------------------------------------------------------------
local curves = {
easeOutQuint = { type = "bezier", points = { { 0.23, 1 }, { 0.32, 1 } } },
easeInOutCubic = { type = "bezier", points = { { 0.65, 0.05 }, { 0.36, 1 } } },
almostLinear = { type = "bezier", points = { { 0.5, 0.5 }, { 0.75, 1 } } },
quick = { type = "bezier", points = { { 0.15, 0 }, { 0.1, 1 } } },
}
for name, curve in pairs(curves) do
hl.curve(name, curve)
end
local animations = {
{ leaf = "global", enabled = true, speed = 10, bezier = "default" },
{ leaf = "border", enabled = true, speed = 5.39, bezier = "easeOutQuint" },
{ leaf = "windows", enabled = true, speed = 4.79, bezier = "easeOutQuint" },
{ leaf = "windowsIn", enabled = true, speed = 4.1, bezier = "easeOutQuint", style = "popin 87%" },
{ leaf = "windowsOut", enabled = true, speed = 1.49, bezier = "linear", style = "popin 87%" },
{ leaf = "fade", enabled = true, speed = 3.03, bezier = "quick" },
{ leaf = "layers", enabled = true, speed = 3.81, bezier = "easeOutQuint" },
{ leaf = "workspaces", enabled = true, speed = 1.94, bezier = "almostLinear", style = "fade" },
}
for _, animation in ipairs(animations) do
hl.animation(animation)
end
-- ---------------------------------------------------------------------------
-- Window rules — float + centre the onboarding popups (kitty --class …).
-- ---------------------------------------------------------------------------
hl.window_rule({ name = "bos-keybinds", match = { class = "^(bos-keybinds)$" }, float = true, size = { 760, 720 } })
hl.window_rule({ name = "bos-welcome", match = { class = "^(bos-welcome)$" }, float = true, size = { 700, 560 } })
hl.window_rule({ name = "bos-netsetup", match = { class = "^(bos-netsetup)$" }, float = true, size = { 700, 560 } })
-- ---------------------------------------------------------------------------
-- Environment (vendor-neutral; no GPU-specific vars so it works on Intel/AMD).
-- ---------------------------------------------------------------------------
@ -120,8 +85,6 @@ hl.bind(mod .. " + E", hl.dsp.exec_cmd("nautilus"))
hl.bind(mod .. " + B", hl.dsp.exec_cmd("zen-browser"))
hl.bind(mod .. " + U", hl.dsp.exec_cmd("breadpad"))
hl.bind(mod .. " + M", hl.dsp.exec_cmd("breadman"))
hl.bind(mod .. " + comma", hl.dsp.exec_cmd("bos-settings"))
hl.bind(mod .. " + slash", hl.dsp.exec_cmd("bos-keybinds"))
hl.bind(mod .. " + L", hl.dsp.exec_cmd("loginctl lock-session"))
hl.bind(mod .. " + F", hl.dsp.window.fullscreen({ action = "toggle" }))
hl.bind(mod .. " + V", hl.dsp.window.float({ action = "toggle" }))
@ -189,9 +152,6 @@ hl.bind("XF86AudioPlay", hl.dsp.exec_cmd("playerctl play-pause"),
-- ---------------------------------------------------------------------------
hl.on("hyprland.start", function()
local startup = {
-- Generate the shared bread GUI stylesheet first, so breadbar/breadbox/
-- bos-settings load it on start (they also live-reload if it changes).
"bread-theme generate",
-- Global dark theme: GTK4/libadwaita + GTK3 theme + icon + cursor.
"gsettings set org.gnome.desktop.interface color-scheme prefer-dark",
"gsettings set org.gnome.desktop.interface gtk-theme Adwaita-dark",
@ -204,17 +164,10 @@ hl.on("hyprland.start", function()
"awww-daemon",
-- set the default wallpaper once the daemon is up (retry until ready)
[[bash -c 'until awww img /usr/share/backgrounds/bos/bread-background.png 2>/dev/null; do sleep 0.3; done']],
-- breadd runs as a systemd user service (~/.config/systemd/user/breadd.service,
-- enabled in skel). It autostarts at login but before Hyprland exists, so
-- push the compositor's Wayland env into the user manager and restart breadd
-- to pick it up — that's how it gets HYPRLAND_INSTANCE_SIGNATURE to talk to Hyprland.
"dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP HYPRLAND_INSTANCE_SIGNATURE",
"systemctl --user restart breadd",
"breadd",
"breadbar",
"breadbox-sync",
"hypridle",
-- first-boot onboarding (self-gates after the first run)
"bos-welcome",
}
for _, cmd in ipairs(startup) do
hl.dispatch(hl.dsp.exec_cmd(cmd))

View file

@ -1,9 +1,8 @@
# BOS kitty config.
# Translucent background so Hyprland's blur shows through behind the terminal,
# while text stays fully opaque. Colours are left to kitty's default / pywal.
# 0.6 matches the reference laptop; the actual blur is supplied by Hyprland's
# decoration:blur (kitty's own background_blur is macOS-only).
background_opacity 0.6
background_opacity 0.88
background_blur 1
font_family JetBrains Mono
font_size 11.0

View file

@ -1,51 +0,0 @@
# Default applications for common file types. Without this, freshly installed
# BOS has no handler registered for images/video/text/etc., so opening a file
# from nautilus does nothing. Maps to the apps shipped in packages.x86_64.
[Default Applications]
# Images -> Loupe
image/png=org.gnome.Loupe.desktop
image/jpeg=org.gnome.Loupe.desktop
image/gif=org.gnome.Loupe.desktop
image/webp=org.gnome.Loupe.desktop
image/bmp=org.gnome.Loupe.desktop
image/tiff=org.gnome.Loupe.desktop
image/svg+xml=org.gnome.Loupe.desktop
# Audio/Video -> VLC
audio/mpeg=vlc.desktop
audio/flac=vlc.desktop
audio/ogg=vlc.desktop
audio/x-wav=vlc.desktop
audio/aac=vlc.desktop
video/mp4=vlc.desktop
video/x-matroska=vlc.desktop
video/webm=vlc.desktop
video/quicktime=vlc.desktop
video/x-msvideo=vlc.desktop
# Plain text / source -> GNOME Text Editor
text/plain=org.gnome.TextEditor.desktop
text/markdown=org.gnome.TextEditor.desktop
application/x-shellscript=org.gnome.TextEditor.desktop
application/json=org.gnome.TextEditor.desktop
application/toml=org.gnome.TextEditor.desktop
text/x-readme=org.gnome.TextEditor.desktop
# Documents / web -> Zen (PDF + HTML)
application/pdf=zen.desktop
text/html=zen.desktop
x-scheme-handler/http=zen.desktop
x-scheme-handler/https=zen.desktop
# Archives -> File Roller
application/zip=org.gnome.FileRoller.desktop
application/x-tar=org.gnome.FileRoller.desktop
application/gzip=org.gnome.FileRoller.desktop
application/x-7z-compressed=org.gnome.FileRoller.desktop
application/x-rar=org.gnome.FileRoller.desktop
application/vnd.rar=org.gnome.FileRoller.desktop
application/x-xz=org.gnome.FileRoller.desktop
application/x-bzip2=org.gnome.FileRoller.desktop
# Directories -> Nautilus
inode/directory=org.gnome.Nautilus.desktop

View file

@ -1,21 +0,0 @@
[Unit]
Description=Bread Runtime Daemon
[Service]
Type=simple
# %h = the user's home — works for any account created from this skel.
ExecStart=%h/.local/bin/breadd
Restart=on-failure
RestartSec=2
UMask=0077
RuntimeDirectory=bread
RuntimeDirectoryMode=0700
# Keep /run/user/<uid>/bread across restarts so the shared theme.css that
# bread-theme writes there (and the daemon socket) survive a `restart breadd`.
RuntimeDirectoryPreserve=yes
KillSignal=SIGTERM
TimeoutStopSec=5
Environment=RUST_LOG=info
[Install]
WantedBy=default.target

File diff suppressed because it is too large Load diff

View file

@ -1,15 +1,4 @@
# BOS default zsh config — Powerlevel10k prompt + plugins + pywal palette.
#
# Mirrors the BOS dev shell, but sources plugins from the distro packages
# (/usr/share/zsh/...) instead of oh-my-zsh, so there's no framework to manage.
# Customise the prompt with `p10k configure` (rewrites ~/.p10k.zsh).
# Enable Powerlevel10k instant prompt. Should stay close to the top of ~/.zshrc.
# Initialization code that may require console input (password prompts, [y/n]
# confirmations, etc.) must go above this block; everything else may go below.
if [[ -r "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" ]]; then
source "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh"
fi
# BOS default zsh config — quality-of-life defaults, easy to extend.
# History
HISTFILE=~/.zsh_history
@ -22,21 +11,10 @@ autoload -Uz compinit && compinit
zstyle ':completion:*' menu select
zstyle ':completion:*' matcher-list 'm:{a-z}={A-Z}'
# Emacs-style key bindings
# Key bindings (emacs style + common extras)
bindkey -e
# Prompt — Powerlevel10k (republished to [breadway] as zsh-theme-powerlevel10k).
source /usr/share/zsh-theme-powerlevel10k/powerlevel10k.zsh-theme
# Plugins (order matters: syntax-highlighting must be sourced LAST).
ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE='fg=60'
source /usr/share/zsh/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh 2>/dev/null
source /usr/share/zsh/plugins/zsh-history-substring-search/zsh-history-substring-search.zsh 2>/dev/null
source /usr/share/zsh/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh 2>/dev/null
# history-substring-search: ↑/↓ search history by the typed prefix.
bindkey '^[[A' history-substring-search-up
bindkey '^[[B' history-substring-search-down
bindkey '^[[A' history-search-backward
bindkey '^[[B' history-search-forward
# fzf — fuzzy history search on Ctrl+R, fuzzy file find on Ctrl+T
if command -v fzf &>/dev/null; then
@ -76,18 +54,12 @@ alias free='free -h'
alias grep='grep --color=auto'
alias ip='ip --color=auto'
# Updates — bos-update runs both channels (pacman + bakery). pacman aliased to
# sudo so `pacman -Syu` etc. just work.
alias update='bos-update'
alias pacman='sudo pacman'
# bakery / bread
alias update='bakery update'
# ~/.local/bin holds the bread* binaries baked in at build time.
export PATH="$HOME/.local/bin:$PATH"
# Powerlevel10k prompt configuration.
[[ ! -f ~/.p10k.zsh ]] || source ~/.p10k.zsh
# Import pywal colour palette (drives the terminal colours from the wallpaper).
if [ -f "$HOME/.cache/wal/sequences" ]; then
cat "$HOME/.cache/wal/sequences"
fi
# Prompt — simple and fast (no starship dep)
autoload -Uz vcs_info
precmd() { vcs_info }
zstyle ':vcs_info:git:*' formats ' (%b)'
setopt PROMPT_SUBST
PROMPT='%F{cyan}%~%f%F{yellow}${vcs_info_msg_0_}%f %(?.%F{green}.%F{red})%f '

View file

@ -1,6 +0,0 @@
# Compressed RAM swap. systemd-zram-generator reads this and creates a zram
# device + swap at boot — no on-disk swap partition needed. Sized at half RAM
# capped to 4 GiB, zstd-compressed (typically ~3:1, so cheap headroom).
[zram0]
zram-size = min(ram / 2, 4096)
compression-algorithm = zstd

View file

@ -1,4 +0,0 @@
#!/bin/bash
# Show the BOS keybind cheatsheet in a floating terminal (bound to SUPER+/).
# The bos-keybinds window class is floated/centred by a Hyprland window rule.
exec kitty --class bos-keybinds --title "BOS Keybinds" -- less -R /usr/share/bos/keybinds.txt

View file

@ -11,7 +11,7 @@ set -e
# (breadd + breadbar + breadbox + keybinds) — proper live-media functionality,
# not an installer kiosk.
if ! id liveuser &>/dev/null; then
useradd -m -s /usr/bin/zsh liveuser
useradd -m -s /bin/bash liveuser
for g in wheel video input audio storage power; do
getent group "$g" >/dev/null 2>&1 && gpasswd -a liveuser "$g" >/dev/null || true
done

View file

@ -1,32 +0,0 @@
#!/bin/bash
# bos-update — update all of BOS in one go.
#
# BOS packages come from two channels, so a full update touches both:
# 1. pacman — Arch base/desktop + the [breadway] repo (bos-settings, etc.).
# Every transaction is snapshotted by snap-pac, so you can roll
# back from the GRUB "snapshots" submenu or BOS Settings.
# 2. bakery — the bread ecosystem apps in ~/.local/bin (bread, breadbar,
# breadbox, breadcrumbs, breadpad, breadman, bread-theme).
#
# Best-effort: a failure in one channel doesn't abort the other.
set -uo pipefail
bold() { printf '\033[1m%s\033[0m\n' "$1"; }
bold "==> System packages (pacman -Syu)"
if command -v pacman >/dev/null; then
sudo pacman -Syu || echo "WARN: pacman update failed"
else
echo "pacman not found; skipping"
fi
echo
bold "==> Bread ecosystem (bakery update --all)"
if command -v bakery >/dev/null; then
bakery update --all || echo "WARN: bakery update failed"
else
echo "bakery not found; skipping"
fi
echo
bold "==> BOS is up to date."

View file

@ -1,34 +0,0 @@
#!/bin/bash
# First-run welcome. Shows a short getting-started message once, then drops a
# marker so it never shows again. Launched from the Hyprland autostart; the
# bos-welcome window class is floated/centred by a Hyprland window rule.
set -u
# Never run in the live/installer session — only on an installed system.
[[ "$(id -un)" == "liveuser" ]] && exit 0
marker="${XDG_CONFIG_HOME:-$HOME/.config}/bos/.welcomed"
[[ -f "$marker" ]] && exit 0
mkdir -p "$(dirname "$marker")"
# First-run network check. A fresh install usually boots with no connection
# (Wi-Fi isn't configured during install), and the first `bos-update`/pacman run
# then fails with confusing DNS/"could not resolve host" errors. If
# NetworkManager reports we're not fully online, open nmtui so the user can join
# a network before anything else. Best-effort: missing nmcli/nmtui/kitty, or the
# user quitting nmtui, must never block the welcome below.
if command -v nmcli &>/dev/null; then
conn="$(nmcli networking connectivity check 2>/dev/null)"
if [[ "$conn" != "full" ]]; then
notify-send -u normal "BOS" "No internet yet — opening network setup so updates work." 2>/dev/null || true
if command -v nmtui &>/dev/null; then
kitty --class bos-netsetup --title "Connect to a network" -- nmtui connect 2>/dev/null || true
fi
fi
fi
# Mark welcomed only now, so an interrupted/aborted network step still re-prompts
# next login rather than being suppressed forever.
touch "$marker"
exec kitty --class bos-welcome --title "Welcome to BOS" -- less -R /usr/share/bos/welcome.txt

View file

@ -1,50 +0,0 @@
██████ ██████ ███████ keyboard shortcuts
██ ██ ██ ██ ██ SUPER is the Windows/Cmd key
██████ ██ ██ ███████
══════════════════════════════════════════════════════════
APPS & WINDOWS
SUPER + Return terminal (kitty)
SUPER + Space app launcher (breadbox)
SUPER + E files (nautilus)
SUPER + B browser (zen)
SUPER + U notes / reminders (breadpad)
SUPER + M package manager (breadman)
SUPER + , BOS Settings
SUPER + / this keybind cheatsheet
SUPER + L lock screen
SUPER + Backspace close window
SUPER + F fullscreen
SUPER + V toggle floating
SUPER + Shift + V clipboard history
SUPER + T toggle split direction
SUPER + Tab last window
SUPER + N exit Hyprland (log out)
SCREENSHOTS
SUPER + Shift + S select region -> file
SUPER + Shift + C select region -> clipboard
SUPER + Shift + P whole screen -> file
FOCUS & MOVE
SUPER + arrows move focus
SUPER + Shift + h/j/k/l move window
SUPER + Shift + arrows resize window
WORKSPACES
SUPER + 1..0 switch to workspace 1..10
SUPER + Shift + 1..0 move window to workspace
SUPER + [ / ] previous / next workspace
SUPER + Shift + [ / ] move window prev / next workspace
SUPER + scroll cycle workspaces
MOUSE
SUPER + left-drag move window
SUPER + right-drag resize window
MEDIA & HARDWARE KEYS
volume / brightness / play-pause / next / prev (work on lock screen)
──────────────────────────────────────────────────────────
Press q to close. Configure everything in BOS Settings (SUPER + ,).

View file

@ -1,24 +0,0 @@
Welcome to BOS — the Bread Operating System
══════════════════════════════════════════════════════════
You're running a complete Hyprland desktop with the bread
ecosystem preinstalled. A few things to get you started:
• SUPER + / show the keybind cheatsheet (any time)
• SUPER + , open BOS Settings — configure bread, the
bar, launcher, Wi-Fi profiles, notes,
snapshots and package updates, all in one
place (no config files needed)
• SUPER + Space the app launcher (breadbox)
• SUPER + Return a terminal
The bar at the top (breadbar) shows workspaces, the clock,
system stats, and your tray. Notifications appear top-right.
Your system is snapshotted on every package change — if an
update breaks something, roll back from BOS Settings or pick
a snapshot from the GRUB menu at boot.
──────────────────────────────────────────────────────────
Press q to close. This message won't show again.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

Before After
Before After

View file

@ -1,5 +0,0 @@
title Bread OS install medium (copy to RAM, UEFI)
sort-key 015
linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux
initrd /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img
options archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% copytoram=y

View file

@ -75,11 +75,6 @@ pipewire-jack
networkmanager
network-manager-applet
iw
# mDNS service/name resolution — lets CUPS auto-discover network printers and
# resolves .local hostnames (avahi-daemon enabled + nss-mdns wired in
# post-install.sh).
avahi
nss-mdns
# Wi-Fi backend for NetworkManager (its default; no extra config needed).
wpa_supplicant
bluez
@ -95,12 +90,6 @@ libpulse
# 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.
gnome-themes-extra
# Schema + backend behind `gsettings set org.gnome.desktop.interface
# color-scheme prefer-dark` (set in hyprland.lua autostart). Without these the
# gsettings call fails silently and libadwaita apps (nautilus, gnome-text-editor)
# render in LIGHT mode regardless of the GTK theme.
gsettings-desktop-schemas
dconf
# Credential/keyring storage — browsers, SSH agents, and most apps persist
# passwords here; without it every session loses saved logins. seahorse is the
# GUI to view/manage the stored secrets and keys.
@ -118,12 +107,6 @@ noto-fonts-emoji
ttf-jetbrains-mono
# Nerd font variant — icons in terminal tools (eza --icons, fastfetch, yazi)
ttf-jetbrains-mono-nerd
# Metric-compatible (Arial/Times/Courier) so Office/web docs lay out correctly,
# broad Unicode fallback, and the Font Awesome icon glyph set (otf-, the desktop
# variant — ttf-font-awesome resolves to the web-only woff2 build).
ttf-liberation
ttf-dejavu
otf-font-awesome
# Terminal
kitty
@ -139,13 +122,10 @@ file-roller
# GUI applications a general desktop is expected to have out of the box.
# gnome-text-editor: graphical editor (terminal editors aside); gnome-calculator:
# calculator; loupe: Wayland-native image viewer (default for image files);
# zathura(+pdf-mupdf): lightweight Wayland PDF viewer (BOS had no PDF reader).
# calculator; loupe: Wayland-native image viewer (default for image files).
gnome-text-editor
gnome-calculator
loupe
zathura
zathura-pdf-mupdf
# Media player — BOS ships gstreamer codecs but otherwise has no player app.
vlc
# Web browser (served from the [Breadway] repo; AUR zen-browser-bin republished
@ -193,12 +173,6 @@ plymouth
gst-plugins-good
gst-plugins-bad
gst-plugins-ugly
# Hardware video acceleration (VA-API) — lets the AMD/Intel GPU decode H.264/HEVC/
# VP9 in mpv, VLC, and browsers instead of the CPU (cooler, longer battery on
# video). The open Mesa VA-API backend (radeonsi_drv_video.so etc.) now ships in
# the `mesa` package itself (pulled in already), so only libva (deps) + the
# `vainfo` verification tool need listing here.
libva-utils
# GUI audio mixer — useful when output device needs manual switching.
pavucontrol
@ -216,15 +190,8 @@ man-pages
less
# Base CLI tools every install should have.
# Shell — zsh with the same prompt + plugins as the dev laptop. Powerlevel10k is
# AUR-only, so it's republished to [breadway] (see packaging/powerlevel10k). The
# three plugins come from the official repos; skel/.zshrc sources them in order
# (autosuggestions → history-substring-search → syntax-highlighting LAST).
# Shell
zsh
zsh-theme-powerlevel10k
zsh-autosuggestions
zsh-history-substring-search
zsh-syntax-highlighting
# Editors
nano
micro
@ -269,14 +236,6 @@ system-config-printer
# remote post-install (needs network); the runtime is shipped ready.
flatpak
# Firewall — ufw, enabled deny-incoming in post-install.sh (mDNS allowed so
# printer discovery still works).
ufw
# Firmware updates via LVFS (works with gnome-software / fwupdmgr).
fwupd
# Compressed RAM swap — see /etc/systemd/zram-generator.conf.
zram-generator
# Icon and cursor themes
# Papirus-Dark: cohesive icon set used as the BOS default (set via gsettings in
# hyprland.lua autostart and in skel gtk-3.0/settings.ini).

View file

@ -35,8 +35,7 @@ Include = /etc/pacman.d/mirrorlist
#
# Forgejo signs the repo db with a key pacman can't look up, so TrustAll
# fails. SigLevel = Never skips verification (acceptable for this private
# repo over TLS). Future improvement: import Forgejo's signing key and
# switch to SigLevel = Required for full package verification.
# repo over TLS). TODO: import Forgejo's signing key + SigLevel = Required.
# -----------------------------------------------------------------------
# The section name must match Forgejo's served db filename
# ({owner}.{group}.{domain}.db) — pacman fetches "<section>.db" from Server.

View file

@ -21,7 +21,4 @@ file_permissions=(
["/usr/local/bin/bos-launch-calamares"]="0:0:755"
["/usr/local/bin/bos-copy-kernel"]="0:0:755"
["/usr/local/bin/bos-session"]="0:0:755"
["/usr/local/bin/bos-keybinds"]="0:0:755"
["/usr/local/bin/bos-welcome"]="0:0:755"
["/usr/local/bin/bos-update"]="0:0:755"
)

View file

@ -8,19 +8,6 @@ LINUX /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux
INITRD /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img
APPEND archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID%
# Copy-to-RAM boot option — loads airootfs.sfs entirely into RAM, so the
# installer reads from memory rather than a possibly-flaky USB (avoids SquashFS
# read errors during unpackfs). Needs enough RAM for the image (~3 GB).
LABEL archtoram
TEXT HELP
Boot Bread OS, copying the image into RAM first.
More reliable installs from USB; needs a few GB of RAM.
ENDTEXT
MENU LABEL Bread OS install medium (%ARCH%, BIOS) ^copy to RAM
LINUX /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux
INITRD /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img
APPEND archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% copytoram=y
# Accessibility boot option
LABEL archspeech
TEXT HELP

View file

@ -1,105 +0,0 @@
# BOS in-house rebuild of zsh-theme-powerlevel10k (AUR-only upstream).
# Republished to [breadway] so the ISO can pull the BOS default prompt via pacman
# (same pattern as bibata / zen-browser-bin). Upstream maintainer header kept below.
# Maintainer: Mark Wagie <mark dot wagie at proton dot me>
# Contributor: Christian Rebischke <chris.rebischke@archlinux.org>
# Contributor: Jeff Henson <jeff@henson.io>
# Contributor: Ron Asimi <ron dot asimi at gmail dot com>
# Contributor: Roman Perepelitsa <roman.perepelitsa@gmail.com>
pkgname=zsh-theme-powerlevel10k
# Whenever pkgver is updated, _libgit2ver below must also be updated.
pkgver=1.20.17 ## see P9K_VERSION in internal/p10k.zsh
_libgit2ver="tag-2ecf33948a4df9ef45a66c68b8ef24a5e60eaac6"
pkgrel=1
epoch=1
pkgdesc="Powerlevel10k is a theme for Zsh. It emphasizes speed, flexibility and out-of-the-box experience."
arch=('x86_64' 'aarch64')
url='https://github.com/romkatv/powerlevel10k'
license=('MIT')
depends=(
'glibc'
'zsh'
)
makedepends=(
'git'
'cmake'
)
optdepends=(
# It works well with Nerd Fonts, Source Code Pro, Font Awesome, Powerline,
# and even the default system fonts. The full choice of style options is
# available only when using Nerd Fonts.
'ttf-meslo-nerd-font-powerlevel10k: recommended font'
'powerline-fonts: patched fonts for powerline'
'ttf-font-nerd: full choice of style options'
)
replaces=('zsh-theme-powerlevel9k')
_commit=9253fb1c5034410c43a0c681ff8294181c54016c
# _libgit2ver depends on pkgver. They must be updated together. See libgit2_version in:
# https://raw.githubusercontent.com/romkatv/powerlevel10k/v${pkgver}/gitstatus/build.info
source=(
"git+https://github.com/romkatv/powerlevel10k.git#commit=${_commit}"
# "powerlevel10k-${pkgver}.tar.gz::https://github.com/romkatv/powerlevel10k/archive/v${pkgver}.tar.gz"
# "https://github.com/romkatv/powerlevel10k/releases/download/v$pkgver/powerlevel10k-$pkgver.tar.gz.asc"
"libgit2-${_libgit2ver}.tar.gz::https://github.com/romkatv/libgit2/archive/${_libgit2ver}.tar.gz")
sha256sums=('f0edc2cc5bfcdfcf3b94f10597c252873567a990e651d04059c887046fba6701'
'4ce11d71ee576dbbc410b9fa33a9642809cc1fa687b315f7c23eeb825b251e93')
#validpgpkeys=('8B060F8B9EB395614A669F2A90ACE942EB90C3DD') # Roman Perepelitsa <roman.perepelitsa@gmail.com>
build() {
cd "libgit2-${_libgit2ver}"
local cmake_options=(
-W no-dev
-D CMAKE_BUILD_TYPE='None'
-D ZERO_NSEC='ON'
-D THREADSAFE='ON'
-D USE_BUNDLED_ZLIB='ON'
-D REGEX_BACKEND='builtin'
-D USE_HTTP_PARSER='builtin'
-D USE_SSH='OFF'
-D USE_HTTPS='OFF'
-D BUILD_CLAR='OFF'
-D USE_GSSAPI='OFF'
-D USE_NTLMCLIENT='OFF'
-D BUILD_SHARED_LIBS='OFF'
-D ENABLE_REPRODUCIBLE_BUILDS='ON'
)
cmake "${cmake_options[@]}" .
make
# build gitstatus
cd "$srcdir/powerlevel10k/gitstatus"
export CXXFLAGS+=" -I${srcdir}/libgit2-${_libgit2ver}/include -DGITSTATUS_ZERO_NSEC -D_GNU_SOURCE"
export LDFLAGS+=" -L${srcdir}/libgit2-${_libgit2ver}"
make
}
package() {
cd powerlevel10k
find . -type f -exec install -D '{}' "$pkgdir/usr/share/${pkgname}/{}" ';'
install -d "${pkgdir}/usr/share/licenses/${pkgname}"
ln -s "/usr/share/${pkgname}/LICENSE" "${pkgdir}/usr/share/licenses/${pkgname}"
# delete unnecessary files. See also: https://bugs.archlinux.org/task/66737
rm -r "${pkgdir}/usr/share/${pkgname}/.git"
rm -r "${pkgdir}/usr/share/${pkgname}/gitstatus/deps/"
rm -r "${pkgdir}/usr/share/${pkgname}/gitstatus/obj"
rm -r "${pkgdir}/usr/share/${pkgname}/gitstatus/src/"
rm -r "${pkgdir}/usr/share/${pkgname}/gitstatus/.vscode/"
rm "${pkgdir}/usr/share/${pkgname}/.gitattributes"
rm "${pkgdir}/usr/share/${pkgname}/.gitignore"
rm "${pkgdir}/usr/share/${pkgname}/Makefile"
rm "${pkgdir}/usr/share/${pkgname}/gitstatus/build"
rm "${pkgdir}/usr/share/${pkgname}/gitstatus/Makefile"
rm "${pkgdir}/usr/share/${pkgname}/gitstatus/mbuild"
rm "${pkgdir}/usr/share/${pkgname}/gitstatus/.clang-format"
rm "${pkgdir}/usr/share/${pkgname}/gitstatus/.gitignore"
rm "${pkgdir}/usr/share/${pkgname}/gitstatus/.gitattributes"
rm "${pkgdir}/usr/share/${pkgname}/gitstatus/usrbin/.gitkeep"
cd "${pkgdir}/usr/share/${pkgname}"
for file in *.zsh-theme internal/*.zsh gitstatus/*.zsh gitstatus/install; do
zsh -fc "emulate zsh -o no_aliases && zcompile -R -- $file.zwc $file"
done
}

View file

@ -1,68 +0,0 @@
#!/usr/bin/env bash
# BOS post-install smoke test.
#
# Run this INSIDE a freshly installed BOS (as the main user) to assert the
# install's core invariants. It is read-only and safe to run any time.
#
# ./smoke-test.sh
#
# Exit status is non-zero if any check fails, so it can gate CI / manual QA.
set -uo pipefail
pass=0 fail=0
ok() { printf ' \033[32mPASS\033[0m %s\n' "$1"; pass=$((pass+1)); }
bad() { printf ' \033[31mFAIL\033[0m %s\n' "$1"; fail=$((fail+1)); }
note() { printf ' \033[33m----\033[0m %s\n' "$1"; }
check() { if eval "$2" >/dev/null 2>&1; then ok "$1"; else bad "$1"; fi; }
echo "== btrfs subvolume layout =="
if command -v btrfs >/dev/null; then
# `btrfs subvolume list` needs root; try unprivileged, fall back to non-
# interactive sudo (no hang if creds aren't cached).
paths="$(btrfs subvolume list / 2>/dev/null || sudo -n btrfs subvolume list / 2>/dev/null)"
paths="$(awk '{print $NF}' <<<"$paths")"
if [ -z "$paths" ]; then
note "couldn't list subvolumes (need root) — skipping"
else
for sv in @ @home @snapshots @log @cache; do
if grep -qx "$sv" <<<"$paths"; then ok "subvolume $sv present"; else bad "subvolume $sv missing"; fi
done
fi
else
note "btrfs not installed (not a btrfs root?) — skipping subvolume checks"
fi
echo "== snapshot tooling =="
check "snapper root config exists" "[ -f /etc/snapper/configs/root ]"
check "snap-pac hook present" "pacman -Qq snap-pac"
check "grub-btrfs present" "pacman -Qq grub-btrfs"
echo "== enabled system services =="
for unit in NetworkManager.service greetd.service bluetooth.service tlp.service \
cups.socket avahi-daemon.service ufw.service systemd-timesyncd.service; do
check "$unit enabled" "systemctl is-enabled $unit"
done
check "graphical.target is default" "[ \"\$(systemctl get-default)\" = graphical.target ]"
echo "== bread ecosystem on PATH =="
for bin in bakery bread breadd breadbar breadbox breadbox-sync breadcrumbs breadpad breadman; do
check "$bin found" "command -v $bin"
done
echo "== bos-settings =="
check "bos-settings installed" "command -v bos-settings"
echo "== default dotfiles =="
check "hyprland.lua present" "[ -f \"\$HOME/.config/hypr/hyprland.lua\" ]"
check "mimeapps.list present" "[ -f \"\$HOME/.config/mimeapps.list\" ]"
check "kitty config present" "[ -f \"\$HOME/.config/kitty/kitty.conf\" ]"
echo "== bootloader (EFI) =="
check "GRUB EFI binary present" \
"[ -f /boot/efi/EFI/BOS/grubx64.efi ] || [ -f /boot/efi/EFI/BOOT/BOOTX64.EFI ]"
check "grub.cfg present" "[ -f /boot/grub/grub.cfg ]"
echo
printf 'Result: \033[32m%d passed\033[0m, \033[31m%d failed\033[0m\n' "$pass" "$fail"
[ "$fail" -eq 0 ]