You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
fox ee0f662481 initial commit 2 months ago
LICENSE initial commit 2 months ago initial commit 2 months ago
makefile initial commit 2 months ago


a linux distribution for people who like to breathe manually.

this repository contains documentation and helper scripts only. a working linux distribution with overlayfs support (preferably alpine linux) and an internet connection are required to bootstrap lix os. no images or binaries supplied.


lix os uses the lix package manager to construct a musl-based linux distribution from source code. it is entirely composed of simple, short-as-possible, POSIX-compliant shell scripts. packages are built in chroots constructed from package directories stacked on top of each other using overlayfs. only dependencies explicitly listed in a package's 'deps' file are accessible during the package's build process. there is no concept of "indirect dependencies." packages are made available to the user by soft-linking package directory contents into the system root. packages can be added to or removed from the system root without uninstalling the packages entirely.

lix os is the work of a single maintainer and follows the same "use fewer packages" ethos that kiss linux does. it also tries to use the smallest packages possible in order to create a more readily audited codebase. some exceptions are made in the name of usefulness and self-hosting. generally, suckless utilities are preferred to bsd utilities, and bsd utilities are preferred to gnu utilities due to the preferred in each case generally having a smaller, easier to understand codebase. this does mean lix os's versions of common linux utilities may have fewer options than one may be used to, but many of these missing options are either not commonly used or can be compensated for with a short shell script or function.

in the opinion of lix os' maintainer, it would be ideal if everyone knew and understood every line of code executing on their machines. while a perfect realisation of this dream is unlikely, lix hopes to make the problem less intractable. lix os will never ship binaries.


lix os is an experimental linux distribution. it may not work. it may catch on fire. it may be abandoned at a moment's notice for another experiment. it will not stop you from doing something stupid and destroying your system. it is not and probably never will be "production ready."

nonetheless, the original author currently uses it daily, and it may serve the needs of others as well.


  1. be self-hosting.
  2. keep a small codebase.
  3. remain easy to understand, customize, and fork.
  4. never offer more packages than one maintainer can keep up to date.
  5. build from sources obtained directly from their authors, whenever possible.
  6. support fully open source platforms. (e.g., Talos II.)

system hygiene

lix helps lix os maintain system hygiene by:

  1. building and installing in chroots with only package dependencies available.
  2. patching, configuring, and building in an overlay over package source code.
  3. mounting an overlay over each chroot before entering the chroot at all.
  4. symlinking installed files from package overlays into the filesystem.

all changes during the patching, configuration, building, and installation phases go into overlays maintained by lyr. this helps keep the source code in src's repository pristine and makes it very simple to activate and deactivate packages. it also makes it easy to see what packages have supplied each file in one's system root.

fork at will

lix os is simple. if one disagrees with a decision, one should fork away. it should never take more than a single person to maintain a lix os-based distribution and all its packages.

largely statically linked

there are some blockers to achieving a fully statically linked system.

1) smartcard support

smartcard support is not possible at this time without dynamic linking. in particular, 'scdaemon' from gnupg loads '' at runtime. there is not yet a way to make it statically link the library instead. accordingly, the executables provided by the 'gnupg' package in lix os are linked statically and do not support smartcards, while the executables in the 'gnupg-dynamic' and 'pcsc-lite' packages are linked dynamically and do support smartcards.

2) gui desktops

both x11 and wayland have dependencies which use meson as their build system. meson requires core python3 modules which use dynamic loading (e.g., ctypes), which means python3 must be dynamically linked in order to use the meson build system. the default python3 package is statically linked and does not contain many "core" modules due to their dependency on the 'dlopen' function. if a gui is desired, the 'python3-dynamic' package will have to be built.

removing the meson dependency is possible. oasis linux has pioneered this approach. the best approach would probably be to either port the packaging or the build system from oasis linux. preferably the latter, since doing so would better enable collaboration between our projects, but in theory a lua script could also be written to translate oasis linux packaging into shell scripts for those unwilling to learn the lua-based build system.

mesa and wayland also tend to not work well when statically linked. in theory it should be possible to statically link these components, but in practice it seems to result in segmentation faults. there is probably a way to fix this problem, but in the meantime a graphical desktop will require dynamic linking.

3) gettext

the kbd package, which supplies console utilities like setfont, requires gettext, and gettext will not build statically due to symbol collisions during while linking. this is also probably fixable, but it would be better to remove the gettext dependency entirely if possible.

why prefer static linking?

because it should be possible to quantify all of a program's possible states at compile time, barring hardware-level shenanigans, from source code. a user should never be put in the position where they are surprised by a change in a program's behavior because a dynamically loaded library was updated. this seems analogous to lexical versus dynamic scoping in programming languages.

there are ways to achieve these sorts of guarantees without sacrificing dynamic loading, but they are more complicated than simply linking statically.

also, no dynamic loader means no dynamic loader vulnerabilities.


below is the "official path," but please try other ways and report back.

it is assumed that each step is executed from the same directory. e.g., ~.

if one prefers to use the linux kernel which contains propietary firmware blobs, replace references to linux-libre below with linux-kernel.

  1. create an alpine linux installation. leave the target device untouched as it will need to be formatted and partitioned.

  2. install the bootstrap dependencies: apk add build-base coreutils cmake git curl rsync cryptsetup autoconf automake libtool flex bison ncurses-dev

  3. install a command-line text editor and set the EDITOR variable. (optional) e.g., apk add vim && export EDITOR='vim' (n.b.: if this step is skipped, vi will be used.) (n.b.: in a later step, libraries get moved. if this breaks the text editor, try another one. if all else fails, vi is known to work in alpine linux and comes installed by default.)

  4. clone the lix-os repository and its subrepositories, and install them.

    • git clone http://git.fuwafuwaqtlkkxwc.onion/yafox/lix-os
    • cd lix-os
    • make
    • make install
  5. create a bootstrap layer

    • mkdir bootstrap
    • ./lix-os/utilities/ bootstrap
  6. supply a linux kernel configuration file (strongly recommended but optional)

    • mkdir linux-mnt
    • lix --bootstrap=bootstrap pull linux-libre
    • lix --bootstrap=bootstrap mount linux-mnt linux-libre
    • copy the desired kernel configuration file to linux-mnt/mnt/src/.config (n.b.: many distributions have their own kernel configuration files which can be borrowed. lix os' maintainer keeps a public collection of examples in the linux-confs repository.)
    • lix umount linux-mnt linux-libre
    • rmdir linux-mnt
  7. prepare the destination device.

    • create an ext4 partition to be used as the boot partition. 100MB should be plenty of space, assuming no firmware blobs are to be built into the kernel.
    • create a LUKS-encrypted ext4 partition to be used as the root partition.
    • create whatever other partitions desired.
  8. build lix os

    • "$utilsdir/" bootstrap (n.b.: some packages will require interactive configuration via text editor. if a package fails, bootstrapping can be resumed from the same point later by passing the name of the package as a second argument. e.g., "$utilsdir/" bootstrap <failing package>. this script just loops over the dependencies of the lix-os package, adds them, and then ups them in the lix-os chroot. it's a short script. don't be afraid to examine and modify it.)
  9. copy lix os to the target medium

    • mkdir -p lix-os-tgt/boot
    • mount the root partition to lix-os-tgt
    • mount the boot partition to lix-os-tgt/boot
    • mount any other partitions created earlier under lix-os-tgt.
    • "$utilsdir/" lix-os-tgt (n.b.: this script copies the contents of the lix-os chroot and the src and lyr repos in /usr/var to the target directory given, then chroots in to the target directory and replaces all the real files with softlinks to the appropriate targets in the lyr repository via lix up -f.)
  10. make last-minute configuration changes to boot-related partitions

    • edit the contents of lix-os-tgt/boot/grub/grub.cfg to point to the correct partitions.
    • make any additional last-minute configuration changes desired.

at this point, if one wishes to skip using symlinks to link installed packages into the system root instead of having them copied from the package chroots, one may skip to the last step.

  1. unmount boot-related partitions

    • umount lix-os-tgt/boot
    • unmount any additional partitions which should not have their files converted to symlinks to locations in lyr's layer repository. (n.b.: this is one of the "sharp edges" of lix os which will one day be "filed down" in one way or another. in the meantime, take care to not lix up or lix dn anything with files related to booting. linux-libre, lix-os-bootloader, and lix-os-initramfs immediately come to mind as such packages.)
  2. convert everything else to symlinks

    • "$utilsdir/" lix-os-tgt
  3. clean up and reboot into lix os

    • unmount the target partitions under and at lix-os-tgt.
    • rmdir lix-os-tgt/boot lix-os-tgt
    • reboot into the target medium and make sure lix os works!


if all has gone well and the system is booting, make a backup of your kernel and initramfs. mount the boot partition and copy vmlinux to vmlinux.bak and initramfs.igz to initramfs.igz.bak. this will allow for recovery by changing boot parameters to boot with the backup files if one misconfigures one's kernel or initramfs in the future.

editing lix-os-initramfs

the contents of the initramfs file can be examined and modified using the lix mount command, the same as every other lix package.


lix os tries to be compositional in its architecture, which should make it easier to replace components as desired. this flexibility comes at the expense of having all files relevant to a package in a single location. an earlier iteration of lix os (called 'mu os' in ignorance of the other linux distribution by that same name) opted for a tighter integration of parts, which was nice in some ways but which also tended toward creating a monolith.

ideally the lix package manager should be composed of simple tools that behave in a clear, consistent manner and which have no notions about each other.

helpful scripts

there are also a number of helpful scripts in the lix-os-utilities repository.
for example, the versions utility, which itself also depends on vercmp and shsort, may be useful for keeping a lix os system up to date. versions returns a sorted list of available package versions pulled directly from each project maintainer's website and/or its mirrors. lix does not use it directly, but the author does use it periodically to update src packages.


  • switch to a "-static-pie" build. (on ppc64le, this currently produces "read-only segment has dynamic relocations" errors.)
  • replace libressl with bearssl.
  • replace all gnu tools with simpler alternatives. oasis linux has the right idea here, but work needs to be done to add ppc64le support to cproc and the like.
  • capture file changes to "upped" packages to their own layer.
  • capture file changes to /etc (and others?) to their own layer.
  • allow unprivileged creation of user-specific lix environments, like nix os.
  • find a way to encourage running processes under least-privileged, package- specific user accounts whenever possible without literally making the choice for the user.
  • consider separating state in /var into files deterministically generated and files constituting non-deterministic input. (e.g., 'src' tarballs versus private keys.)
  • consider moving to a model that does require a 'root' user.
  • consider merging the overlayfs approach with the autotools-free, lua-as-make approach of oasis linux.

it would be nice to create something like nix os that captures changed state rather than requiring state be constructed from a configuration file in a domain-specific language.


the development of lix os was inspired and aided by the examples set by the following projects:

  • gobo linux and nix os, for sandboxing and separation of state.
  • sabotage linux, for general approach and code-centricity.
  • kiss linux, for embracing a "remove as many packages as possible" ethos.
  • alpine linux, for patches and scripts showing how to build things with musl.
  • oasis linux, for helping inform userland choices.
  •, for demonstrating a simplified LHFS.
  • openbsd, for providing alternatives such as libressl and doas.
  • musl-cross-make, for building a ppc64le-and-musl-targeting c compiler.
  •, for providing and highlighting simple alternatives, and for setting me down this path in the first place.