The power menu is a Quickshell popout in the moonarch config now, and it was the last caller of moonlock. Inputs, package entries and both build recipes are gone, together with the generated /etc configs and the moonlock PAM stack — swaylock's comes from programs.niri.enable.
7.7 KiB
moonix
NixOS configuration for a scrollable-tiling Wayland desktop: niri as the compositor, Quickshell as the bar and launcher, regreet as the login greeter, swaylock as the screen locker, Catppuccin Mocha throughout.
Ships Nix packages for the components that are not in nixpkgs — stasis,
sweet-cursors, the moonarch helper scripts — and NixOS modules that assemble them
into a working session. Greeter, locker and power menu are no longer own programs:
regreet and swaylock come from nixpkgs, the power menu is a Quickshell popout
inside the moonarch config.
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
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 + regreet, autologin per host
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 .#stasis
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 againstlsblkon the machine you are standing in front of. Do not copy the path below without looking — it is this ThinkPad's SSD.
-
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_SDB7N7740101A8708The LUKS passphrase is asked for twice while formatting. The repo is fetched from the flake reference, so the ISO needs no
gitand nothing is cloned. -
Set the user password. Nothing has created one yet. Autologin gets past the greeter without it, but swaylock authenticates through pam_unix and cannot unlock a screen for an account that has no password:
sudo nixos-enter --root /mnt -c 'passwd dkressler' -
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
-
New directory under
hosts/, with adefault.nixholding the machine specific settings — hostname, users, locale, bootloader. -
Point the shared layout at its disk:
moonarch.disk = { enable = true; device = "/dev/disk/by-id/..."; # check against lsblk };encrypt = falsedrops the LUKS layer,swapSize = ""the swapfile, andespSizeresizes the ESP. Everything else is shared. -
Add a
nixosConfigurations.<name>entry inflake.nix. -
Install with the same
disko-installcommand, 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
stasis is pinned to a release tag. After a new upstream release:
# adjust the tag in flake.nix, then
nix flake update stasis
moonarch tracks main, so its config changes arrive with
nix flake update moonarch.
Configuration files
Config files are deployed unchanged wherever possible. These are generated instead, because they contain a path that does not exist under Nix, or because a NixOS module owns the destination:
| File | Reason |
|---|---|
greetd/regreet.toml, regreet.css |
written by services.displayManager.regreet from module options; the wallpaper points at the store path |
swaylock/config |
wallpaper path, rewritten and seeded into $HOME — swaylock's sysconfdir is inside the store, so a system-wide copy is never read |
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.