Skip to content

A Linux distribution pre-release

One file describes the whole system. Every change can be rolled back.

Laft is a Linux distribution built from source, with all of its tooling written in Rust. Packages live in a content addressed store, and applying the configuration builds a new, immutable generation of the system. The machine switches to it in one atomic step and keeps the previous one to go back to.

/laft/storenamed by content hash
  • 7ah4dyc33f53wzrceczzpaspjwp5nhjx-runit-2.3.1+static
  • igyza4pekasw7tig4ana2lqhgotoy3ek-laft-0.1.0
  • k7pjia6tynb7vepkdzemiobizilxp7tl-busybox-1.37.0+static
  • y6jikgwolsdgntx6mkx5ard3ab3ymkdz-linux-7.2.13
  • yibf6gdfl34n22efd5uiz6obecmbacko-limine-11.2.1
/laft/gensnumbered, never changed
  1. 1installedactive, default
/run/laft/system -> mounts/1one rename(2) switches every path below /usr and /etc/static
done        generation 1 is the default for the next boot
Try the commands. The store ids and output lines come from the test machine, where generation 2 added a service and reused all five store entries.

What makes Laft different

Six ideas shape the whole design. Laft's tools enforce each one, so they hold on every machine instead of depending on convention.

  1. One declarative file

    Packages, users, /etc, filesystems, the boot loader and services are described in Stav, a typed configuration language. The whole file is checked before anything changes.

    laft check
  2. A content addressed store

    Every package unpacks once into /laft/store, named by the BLAKE3 hash of its manifest. Entries are immutable, so many versions of a package can sit side by side.

    /laft/store/7ah4dyc3…-runit-2.3.1+static
  3. Views made of hardlinks

    Each /usr is a complete tree assembled from store entries with hardlinks. Nothing is copied, and the system view is just the view that /usr points at.

    /usr -> /run/laft/system/usr
  4. Generations and rollback

    Applying the configuration builds a new numbered generation and switches to it with one rename. The previous one stays whole, in the boot menu and one command away.

    laft rollback
  5. Services for runit

    Services are declared once, with dependencies and readiness checks, and rendered for runit. Between generations, Laft works out which services to start, stop or restart.

    service sshd { after = [sshd-keygen] }
  6. Hermetic builds from source

    Every package is built from pinned sources in a sandbox with no network, bootstrapped from a cross toolchain, and checked to rebuild byte for byte.

    laft-build check-repro

From a configuration file to a bootable system

This is part of the configuration that the virtual machine test installs. laft install reads it, resolves five packages from a signed repository and assembles generation 1, with its services and boot entry.

Configuring a system →
/etc/laft/system.stav
stav
system {
  include "./hardware.stav"

  hostname = "laft-vm"

  packages = [busybox:static, runit:static, limine, laft]

  repo core {
    url = "file:///repo"
    key = "RWTwpAdtxK4dSghH+5P46WIatF4FFuBMOlHyTe1lpy2BU1mZmhNHjZDf"
  }

  boot {
    cmdline   = ["console=ttyS0", "quiet", "laft.debug"]
    timeout   = 1
    microcode = force(off)
  }

  service vmtest-marker {
    description = "Print a marker line on the console"
    exec        = [
      "/usr/bin/sh",
      "-c",
      "echo 'laft-vmtest: marker service is running'; exec sleep 2147483647",
    ]
    log         = console
  }

  getty { ttys = ["ttyS0"] }
}
# laft install
text
evaluating  /mnt/etc/laft/system.stav  2 files  12 ms
resolving   system
generation 1
  system
    + busybox:static  1.37.0-r1
    + laft            0.1.0-r1
    + limine          11.2.1-r1
    + linux           7.2.13-r1
    + runit:static    2.3.1-r1
  services
    + boot-complete
    + getty-ttys0
    + vmtest-marker
    ...
storing     5 packages
assembling  generation 1  135 ms
boot        entry 1 installed
activating  ok etc ok accounts
done        generation 1 is the default for the next boot

From recipe to running machine

Every tool along the way is a Laft program, written in Rust and linked statically, so it runs even with an empty /usr.

  1. Recipe

    package.stav

    Pinned sources, a build style and declared dependencies.

  2. Build

    laft-build

    A sandbox with a view of the build store as /usr and no network.

  3. Package

    .lpkg

    A reproducible archive whose store id is the hash of its manifest.

  4. Publish

    laft-repo

    A minisign-signed index whose generation never goes back.

  5. Apply

    laft apply

    Resolve, assemble and switch to a new generation in one rename.

  6. Boot

    laft-boot

    The initramfs picks a generation, counts the attempt and starts runit.

Where Laft stands today

Laft boots in a virtual machine today, and its test suite installs and boots it end to end. It is not ready for real hardware or daily use yet.

Status and roadmap →

works

  • Install onto a disk image and boot it under UEFI with Limine
  • Apply, roll back, switch and garbage collect generations
  • Boot counting with fallback to the last good generation
  • A base system of 89 recipes, bootstrapped and rebuilt hermetically

planned

  • Package options and prebuilt profiles
  • Static musl variants of packages
  • Hosted, signed package repositories
  • Installs on real hardware, and a desktop

Boot Laft in a virtual machine

One script builds the packages, installs a system onto a disk image and boots it under QEMU. Keep the image, and it is yours to explore.

Released under the MIT or Apache 2.0 license, at your option.