moonix

NixOS configuration for a scrollable-tiling Wayland desktop: niri as the compositor, Quickshell as the bar and launcher, moongreet as the login greeter, moonlock as the screen locker, Catppuccin Mocha throughout.

Ships Nix packages for the components that are not in nixpkgs — moonlock, moongreet, moonset, stasis, sweet-cursors — and NixOS modules that assemble them into a working session.

The XDG configuration files (niri, Quickshell, foot, GTK, Qt) come from the moonarch repository, which is consumed as a flake input. One set of config files therefore stays valid on both NixOS and Arch.

Layout

flake.nix            Inputs and outputs
pkgs/                Build recipes
  moonlock.nix       screen locker (GTK4, PAM, fingerprint)
  moongreet.nix      greetd greeter
  moonset.nix        session power menu
  stasis.nix         idle manager
  sweet-cursors.nix  cursor theme
  moonarch-scripts.nix  battery and camera helpers
modules/
  disk.nix           partitioning: GPT, optional LUKS2, btrfs subvolumes
  desktop.nix        /etc deployment, package set, theme
  greetd.nix         greetd + moongreet
  services.nix       systemd system and user services
hosts/
  testvm/            QEMU test VM
  thinkpad/          ThinkPad T14 Gen 3 (AMD)

nixosModules.moonarch is the entry point: it imports all three modules and hands them the packages and the config source.

Usage

Build a single package:

nix build .#moonlock

Build a whole system without activating it:

nix build .#nixosConfigurations.testvm.config.system.build.toplevel

Activate:

sudo nixos-rebuild switch --flake .#testvm

Installing on a machine

The disk layout is declared in modules/disk.nix, so nothing has to be generated on the target machine and no file travels back into this repo. Boot the NixOS ISO and run one command.

This erases the disk given to --disk. Check the path against lsblk on the machine you are standing in front of. Do not copy the path below without looking — it is this ThinkPad's SSD.

  1. Partition and install in one go:

    sudo nix --experimental-features "nix-command flakes" run \
      'github:nix-community/disko/latest#disko-install' -- \
      --flake 'git+https://gitea.moonarch.de/nevaforget/moonix.git#thinkpad' \
      --disk main /dev/disk/by-id/nvme-SKHynix_HFS001TEJ9X102N_SDB7N7740101A8708
    

    The LUKS passphrase is asked for twice while formatting. The repo is fetched from the flake reference, so the ISO needs no git and nothing is cloned.

  2. Set the user password. Nothing has created one yet, and greetd is the only way in:

    sudo nixos-enter --root /mnt -c 'passwd dkressler'
    
  3. Reboot.

To skip step 2, put a mkpasswd -m sha-512 hash into users.users.dkressler.initialHashedPassword — the commented-out line in hosts/thinkpad/default.nix shows where. That commits a password hash to the repository.

hardware-configuration.nix is not used and not needed. NixOS puts nvme, ahci, sd_mod and the USB HID modules into the initrd by default (boot.initrd.includeDefaultModules), nixpkgs.hostPlatform comes from the flake, and the nixos-hardware profile for the T14 Gen 3 (AMD) covers firmware, microcode, power management and graphics quirks.

The generated fileSystems entries point at /dev/disk/by-partlabel/, so the booted system does not care whether the disk enumerates the same way next time. Only the partitioning step reads moonarch.disk.device.

Adding another machine

  1. New directory under hosts/, with a default.nix holding the machine specific settings — hostname, users, locale, bootloader.

  2. Point the shared layout at its disk:

    moonarch.disk = {
      enable = true;
      device = "/dev/disk/by-id/...";   # check against lsblk
    };
    

    encrypt = false drops the LUKS layer, swapSize = "" the swapfile, and espSize resizes the ESP. Everything else is shared.

  3. Add a nixosConfigurations.<name> entry in flake.nix.

  4. Install with the same disko-install command, with #<name> and that machine's disk.

Without a matching nixos-hardware profile, set hardware.cpu.intel.updateMicrocode or hardware.cpu.amd.updateMicrocode in the host file — the profile is what provides it for the ThinkPad.

Disk layout

Partition / subvolume Mountpoint Notes
ESP, espSize (1G) /boot vfat, umask=0077
root / btrfs, compress=zstd, noatime
home /home same
nix /nix same; separate to keep the store out of snapshots
swap /.swapvol swapfile, swapSize (8G)

With encrypt = true everything below the ESP sits in a LUKS2 container named crypted. Hibernation is not set up — see DECISIONS.md.

Version bumps

The application repos are pinned to release tags. After tagging a new version:

# adjust the tag in flake.nix, then
nix flake update moonlock

Configuration files

Config files are deployed unchanged wherever possible. Three are generated instead, because they contain a path that does not exist under Nix:

File Reason
moongreet.toml wallpaper path, rewritten to the store path
waypaper/config.ini wallpaper path, rewritten and seeded into $HOME
fontconfig defaults /etc/fonts/conf.d is owned by the NixOS module

kanshi/config is not deployed: the file is empty, kanshi rejects it and restarts in a loop. Monitor profiles are machine specific and belong in ~/.config/kanshi/config; the service starts only when that file exists.

Programs that read only from $HOME and have no system-wide fallback get a copy seeded through systemd.user.tmpfiles with the C directive, which never overwrites later edits.

Test VM

hosts/testvm targets a QEMU guest. The launcher script lives outside this repo at ~/VMs/moonix-vm.sh.

It uses the same layout module as real hardware, pointed at /dev/vda — the launcher attaches the qcow2 as if=virtio and there is no by-id path for it. LUKS stays enabled and the swapfile shrinks to 2G. Installing the guest is therefore also the test for modules/disk.nix.

Recreating the disk from scratch:

qemu-img create -f qcow2 ~/VMs/moonix.qcow2 60G
cp /usr/share/edk2/x64/OVMF_VARS.4m.fd ~/VMs/moonix-vars.fd
~/VMs/moonix-vm.sh ~/Downloads/nixos-graphical-*.iso

Then run the disko-install command from above inside the live session, with #testvm and --disk main /dev/vda.

It deliberately contains no testing shortcuts — no passwordless sudo, no SSH password authentication. Set those at runtime when needed, for example through a drop-in under /run/systemd/system/, so they do not end up in version control.

The guest needs a 3D capable virtio GPU: niri's TTY backend fails every buffer import with Error::DeviceMissing otherwise, and software rendering is not a workaround. On the current host that path has twice triggered an amdgpu hard recovery, killing the QEMU process — shut the VM down when not testing.

Known gaps

Super+C opens the clipboard history through a launcher that is not installed here, so the binding does nothing. cliphist records the clipboard, only the picker front-end is missing.

S
Description
No description provided
Readme
360 KiB
Languages
Nix 100%