Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
591273f
docs: lean audit with baseline metrics from the last ISO build
claude Oct 20, 2026
866fb9f
perf: leave kernel headers, spectacle, the X server and inxi off the …
claude Oct 20, 2026
a4526e2
feat(seg): seg, one small Rust tool for the system (tools/seg)
claude Oct 20, 2026
593b379
feat(seg): segctl package, services and image wiring
claude Oct 20, 2026
61e3e6c
perf: makepkg builds with every thread; I/O scheduler rule skips part…
claude Oct 20, 2026
775db51
feat: ripgrep, fd, eza and zoxide in the full edition
claude Oct 20, 2026
749fe5e
chore(ci): check seg, the udev rules, repeated packages and more scripts
claude Oct 20, 2026
67dc6e0
docs: Rust tools, bug log, README and building notes for seg and the …
claude Oct 20, 2026
57a1b1f
chore(ci): temporary ISO check for the work branch [iso check]
claude Oct 20, 2026
cfd6ba8
fix(seg): keep Setup's zram swappiness and leave its zram device alone
claude Oct 20, 2026
6e323a4
fix(seg): doctor no longer reports shutdown-runlevel services as failed
claude Oct 20, 2026
f492cae
chore(ci): remove the temporary ISO check; docs: after numbers and li…
claude Oct 20, 2026
cd1f78c
feat(seg-game): seg game run|check|setup|apply|audio|unsafe-boost, se…
claude Oct 20, 2026
7440b18
feat(seg-game): package the game services, sudoers rule and presets
claude Oct 20, 2026
fa1a139
perf: gaming sysctl, open-file limit, shader cache sizes and NVIDIA m…
claude Oct 20, 2026
6fc01a4
perf: wine-staging and lib32-mangohud in the full edition
claude Oct 20, 2026
66a8cfd
feat(setup): a fallback kernel, amd_pstate=active, THP madvise and se…
claude Oct 20, 2026
766b976
chore(ci): allow libc in seg, keep unsafe to proc.rs, check the new f…
claude Oct 20, 2026
3024bc2
docs: seg game, Steam launch options, check reference, benchmark meth…
claude Oct 20, 2026
0d857af
fix: drop lib32-mangohud, it is not in the Artix repositories
claude Oct 20, 2026
9105b63
chore(ci): resolve the packages seg game setup can choose on Artix
claude Oct 20, 2026
84f626e
fix(upgrade): sudo seglinux-upgrade also builds and installs segctl
claude Oct 20, 2026
4bbb12b
fix(seg-game): seg game apply sets hugepages to madvise
claude Oct 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 83 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,44 @@ jobs:
- name: Validate the default segrun configuration
run: cargo run --locked -q -p segrun -- --config data/seglinux/segrun.toml config

# seg (tools/seg) is a workspace of its own with its own release profile.
seg:
name: seg (fmt, clippy, tests, size)
runs-on: ubuntu-latest
defaults:
run:
working-directory: tools/seg
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- uses: Swatinem/rust-cache@v2
with:
workspaces: tools/seg
- run: cargo fmt --check
- run: cargo clippy --locked --all-targets -- -D warnings
# Without the test cfg, where unwrap and expect are compile errors.
- run: cargo clippy --locked -- -D warnings
- run: cargo test --locked
- name: Dependencies stay minimal (seg, lexopt and libc only)
run: |
cargo tree --locked -e normal --prefix none | sort -u
test "$(cargo tree --locked -e normal --prefix none | sort -u | wc -l)" -le 3
# The only unsafe code is the few libc calls of src/game/proc.rs.
- name: unsafe stays in one file
run: |
test "$(grep -rEl '\bunsafe[[:space:]]*(\{|fn|impl|extern)' segctl/src)" = "segctl/src/game/proc.rs"
- name: sudoers rule is valid
run: |
sudo visudo -cf dist/sudoers-seg-game
- name: Release binary size
run: |
cargo build --locked --release
ls -l target/release/seg
target/release/seg --version
echo "seg is $(stat -c %s target/release/seg) bytes" >> "$GITHUB_STEP_SUMMARY"

scripts:
name: Shell scripts and profile files
runs-on: ubuntu-latest
Expand All @@ -68,12 +106,15 @@ jobs:
run: |
shellcheck -s bash iso/build.sh iso/scripts/*.sh packaging/seglinux/seglinux.install data/system/seglinux-upgrade data/system/seglinux-update \
packaging/make-signing-key.sh crates/seglinux-shell/tests/desktop-services.sh \
data/system/fastfetch.bashrc
data/system/fastfetch.bashrc data/system/rust-tools.bashrc
shellcheck -s sh data/system/apply-branding data/system/pipewire-session \
iso/iso-profiles/seglinux/live-overlay/usr/lib/seglinux/live-autostart \
data/desktop/seglinux-session data/desktop/seglinux-gaming-session \
data/desktop/labwc/autostart data/system/seglinux-terminal data/system/restore-trimmed-files \
data/system/profile-gaming.sh data/greeter/seglinux-greeter-session
data/system/profile-gaming.sh data/greeter/seglinux-greeter-session data/system/zram-setup \
tools/seg/dist/fstrim-weekly tools/seg/dist/runit/*/run tools/seg/dist/runit/seg-zram/finish \
tools/seg/dist/openrc/seg-tune tools/seg/dist/openrc/seg-zram tools/seg/dist/openrc/seg-game
shellcheck -s bash pkg/segctl/segctl.install
shellcheck -s bash iso/iso-profiles/seglinux-lite/live-overlay/etc/skel/.bash_profile
- name: YAML, JSON and XML syntax
run: |
Expand All @@ -85,6 +126,30 @@ jobs:
sed '/^ *\/\//d' data/fastfetch/config.jsonc | python3 -m json.tool > /dev/null
python3 -c 'import sys, xml.dom.minidom as m; [m.parse(f) for f in sys.argv[1:]]' data/desktop/labwc/rc.xml \
data/greeter/labwc/rc.xml data/greeter/labwc/menu.xml
- name: Package lists have no repeats
run: |
python3 - <<'EOF'
import collections, yaml
failed = False
for profile, common in (("seglinux", "iso/iso-profiles/common/common.yaml"),
("seglinux-lite", "iso/iso-profiles/seglinux-lite/common.yaml")):
c = yaml.safe_load(open(common))
p = yaml.safe_load(open(f"iso/iso-profiles/{profile}/profile.yaml"))
xorg = "packages-xlibre" if p["live-session"].get("use-xlibre") else "packages-xorg"
for init in ("openrc", "runit", "dinit"):
names = []
for key in ("packages-base", "packages-apps", xorg, "packages-misc"):
names += c.get(key) or []
names += (c.get("packages-init") or {}).get(init) or []
names += p["rootfs"]["packages"] + ((p["rootfs"].get("packages-init") or {}).get(init) or [])
repeats = [n for n, k in collections.Counter(names).items() if k > 1]
if repeats:
failed = True
print(f"{profile} ({init}): listed more than once: {', '.join(repeats)}")
raise SystemExit(1 if failed else 0)
EOF
- name: udev rules
run: udevadm verify --no-style data/system/*.rules
- name: Overlay links resolve
run: |
broken=$(find iso/iso-profiles -xtype l)
Expand Down Expand Up @@ -142,7 +207,9 @@ jobs:
pacman-key --populate artix
pacman -Sy --noconfirm --needed go-yq
iso/scripts/chaotic-aur.sh
- run: iso/scripts/check-packages.sh -p ${{ matrix.profile }} -i ${{ matrix.init }} -k linux-zen
# -x: the packages `seg game setup` can choose (NVIDIA's driver, the
# Vulkan drivers, the games stack) must exist and resolve on top of the image.
- run: iso/scripts/check-packages.sh -p ${{ matrix.profile }} -i ${{ matrix.init }} -k linux-zen -x tools/seg/segctl/tests/setup-packages.txt
- name: Packages the Lite edition downloads during Setup
if: matrix.profile == 'seglinux-lite'
run: iso/scripts/online-packages.sh -i openrc -k linux-zen
Expand Down Expand Up @@ -235,6 +302,19 @@ jobs:
SUDO_USER=tester SEGLINUX_SOURCE="$PWD" SEGLINUX_VERSION=0.1.0.999999 \
bash data/system/seglinux-upgrade --noconfirm
pacman -Q seglinux | grep -q ' 0\.1\.0\.999999-'
# seg (package segctl) comes with it, with its group, sudoers rule
# and services (pacman -U ran the package's install script).
pacman -Q segctl
seg --version
getent group gamemode
test "$(stat -c %a /etc/sudoers.d/seg-game)" = 440
visudo -cf /etc/sudoers.d/seg-game
test -x /etc/init.d/seg-game && test -f /etc/runit/sv/seg-game/run && test -f /etc/dinit.d/seg-game
ls /etc/dinit.d/boot.d /etc/runlevels/default 2>/dev/null | grep -E '^seg-' || true
# seg game works on a machine with no GPU: it reports, it does not crash.
seg game check > /tmp/game-check.txt || test $? -eq 1
grep -q 'max_map_count' /tmp/game-check.txt
seg game run --dry-run -- true
test -x /usr/bin/seglinux-upgrade && test -L /usr/bin/seglinux-update
test ! -e /usr/local/bin/seglinux-upgrade
seglinux-shell --version
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
/packaging/seglinux/src/
/packaging/seglinux/pkg/
/packaging/seglinux/*.pkg.tar.*
/tools/seg/target/
/pkg/segctl/*.tar.gz
/pkg/segctl/src/
/pkg/segctl/pkg/
/pkg/segctl/*.pkg.tar.*
*.iso
*.iso.sha256
_site/
Expand Down
94 changes: 94 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Changelog

Notable changes to SegLinux. The project has no version numbers yet, so this
lists what changed since the last ISO build of 19 October 2026 (`4ca2f45`).
Measurements and the reasons behind each removal are in
[docs/lean-audit.md](docs/lean-audit.md).

## Unreleased

### Added

- **`seg game`**, a small gaming layer in the `seg` tool, the same on OpenRC,
runit, dinit and s6 ([docs/gaming.md](docs/gaming.md#seg-game)):
- `seg game run -- CMD` (Steam: `seg game run -- %command%`) raises the CPU
governor and energy preference and an AMD GPU's power level while a game
runs, gives the game a little more CPU and the best I/O priority, holds
off the screen saver, can pin it to the V-Cache chiplet of a two-chiplet
X3D CPU, and restores everything when the game ends, on Ctrl+C, SIGTERM,
SIGHUP or a crash of `seg`.
- `seg game check`: PASS/WARN/FAIL/SKIP for Vulkan, the GPU driver, 32-bit
drivers, NTSYNC, `vm.max_map_count`, open files, kernel, fallback kernel,
CPU governor and driver, boost permission, hugepages, split lock,
compaction and mitigations, each with the exact fix.
- `seg game setup [--yes]`: the driver packages for the GPUs found in the
computer, and only those.
- `seg game profiles`, per-game profiles in `~/.config/seg/games.toml`
(governor, CPU pinning, MangoHud, gamescope arguments, nice).
- `seg game apply` and the **`seg-game` service** (OpenRC, runit, dinit,
s6): the gaming sysctls and `/dev/ntsync` permissions at boot.
- `seg game audio on|off`: low-latency PipeWire, off by default.
- `seg game unsafe-boost on|off`: `mitigations=off`, the only way to get
it, behind a warning and `--i-understand`.
- `seg tune gaming`: low swappiness, warm caches, short write-back, CPU
left dynamic.
- A sudoers rule for the `gamemode` group (two commands, nothing else), a
MangoHud preset and PipeWire drop-ins under `/usr/share/seg` (all off
until asked for).
- Setup installs the **other kernel** (`linux` beside `linux-zen`, or the
reverse) as a boot menu entry to fall back on when online
(`[performance] fallback_kernel`); the installed kernel is never removed.
- Transparent hugepages go from `always` to `madvise` at boot (`seg-game`
service), so the live image and upgraded systems get it too, not only
fresh installs.
- Kernel command line: `transparent_hugepage=madvise` everywhere and
`amd_pstate=active` on AMD processors (an answer file's parameter of the
same name replaces the default).
- `vm.compaction_proactiveness = 0` with the other gaming sysctls.
- NVIDIA: `nvidia_drm modeset=1 fbdev=1` (Wayland compositors, VT switch).
- `seg` (`tools/seg`, package `segctl`): `info`, `svc`, `clean`, `tune`,
`zram`, `doctor` for OpenRC, runit, dinit and s6, with the `seg-zram` and
`seg-tune` services and a weekly `fstrim` cron job.
- `ripgrep`, `fd`, `eza` and `zoxide` in the full edition.
- `docs/lean-audit.md`: what the images weigh before and after, the bloat
table, the bugs found, what was not verified.
- CI: a job for `seg` (fmt, clippy, tests, release size, dependency and
`unsafe` guards, `visudo -cf` of the sudoers rule), `udevadm verify` for
the udev rules, a check for repeated packages, more `shellcheck` targets.

### Changed

- `wine` -> `wine-staging` (the Staging patches: esync/fsync/NTSYNC, game
fixes) in the full edition. `lib32-mangohud` was tried and is not in the
Artix repositories, so 32-bit games get no MangoHud overlay.
- Open-file hard limit 524288 -> 1048576 (the kernel's maximum); the
`gamemode` group may use `nice -10`.
- Shader caches: Mesa 4 GB -> 10 GB; NVIDIA's shader cache set to 10 GB and
no longer deleted when full (`/etc/profile.d`, so on every init).
- `sudo seglinux-upgrade` (the from-source path) now also builds and
installs `segctl`, so an older install gets `seg`, `seg game`, the
services and the sudoers rule; CI checks this on an Artix container.
- `makepkg` builds with every thread; the I/O scheduler rule skips partitions
and covers virtual disks.

### Removed

- From the full image: `linux-headers` (325 MiB; Setup downloads the running
kernel's headers together with the NVIDIA DKMS driver), `spectacle` and
`opencv`, the X server and its drivers (`xorg-xwayland` stays), `inxi`,
and five packages that were listed twice. The full ISO is 4.7 % smaller and
475 MiB less is installed.
- `cupsd` is no longer started by default (the packages stay; `seg svc enable
cupsd`).

### Fixed

- The NVIDIA DKMS driver could only build because the headers were on every
image: Setup now installs them in that step.
- The artools patch that reads the kernel version without headers applies to
every kernel, not only the non-stock ones.
- The I/O scheduler udev rule matched partitions (errors in the log) and
missed virtual disks.
- `data/system/zram-setup` and the udev rules were not checked by CI.
- `seg doctor` called OpenRC services of the `shutdown` runlevel (`killprocs`,
`mount-ro`, `savecache`) failed; found by booting the Lite image.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ members = [
"crates/seglinux-installer",
"crates/seglinux-greeter",
]
# seg has its own workspace and release profile (tools/seg/Cargo.toml).
exclude = ["tools/seg"]

[workspace.package]
version = "0.1.0"
Expand Down
22 changes: 17 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,17 +90,29 @@ published.
kitty as the terminal (File Explorer → Dolphin, Store → Discover, ...).
The terminal opens with system information under the SegLinux logo
(fastfetch).
* **Built for gaming.** linux-zen kernel, Steam with Proton, Lutris,
GameMode, MangoHud and gamescope; a Steam "Gaming Mode" session; variable
refresh rate and tearing for fullscreen games; zram swap, NTSYNC, I/O
scheduler and sysctl tuning; NVIDIA's driver installed automatically on
GeForce GTX 16/RTX cards. See [docs/gaming.md](docs/gaming.md).
* **Built for gaming.** linux-zen kernel (with the stock kernel beside it to
fall back on), Steam with Proton, Lutris, Wine Staging, GameMode, MangoHud
and gamescope; a Steam "Gaming Mode" session; variable refresh rate and
tearing for fullscreen games; zram swap, NTSYNC, I/O scheduler and sysctl
tuning; NVIDIA's driver installed automatically on GeForce GTX 16/RTX
cards, and only for hardware that is there. `seg game run -- %command%` in
Steam's launch options boosts the CPU and GPU while a game runs and puts
everything back after; `seg game check` says what is missing, with the
fix. See [docs/gaming.md](docs/gaming.md).
* **Runs Windows programs.** Double-click an `.exe`, `.msi`, `.lnk` or
`.bat`, or type `./setup.exe`: `segrun` inspects the program, prepares a
Wine prefix (with DXVK/VKD3D-Proton when the program uses Direct3D) and
starts it, under GameMode when it is a game.
* **Fast, systemd-free base.** OpenRC by default (parallel boot), or runit
or dinit; rolling release.
* **`seg`, one small tool for the system.** `seg info`, `seg svc` (the same
commands on OpenRC, runit, dinit and s6), `seg clean`, `seg tune`
(light, balanced, gaming or performance), `seg zram`, `seg game` (see
above) and `seg doctor`, which finds problems and prints their fixes. A
single Rust binary of about 700 KiB; see
[tools/seg](tools/seg/README.md). What the images weigh and what was taken
off them: [docs/lean-audit.md](docs/lean-audit.md). What changed:
[CHANGELOG.md](CHANGELOG.md).

## Website

Expand Down
2 changes: 2 additions & 0 deletions crates/seglinux-installer/src/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,8 @@ pub fn demo_facts() -> Facts {
network: true,
memory: 8 * GIB,
gpus: vec![],
kernel: Some("linux-zen".into()),
cpu_vendor: None,
timezones: Some(set(&[
"UTC",
"Africa/Tunis",
Expand Down
3 changes: 2 additions & 1 deletion crates/seglinux-setup/autounattend.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ os_prober = true # list Windows in the boot menu
# Gaming and boot tuning.
[performance]
parallel_boot = true # OpenRC: start services in parallel
kernel_parameters = [] # added to: loglevel=3 quiet nowatchdog nmi_watchdog=0
kernel_parameters = [] # added to: loglevel=3 quiet nowatchdog nmi_watchdog=0 transparent_hugepage=madvise
fallback_kernel = true # also install the other kernel (linux beside linux-zen), a boot menu entry to fall back on

# GPU drivers. "auto" installs NVIDIA's driver for GeForce GTX 16/RTX cards
# when the network is up; "never" keeps the open-source driver.
Expand Down
17 changes: 15 additions & 2 deletions crates/seglinux-setup/src/answer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,15 +266,22 @@ pub struct PerformanceSection {
/// OpenRC: start services in parallel (`rc_parallel`) for a faster boot.
pub parallel_boot: bool,
/// Kernel parameters added to SegLinux's defaults
/// (`loglevel=3 quiet nowatchdog nmi_watchdog=0`).
/// (`loglevel=3 quiet nowatchdog nmi_watchdog=0 transparent_hugepage=madvise`,
/// `zswap.enabled=0` with zram, `amd_pstate=active` on AMD processors).
/// A parameter given here replaces the default of the same name.
pub kernel_parameters: Vec<String>,
/// Install the other kernel (`linux` next to `linux-zen`, the reverse for
/// the stock kernel) as a boot menu entry to fall back on, when the
/// network is reachable. The kernel that is installed is never removed.
pub fallback_kernel: bool,
}

impl Default for PerformanceSection {
fn default() -> Self {
Self {
parallel_boot: true,
kernel_parameters: Vec::new(),
fallback_kernel: true,
}
}
}
Expand Down Expand Up @@ -323,6 +330,8 @@ pub enum CommandPass {
}

/// Services enabled on every installation (missing ones are skipped).
/// `cupsd` is installed but not started: few gaming PCs print, and OpenRC has
/// no socket activation to start it on demand (`seg svc enable cupsd`).
pub const DEFAULT_SERVICES: &[&str] = &[
"dbus",
"elogind",
Expand All @@ -332,12 +341,16 @@ pub const DEFAULT_SERVICES: &[&str] = &[
"NetworkManager",
"greetd",
"bluetoothd",
"cupsd",
"cronie",
"syslog-ng",
"ntpd",
"acpid",
"power-profiles-daemon",
// From the segctl package: zram swap and the saved `seg tune` profile.
"seg-zram",
"seg-tune",
// The gaming sysctls and /dev/ntsync permissions, on every init.
"seg-game",
];

/// Groups every user joins (missing ones are skipped).
Expand Down
Loading
Loading