dev #1

Merged
Breadway merged 12 commits from dev into main 2026-07-04 12:39:58 +08:00
24 changed files with 373 additions and 56 deletions
Showing only changes of commit a8f1592e75 - Show all commits

View file

@ -49,7 +49,7 @@ grep airootfs_image_tool_options "$STAGE/profiledef.sh"
# created from skel (the live user and the installed user) then gets the same # 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 # 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. # 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 breadpaper bread-theme breadmon breadsearch breadmill breadclip breadclipd breadshot)
LAPTOP_HOME="${LAPTOP_HOME:-$(getent passwd "${SUDO_USER:-$USER}" | cut -d: -f6)}" LAPTOP_HOME="${LAPTOP_HOME:-$(getent passwd "${SUDO_USER:-$USER}" | cut -d: -f6)}"
BAKERY_BIN="$LAPTOP_HOME/.local/bin" BAKERY_BIN="$LAPTOP_HOME/.local/bin"
BAKERY_STATE="$LAPTOP_HOME/.local/state/bakery" BAKERY_STATE="$LAPTOP_HOME/.local/state/bakery"
@ -69,6 +69,50 @@ install -m 0644 "$BAKERY_STATE/installed.json" "$SKEL/.local/state/bakery/instal
install -m 0644 "$BAKERY_CACHE/index.json" "$SKEL/.cache/bakery/index.json" install -m 0644 "$BAKERY_CACHE/index.json" "$SKEL/.cache/bakery/index.json"
echo "baked: $(ls "$SKEL/.local/bin")" echo "baked: $(ls "$SKEL/.local/bin")"
# --- Bake systemd user services for bakery-managed bread packages -----------
# Historically only breadd.service was hand-committed to skel; every other
# bakery package's service (breadbox-sync, breadmill, breadclipd, ...) was
# silently left out, so those daemons never start on a fresh install/live
# boot until the user re-runs `bakery install` (which needs network).
# Generalize from the same source of truth as the binary bake above: read
# the services this laptop's bakery actually installed, copy each unit file
# into skel with ExecStart rewritten from this laptop's literal home path to
# the portable `%h` specifier, and recreate whichever *.target.wants enable
# symlink bakery created locally. Units already committed by hand (breadd.service
# carries a RuntimeDirectoryPreserve=yes fix not yet upstreamed — see bread-release-build
# notes) are left alone rather than overwritten.
echo "=== baking bakery service units into skel ==="
SYSTEMD_USER_DIR="$LAPTOP_HOME/.config/systemd/user"
SKEL_SYSTEMD="$SKEL/.config/systemd/user"
mapfile -t SERVICE_UNITS < <(python3 -c "
import json
with open('$BAKERY_STATE/installed.json') as f:
d = json.load(f)
for pkg in d.get('packages', d).values():
for s in pkg.get('services', []):
print(s)
")
for unit in "${SERVICE_UNITS[@]}"; do
if [[ -f "$SKEL_SYSTEMD/$unit" ]]; then
echo " $unit already committed in skel, leaving as-is"
continue
fi
src="$SYSTEMD_USER_DIR/$unit"
if [[ ! -f "$src" ]]; then
echo " warning: $unit not found at $src, skipping"
continue
fi
install -d -m 0755 "$SKEL_SYSTEMD"
sed "s#ExecStart=$LAPTOP_HOME/.local/bin/#ExecStart=%h/.local/bin/#" "$src" > "$SKEL_SYSTEMD/$unit"
for wants_dir in "$SYSTEMD_USER_DIR"/*.target.wants; do
[[ -L "$wants_dir/$unit" ]] || continue
target_name="$(basename "$wants_dir")"
install -d -m 0755 "$SKEL_SYSTEMD/$target_name"
ln -sf "../$unit" "$SKEL_SYSTEMD/$target_name/$unit"
done
echo " baked $unit"
done
# mkarchiso resets every airootfs file to 0644, so executables must be declared # mkarchiso resets every airootfs file to 0644, so executables must be declared
# in profiledef.sh's file_permissions array or they ship non-executable and the # in profiledef.sh's file_permissions array or they ship non-executable and the
# exec-once launches fail with "permission denied". Inject a 0755 entry for each # exec-once launches fail with "permission denied". Inject a 0755 entry for each

View file

@ -5,22 +5,14 @@ efiSystemPartitionName: "EFI"
defaultFileSystemType: "btrfs" defaultFileSystemType: "btrfs"
btrfsSubvolumes: # NOTE: there is no `btrfsSubvolumes:` key in this Calamares version's
- mountPoint: / # partition module schema (confirmed against /usr/share/calamares/modules/
subvolume: "@" # partition.conf and on real hardware — zero mentions of "subvolume"
mountOptions: "noatime,compress=zstd,space_cache=v2" # anywhere in the stock reference config). A previous version of this file
- mountPoint: /home # had one; Calamares silently ignored it. Calamares' partition module only
subvolume: "@home" # natively creates @ (root) and @home (home) when btrfs + separate /home is
mountOptions: "noatime,compress=zstd,space_cache=v2" # selected — nothing else. @snapshots/@log/@cache are created by hand in
- mountPoint: /.snapshots # post-install.sh instead, after unpackfs has populated the filesystem.
subvolume: "@snapshots"
mountOptions: "noatime,compress=zstd,space_cache=v2"
- mountPoint: /var/log
subvolume: "@log"
mountOptions: "noatime,compress=zstd,space_cache=v2"
- mountPoint: /var/cache
subvolume: "@cache"
mountOptions: "noatime,compress=zstd,space_cache=v2"
userSwapChoices: userSwapChoices:
- none - none

View file

@ -0,0 +1,8 @@
---
# Runs before unpackfs so it can hand it a source path that survives
# copytoram unmounting /run/archiso/bootmnt. See bos-resolve-airootfs.
dontChroot: true
timeout: 30
script:
- "/usr/bin/bash /usr/local/bin/bos-resolve-airootfs"

View file

@ -1,7 +1,11 @@
--- ---
# Unpack the live squashfs onto the target partition. # Unpack the live squashfs onto the target partition.
# "arch" matches profiledef.sh install_dir; adjust if that changes. # The source is a symlink written by shellprocess@resolve-source (see
# bos-resolve-airootfs) rather than a path under /run/archiso/bootmnt directly
# — that mount gets torn down by archiso's own copytoram handling, which
# self-enables on most real hardware, so a hardcoded bootmnt path would point
# at nothing on exactly the machines this install is meant to run on.
unpack: unpack:
- source: "/run/archiso/bootmnt/arch/x86_64/airootfs.sfs" - source: "/run/archiso/resolved-airootfs.sfs"
sourcefs: "squashfs" sourcefs: "squashfs"
destination: "" destination: ""

View file

@ -38,4 +38,11 @@ passwordRequirements:
- minlen=6 - minlen=6
allowWeakPasswords: false allowWeakPasswords: false
userShell: /bin/zsh
# `userShell` (top-level) is not a real key in this Calamares version's users
# module schema (3.4.2) — it's silently ignored, and an installed user gets
# whatever the module's own default is (bash), confirmed on real hardware.
# The actual key is nested: user.shell. See /usr/share/calamares/modules/users.conf
# for the documented schema.
user:
shell: /usr/bin/zsh

View file

@ -79,31 +79,137 @@ fi
mkinitcpio -P || echo "WARN: mkinitcpio -P failed" mkinitcpio -P || echo "WARN: mkinitcpio -P failed"
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Install GRUB (UEFI). /boot now has the kernel + initramfs, and the mount # Install GRUB. /boot now has the kernel + initramfs, and the mount module has
# module has bind-mounted /proc /sys /dev /run + efivars into this chroot, so # bind-mounted /proc /sys /dev /run (+ efivars on UEFI) into this chroot, so
# both grub-install passes and grub-mkconfig succeed. # both grub-install passes and grub-mkconfig succeed.
# 1. NVRAM entry (EFI/BOS/grubx64.efi + a firmware boot entry) #
# 2. --removable copy to EFI/BOOT/BOOTX64.EFI, so firmware that ignores/loses # BOS ships a syslinux BIOS boot mode on the ISO (profiledef.sh bootmodes
# the NVRAM entry (the "no boot device / PXE fallback" failure) still finds # includes bios.syslinux), but this only ever ran the UEFI grub-install path —
# a bootloader. # a BIOS install would complete successfully and then have no bootloader at
# all. Branch on /sys/firmware/efi (present only when booted UEFI) and install
# the matching GRUB target; on BIOS, grub-install needs the whole disk, not a
# partition, so it's derived from the mounted root via lsblk.
# UEFI: 1. NVRAM entry (EFI/BOS/grubx64.efi + a firmware boot entry)
# 2. --removable copy to EFI/BOOT/BOOTX64.EFI, so firmware that
# ignores/loses the NVRAM entry still finds a bootloader.
# BIOS: MBR install onto the disk hosting /.
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
if command -v grub-install &>/dev/null; then if command -v grub-install &>/dev/null; then
if [[ -d /sys/firmware/efi ]]; then
grub-install --target=x86_64-efi --efi-directory=/boot/efi \ grub-install --target=x86_64-efi --efi-directory=/boot/efi \
--bootloader-id=BOS --recheck \ --bootloader-id=BOS --recheck \
|| echo "WARN: grub-install (nvram) failed" || echo "WARN: grub-install (nvram) failed"
grub-install --target=x86_64-efi --efi-directory=/boot/efi \ grub-install --target=x86_64-efi --efi-directory=/boot/efi \
--removable --recheck \ --removable --recheck \
|| echo "WARN: grub-install (removable) failed" || echo "WARN: grub-install (removable) failed"
else
ROOT_DEV="$(findmnt -no SOURCE / | sed 's/\[.*\]//')"
ROOT_DISK="$(lsblk -no pkname "$ROOT_DEV" 2>/dev/null)"
if [[ -n "$ROOT_DISK" ]]; then
grub-install --target=i386-pc --recheck "/dev/$ROOT_DISK" \
|| echo "WARN: grub-install (BIOS) failed"
else
echo "WARN: could not determine the disk hosting / (root device: ${ROOT_DEV:-unknown}) — BIOS grub-install skipped"
fi
fi
fi fi
if command -v grub-mkconfig &>/dev/null; then if command -v grub-mkconfig &>/dev/null; then
grub-mkconfig -o /boot/grub/grub.cfg || echo "WARN: grub-mkconfig failed" grub-mkconfig -o /boot/grub/grub.cfg || echo "WARN: grub-mkconfig failed"
fi fi
# ---------------------------------------------------------------------------
# Create @snapshots, @log, @cache as top-level btrfs subvolumes (peers of @,
# not nested under it — so snapshots of @ don't recursively include
# themselves, and log/cache churn doesn't bloat @'s snapshot history).
#
# iso/partition.conf's `btrfsSubvolumes:` key does NOT do this — verified on
# real hardware that it's not a recognized key in this Calamares version's
# partition module schema at all (same class of bug as the `userShell` fix
# below: Calamares silently ignores unknown top-level keys). Calamares' own
# btrfs support only natively creates @ and @home; everything else has to be
# done by hand, here, after unpackfs has populated / but before anything
# reads/writes /var/log or /var/cache going forward. Existing content in
# those two dirs (real files already unpacked from the squashfs) is migrated
# into the new subvolumes before mounting over them, so nothing is lost —
# just shadowed by the mount, same as any other mountpoint.
# ---------------------------------------------------------------------------
if command -v btrfs &>/dev/null; then
ROOT_DEV="$(findmnt -no SOURCE / | sed 's/\[.*\]//')"
ROOT_UUID="$(blkid -s UUID -o value "$ROOT_DEV" 2>/dev/null)"
BTRFS_TOP=/.btrfs-top-tmp
if [[ -z "$ROOT_UUID" ]]; then
echo "WARN: could not determine root filesystem UUID — skipping @snapshots/@log/@cache creation"
else
mkdir -p "$BTRFS_TOP"
if mount -o subvolid=5 "$ROOT_DEV" "$BTRFS_TOP"; then
for sv in @snapshots @log @cache; do
if ! btrfs subvolume show "$BTRFS_TOP/$sv" &>/dev/null; then
btrfs subvolume create "$BTRFS_TOP/$sv" || echo "WARN: creating $sv failed"
fi
done
rsync -aAX /var/log/ "$BTRFS_TOP/@log/" || echo "WARN: migrating /var/log into @log failed"
rsync -aAX /var/cache/ "$BTRFS_TOP/@cache/" || echo "WARN: migrating /var/cache into @cache failed"
umount "$BTRFS_TOP"
rmdir "$BTRFS_TOP"
OPTS="noatime,compress=zstd,space_cache=v2"
grep -q "@snapshots" /etc/fstab || echo "UUID=$ROOT_UUID /.snapshots btrfs subvol=/@snapshots,$OPTS 0 0" >> /etc/fstab
grep -q "@log" /etc/fstab || echo "UUID=$ROOT_UUID /var/log btrfs subvol=/@log,$OPTS 0 0" >> /etc/fstab
grep -q "@cache" /etc/fstab || echo "UUID=$ROOT_UUID /var/cache btrfs subvol=/@cache,$OPTS 0 0" >> /etc/fstab
mkdir -p /.snapshots
mount /.snapshots || echo "WARN: mounting /.snapshots failed"
mount /var/log || echo "WARN: mounting /var/log failed"
mount /var/cache || echo "WARN: mounting /var/cache failed"
else
echo "WARN: could not mount btrfs top-level — skipping @snapshots/@log/@cache creation"
fi
fi
fi
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Snapper root config (root is btrfs). # Snapper root config (root is btrfs).
#
# @snapshots is now mounted at /.snapshots (created above) — a dedicated
# top-level subvolume, a peer of @ rather than nested under it, so snapshots
# aren't themselves recursively snapshotted. `snapper create-config` insists
# on creating that subvolume itself and refuses whenever /.snapshots already
# exists, mounted or not — silently, so without this dance every downstream
# sed below is a no-op and BOS ships with its advertised auto-snapshot/
# rollback feature entirely non-functional.
# Unmount the real subvolume, let snapper create + own its nested one, then
# discard that and remount the real @snapshots in its place (fstab entry was
# added above, right after the subvolume was created).
#
# Confirmed on real hardware: a plain `umount /.snapshots` here can
# transiently fail inside the Calamares chroot (the same mount unmounts
# cleanly moments later once booted normally — a chroot-specific busy-mount
# race, not a logic error), which cascades into snapper create-config
# refusing because .snapshots "already exists". Retry a few times, then
# fall back to a lazy unmount (detaches the mountpoint immediately even if
# something still transiently references it) rather than give up.
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
if command -v snapper &>/dev/null; then if command -v snapper &>/dev/null; then
unmounted=0
for _ in 1 2 3 4 5; do
if umount /.snapshots 2>/dev/null; then
unmounted=1
break
fi
sleep 1
done
if [[ "$unmounted" != "1" ]]; then
echo "WARN: umount /.snapshots failed after retries, forcing lazy unmount"
umount -l /.snapshots || echo "WARN: lazy umount /.snapshots also failed"
fi
rmdir /.snapshots || echo "WARN: rmdir /.snapshots failed"
snapper -c root create-config / || echo "WARN: snapper create-config failed" snapper -c root create-config / || echo "WARN: snapper create-config failed"
if [[ -d /.snapshots ]]; then
btrfs subvolume delete /.snapshots || echo "WARN: deleting snapper's own .snapshots subvolume failed"
fi
mkdir -p /.snapshots
mount /.snapshots || echo "WARN: remounting the real @snapshots subvolume failed"
if [[ -f /etc/snapper/configs/root ]]; then if [[ -f /etc/snapper/configs/root ]]; then
sed -i 's/TIMELINE_CREATE="yes"/TIMELINE_CREATE="no"/' /etc/snapper/configs/root sed -i 's/TIMELINE_CREATE="yes"/TIMELINE_CREATE="no"/' /etc/snapper/configs/root
sed -i 's/NUMBER_CLEANUP="no"/NUMBER_CLEANUP="yes"/' /etc/snapper/configs/root sed -i 's/NUMBER_CLEANUP="no"/NUMBER_CLEANUP="yes"/' /etc/snapper/configs/root

View file

@ -3,10 +3,16 @@ modules-search: [/etc/calamares/modules, /usr/lib/calamares/modules]
# Second shellprocess instance: copies the live kernel into the target /boot # Second shellprocess instance: copies the live kernel into the target /boot
# (archiso keeps it out of the squashfs) before the bootloader step runs. # (archiso keeps it out of the squashfs) before the bootloader step runs.
# Third: resolves the live squashfs's real location before unpackfs runs —
# copytoram unmounts /run/archiso/bootmnt, so a hardcoded path there can point
# at nothing depending on how the medium was booted.
instances: instances:
- id: kernel - id: kernel
module: shellprocess module: shellprocess
config: shellprocess-kernel.conf config: shellprocess-kernel.conf
- id: resolve-source
module: shellprocess
config: shellprocess-resolve-source.conf
sequence: sequence:
- show: - show:
@ -19,6 +25,7 @@ sequence:
- exec: - exec:
- partition - partition
- mount - mount
- shellprocess@resolve-source
- unpackfs - unpackfs
- machineid - machineid
- fstab - fstab

View file

@ -1,3 +1,3 @@
HOOKS=(base udev microcode modconf kms memdisk archiso archiso_loop_mnt archiso_pxe_common archiso_pxe_nbd archiso_pxe_http archiso_pxe_nfs block filesystems keyboard) HOOKS=(base udev plymouth microcode modconf kms memdisk archiso archiso_loop_mnt archiso_pxe_common archiso_pxe_nbd archiso_pxe_http archiso_pxe_nfs block filesystems keyboard)
COMPRESSION="xz" COMPRESSION="xz"
COMPRESSION_OPTIONS=(-9e) COMPRESSION_OPTIONS=(-9e)

View file

@ -0,0 +1,12 @@
NAME="BOS"
PRETTY_NAME="Bread OS"
ID=bos
ID_LIKE=arch
BUILD_ID=rolling
ANSI_COLOR="38;2;23;147;209"
HOME_URL="https://breadway.dev"
DOCUMENTATION_URL="https://wiki.archlinux.org/"
SUPPORT_URL="https://bbs.archlinux.org/"
BUG_REPORT_URL="https://git.breadway.dev/Breadway/bos/issues"
PRIVACY_POLICY_URL="https://terms.archlinux.org/docs/privacy-policy/"
LOGO=archlinux-logo

View file

@ -0,0 +1,2 @@
[Daemon]
Theme=bos

View file

@ -1,3 +1,3 @@
[[context]] [[context]]
name = "default" name = "default"
apps = ["firefox", "foot", "nautilus", "code"] priority = ["Zen Browser", "kitty", "Files"]

View file

@ -0,0 +1,20 @@
              
      ....',,;;;;,,'....      
   ..'cdkKXWMMMWNNNNNWMMMNKOdc'..    
  ..;xNW0xoc;,'...........',:oxKWNOc..  
 .'XWx;..........................,dWN,. 
 .xMo..............................cMk. 
 .;WK'............................'OM:. 
   'oW0.....d:...................0Md'   
   .kM:...lNMKc................,MO.   
  .dMc.....lXMXl..............:Mk.  
  .xMc.......kMMX'............;Mk.  
  .kM:.....:KMNo..............,M0.  
  .KM,...cXMXl....lllllllll....MX.  
 ..NM....,kc......0000OOOOO'...MW.. 
  ..MW..........................NM..  
  ..MX..........................KM,.  
  .'MX..........................XM;.  
   .XMKOkxxddddoooooodddddxxkkO0MN.   
      ,:ccclllllooolllllccc::;.     
                        

View file

@ -0,0 +1,38 @@
{
"$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json",
"logo": {
"type": "file",
"source": "~/.config/fastfetch/bread.txt",
"width": 30,
"padding": {
"right": 5
}
},
"display": {
"separator": " ",
"color": {
"keys": "cyan",
"title": "bright_blue"
}
},
"modules": [
"title",
"separator",
"os",
"host",
"kernel",
"uptime",
"packages",
"shell",
"display",
"wm",
"terminal",
"cpu",
"gpu",
"memory",
"swap",
"disk",
"battery",
"colors"
]
}

View file

@ -125,7 +125,10 @@ hl.bind(mod .. " + slash", hl.dsp.exec_cmd("bos-keybinds"))
hl.bind(mod .. " + L", hl.dsp.exec_cmd("loginctl lock-session")) hl.bind(mod .. " + L", hl.dsp.exec_cmd("loginctl lock-session"))
hl.bind(mod .. " + F", hl.dsp.window.fullscreen({ action = "toggle" })) hl.bind(mod .. " + F", hl.dsp.window.fullscreen({ action = "toggle" }))
hl.bind(mod .. " + V", hl.dsp.window.float({ action = "toggle" })) hl.bind(mod .. " + V", hl.dsp.window.float({ action = "toggle" }))
hl.bind(mod .. " + SHIFT + V", hl.dsp.exec_cmd([[bash -c 'cliphist list | fzf --reverse --prompt="Clipboard > " | cliphist decode | wl-copy']])) -- breadclip (its own gtk4-layer-shell popup — not a TUI, so no terminal
-- needed). Previously piped cliphist through fzf directly from the
-- compositor with no terminal attached, which was a silent no-op.
hl.bind(mod .. " + SHIFT + V", hl.dsp.exec_cmd("breadclip"))
hl.bind(mod .. " + T", hl.dsp.layout("togglesplit")) hl.bind(mod .. " + T", hl.dsp.layout("togglesplit"))
hl.bind(mod .. " + Tab", hl.dsp.focus({ urgent_or_last = true })) hl.bind(mod .. " + Tab", hl.dsp.focus({ urgent_or_last = true }))
hl.bind(mod .. " + N", hl.dsp.exit()) hl.bind(mod .. " + N", hl.dsp.exit())
@ -198,8 +201,9 @@ hl.on("hyprland.start", function()
"gsettings set org.gnome.desktop.interface icon-theme Papirus-Dark", "gsettings set org.gnome.desktop.interface icon-theme Papirus-Dark",
"gsettings set org.gnome.desktop.interface cursor-theme Bibata-Modern-Ice", "gsettings set org.gnome.desktop.interface cursor-theme Bibata-Modern-Ice",
"gsettings set org.gnome.desktop.interface cursor-size 24", "gsettings set org.gnome.desktop.interface cursor-size 24",
-- Clipboard history daemon (feeds SUPER+V history picker via wl-paste). -- Clipboard history is breadclipd, a bakery-managed systemd --user
"wl-paste --type text --watch cliphist store", -- service (auto-started via skel — see build-local.sh's service bake)
-- rather than an exec-once here.
"/usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1", "/usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1",
"awww-daemon", "awww-daemon",
-- set the default wallpaper once the daemon is up (retry until ready) -- set the default wallpaper once the daemon is up (retry until ready)
@ -210,8 +214,20 @@ hl.on("hyprland.start", function()
-- to pick it up — that's how it gets HYPRLAND_INSTANCE_SIGNATURE to talk to Hyprland. -- 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", "dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP HYPRLAND_INSTANCE_SIGNATURE",
"systemctl --user restart breadd", "systemctl --user restart breadd",
-- graphical-session.target ships with RefuseManualStart=yes (systemd
-- convention — only a session manager like uwsm is meant to activate
-- it), confirmed on real hardware: `systemctl --user start
-- graphical-session.target` fails outright ("Operation refused").
-- BOS doesn't use uwsm, and nothing else activates that target, so
-- breadclipd (WantedBy=graphical-session.target) never started.
-- Start it directly instead. If more graphical-session.target
-- services show up later, add them here too.
"systemctl --user start breadclipd.service",
"breadbar", "breadbar",
"breadbox-sync", -- breadbox-sync is a Type=oneshot systemd --user service
-- (WantedBy=default.target, no Hyprland IPC dependency) — it
-- already runs on login via the unit baked into skel; exec'ing it
-- again here would just start it twice.
"hypridle", "hypridle",
-- first-boot onboarding (self-gates after the first run) -- first-boot onboarding (self-gates after the first run)
"bos-welcome", "bos-welcome",

View file

@ -7,19 +7,40 @@
# (the stock linux.preset points ALL_kver at /boot/vmlinuz-linux) and before the # (the stock linux.preset points ALL_kver at /boot/vmlinuz-linux) and before the
# `bootloader` module runs grub — otherwise the installed system is unbootable. # `bootloader` module runs grub — otherwise the installed system is unbootable.
# #
# Runs in the LIVE environment (Calamares shellprocess, dontChroot) so it can # Runs in the LIVE environment (Calamares shellprocess, dontChroot); the
# read /run/archiso/bootmnt; the target root mount point is passed as $1. # target root mount point is passed as $1.
#
# The kernel is read from /usr/lib/modules/$(uname -r)/vmlinuz — part of the
# live squashfs itself (the linux package always installs it there), not from
# the ISO's separate arch/boot/x86_64/ dir under /run/archiso/bootmnt. That
# mount gets torn down by archiso's own copytoram handling, which self-enables
# on most real hardware, so depending on it here would leave $ROOT/boot empty
# and the install unbootable on exactly the machines this is meant to run on.
set -uo pipefail set -uo pipefail
ROOT="${1:?target root required}" ROOT="${1:?target root required}"
SRC="/run/archiso/bootmnt/arch/boot/x86_64" KVER="$(uname -r)"
SRC_KERNEL="/usr/lib/modules/${KVER}/vmlinuz"
SRC_BOOT="/run/archiso/bootmnt/arch/boot/x86_64"
install -d -m 0755 "$ROOT/boot" install -d -m 0755 "$ROOT/boot"
cp -f "$SRC/vmlinuz-linux" "$ROOT/boot/vmlinuz-linux"
# Microcode, if the live medium carries it (grub-mkconfig picks it up). if [ -f "$SRC_KERNEL" ]; then
cp -f "$SRC_KERNEL" "$ROOT/boot/vmlinuz-linux"
elif [ -f "$SRC_BOOT/vmlinuz-linux" ]; then
echo "WARN: $SRC_KERNEL missing, falling back to $SRC_BOOT"
cp -f "$SRC_BOOT/vmlinuz-linux" "$ROOT/boot/vmlinuz-linux"
else
echo "ERROR: no kernel image found (checked $SRC_KERNEL and $SRC_BOOT/vmlinuz-linux) — install would be unbootable" >&2
exit 1
fi
# Microcode, if the live medium carries it (grub-mkconfig picks it up). This
# is best-effort: post-install.sh's mkinitcpio `microcode` HOOKS entry embeds
# microcode from /usr/lib/firmware directly, so a missing standalone ucode.img
# here is not boot-critical, unlike the kernel image above.
for u in amd-ucode.img intel-ucode.img; do for u in amd-ucode.img intel-ucode.img; do
[ -f "$SRC/$u" ] && cp -f "$SRC/$u" "$ROOT/boot/$u" [ -f "$SRC_BOOT/$u" ] && cp -f "$SRC_BOOT/$u" "$ROOT/boot/$u"
done done
# Replace the archiso initramfs setup that unpackfs copied from the live medium. # Replace the archiso initramfs setup that unpackfs copied from the live medium.

View file

@ -34,15 +34,18 @@ fi
# Start Hyprland on tty1 login; capture output and fall back to a shell so a # Start Hyprland on tty1 login; capture output and fall back to a shell so a
# failed compositor start is visible rather than a blank looping cursor. # failed compositor start is visible rather than a blank looping cursor.
cat >/home/liveuser/.bash_profile <<'EOF' # liveuser's shell is zsh (see useradd above), which never sources
# .bash_profile — this must be .zprofile (zsh's login-shell hook) or it never
# runs at all and the live session boots to a bare console.
cat >/home/liveuser/.zprofile <<'EOF'
if [[ "$(tty)" == /dev/tty1 ]] && [[ -z "$WAYLAND_DISPLAY" ]]; then if [[ "$(tty)" == /dev/tty1 ]] && [[ -z "$WAYLAND_DISPLAY" ]]; then
export WLR_RENDERER_ALLOW_SOFTWARE=1 export WLR_RENDERER_ALLOW_SOFTWARE=1
export WLR_NO_HARDWARE_CURSORS=1 export WLR_NO_HARDWARE_CURSORS=1
# Log to a user-writable path (/var/log is root-only; redirecting there # Log to a user-writable path (/var/log is root-only; redirecting there
# would fail and silently keep the compositor from ever launching). # would fail and silently keep the compositor from ever launching).
Hyprland &>/tmp/hyprland-live.log start-hyprland &>/tmp/hyprland-live.log
echo "Hyprland exited (rc=$?). Log: /tmp/hyprland-live.log" echo "Hyprland exited (rc=$?). Log: /tmp/hyprland-live.log"
exec bash -i exec zsh -i
fi fi
EOF EOF

View file

@ -0,0 +1,29 @@
#!/bin/bash
# Resolve the real location of the live squashfs before Calamares' unpackfs
# module runs, and leave a stable symlink for it to unpack from.
#
# archiso's own initcpio hook unmounts /run/archiso/bootmnt once copytoram has
# copied the image to a tmpfs (/run/archiso/copytoram/airootfs.sfs) — and
# copytoram=auto self-enables on most real hardware (non-optical boot device,
# image under 4 GiB, enough free RAM), not just when explicitly requested. A
# Calamares unpackfs.conf source path hardcoded to /run/archiso/bootmnt/...
# would then point at nothing. Check both locations here, in a real script,
# and hand unpackfs.conf one fixed path that always resolves.
set -uo pipefail
DEST="/run/archiso/resolved-airootfs.sfs"
CANDIDATES=(
"/run/archiso/copytoram/airootfs.sfs"
"/run/archiso/bootmnt/arch/x86_64/airootfs.sfs"
)
for c in "${CANDIDATES[@]}"; do
if [ -f "$c" ]; then
ln -sf "$c" "$DEST"
echo "resolved live squashfs: $c -> $DEST"
exit 0
fi
done
echo "ERROR: could not find the live squashfs in any known location (checked: ${CANDIDATES[*]})" >&2
exit 1

View file

@ -7,9 +7,14 @@
# breadbox-sync, …) would be missing from PATH and the Hyprland `exec-once` # breadbox-sync, …) would be missing from PATH and the Hyprland `exec-once`
# launches would fail. Source the login profile here so PATH is correct, set the # launches would fail. Source the login profile here so PATH is correct, set the
# Wayland session hints, then hand off to Hyprland. # Wayland session hints, then hand off to Hyprland.
#
# Launched via start-hyprland (ships with the hyprland package) rather than the
# raw Hyprland binary — Hyprland upstream no longer recommends exec'ing it
# directly; start-hyprland wraps it in a watchdog process that also gives us
# crash recovery for free.
source /etc/profile 2>/dev/null source /etc/profile 2>/dev/null
export XDG_SESSION_TYPE=wayland export XDG_SESSION_TYPE=wayland
export XDG_CURRENT_DESKTOP=Hyprland export XDG_CURRENT_DESKTOP=Hyprland
exec Hyprland exec start-hyprland

View file

@ -5,8 +5,10 @@
# 1. pacman — Arch base/desktop + the [breadway] repo (bos-settings, etc.). # 1. pacman — Arch base/desktop + the [breadway] repo (bos-settings, etc.).
# Every transaction is snapshotted by snap-pac, so you can roll # Every transaction is snapshotted by snap-pac, so you can roll
# back from the GRUB "snapshots" submenu or BOS Settings. # back from the GRUB "snapshots" submenu or BOS Settings.
# 2. bakery — the bread ecosystem apps in ~/.local/bin (bread, breadbar, # 2. bakery — the bread ecosystem apps in ~/.local/bin (whatever `bakery list`
# breadbox, breadcrumbs, breadpad, breadman, bread-theme). # reports as installed — bread, breadbar, breadbox, breadcrumbs,
# breadpad, breadman, bread-theme, breadpaper, breadmon,
# breadsearch, breadclip, breadshot, ...).
# #
# Best-effort: a failure in one channel doesn't abort the other. # Best-effort: a failure in one channel doesn't abort the other.
set -uo pipefail set -uo pipefail

View file

@ -2,4 +2,4 @@ title Bread OS install medium (copy to RAM, UEFI)
sort-key 015 sort-key 015
linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux
initrd /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img initrd /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img
options archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% copytoram=y options archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% copytoram=y quiet splash vt.global_cursor_default=0 systemd.show_status=false rd.systemd.show_status=false rd.udev.log_level=3

View file

@ -2,4 +2,4 @@ title Bread OS install medium (%ARCH%, UEFI)
sort-key 01 sort-key 01
linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux
initrd /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img initrd /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img
options archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% options archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% quiet splash vt.global_cursor_default=0 systemd.show_status=false rd.systemd.show_status=false rd.udev.log_level=3

View file

@ -178,10 +178,10 @@ brightnessctl
grim grim
slurp slurp
# Clipboard (Wayland copy/paste; also clipboard screenshots) and media keys. # Clipboard (Wayland copy/paste; also clipboard screenshots) and media keys.
# Clipboard history is breadclip/breadclipd (bakery-managed, SUPER+SHIFT+V),
# not cliphist — wl-clipboard is still needed directly by breadclip/breadshot.
wl-clipboard wl-clipboard
playerctl playerctl
# Clipboard history daemon (stores wl-clipboard events; breadbox bind replays them).
cliphist
# Wallpaper daemon + pywal (drives the bread* colour palette from the wallpaper). # Wallpaper daemon + pywal (drives the bread* colour palette from the wallpaper).
awww awww
python-pywal python-pywal

View file

@ -20,6 +20,7 @@ file_permissions=(
["/usr/local/bin/bos-live-setup"]="0:0:755" ["/usr/local/bin/bos-live-setup"]="0:0:755"
["/usr/local/bin/bos-launch-calamares"]="0:0:755" ["/usr/local/bin/bos-launch-calamares"]="0:0:755"
["/usr/local/bin/bos-copy-kernel"]="0:0:755" ["/usr/local/bin/bos-copy-kernel"]="0:0:755"
["/usr/local/bin/bos-resolve-airootfs"]="0:0:755"
["/usr/local/bin/bos-session"]="0:0:755" ["/usr/local/bin/bos-session"]="0:0:755"
["/usr/local/bin/bos-keybinds"]="0:0:755" ["/usr/local/bin/bos-keybinds"]="0:0:755"
["/usr/local/bin/bos-welcome"]="0:0:755" ["/usr/local/bin/bos-welcome"]="0:0:755"

View file

@ -6,7 +6,7 @@ ENDTEXT
MENU LABEL Bread OS install medium (%ARCH%, BIOS) MENU LABEL Bread OS install medium (%ARCH%, BIOS)
LINUX /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux LINUX /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux
INITRD /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img INITRD /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img
APPEND archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% APPEND archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% quiet splash vt.global_cursor_default=0 systemd.show_status=false rd.systemd.show_status=false rd.udev.log_level=3
# Copy-to-RAM boot option — loads airootfs.sfs entirely into RAM, so the # 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 # installer reads from memory rather than a possibly-flaky USB (avoids SquashFS
@ -19,7 +19,7 @@ ENDTEXT
MENU LABEL Bread OS install medium (%ARCH%, BIOS) ^copy to RAM MENU LABEL Bread OS install medium (%ARCH%, BIOS) ^copy to RAM
LINUX /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux LINUX /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux
INITRD /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img INITRD /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img
APPEND archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% copytoram=y APPEND archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% copytoram=y quiet splash vt.global_cursor_default=0 systemd.show_status=false rd.systemd.show_status=false rd.udev.log_level=3
# Accessibility boot option # Accessibility boot option
LABEL archspeech LABEL archspeech