diff --git a/DECISIONS.md b/DECISIONS.md index 8f0cbae..2ae5459 100644 --- a/DECISIONS.md +++ b/DECISIONS.md @@ -1,5 +1,53 @@ # Decisions +## 2026-08-14 – regreet and swaylock from nixpkgs, autologin after LUKS + +- **Who**: Dominik, ClaudeCode +- **Why**: moongreet and moonlock were dropped as self-maintained projects; the + reasoning is in `moonarch/DECISIONS.md` for the same date. Here the change is + mostly subtraction: two flake inputs, two build recipes and a generated config + disappear in favour of a nixpkgs module and a nixpkgs package. Since LUKS + already asks for a passphrase at boot, the greeter was also asking for a + second one for no gain, so autologin replaces it on both hosts. +- **Tradeoffs**: (1) **Greeter host** — the regreet module sets + `services.greetd.settings.default_session.command` only as `mkDefault` + (`dbus-run-session cage -- regreet`), so the existing unconditional niri line + keeps winning. Taking cage would have meant one Wayland compositor fewer in + the greeter path, but `niri-greeter.kdl` is where `layout "de"` and `numlock` + live, and nothing in nixpkgs 26.11 exports `XKB_DEFAULT_LAYOUT` from + `services.xserver.xkb.layout` — verified. A greeter on a US layout rejects + every password with an affected character, so niri stays and Arch and NixOS + keep sharing that one file. (2) **regreet.toml** — the module writes + `/etc/greetd/regreet.toml` itself, so the committed file from + `defaults/etc/greetd/` is deliberately *not* deployed verbatim; declaring the + same path in `environment.etc` would collide. The values are duplicated into + module options, which is the price for the wallpaper store path. (3) + **swaylock config** — nixpkgs passes only `--prefix` to meson, so swaylock's + `SYSCONFDIR` sits inside the store and `/etc/swaylock/config` is never read. + The config is therefore seeded into `$HOME` with the same `C` tmpfiles rule + waypaper uses, which also means later user edits survive but updates do not + propagate. (4) **Colloid overrides** — desktop and greeter need the same + derivation, and two module files cannot share a `let`. Moved into `flake.nix` + and passed as `_module.args.moonarchThemes` rather than duplicating the + override, which would drift. +- **How**: `moongreet` input, `moonarchPackages.moongreet` and + `pkgs/moongreet.nix` removed. `modules/greetd.nix` now configures + `services.displayManager.regreet` (background, GTK, commands, theme, icon + theme, cursor theme, font, `extraCss` pointing at the CSS in the moonarch + input) and keeps the niri `default_session`. `modules/desktop.nix` gains + `swaylock`, the seeded `~/.config/swaylock/config` and — moved out of + `greetd.nix`, next to the package it belongs to — the moonlock PAM stack. + `initial_session` is set per host, because the accounts are named differently. +- **Remaining exception**: `moonlock` stays packaged and installed. + `moonset/src/power.rs` spawns it by name and has no `lock_command` option, so + its Lock action would otherwise fail. Idle and lid-close already go through + swaylock. Two lockers coexist until moonset is released with that call + changed; then the input, the recipe, the PAM stack and + `environment.etc."moonlock/moonlock.toml"` all go. +- **Consequence**: With autologin, the greeter no longer forces an account to + have a password, but swaylock still needs one — `passwd` after installing + stays mandatory, and `hosts/testvm` has no password declared at all. + ## 2026-08-11 – Disk layout via disko after all, as a shared module - **Who**: Dominik, ClaudeCode diff --git a/README.md b/README.md index 6a4130a..d6f8df8 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,13 @@ # 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. +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 — moonlock, -moongreet, moonset, stasis, sweet-cursors — and NixOS modules that assemble -them into a working session. +Ships Nix packages for the components that are not in nixpkgs — moonset, stasis, +sweet-cursors — and NixOS modules that assemble them into a working session. +`moonlock` is still packaged here for one reason: moonset's Lock action invokes +it by name. It goes with the moonset release that changes that. 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 @@ -17,8 +18,7 @@ files therefore stays valid on both NixOS and Arch. ``` flake.nix Inputs and outputs pkgs/ Build recipes - moonlock.nix screen locker (GTK4, PAM, fingerprint) - moongreet.nix greetd greeter + moonlock.nix screen locker, retired but still called by moonset moonset.nix session power menu stasis.nix idle manager sweet-cursors.nix cursor theme @@ -26,7 +26,7 @@ pkgs/ Build recipes modules/ disk.nix partitioning: GPT, optional LUKS2, btrfs subvolumes desktop.nix /etc deployment, package set, theme - greetd.nix greetd + moongreet + greetd.nix greetd + regreet, autologin per host services.nix systemd system and user services hosts/ testvm/ QEMU test VM @@ -78,8 +78,9 @@ the NixOS ISO and run one command. 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: +2. 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: ```bash sudo nixos-enter --root /mnt -c 'passwd dkressler' @@ -149,12 +150,14 @@ 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: +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 | |---|---| -| `moongreet.toml` | wallpaper path, rewritten to the store path | +| `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 | diff --git a/flake.lock b/flake.lock index 208c78c..b8cb2c8 100644 --- a/flake.lock +++ b/flake.lock @@ -37,23 +37,6 @@ "url": "https://gitea.moonarch.de/nevaforget/moonarch.git" } }, - "moongreet": { - "flake": false, - "locked": { - "lastModified": 1786110932, - "narHash": "sha256-0idBtqnIAGbRTXqh/EBS0ZxUMf4EgpUPtNQoJ/ogW7U=", - "ref": "refs/tags/v0.10.2", - "rev": "8dd90b016fc91adbe5d6b6f3fb13d470b8cdd5cf", - "revCount": 76, - "type": "git", - "url": "https://gitea.moonarch.de/nevaforget/greetd-moongreet.git" - }, - "original": { - "ref": "refs/tags/v0.10.2", - "type": "git", - "url": "https://gitea.moonarch.de/nevaforget/greetd-moongreet.git" - } - }, "moonlock": { "flake": false, "locked": { @@ -139,7 +122,6 @@ "inputs": { "disko": "disko", "moonarch": "moonarch", - "moongreet": "moongreet", "moonlock": "moonlock", "moonset": "moonset", "nixos-hardware": "nixos-hardware", diff --git a/flake.nix b/flake.nix index 28d35ad..23ff9ab 100644 --- a/flake.nix +++ b/flake.nix @@ -24,11 +24,6 @@ url = "git+https://gitea.moonarch.de/nevaforget/moonlock.git?ref=refs/tags/v0.6.21"; flake = false; }; - # Note the repo name: the project is moongreet, the repo is greetd-moongreet. - moongreet = { - url = "git+https://gitea.moonarch.de/nevaforget/greetd-moongreet.git?ref=refs/tags/v0.10.2"; - flake = false; - }; moonset = { url = "git+https://gitea.moonarch.de/nevaforget/moonset.git?ref=refs/tags/v0.9.2"; flake = false; @@ -55,11 +50,6 @@ version = "0.6.21"; }; - moongreet = pkgs.callPackage ./pkgs/moongreet.nix { - src = inputs.moongreet; - version = "0.10.2"; - }; - moonset = pkgs.callPackage ./pkgs/moonset.nix { src = inputs.moonset; version = "0.9.2"; @@ -79,6 +69,21 @@ src = inputs.moonarch; }; }; + + # Colloid overrides, used by the desktop session and by the greeter. + # Defined once so the two cannot drift apart. + themePackages = { + gtk = pkgs.colloid-gtk-theme.override { + themeVariants = [ "grey" ]; + colorVariants = [ "dark" ]; + tweaks = [ "catppuccin" ]; + }; + + icons = pkgs.colloid-icon-theme.override { + schemeVariants = [ "catppuccin" ]; + colorVariants = [ "grey" ]; + }; + }; in { packages.${system} = moonarchPackages; @@ -97,6 +102,7 @@ _module.args = { moonarchSrc = inputs.moonarch; moonarchPkgs = moonarchPackages; + moonarchThemes = themePackages; }; }; diff --git a/hosts/testvm/default.nix b/hosts/testvm/default.nix index 4f20acd..c636ad8 100644 --- a/hosts/testvm/default.nix +++ b/hosts/testvm/default.nix @@ -39,6 +39,18 @@ shell = pkgs.zsh; }; + # LUKS already asked for a passphrase at boot, so the greeter would only ask + # for a second one. initial_session runs once per boot — the greeter comes + # back on logout, which is also where a session gets switched. greetd's + # restart option flips itself off when this is set. + # + # The account still needs a password: swaylock authenticates through + # pam_unix, and without a /etc/shadow entry the screen cannot be unlocked. + services.greetd.settings.initial_session = { + command = "${pkgs.niri}/bin/niri-session"; + user = "kresdo"; + }; + # Development access from the host via the QEMU port forward on 127.0.0.1:2222. services.openssh.enable = true; diff --git a/hosts/thinkpad/default.nix b/hosts/thinkpad/default.nix index d6598bc..71a7731 100644 --- a/hosts/thinkpad/default.nix +++ b/hosts/thinkpad/default.nix @@ -41,8 +41,9 @@ extraGroups = [ "networkmanager" "wheel" "video" "input" "plugdev" "docker" ]; shell = pkgs.zsh; # No password is declared here, so a freshly installed system has no - # /etc/shadow entry for this account and greetd cannot let anyone in. The - # password is set once after installing, see README. + # /etc/shadow entry for this account. Autologin gets past the greeter + # without one, but swaylock then cannot unlock the screen, so the password + # is still set once after installing, see README. # # To make it declarative instead, put the output of `mkpasswd -m sha-512` # in initialHashedPassword. That commits a hash to the repository, which is @@ -50,6 +51,15 @@ # initialHashedPassword = "$6$..."; }; + # LUKS already asked for a passphrase at boot, so the greeter would only ask + # for a second one. initial_session runs once per boot — the greeter comes + # back on logout, which is also where a session gets switched. greetd's + # restart option flips itself off when this is set. + services.greetd.settings.initial_session = { + command = "${pkgs.niri}/bin/niri-session"; + user = "dkressler"; + }; + # Snapshots of root and home. snap-pac has no counterpart here: NixOS keeps # its own generations, so pre/post package snapshots are redundant. services.snapper = { diff --git a/modules/desktop.nix b/modules/desktop.nix index b93f423..f6fdb07 100644 --- a/modules/desktop.nix +++ b/modules/desktop.nix @@ -1,7 +1,7 @@ # ABOUTME: Deploys the XDG configs from the moonarch repo and the package set. # ABOUTME: Niri, Quickshell, terminal, theming, portals. -{ pkgs, lib, moonarchSrc, moonarchPkgs, ... }: +{ pkgs, lib, moonarchSrc, moonarchPkgs, moonarchThemes, ... }: let xdg = "${moonarchSrc}/defaults/xdg"; @@ -23,6 +23,15 @@ let substitute ${xdg}/waypaper/config.ini "$out" \ --replace-fail /usr/share/moonarch/wallpaper.jpg ${wallpaper} ''; + + # Same treatment for swaylock's wallpaper path. swaylock searches + # ~/.swaylock/config, $XDG_CONFIG_HOME/swaylock/config and + # SYSCONFDIR/swaylock/config — and its sysconfdir is inside the store here, + # so the home copy seeded below is the only one it ever reads. + swaylockConfig = pkgs.runCommand "swaylock-config" { } '' + substitute ${xdg}/swaylock/config "$out" \ + --replace-fail /usr/share/moonarch/wallpaper.jpg ${wallpaper} + ''; in { # --- XDG configs -> /etc/xdg/ --- @@ -148,11 +157,16 @@ in environment.systemPackages = with pkgs; [ # Own programs - moonarchPkgs.moonlock moonarchPkgs.moonset moonarchPkgs.sweet-cursors moonarchPkgs.stasis moonarchPkgs.moonarch-scripts + # Only still installed because moonset's Lock action invokes moonlock by + # name and has no lock_command option. Idle and lid-close use swaylock. + moonarchPkgs.moonlock + + # Session lock + swaylock # Compositor extras xwayland-satellite @@ -191,16 +205,9 @@ in udisks2 ntfs3g - # Theming - (colloid-gtk-theme.override { - themeVariants = [ "grey" ]; - colorVariants = [ "dark" ]; - tweaks = [ "catppuccin" ]; - }) - (colloid-icon-theme.override { - schemeVariants = [ "catppuccin" ]; - colorVariants = [ "grey" ]; - }) + # Theming. Same derivations the greeter uses, see flake.nix. + moonarchThemes.gtk + moonarchThemes.icons libsForQt5.qtstyleplugin-kvantum qt6Packages.qtstyleplugin-kvantum qt6Packages.qt6ct @@ -235,5 +242,12 @@ in # edits survive. This is what post-install.sh does imperatively on Arch. systemd.user.tmpfiles.rules = [ "C %h/.config/waypaper/config.ini 0644 - - - ${waypaperConfig}" + "C %h/.config/swaylock/config 0644 - - - ${swaylockConfig}" ]; + + # PAM stack for moonlock, shipped by the package but owned by the system. + # swaylock needs none of this: programs.niri.enable brings + # security.pam.services.swaylock with it. Goes away with moonlock. + security.pam.services.moonlock.text = + builtins.readFile "${moonarchPkgs.moonlock}/share/moonlock/moonlock-pam"; } diff --git a/modules/greetd.nix b/modules/greetd.nix index 565de23..940340c 100644 --- a/modules/greetd.nix +++ b/modules/greetd.nix @@ -1,61 +1,77 @@ -# ABOUTME: Sets up greetd with moongreet as the login greeter. +# ABOUTME: Sets up greetd with regreet as the login greeter. # ABOUTME: Counterpart to defaults/etc/greetd/ in the moonarch repo. -{ pkgs, lib, moonarchSrc, moonarchPkgs, ... }: +{ pkgs, lib, moonarchSrc, moonarchPkgs, moonarchThemes, ... }: let wallpaper = "${moonarchSrc}/defaults/backgrounds/wallpaper.jpg"; - # moongreet.toml cannot be copied verbatim: the upstream file points at - # /usr/share/moonarch/wallpaper.jpg, which does not exist under Nix. - # Everything else matches defaults/etc/moongreet/moongreet.toml. - moongreetConfig = pkgs.writeText "moongreet.toml" '' - [appearance] - background = "${wallpaper}" - cursor-theme = "Sweet-cursors" - cursor-size = 24 - ''; - greeterNiriConfig = "${moonarchSrc}/defaults/etc/greetd/niri-greeter.kdl"; in { - environment.etc = { - "greetd/niri-greeter.kdl".source = greeterNiriConfig; - "moongreet/moongreet.toml".source = moongreetConfig; - }; + environment.etc."greetd/niri-greeter.kdl".source = greeterNiriConfig; - # moongreet caches the last user and session under /var/cache/moongreet. - # Without the directory it logs a permission error on every start. - systemd.tmpfiles.rules = [ - "d /var/cache/moongreet 0755 greeter greeter -" - "d /var/cache/moongreet/last-session 0755 greeter greeter -" - ]; + # /etc/greetd/regreet.toml and regreet.css are written by the module, so + # defaults/etc/greetd/regreet.toml is not deployed verbatim: its wallpaper + # path points at /usr/share/moonarch/, which does not exist here. Every other + # value matches that file. Declaring those two paths in environment.etc would + # collide with the module. + services.displayManager.regreet = { + enable = true; + + settings = { + background = { + path = wallpaper; + fit = "Cover"; + }; + + GTK.application_prefer_dark_theme = true; + + commands = { + reboot = [ "systemctl" "reboot" ]; + poweroff = [ "systemctl" "poweroff" ]; + }; + }; + + # The rest of the GTK table is generated from these options. + theme = { + package = moonarchThemes.gtk; + name = "Colloid-Grey-Dark-Catppuccin"; + }; + + iconTheme = { + package = moonarchThemes.icons; + name = "Colloid-Grey-Catppuccin-Dark"; + }; + + cursorTheme = { + package = moonarchPkgs.sweet-cursors; + name = "Sweet-cursors"; + }; + + font = { + package = pkgs.nerd-fonts.ubuntu-sans; + name = "UbuntuSans Nerd Font"; + size = 12; + }; + + # readFile, not the path: the module only treats a direct /nix/store child + # as a path (lib.isStorePath), so passing this file's path as a string would + # write the path itself into /etc/greetd/regreet.css. + extraCss = builtins.readFile "${moonarchSrc}/defaults/etc/greetd/regreet.css"; + }; services.greetd = { enable = true; - settings = { - terminal.vt = 1; - default_session = { - # moongreet needs a Wayland compositor; niri provides it. - command = "${lib.getExe pkgs.niri} -c /etc/greetd/niri-greeter.kdl"; - user = "greeter"; - }; + settings.default_session = { + # regreet needs a Wayland compositor. niri instead of the module's cage + # default: niri-greeter.kdl carries the German keyboard layout, numlock + # and the cursor theme, and cage would come up with none of them. + command = "${lib.getExe pkgs.niri} -c /etc/greetd/niri-greeter.kdl"; + user = "greeter"; }; }; - # greetd is WantedBy=graphical.target. An installation without a desktop - # boots into multi-user.target, where the unit never starts. - systemd.defaultUnit = "graphical.target"; - - # moongreet walks XDG_DATA_DIRS for wayland-sessions/ and xsessions/. - # NixOS does not link those directories into the system profile by default. + # regreet walks XDG_DATA_DIRS for wayland-sessions/ and xsessions/. environment.pathsToLink = [ "/share/wayland-sessions" "/share/xsessions" ]; - - # The greeter session spawns `moongreet` by name from niri-greeter.kdl, - # so it has to resolve in PATH. - environment.systemPackages = [ moonarchPkgs.moongreet ]; - - # PAM stack for moonlock, shipped by the package but owned by the system. - security.pam.services.moonlock.text = - builtins.readFile "${moonarchPkgs.moonlock}/share/moonlock/moonlock-pam"; } diff --git a/pkgs/moongreet.nix b/pkgs/moongreet.nix deleted file mode 100644 index 5f442a5..0000000 --- a/pkgs/moongreet.nix +++ /dev/null @@ -1,33 +0,0 @@ -# ABOUTME: Nix build recipe for moongreet, the greetd greeter for Wayland. -# ABOUTME: Counterpart to moonarch-pkgbuilds/moongreet/PKGBUILD. - -{ lib -, rustPlatform -, pkg-config -, wrapGAppsHook4 -, glib -, gtk4 -, gtk4-layer-shell -, src -, version -}: - -rustPlatform.buildRustPackage { - pname = "moongreet"; - inherit version src; - - cargoLock.lockFile = "${src}/Cargo.lock"; - - # glib provides glib-compile-resources for the GResource bundle in build.rs. - nativeBuildInputs = [ pkg-config wrapGAppsHook4 glib ]; - - buildInputs = [ gtk4 gtk4-layer-shell ]; - - meta = { - description = "greetd greeter for Wayland with GTK4 and Layer Shell"; - homepage = "https://gitea.moonarch.de/nevaforget/moongreet"; - license = lib.licenses.mit; - mainProgram = "moongreet"; - platforms = lib.platforms.linux; - }; -}