Skip to content
Design principles

Design principles

Runink River’s design is a short list of invariants, written down in AGENTS.md. They apply to every change, whoever writes it, and changing one needs a vote of the technical steering committee (Governance). Most are checked by a lint or a test, so a change that breaks one fails CI instead of relying on review alone.

The invariants

#InvariantWhat it means on your machineEnforced by
1s6, never systemdNo .service units, no systemctl. Services are s6-rc oneshots and longruns; the desktop’s services come from their -s6 packages.the service tree under /etc/s6/, 80-enable-s6
2Exactly one kernel, linux-runinkOne pinned zen-kernel stable tag, verified sources, and a build that refuses any other kernel release or a configuration below config.require. OpenZFS stays a separate module package.the kernel PKGBUILD, build/verify-kernel-zfs.sh
3ZFS root, encryptedEvery dataset inherits aes-256-gcm from the pool root. The key is printed once as the recovery key and never written to a file. The EFI partition at /boot is the only unencrypted filesystem.10-disk-zfs (refuses to continue if the pool came out unencrypted), tests/assert-golden.sh
4Secrets are handled like /etc/shadow0600 files in 0700 directories, owned by their one reader, re-asserted at every boot. No secret, key or token is ever baked into an image.river-perms
5Pinned, signed software supplySoftware comes from the pinned repositories and the [runink] packages. There is no app store, no Flatpak, no container runtime in the base image and no telemetry.forbidden.explicit, forbidden.closure, scripts/lint-closure.sh
6The manifest is an allow-listPackages-Root is curated package by package, never by group.scripts/lint-profile-manifest.sh, scripts/lint-closure.sh
7Default-deny firewall from first bootInbound traffic is dropped unless it is one of a short list or a port you opened; outbound is open; forwarding is dropped. SSH is closed until you open it.runink-fw, scripts/lint-firewall.sh
8Untrusted code runs under river-sandboxCode you do not trust gets no network (unless asked), no capabilities and no view of the machine’s secrets. The deny-list is never widened.river-sandbox
9Pin every upstream by version and checksum, and by signature where it signsMirror snapshots, the kernel, OpenZFS, container base images and every GitHub Action (by full commit SHA).the PKGBUILDs, pacman/mirrorlist.pin, the workflow files

Details of each mechanism: s6, the kernel, ZFS & encryption, firewall, river-sandbox.

What the project promises

The security assurance case, docs/SECURITY-ASSURANCE.md, states the requirements the mechanisms above serve. Those that apply to the workstation:

  • Data at rest is unreadable without the pool key (ZFS native encryption).
  • No secret exists in any image, and a machine’s own secrets are readable only by their one reader (river-perms).
  • Code run on behalf of others cannot reach the machine’s secrets (river-sandbox).
  • What you install is what the project built from reviewed source: pinned and verified upstreams, signed release checksums, and an install.sh that fails closed.
  • The installer never destroys data you did not choose to destroy: disks are confirmed by serial, the boot medium is never a target, and the machine is probed again right before anything is written.

The same document names the gaps, among them:

  • Secure Boot is off, so firmware does not verify the boot chain;
  • the pool passphrase is typed at every boot until TPM unlock exists;
  • the live medium has a default account with autologin (never boot it on an untrusted network; the installed system does not carry it);
  • unprivileged user namespaces stay enabled because river-sandbox needs them;
  • the boot, ZFS and encryption tests do not run in CI yet and are run by hand;
  • the ISO is not reproducible yet;
  • there is one maintainer, and no external security review has been done.

Scope

Runink River is the operating system, its packaging and its installer. It holds no application code and names no specific downstream product. A downstream distribution builds its own images from this repository with an out-of-tree profile (RIVER_PROFILE_DIR) and, for a private image, out-of-tree payloads (RIVER_PAYLOAD_DIR); the public Runink River image carries no payload, and its build refuses to attach one (Build from source).

What is deliberately left out

  • systemd, or a second init system;
  • more than one kernel, or ZFS built into the kernel;
  • telemetry, phone-home behaviour or third-party hosted AI services in the image;
  • application logic, or a specific downstream product, in this repository.