Skip to content
Architecture

Architecture

This page describes how the workstation image is built and how an installed machine comes up. Every name on it (packages, services, files) is taken from the source tree; follow the links to read the code.

The layers

LayerWhat it isSource
Kernellinux-runink 7.2.7-zen1: the zen kernel’s 7.2.x stable series, the only kernel on the imagebuild/pkgbuilds/runink-kernel/
Root filesystemOpenZFS 2.4.4 as a separate module package (runink-zfs) plus its userland and initramfs hook (runink-zfs-utils)build/pkgbuilds/runink-zfs/
Inits6, s6-rc and s6-linux-init, driven by the s6 front endiso-profiles/river/root-overlay/etc/s6/
DesktopKDE Plasma 6 on Wayland, SDDM, PipeWire, NetworkManager, Bluetooth and printing, each with its s6 service packageiso-profiles/river/profile.yaml
Installerriver-installer (graphical), runink-install (text), river-hwprobe, river-plan, river-netsetup and the numbered install stepsinstaller/
Host policythe runink-fw firewall, river-perms, river-sandbox, sysctl and zram tuningiso-profiles/river/root-overlay/usr/local/bin/

Everything else on the image (Plasma, Firefox, the libraries and tools) is a distribution package from pinned mirrors. Today those come from Artix Linux, through Artix’s ISO tooling: that is the transitional builder, which Runink River is replacing with its own from-source base (below).

The image: two layers, one golden system

The ISO holds two squashfs layers (iso-profiles/river/profile.yaml):

  • rootfs is the finished system: every package the installed machine has, plus the profile’s root-overlay/. An installed machine is a copy of it.
  • livefs is for the live medium only and sits on top of rootfs: the live user’s autologin (Plasma and a text console on Ctrl+Alt+F2), its passwordless sudo, the live hostname, the graphical installer’s s6 service, and the live GRUB menu package (artix-grub-live, the whole of Packages-Live).

The installer does not download or resolve packages. Step 20-clone-rootfs copies the running live system onto the new ZFS boot environment with rsync, and then removes what belongs only to the live medium (the autologin drop-ins, the live sudo rule, the installer itself and its service, the machine-id and the SSH host keys). This is why an install works with no network at all.

The package set is an allow-list. Packages-Root is curated by hand, never by package group; scripts/lint-profile-manifest.sh checks that every package profile.yaml installs is on it, and scripts/lint-closure.sh checks the whole dependency closure against forbidden.closure. Nothing on this list may enter the image, directly or as a dependency:

Kept outWhy
podman, docker, docker-compose, podman-composeno container runtime in the base image
flatpak, flatpak-kcm, discover, packagekit, packagekit-qt6no second software supply chain beside the pinned pacman repositories
aws-cli, aws-cli-v2, google-cloud-cli, google-cloud-sdk, azure-clino cloud vendor tooling in the base image
linuxexactly one kernel, linux-runink

From power-on to the desktop

Firmware and GRUB

UEFI firmware loads GRUB from the EFI system partition’s removable path (EFI/BOOT/BOOTX64.EFI, installed with grub-install --removable), so the machine boots even without a firmware boot entry. The EFI partition is mounted at /boot and holds the kernel, the initramfs and grub.cfg: GRUB cannot read a natively encrypted ZFS pool, so nothing it needs lives on the pool.

The initramfs

The kernel command line names the root dataset, root=ZFS=<pool>/ROOT/<be> (/etc/default/grub.d/10-runink-zfs.cfg, written by 40-boot-grub-zfs). The initramfs is built by mkinitcpio with these hooks (iso-profiles/river/root-overlay/etc/mkinitcpio.conf.d/zfs.conf):

HOOKS=(base udev autodetect microcode modconf kms plymouth keyboard keymap block zfs filesystems)

The zfs hook imports the pool (matching the hostid baked into the image), runs any key provider in /etc/zfs/initramfs-tools-load-key.d/, and otherwise asks on the console for the pool’s passphrase. It then mounts the boot environment at /. The Plymouth splash covers the import.

s6-linux-init and s6-rc

s6-linux-init becomes PID 1 and starts the supervision tree; s6-rc brings up the default bundle compiled into /etc/s6/rc/compiled at install time (step 80-enable-s6). The services the project adds, and the order they impose:

ServiceTypeDepends onJob
runink-fwoneshotmodulesloads the default-deny firewall
zfs-mountoneshotmodules, mount-tmpfs, udevadmzfs mount -a: mounts /home and the pool’s other datasets
mount-filesystemsoneshot (s6 base)zfs-mount (added)the rest of /etc/fstab, including the EFI partition at /boot
river-permsoneshotmount-filesystemsre-asserts 0600/0700 modes on secret files
NetworkManager-srvlongrunrunink-fw (added)the network
sshd-srvlongrunrunink-fw (added)SSH, still closed by the firewall until you open port 22
plymouth-quitoneshotends the boot splash
sddm-srvlongrunplymouth-quit, zfs-mount (added)the login screen
rc-localoneshotmount-filesystems, runink-fwruns /etc/s6/rc.local

Bluetooth (bluetoothd) and printing (cupsd) come from the bluez-s6 and cups-s6 packages, and dbus and elogind are pulled in as dependencies. “(added)” marks a dependency Runink River adds to a packaged service, as a file in its dependencies.d/ directory.

rc.local

/etc/s6/rc.local runs last, in init context rather than in a login session, so elogind never reaps what it starts. On the live medium (overlay=livefs on the kernel command line) it exits at once. On an installed machine it runs sysctl --system, sets up zram swap (runink-zram.sh), and runs any first-boot hooks a downstream image carries (none on Runink River).

Login

SDDM shows the Runink River greeter; the session is Plasma on Wayland (KWin), with PipeWire and WirePlumber as user services.

Packages Runink River builds

The [runink] repository is built from the PKGBUILDs under build/pkgbuilds/ and placed ahead of the distribution repositories, so its packages win. On the workstation image:

PackageContents
linux-runink, linux-runink-headersthe kernel, built from the signed kernel.org tarball and the signed zen patch
runink-zfsOpenZFS kernel modules prebuilt for exactly that kernel release
runink-zfs-utilsthe matching OpenZFS userland and the mkinitcpio zfs hook, from the same PKGBUILD, so module and userland cannot drift apart
runink-installerriver-installer, river-hwprobe, river-plan, river-netsetup and river-netcheck, from installer/ (Go standard library only)

The tree builds further packages for downstream distributions; they are not on the Runink River image.

The transitional builder and the own base

Today make iso runs Artix’s buildiso with the profile under iso-profiles/river/, and scripts/patch-artools.go adjusts it (the kernel swap, the image name and volume label, the live menu). That tooling is transitional. The plan in docs/OWN-BASE.md replaces it with Runink River’s own base, built from source:

PhaseScopeState
0plan and scaffold: the recipe format, base/river-build, base/river-sign, recipes for s6, skalibs and execlinedone: the three recipes build bit-for-bit reproducibly in a pinned, network-less container
1a reproducible toolchainplanned
2the base built from recipes, its own image assembly (river-compose) and river-updateplanned
3migration of existing installs, removal of the Artix toolingplanned
5the Plasma 6 workstation built from recipesplanned

Planned changes that users will notice: the kernel booted as a signed unified kernel image instead of through GRUB, TPM 2.0 unlock of the pool, and updates installed as a new boot environment per release. None of these exists yet.

Where to read more