Add Gentoo Portage host configuration

This commit is contained in:
wfalkowski committed 2026-10-15 21:54:34 +00:00
1 parent d40cb10f21
commit b19d64a282
57 files changed
+23234 -2

No files matched your search

+10
View File
@@ -0,0 +1,10 @@
# Local OpenPGP signing keys and trust state
/gnupg/
# Protected-update candidates generated by dispatch-conf / etc-update
**/._cfg*
# Portage backups
**/*~
**/*.bak*
**/*.backup*
+136 -2
View File
@@ -1,3 +1,137 @@
# gentoo-portage-config
# Gentoo Portage: amd64, musl, LLVM
My config files for Gentoo's Portage.
This repository is a versioned snapshot of the local `/etc/portage` policy on my Gentoo machine. It targets the `default/linux/amd64/23.0/musl/llvm` profile and `x86_64-pc-linux-musl`; it is not a generic Gentoo configuration. The profile selection itself remains a system symlink and is recorded here rather than copied into the repository.
The screenshot below shows the host and active Gentoo profile from the terminal. The captured kernel and package counts describe the system at screenshot time, not necessarily its current state.
![Gentoo Portage host information and active profile](screenshots/gentoo-portage-host.png)
## Explicit world set
This is the host's `/var/lib/portage/world` snapshot, captured on 2026-10-15. It records packages selected explicitly with Portage; dependencies installed to satisfy them aren't necessarily listed here. The plain source list is also tracked as [`world`](world).
```text
app-admin/doas
app-editors/nano
app-editors/neovim
app-eselect/eselect-repository
app-misc/brightnessctl
app-misc/fastfetch
app-portage/gentoolkit
app-shells/fish
dev-cpp/cairomm
dev-lang/execline
dev-util/codex
dev-util/flatpak-builder
gui-apps/foot
gui-apps/grim
gui-apps/hyprpaper
gui-apps/hyprsunset
gui-apps/quickshell
gui-apps/slurp
gui-apps/tuigreet
gui-apps/wl-clip-persist
gui-apps/wl-clipboard
gui-libs/display-manager-init
gui-libs/greetd
gui-libs/hyprcursor
gui-libs/hyprland-guiutils
gui-wm/cage
gui-wm/hyprland
media-fonts/fontawesome
media-fonts/maple-mono
media-fonts/nerdfonts
media-gfx/imv
media-libs/gst-plugins-good
media-libs/libpulse
media-sound/pavucontrol
media-sound/playerctl
media-video/pipewire
net-misc/chrony
net-vpn/wireguard-tools
net-wireless/iwd
sys-apps/flatpak
sys-apps/pciutils
sys-apps/s6
sys-apps/s6-linux-init
sys-apps/s6-rc
sys-auth/seatd
sys-boot/efibootmgr
sys-boot/limine
sys-cluster/kubectl
sys-devel/mold
sys-firmware/intel-microcode
sys-fs/btrfs-progs
sys-fs/fuse:0
sys-kernel/cachyos-kernel
sys-kernel/dracut
sys-kernel/gentoo-kernel-bin
sys-kernel/gentoo-sources
sys-kernel/installkernel
sys-kernel/linux-firmware
sys-libs/gcompat
sys-process/htop
x11-themes/bibata-xcursors
```
## Build contract
| Setting | Current value | Effect |
| --- | --- | --- |
| Profile | `default/linux/amd64/23.0/musl/llvm` | amd64, musl, LLVM profile defaults |
| `CHOST` | `x86_64-pc-linux-musl` | Build target tuple for the musl userspace |
| C/C++ flags | `-march=native -O3 -pipe` | Tune builds for the local CPU; resulting binaries are not portable to older or different CPUs |
| Rust | `-C target-cpu=native` | Apply the same local-CPU assumption to Rust builds |
| Linker | `-fuse-ld=mold` globally | Use mold for builds that honor `LDFLAGS` |
| Build parallelism | `MAKEOPTS="-j4 -l5"`, emerge `--jobs=4` | Limit make jobs and concurrent emerge work |
| Global USE | `wayland -X hyprland dbus wifi bluetooth system-pipewire pulseaudio` | Set the desktop and service feature baseline |
| Licenses | `ACCEPT_LICENSE="*"` | Accept every license identifier; review this policy before reusing the config |
The selected profile owns the system toolchain defaults; `make.conf` does not globally force `CC` or `CXX`. Compiler and resource changes are attached to package atoms through `package.env`. Some of those snippets select Clang/LLVM tools and LLD with ThinLTO, while the global linker is mold, so check the resolved environment for the package you are building instead of assuming one linker applies everywhere.
## Repository map
The files at the repository root mirror `/etc/portage`:
| Path | Role |
| --- | --- |
| `make.conf` | Global flags, USE policy, license policy, jobs, binary-package features, and mirrors |
| `repos.conf/` | Gentoo rsync configuration and Git overlays, including CachyOS kernels, GURU, Hyprland, LibreWolf, and local repositories |
| `binrepos.conf/` | Signed Gentoo binary-package repository |
| `package.use/` | Directory-form per-package USE overrides, split into desktop, kernel, audio, and other groups |
| `package.accept_keywords/` | Narrow keyword exceptions for selected packages and overlays |
| `package.unmask/` | Local version policy exceptions; no local package masks are configured |
| `package.env/`, `env/` | Package-scoped environment files for compiler, linker, and resource settings |
| `savedconfig/` | Saved CachyOS kernel and linux-firmware package configuration |
| `world` | Captured explicit Portage world atoms from `/var/lib/portage/world` |
| `bashrc` | Portage package hooks, including Btrfs snapshots and a Firefox-specific Rust flag |
| `usr/local/libexec/` | Executable helpers called by the Portage hooks: Btrfs snapshot lifecycle and Limine menu maintenance |
## Btrfs and Limine hooks
`bashrc` calls `portage-btrfs-snapshot` after each package install and removal. The helper limits itself to operations against the live root, checks that `/` and `/.snapshots` are on the same Btrfs filesystem, requires at least 2 GiB free, creates read-only snapshots under `/.snapshots/portage`, and retains up to 32 of its own snapshots. When pruning, it removes the corresponding Limine recovery entry and writable clone before deleting the old read-only snapshot.
`portage-limine-menu` maintains `/boot/limine.conf`. After a `sys-kernel` install, the snapshot helper asks it to reconcile kernel entries and attach compatible snapshots. Other package installs create recovery entries when a compatible kernel/initramfs group exists; removals clean up the matching Limine entries and writable recovery clones. The manager validates the menu before writing it and keeps a one-time pre-change copy alongside the Limine config. Both scripts assume the host layout and Btrfs/Limine setup above; they are not portable drop-in hooks for another machine.
The scripts are stored at their corresponding paths under `usr/local/libexec/` in this repository. To install or update them on this host, from the repository root:
```sh
doas install -D -m 0755 usr/local/libexec/portage-btrfs-snapshot /usr/local/libexec/portage-btrfs-snapshot
doas install -D -m 0755 usr/local/libexec/portage-limine-menu /usr/local/libexec/portage-limine-menu
```
## Snapshot boundaries
The non-backup CachyOS kernel configs and the `linux-firmware` saved config are included. Portage’s generated `._cfg*` merge candidates and timestamped backups are omitted; those are pending-update artifacts or historical copies, not active policy. `/etc/portage/gnupg` is also excluded because it can contain private signing keys and local trust state. The signed-binhost configuration is tracked, but its key material is not.
## Applying the snapshot
First compare the target host’s state and profile; don’t copy this over a different ABI or profile. The diff excludes repository metadata and files deliberately omitted from the snapshot:
```sh
eselect profile show
emerge --info
diff -ru --exclude=.git --exclude=gnupg --exclude=make.profile --exclude=README.md --exclude=LICENSE --exclude=.gitignore --exclude=usr --exclude='._cfg*' --exclude='*~' --exclude='*.backup*' --exclude='*.bak*' /etc/portage .
```
After reviewing the diff, merge only the intended files into `/etc/portage`. This checkout also contains Git metadata, documentation, and ignore rules, so copying the repository root wholesale is not appropriate. Before any package operation, preview the resolver plan with `emerge -pv` and inspect USE, slots, keywords, masks, and removals.
+18
View File
@@ -0,0 +1,18 @@
# BEGIN managed Portage Btrfs snapshots
# Portage invokes these phase hooks after package post-install and post-remove.
# This is one snapshot per changed package, not one per emerge command.
post_pkg_postinst() {
/usr/local/libexec/portage-btrfs-snapshot install "${CATEGORY:-unknown}" "${PF:-unknown}" "${ROOT:-/}" "${EROOT:-/}" || \
printf '!!! Btrfs snapshot failed after installing %s/%s.\n' "${CATEGORY:-unknown}" "${PF:-unknown}" >&2
}
post_pkg_postrm() {
/usr/local/libexec/portage-btrfs-snapshot remove "${CATEGORY:-unknown}" "${PF:-unknown}" "${ROOT:-/}" "${EROOT:-/}" || \
printf '!!! Btrfs snapshot failed after removing %s/%s.\n' "${CATEGORY:-unknown}" "${PF:-unknown}" >&2
}
# END managed Portage Btrfs snapshots
if [ "${PN}" = "firefox" ]; then
export RUSTFLAGS="${RUSTFLAGS} -C target-feature=-crt-static"
fi
+9
View File
@@ -0,0 +1,9 @@
# These settings were set by the catalyst build script that automatically
# built this stage.
# Please consider using a local mirror.
[gentoo]
priority = 9999
sync-uri = https://distfiles.gentoo.org/releases/amd64/binpackages/23.0/x86-64_musl_llvm
location = /var/cache/binhost/gentoo
verify-signature = true
+20
View File
@@ -0,0 +1,20 @@
# /etc/portage/env/codex-lowmem.conf
# Alternative compiler configuration (Clang/LLVM) to mitigate low-memory compilation issues
# 1. Alternative compiler: Clang/LLVM toolchain
CC="clang"
CXX="clang++"
CPP="clang-cpp"
AR="llvm-ar"
NM="llvm-nm"
RANLIB="llvm-ranlib"
# 2. Parallel jobs reduction: limit make to 1 job to prevent RAM exhaustion
MAKEOPTS="-j1"
# 3. Memory-efficient linker: use LLD and release unneeded memory pages
LDFLAGS="${LDFLAGS} -fuse-ld=lld -Wl,--no-keep-memory"
# 4. Memory-efficient LTO: use ThinLTO instead of full monolithic LTO
CFLAGS="${CFLAGS} -flto=thin"
CXXFLAGS="${CXXFLAGS} -flto=thin"
+7
View File
@@ -0,0 +1,7 @@
# /etc/portage/env/compiler-clang.conf
CC="clang"
CXX="clang++"
CPP="clang-cpp"
AR="llvm-ar"
NM="llvm-nm"
RANLIB="llvm-ranlib"
+20
View File
@@ -0,0 +1,20 @@
# /etc/portage/env/codex-lowmem.conf
# Alternative compiler configuration (Clang/LLVM) to mitigate low-memory compilation issues
# 1. Alternative compiler: Clang/LLVM toolchain
CC="clang"
CXX="clang++"
CPP="clang-cpp"
AR="llvm-ar"
NM="llvm-nm"
RANLIB="llvm-ranlib"
# 2. Parallel jobs reduction: limit make to 1 job to prevent RAM exhaustion
MAKEOPTS="-j1"
# 3. Memory-efficient linker: use LLD and release unneeded memory pages
LDFLAGS="${LDFLAGS} -fuse-ld=lld -Wl,--no-keep-memory"
# 4. Memory-efficient LTO: use ThinLTO instead of full monolithic LTO
CFLAGS="${CFLAGS} -flto=thin"
CXXFLAGS="${CXXFLAGS} -flto=thin"
+1
View File
@@ -0,0 +1 @@
export RUSTFLAGS="${RUSTFLAGS} -C target-feature=-crt-static"
+5
View File
@@ -0,0 +1,5 @@
# /etc/portage/env/low-memory.conf
MAKEOPTS="-j1"
LDFLAGS="${LDFLAGS} -fuse-ld=lld -Wl,--no-keep-memory"
CFLAGS="${CFLAGS} -flto=thin"
CXXFLAGS="${CXXFLAGS} -flto=thin"
Vendored
+1
View File
@@ -0,0 +1 @@
LDFLAGS="-Wl,-O1 -Wl,--as-needed"
+34
View File
@@ -0,0 +1,34 @@
# These settings were set by the catalyst build script that automatically
# built this stage.
# Please consult /usr/share/portage/config/make.conf.example for a more
# detailed example.
COMMON_FLAGS="-march=native -O3 -pipe"
RUSTFLAGS=" -C target-cpu=native"
LDFLAGS="${LDFLAGS} -fuse-ld=mold"
CFLAGS="${COMMON_FLAGS}"
CXXFLAGS="${COMMON_FLAGS}"
FCFLAGS="${COMMON_FLAGS}"
FFLAGS="${COMMON_FLAGS}"
USE="wayland -X hyprland dbus wifi bluetooth system-pipewire pulseaudio"
# WARNING: Changing your CHOST is not something that should be done lightly.
# Please consult https://wiki.gentoo.org/wiki/Changing_the_CHOST_variable before changing.
CHOST="x86_64-pc-linux-musl"
MAKEOPTS="-j4 -l5"
ACCEPT_LICENSE="*"
FEATURES="getbinpkg binpkg-request-signature"
EMERGE_DEFAULT_OPTS="--jobs=4"
# NOTE: This stage was built with the bindist USE flag enabled
# This sets the language of build output to English.
# Please keep this setting intact when reporting bugs.
LC_MESSAGES=C.UTF-8
GENTOO_MIRRORS="https://ftp.agdsn.de/gentoo \
https://mirror.alldaydev.com/gentoo"
+1
View File
@@ -0,0 +1 @@
sys-process/bashtop ~amd64
+1
View File
@@ -0,0 +1 @@
*/*::cachy ~amd64
+3
View File
@@ -0,0 +1,3 @@
sys-libs/libucontext ~amd64
sys-libs/gcompat ~amd64
sys-libs/gcompat ** ~amd64
+1
View File
@@ -0,0 +1 @@
*/*::guru ~amd64
+1
View File
@@ -0,0 +1 @@
*/*::hypr ~amd64
+3
View File
@@ -0,0 +1,3 @@
*/*::librewolf ~amd64
dev-libs/nss ~amd64
www-client/librewolf -X
+1
View File
@@ -0,0 +1 @@
sys-boot/limine ~amd64
+1
View File
@@ -0,0 +1 @@
*/*::local ~amd64
+1
View File
@@ -0,0 +1 @@
www-client/luakit ~amd64
+2
View File
@@ -0,0 +1,2 @@
dev-libs/libdwarf ~amd64
dev-cpp/cpptrace ~amd64
+1
View File
@@ -0,0 +1 @@
media-sound/sidra-bin ~amd64
+1
View File
@@ -0,0 +1 @@
dev-util/codex codex-lowmem.conf
+1
View File
@@ -0,0 +1 @@
www-client/firefox firefox.conf
+1
View File
@@ -0,0 +1 @@
sys-kernel/cachyos-kernel no-mold
+2
View File
@@ -0,0 +1,2 @@
sys-libs/gcompat
sys-libs/libucontext
+1
View File
@@ -0,0 +1 @@
*/* CPU_FLAGS_X86: aes avx f16c mmx mmxext pclmul popcnt rdrand sse sse2 sse3 sse4_1 sse4_2 ssse3
+1
View File
@@ -0,0 +1 @@
*/* VIDEO_CARDS: -* intel i915
+12
View File
@@ -0,0 +1,12 @@
sys-boot/limine -bios -bios-cd -bios-pxe -uefi-aarch64 -uefi-cd -uefi-ia32 -uefi-loongarch64 -uefi-riscv64 uefi-x86-64
# required by www-client/librewolf-156.0.1_p1::librewolf[system-libvpx]
# required by librewolf (argument)
>=media-libs/libvpx-1.16.0 postproc
# required by x11-base/xwayland-24.1.13::gentoo
# required by gui-wm/hyprland-0.56.2::hypr[X]
# required by hyprland (argument)
>=media-libs/libepoxy-1.5.10-r3 X
# required by x11-base/xwayland-24.1.13::gentoo
# required by gui-wm/hyprland-0.56.2::hypr[X]
# required by hyprland (argument)
>=media-libs/mesa-26.1.8 X
+1
View File
@@ -0,0 +1 @@
sys-boot/limine -bios -bios-cd -bios-pxe -uefi-aarch64 -uefi-cd -uefi-ia32 -uefi-loongarch64 -uefi-riscv64 uefi-x86-64
+41
View File
@@ -0,0 +1,41 @@
# required by dev-qt/qttools-6.11.2::gentoo[widgets]
# required by dev-qt/qttranslations-6.11.2::gentoo
>=dev-qt/qtbase-6.11.2-r2 vulkan opengl wayland
# required by gui-apps/quickshell-0.3.1::guru[crash-handler]
# required by quickshell (argument)
#>=dev-cpp/cpptrace-1.0.4-r1 -unwind llvm
# required by net-misc/networkmanager-1.56.1::gentoo[-iwd,wifi]
# required by gui-apps/quickshell-0.3.1::guru[networkmanager]
# required by quickshell (argument)
>=net-wireless/wpa_supplicant-2.11-r4 dbus
# required by media-libs/mesa-26.1.8::gentoo
# required by gui-libs/aquamarine-0.15.1::hypr
# required by gui-libs/hyprtoolkit-0.6.0::hypr
# required by gui-libs/hyprland-guiutils-0.2.2::hypr
# required by gui-wm/hyprland-0.56.2::hypr[guiutils]
# required by hyprland (argument)
>=media-libs/libglvnd-1.7.0 X
# required by gui-apps/quickshell-0.3.1::guru
# required by quickshell (argument)
>=dev-qt/qtdeclarative-6.11.2-r1 opengl vulkan wayland
# required by dev-qt/qtbase-6.11.2-r2::gentoo[gui]
# required by dev-qt/qttools-6.11.2::gentoo[assistant]
# required by dev-qt/qttranslations-6.11.2::gentoo
>=x11-libs/libxkbcommon-1.13.2 X
# required by dev-qt/qttranslations-6.11.2::gentoo
# required by dev-qt/qtbase-6.11.2-r2::gentoo[nls]
# required by dev-qt/qtsvg-6.11.2::gentoo
# required by gui-apps/quickshell-0.3.1::guru
# required by quickshell (argument)
>=dev-qt/qttools-6.11.2 opengl
# required by x11-libs/pango-1.57.1::gentoo
# required by gui-libs/aquamarine-0.15.1::hypr
# required by gui-libs/hyprtoolkit-0.6.0::hypr
# required by gui-libs/hyprland-guiutils-0.2.2::hypr
# required by gui-wm/hyprland-0.56.2::hypr[guiutils]
# required by hyprland (argument)
>=media-libs/freetype-2.14.3 harfbuzz
gui-apps/quickshell -i3 hyprland -crash-handler
gui-wm/hyprland -guiutils X hyprpm
net-misc/networkmanager bluetooth iwd
+1
View File
@@ -0,0 +1 @@
app-admin/doas persist
+2
View File
@@ -0,0 +1,2 @@
app-containers/docker btrfs
app-containers/containerd btrfs
View File
Whitespace-only changes.
+3
View File
@@ -0,0 +1,3 @@
# required by dev-util/flatpak-builder-1.4.8::gentoo
# required by flatpak-builder (argument)
>=dev-libs/appstream-1.0.6 compose
+1
View File
@@ -0,0 +1 @@
sys-libs/gcompat libucontext obstack
+1
View File
@@ -0,0 +1 @@
sys-kernel/installkernel dracut
+7
View File
@@ -0,0 +1,7 @@
sys-boot/limine -bios -bios-cd -bios-pxe -uefi-aarch64 -uefi-cd -uefi-ia32 -uefi-loongarch64 -uefi-riscv64 uefi-x86-64
# required by sys-kernel/cachyos-kernel-7.2.8::cachy[autofdo]
# required by virtual/dist-kernel-7.2.8_p0::cachy
# required by sys-kernel/gentoo-kernel-bin-6.18.50::gentoo
# required by @selected
# required by @world (argument)
>=dev-util/perf-6.19.12 libpfm
+1
View File
@@ -0,0 +1 @@
media-fonts/maple-mono nerd
+1
View File
@@ -0,0 +1 @@
sys-apps/s6-linux-init -sysv-utils
+1
View File
@@ -0,0 +1 @@
sys-auth/seatd builtin server
+2
View File
@@ -0,0 +1,2 @@
media-video/pipewire sound-server pipewire-alsa system-service
media-video/wireplumber system-service
+1
View File
@@ -0,0 +1 @@
net-vpn/wireguard-tools wg-quick
+1
View File
@@ -0,0 +1 @@
sys-kernel/cachyos-kernel savedconfig
+6
View File
@@ -0,0 +1,6 @@
[cachy]
location = /var/db/repos/cachy
sync-type = git
sync-uri = https://github.com/Szowisz/CachyOS-kernels.git
auto-sync = yes
force-name = cachy
+12
View File
@@ -0,0 +1,12 @@
# created by eselect-repo
[librewolf]
location = /var/db/repos/librewolf
sync-type = git
sync-uri = https://codeberg.org/librewolf/gentoo.git
[ruizhi-overlay]
location = /var/db/repos/ruizhi-overlay
sync-type = git
sync-uri = https://github.com/ruizhi-lab/gentoo-overlay.git
+21
View File
@@ -0,0 +1,21 @@
[DEFAULT]
main-repo = gentoo
[gentoo]
location = /var/db/repos/gentoo
sync-type = rsync
sync-uri = rsync://rsync.gentoo.org/gentoo-portage/
auto-sync = yes
sync-rsync-verify-jobs = 1
sync-rsync-verify-metamanifest = yes
sync-rsync-verify-max-age = 3
sync-openpgp-key-package = sec-keys/openpgp-keys-gentoo-release
sync-openpgp-key-path = /usr/share/openpgp-keys/gentoo-release.asc
sync-openpgp-keyserver = hkps://keys.gentoo.org
sync-openpgp-key-refresh-retry-count = 40
sync-openpgp-key-refresh-retry-overall-timeout = 1200
sync-openpgp-key-refresh-retry-delay-exp-base = 2
sync-openpgp-key-refresh-retry-delay-max = 60
sync-openpgp-key-refresh-retry-delay-mult = 4
sync-webrsync-verify-signature = yes
sync-git-verify-commit-signature = true
+5
View File
@@ -0,0 +1,5 @@
[guru]
location = /var/db/repos/guru
sync-type = git
sync-uri = https://github.com/gentoo-mirror/guru.git
auto-sync = yes
+6
View File
@@ -0,0 +1,6 @@
[hypr]
location = /var/db/repos/hypr
sync-type = git
sync-uri = https://codeberg.org/hyproverlay/hyproverlay.git
auto-sync = yes
force-name = hypr
+5
View File
@@ -0,0 +1,5 @@
[local]
location = /var/db/repos/local
masters = gentoo
auto-sync = yes
priority = 50
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 220 KiB

+108
View File
@@ -0,0 +1,108 @@
#!/usr/bin/env bash
set -euo pipefail
readonly snapshot_mount='/.snapshots'
readonly snapshot_dir='/.snapshots/portage'
readonly keep_count=32
readonly minimum_free_bytes=2147483648
warn() {
printf 'portage-btrfs-snapshot: %s\n' "$*" >&2
}
fail() {
warn "$*"
exit 1
}
[[ ${EUID} -eq 0 ]] || fail 'must run as root from a Portage package phase.'
[[ $# -eq 5 ]] || fail 'usage: portage-btrfs-snapshot {install|remove} CATEGORY PF ROOT EROOT'
operation=$1
category=$2
package_pf=$3
target_root=$4
effective_root=$5
case "$operation" in
install|remove) ;;
*) fail 'unsupported package operation.' ;;
esac
# Restrict this hook to merges for the live root. Do not snapshot a prefix or
# alternate ROOT that happens to share the same Portage configuration.
[[ "$target_root" == / && "$effective_root" == / ]] || exit 0
for tool in awk btrfs date df find findmnt head install mountpoint python3 sed sort tr; do
command -v "$tool" >/dev/null 2>&1 || fail "required command not found: $tool"
done
[[ $(findmnt -n -o FSTYPE --target /) == btrfs ]] || fail 'the active root is not Btrfs.'
mountpoint -q -- "$snapshot_mount" || fail "$snapshot_mount is not a mounted filesystem."
[[ $(findmnt -n -o FSTYPE --mountpoint "$snapshot_mount") == btrfs ]] || \
fail "$snapshot_mount is not a Btrfs mountpoint."
root_uuid=$(btrfs filesystem show / 2>/dev/null | sed -n 's/.*uuid: \([^[:space:]]*\).*/\1/p' | head -n 1)
snap_uuid=$(btrfs filesystem show "$snapshot_mount" 2>/dev/null | sed -n 's/.*uuid: \([^[:space:]]*\).*/\1/p' | head -n 1)
[[ -n "$root_uuid" && "$root_uuid" == "$snap_uuid" ]] || \
fail 'root and snapshot destination are not on the same Btrfs filesystem.'
root_id=$(btrfs inspect-internal rootid / 2>/dev/null) || fail 'cannot identify the root subvolume.'
dest_id=$(btrfs inspect-internal rootid "$snapshot_mount" 2>/dev/null) || \
fail 'cannot identify the snapshot destination subvolume.'
[[ "$root_id" != "$dest_id" ]] || fail 'snapshot destination resolves to the live root subvolume.'
space_info=$(df -P -B1 "$snapshot_mount" | awk 'NR == 2 { print $2, $4 }') || \
fail 'cannot determine free space on the Btrfs filesystem.'
read -r _total_bytes available_bytes <<< "$space_info"
[[ ${available_bytes:-} =~ ^[0-9]+$ ]] || fail 'cannot determine free space on the Btrfs filesystem.'
(( available_bytes >= minimum_free_bytes )) || \
fail 'less than 2 GiB is available on Btrfs; refusing to create another snapshot.'
install -d -o root -g root -m 0700 -- "$snapshot_dir"
safe_package=$(printf '%s_%s' "$category" "$package_pf" | LC_ALL=C tr -c 'A-Za-z0-9._+-' '_')
timestamp=$(date -u +%Y%m%dT%H%M%S%N)
snapshot_path="${snapshot_dir}/portage-${timestamp}-${operation}-${safe_package}"
btrfs subvolume snapshot -r / "$snapshot_path" >/dev/null || \
fail "snapshot creation failed for ${category}/${package_pf} (${operation})."
printf 'Created read-only Btrfs snapshot: %s\n' "$snapshot_path"
menu_manager='/usr/local/libexec/portage-limine-menu'
if [[ -x "$menu_manager" ]]; then
if [[ "$operation" == install && "$category" == sys-kernel ]]; then
# Kernel package post-install runs after its image has been installed.
# Sync kernel groups and attach all compatible snapshots, including
# the snapshot just created above.
"$menu_manager" sync-kernels || \
warn "Limine kernel/snapshot menu reconciliation failed after ${category}/${package_pf}."
else
"$menu_manager" add-snapshot "$snapshot_path" || \
warn "Limine recovery entry creation failed for $snapshot_path."
fi
else
warn 'Limine menu manager is missing; the read-only snapshot was created without a boot entry.'
fi
# Names begin with a sortable UTC timestamp. Only remove our own snapshots,
# after confirming each candidate is an actual Btrfs subvolume.
mapfile -t snapshots < <(find "$snapshot_dir" -mindepth 1 -maxdepth 1 -type d \
-name 'portage-*' -printf '%f\n' | LC_ALL=C sort)
excess=$((${#snapshots[@]} - keep_count))
if (( excess > 0 )); then
for ((i = 0; i < excess; i++)); do
old_path="${snapshot_dir}/${snapshots[i]}"
if btrfs subvolume show "$old_path" >/dev/null 2>&1; then
if [[ -x "$menu_manager" ]]; then
"$menu_manager" remove-snapshot "$old_path" || {
warn "retention kept snapshot because its Limine entry/clone could not be removed: $old_path"
continue
}
fi
btrfs subvolume delete "$old_path" >/dev/null || \
warn "could not prune old snapshot: $old_path"
else
warn "retention skipped a non-subvolume entry: $old_path"
fi
done
fi
+582
View File
@@ -0,0 +1,582 @@
#!/usr/bin/env python3
"""Maintain Limine entries for Gentoo kernel installs and Portage snapshots."""
from __future__ import annotations
import fcntl
import hashlib
import os
import re
import shutil
import subprocess
import sys
import tempfile
from dataclasses import dataclass
from pathlib import Path
CONFIG = Path("/boot/limine.conf")
BOOT = Path("/boot")
MODULE_ROOTS = (Path("/usr/lib/modules"), Path("/lib/modules"))
SNAPSHOT_ROOT = Path("/.snapshots/portage")
CLONE_ROOT = Path("/.snapshots/bootable")
MARKER_START = "# BEGIN PORTAGE SNAPSHOT "
MARKER_END = "# END PORTAGE SNAPSHOT "
ENTRY_RE = re.compile(r"(?m)^(/+)([^\r\n]*)\r?\n")
class MenuError(RuntimeError):
pass
@dataclass
class Entry:
depth: int
title: str
start: int
end: int
block: str
def run(argv: list[str], *, capture: bool = True) -> str:
result = subprocess.run(
argv,
check=False,
text=True,
stdout=subprocess.PIPE if capture else None,
stderr=subprocess.PIPE if capture else None,
)
if result.returncode:
details = (result.stderr or result.stdout or "").strip()
raise MenuError(f"command failed ({result.returncode}): {' '.join(argv)}\n{details}")
return result.stdout or ""
def split_entries(text: str) -> tuple[str, list[Entry]]:
matches = list(ENTRY_RE.finditer(text))
if not matches:
raise MenuError("Limine configuration has no menu entries")
entries: list[Entry] = []
for index, match in enumerate(matches):
end = matches[index + 1].start() if index + 1 < len(matches) else len(text)
entries.append(
Entry(
depth=len(match.group(1)),
title=match.group(2),
start=match.start(),
end=end,
block=text[match.start():end],
)
)
return text[:matches[0].start()], entries
def value(entry: Entry, key: str) -> str | None:
match = re.search(rf"(?mi)^\s*{re.escape(key)}\s*:\s*(.*?)\s*$", entry.block)
return match.group(1) if match else None
def all_entries(text: str) -> list[Entry]:
return split_entries(text)[1]
def children_of(entries: list[Entry], parent: Entry) -> list[Entry]:
try:
index = entries.index(parent)
except ValueError:
return []
children: list[Entry] = []
for entry in entries[index + 1:]:
if entry.depth <= parent.depth:
break
children.append(entry)
return children
def kernel_release(kernel_path: str | None) -> str | None:
if not kernel_path:
return None
path = kernel_path.rsplit(":", 1)[-1]
name = Path(path).name
if not name.startswith("vmlinuz-") or name.endswith(".old"):
return None
return name.removeprefix("vmlinuz-")
def root_subvol(cmdline: str | None) -> str | None:
if not cmdline:
return None
match = re.search(r"(?:^|\s)rootflags=([^\s]+)", cmdline)
if not match:
return None
for option in match.group(1).split(","):
if option.startswith("subvol="):
return option.removeprefix("subvol=")
return None
def read_config() -> str:
try:
return CONFIG.read_text(encoding="utf-8")
except OSError as error:
raise MenuError(f"cannot read {CONFIG}: {error}") from error
def backup_config() -> None:
backup = Path(str(CONFIG) + ".pre-portage-recovery-menu")
if not backup.exists():
shutil.copy2(CONFIG, backup)
print(f"Saved original Limine config: {backup}")
def write_config(text: str) -> None:
if not text.endswith("\n"):
text += "\n"
validate_config(text)
backup_config()
fd, temporary = tempfile.mkstemp(prefix="limine.conf.portage.", dir=CONFIG.parent)
try:
with os.fdopen(fd, "w", encoding="utf-8", newline="\n") as stream:
stream.write(text)
stream.flush()
os.fsync(stream.fileno())
os.replace(temporary, CONFIG)
except BaseException:
try:
os.unlink(temporary)
except OSError:
pass
raise
def validate_config(text: str) -> None:
prefix, entries = split_entries(text)
if not re.search(r"(?mi)^\s*timeout\s*:\s*", prefix):
raise MenuError("Limine config validation failed: global timeout is missing")
roots = [entry for entry in entries if entry.depth == 1]
if not roots:
raise MenuError("Limine config validation failed: no top-level entries")
current_path = None
default = re.search(r"(?mi)^\s*default_entry\s*:\s*(.*?)\s*$", prefix)
if default:
current_path = default.group(1).split("#", 1)[0]
if any(children_of(entries, root) for root in roots) and not current_path:
raise MenuError("Limine config validation failed: nested kernel entries require an explicit default_entry path")
if current_path:
if current_path.isdecimal():
raise MenuError("Limine config validation failed: use a leaf entry path for default_entry when kernel directories exist")
parts = current_path.split("/")
parent_title = parts[0]
child_title = parts[1] if len(parts) > 1 else None
parent = next((entry for entry in roots if entry.title == parent_title), None)
if parent is None:
raise MenuError(f"Limine config validation failed: default parent is missing: {parent_title}")
if child_title and not any(
entry.depth == 2 and entry.title == child_title
for entry in children_of(entries, parent)
):
raise MenuError(f"Limine config validation failed: default subentry is missing: {current_path}")
if child_title is None and children_of(entries, parent):
raise MenuError(f"Limine config validation failed: default_entry points to a directory: {current_path}")
for entry in entries:
if entry.depth == 2 and entry.title.startswith("Portage "):
for key in ("protocol", "kernel_path", "module_path", "cmdline"):
if value(entry, key) is None:
raise MenuError(f"Limine config validation failed: generated entry {entry.title!r} lacks {key}")
def migrate() -> None:
text = read_config()
prefix, entries = split_entries(text)
if any(entry.depth > 1 for entry in entries):
validate_config(text)
if kernel_groups(text):
print("Limine kernel groups are already configured.")
return
raise MenuError("Limine config has nested entries but no managed kernel groups; refusing migration")
kernels: list[Entry] = []
recovery: list[Entry] = []
other: list[Entry] = []
for entry in entries:
if value(entry, "protocol") != "linux" or not value(entry, "kernel_path"):
other.append(entry)
continue
root = root_subvol(value(entry, "cmdline"))
if root == "@":
kernels.append(entry)
elif root and root.startswith("@snapshots/bootable/"):
recovery.append(entry)
else:
other.append(entry)
dist = next((entry for entry in kernels if "Dist Kernel" in entry.title), None)
if dist is None:
raise MenuError("expected Gentoo Dist Kernel entry was not found; refusing to change the boot menu")
if not any("CachyOS Kernel" in entry.title for entry in kernels):
raise MenuError("expected CachyOS Kernel entry was not found; refusing to change the boot menu")
groups: dict[str, list[str]] = {}
releases: dict[str, str] = {}
for entry in kernels:
title = entry.title
release = kernel_release(value(entry, "kernel_path"))
if not release:
raise MenuError(f"cannot derive kernel release for {title}")
if title in groups:
raise MenuError(f"duplicate kernel group title: {title}")
body = entry.block[entry.block.find("\n") + 1:]
groups[title] = [f"/{title}\n", f"//Current boot\n{body}"]
releases[release] = title
for entry in recovery:
release = kernel_release(value(entry, "kernel_path"))
title = releases.get(release or "")
if title is None:
other.append(entry)
continue
body = entry.block[entry.block.find("\n") + 1:]
child_title = entry.title
match = re.match(r"Gentoo Linux \((.*)\)", child_title)
if match:
child_title = match.group(1)
groups[title].append(f"//{child_title}\n{body}")
result_prefix = prefix
if re.search(r"(?mi)^\s*default_entry\s*:", result_prefix):
result_prefix = re.sub(
r"(?mi)^\s*default_entry\s*:\s*[^\r\n]*",
"default_entry: Gentoo Linux (Dist Kernel)/Current boot",
result_prefix,
count=1,
)
else:
result_prefix = result_prefix.rstrip() + "\ndefault_entry: Gentoo Linux (Dist Kernel)/Current boot\n\n"
pieces = [result_prefix.rstrip() + "\n\n"]
written: set[str] = set()
for entry in entries:
if entry.title in groups and entry.title not in written:
pieces.extend(groups[entry.title])
pieces.append("\n")
written.add(entry.title)
elif entry in other:
pieces.append(entry.block)
for title, group in groups.items():
if title not in written:
pieces.extend(group)
pieces.append("\n")
write_config("".join(pieces))
print("Migrated existing kernel entries into Limine kernel groups; Dist Kernel remains the default.")
def kernel_groups(text: str) -> list[tuple[Entry, Entry, str]]:
_, entries = split_entries(text)
groups: list[tuple[Entry, Entry, str]] = []
for parent in entries:
if parent.depth != 1:
continue
for child in children_of(entries, parent):
if child.depth != 2 or child.title != "Current boot":
continue
release = kernel_release(value(child, "kernel_path"))
if release:
groups.append((parent, child, release))
return groups
def option_block(title: str, kernel: str, module_paths: list[str], cmdline: str, depth: int = 2) -> str:
slashes = "/" * depth
content = (
f"{slashes}{title}\n"
" protocol: linux\n"
f" kernel_path: boot():/{kernel}\n"
)
content += "".join(f" module_path: boot():/{module}\n" for module in module_paths)
return content + f" cmdline: {cmdline}\n"
def module_releases(root: Path) -> set[str]:
releases: set[str] = set()
for base in (root / "usr/lib/modules", root / "lib/modules"):
try:
releases.update(path.name for path in base.iterdir() if path.is_dir())
except OSError:
pass
return releases
def compatible_groups(root: Path, text: str) -> list[tuple[Entry, Entry, str]]:
releases = module_releases(root)
compatible: list[tuple[Entry, Entry, str]] = []
for group, current, release in kernel_groups(text):
kernel_path = value(current, "kernel_path")
module_paths = re.findall(r"(?mi)^\s*module_path\s*:\s*(.*?)\s*$", current.block)
if release not in releases or not kernel_path or not module_paths:
continue
if not boot_asset_exists(kernel_path) or not all(boot_asset_exists(path) for path in module_paths):
continue
compatible.append((group, current, release))
return compatible
def boot_asset_exists(limine_path: str) -> bool:
if not limine_path.startswith("boot():/"):
return False
return (BOOT / limine_path.removeprefix("boot():/")).is_file()
def legacy_clone_for(source: Path, group: Entry, text: str) -> tuple[Path, str] | None:
"""Reuse a pre-manager recovery clone when it clearly matches this package."""
lowered = source.name.lower()
token = "fastfetch" if "install-app-misc_fastfetch" in lowered else None
if token is None:
return None
_, entries = split_entries(text)
fresh_group = next((entry for entry in entries if entry.depth == 1 and entry.title == group.title), None)
if fresh_group is None:
return None
for child in children_of(entries, fresh_group):
if child.depth != 2 or child.title == "Current boot" or child.title.startswith("Portage "):
continue
if token not in child.title.lower():
continue
subvol = root_subvol(value(child, "cmdline"))
if not subvol or not subvol.startswith("@snapshots/bootable/"):
continue
clone = CLONE_ROOT / subvol.removeprefix("@snapshots/bootable/")
if not clone.is_dir():
continue
try:
if run(["btrfs", "property", "get", "-ts", str(clone), "ro"]).strip() == "ro=false":
return clone, subvol
except MenuError:
continue
return None
def marker_block(snapshot_name: str, release: str, clone_subvol: str, group_title: str, child_title: str) -> str:
_, entries = split_entries(read_config())
group = next((entry for entry in entries if entry.depth == 1 and entry.title == group_title), None)
if group is None:
raise MenuError(f"kernel group disappeared during menu update: {group_title}")
current = next((entry for entry in children_of(entries, group) if entry.depth == 2 and entry.title == "Current boot"), None)
if current is None:
raise MenuError(f"kernel group has no Current boot subentry: {group.title}")
cmdline = value(current, "cmdline")
if not cmdline or not re.search(r"(?:^|\s)rootflags=[^\s]+", cmdline):
raise MenuError(f"kernel group has no rootflags in its command line: {group.title}")
cmdline = re.sub(r"((?:^|\s)rootflags=)[^\s]+", rf"\g<1>subvol={clone_subvol}", cmdline, count=1)
modules = re.findall(r"(?mi)^\s*module_path\s*:\s*(.*?)\s*$", current.block)
module_names = [Path(module.split(":", 1)[-1]).name for module in modules]
body = option_block(child_title, Path(value(current, "kernel_path").split(":", 1)[-1]).name,
module_names, cmdline)
marker_id = f"{snapshot_name} {release}"
return f"{MARKER_START}{marker_id}\n{body}{MARKER_END}{marker_id}\n"
def clone_name(snapshot_name: str) -> str:
digest = hashlib.sha256(snapshot_name.encode("utf-8")).hexdigest()[:20]
return f"portage-{digest}"
def subvol_path(path: Path) -> str:
listing = run(["btrfs", "subvolume", "list", "-a", "/"])
wanted = path.name
found: list[str] = []
for line in listing.splitlines():
if " path " not in line:
continue
subvol = line.split(" path ", 1)[1].strip()
if subvol.startswith("<FS_TREE>/"):
subvol = subvol[len("<FS_TREE>/"):]
if subvol == wanted or subvol.endswith("/" + wanted):
found.append(subvol)
if len(found) != 1:
raise MenuError(f"cannot uniquely resolve Btrfs top-level path for {path}")
return found[0]
def writable_clone(source: Path) -> tuple[Path, str]:
if not source.is_dir():
raise MenuError(f"snapshot source is missing: {source}")
ro = run(["btrfs", "property", "get", "-ts", str(source), "ro"]).strip()
if ro != "ro=true":
raise MenuError(f"refusing to clone a non-read-only source snapshot: {source} ({ro})")
CLONE_ROOT.mkdir(mode=0o700, parents=True, exist_ok=True)
target = CLONE_ROOT / clone_name(source.name)
if not target.exists():
available = shutil.disk_usage("/.snapshots").free
if available < 2 * 1024**3:
raise MenuError("less than 2 GiB is free; refusing to create a writable snapshot clone")
run(["btrfs", "subvolume", "snapshot", str(source), str(target)], capture=False)
print(f"Created writable recovery clone: {target}")
clone_ro = run(["btrfs", "property", "get", "-ts", str(target), "ro"]).strip()
if clone_ro != "ro=false":
raise MenuError(f"recovery clone is not writable: {target} ({clone_ro})")
return target, subvol_path(target)
def insert_child(text: str, parent_title: str, child: str) -> str:
prefix, entries = split_entries(text)
parent_index = next((i for i, entry in enumerate(entries) if entry.depth == 1 and entry.title == parent_title), None)
if parent_index is None:
raise MenuError(f"kernel group disappeared during menu update: {parent_title}")
insert_at = len(text)
for entry in entries[parent_index + 1:]:
if entry.depth == 1:
insert_at = entry.start
break
before = text[:insert_at]
if before and not before.endswith("\n\n"):
before = before.rstrip("\n") + "\n\n"
after = text[insert_at:]
if after and not child.endswith("\n\n"):
child += "\n"
return before + child + after
def add_snapshot(source: Path) -> bool:
name = source.name
text = read_config()
compatible = compatible_groups(source, text)
if not compatible:
print(f"Snapshot has no matching kernel/initramfs menu group; keeping snapshot only: {source}", file=sys.stderr)
return False
adopted: tuple[Path, str] | None = None
for group, _current, _release in compatible:
adopted = legacy_clone_for(source, group, text)
if adopted:
break
clone, subvol = adopted if adopted else writable_clone(source)
changed = False
for group, _current, _release in compatible:
marker = f"{MARKER_START}{name} {_release}\n"
if marker in text:
continue
if legacy_clone_for(source, group, text):
print(f"Existing recovery entry under {group.title} already covers {source.name}")
continue
title = f"Portage {name.removeprefix('portage-')}"
child = marker_block(name, _release, subvol, group.title, title)
text = insert_child(text, group.title, child)
changed = True
if changed:
write_config(text)
print(f"Added Limine recovery option(s) for {source.name} using {clone.name}")
return changed
def remove_snapshot(source: Path) -> None:
name = source.name
text = read_config()
pattern = re.compile(
rf"(?ms)^# BEGIN PORTAGE SNAPSHOT {re.escape(name)} [^\n]+\n.*?^# END PORTAGE SNAPSHOT {re.escape(name)} [^\n]+\n?"
)
new_text, count = pattern.subn("", text)
if count:
write_config(new_text)
print(f"Removed {count} Limine recovery option(s) for expired snapshot {name}")
clone = CLONE_ROOT / clone_name(name)
if clone.exists():
show = run(["btrfs", "subvolume", "show", str(clone)])
if "Subvolume ID:" not in show:
raise MenuError(f"refusing to delete unverified clone path: {clone}")
run(["btrfs", "subvolume", "delete", str(clone)], capture=False)
print(f"Deleted expired writable recovery clone: {clone}")
def ensure_kernel_groups() -> int:
text = read_config()
groups = kernel_groups(text)
releases = module_releases(Path("/"))
created = 0
template = next((entry for _group, entry, _release in groups if value(entry, "cmdline")), None)
if template is None:
raise MenuError("cannot add kernel groups without an existing Current boot command line")
template_cmdline = value(template, "cmdline") or ""
for release in sorted(releases):
kernel = BOOT / f"vmlinuz-{release}"
initramfs = BOOT / f"initramfs-{release}.img"
if not kernel.is_file():
continue
if not initramfs.is_file():
print(f"Kernel {release} has no matching initramfs at {initramfs}; no Limine entry was created.", file=sys.stderr)
continue
text = read_config()
groups = kernel_groups(text)
if any(existing == release for _group, _entry, existing in groups):
continue
title = f"Gentoo Linux (Kernel {release})"
if any(entry.depth == 1 and entry.title == title for entry in all_entries(text)):
continue
cmdline = template_cmdline
if not re.search(r"(?:^|\s)rootflags=", cmdline):
raise MenuError("existing kernel template has no rootflags; refusing to invent a root command line")
body = option_block("Current boot", kernel.name, [initramfs.name], cmdline)
text = text.rstrip() + f"\n\n/{title}\n{body}\n"
write_config(text)
created += 1
print(f"Added Limine kernel group for {release}")
text = read_config()
try:
snapshots = sorted(
path for path in SNAPSHOT_ROOT.iterdir()
if path.is_dir() and path.name.startswith("portage-")
)
except OSError:
snapshots = []
for snapshot in snapshots:
add_snapshot(snapshot)
return created
def reconcile() -> None:
ensure_kernel_groups()
def main(argv: list[str]) -> int:
if os.geteuid() != 0:
raise MenuError("must run as root from the Portage hook or installer")
if len(argv) < 2 or argv[1] not in {"migrate", "reconcile", "sync-kernels", "add-snapshot", "remove-snapshot"}:
raise MenuError("usage: portage-limine-menu {migrate|reconcile|sync-kernels|add-snapshot|remove-snapshot} [SNAPSHOT]")
action = argv[1]
os.makedirs("/run/lock", exist_ok=True)
with open("/run/lock/portage-limine-menu.lock", "w", encoding="ascii") as lock:
fcntl.flock(lock.fileno(), fcntl.LOCK_EX)
if action == "migrate":
migrate()
elif action == "reconcile":
migrate_if_needed()
reconcile()
elif action == "sync-kernels":
ensure_kernel_groups()
elif action in {"add-snapshot", "remove-snapshot"}:
migrate_if_needed()
source = Path(argv[2]) if len(argv) > 2 else None
if source is None:
raise MenuError(f"{action} requires a snapshot path")
if action == "add-snapshot":
add_snapshot(source)
else:
remove_snapshot(source)
return 0
def migrate_if_needed() -> None:
text = read_config()
_, entries = split_entries(text)
if not any(entry.depth > 1 for entry in entries):
migrate()
return
validate_config(text)
if not kernel_groups(text):
raise MenuError("Limine menu is nested but has no managed kernel groups")
if __name__ == "__main__":
try:
sys.exit(main(sys.argv))
except (MenuError, OSError, subprocess.SubprocessError) as error:
print(f"portage-limine-menu: {error}", file=sys.stderr)
sys.exit(1)
+61
View File
@@ -0,0 +1,61 @@
app-admin/doas
app-editors/nano
app-editors/neovim
app-eselect/eselect-repository
app-misc/brightnessctl
app-misc/fastfetch
app-portage/gentoolkit
app-shells/fish
dev-cpp/cairomm
dev-lang/execline
dev-util/codex
dev-util/flatpak-builder
gui-apps/foot
gui-apps/grim
gui-apps/hyprpaper
gui-apps/hyprsunset
gui-apps/quickshell
gui-apps/slurp
gui-apps/tuigreet
gui-apps/wl-clip-persist
gui-apps/wl-clipboard
gui-libs/display-manager-init
gui-libs/greetd
gui-libs/hyprcursor
gui-libs/hyprland-guiutils
gui-wm/cage
gui-wm/hyprland
media-fonts/fontawesome
media-fonts/maple-mono
media-fonts/nerdfonts
media-gfx/imv
media-libs/gst-plugins-good
media-libs/libpulse
media-sound/pavucontrol
media-sound/playerctl
media-video/pipewire
net-misc/chrony
net-vpn/wireguard-tools
net-wireless/iwd
sys-apps/flatpak
sys-apps/pciutils
sys-apps/s6
sys-apps/s6-linux-init
sys-apps/s6-rc
sys-auth/seatd
sys-boot/efibootmgr
sys-boot/limine
sys-cluster/kubectl
sys-devel/mold
sys-firmware/intel-microcode
sys-fs/btrfs-progs
sys-fs/fuse:0
sys-kernel/cachyos-kernel
sys-kernel/dracut
sys-kernel/gentoo-kernel-bin
sys-kernel/gentoo-sources
sys-kernel/installkernel
sys-kernel/linux-firmware
sys-libs/gcompat
sys-process/htop
x11-themes/bibata-xcursors