diff --git a/README.md b/README.md index ac37ab5..d27441f 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,39 @@ # moonix -Moonarch on NixOS — packages and NixOS modules that reproduce the Moonarch -desktop from the same sources as the Arch packages. +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. -Arch stays the primary target. This repo is the parallel NixOS path: it -consumes `moonarch/defaults/` unchanged and builds the Rust projects with Nix -instead of `makepkg`. +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 (project repos pinned to tags) and outputs -pkgs/ Build recipes — counterparts to the PKGBUILDs - moonlock.nix - moongreet.nix - moonset.nix - stasis.nix - sweet-cursors.nix +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 modules/ desktop.nix /etc deployment, package set, theme greetd.nix greetd + moongreet services.nix systemd system and user services hosts/ - moonarch-vm/ QEMU test VM + 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: @@ -33,30 +42,30 @@ Build a single package: nix build .#moonlock ``` -Build the whole system without activating it: +Build a whole system without activating it: ```bash -nix build .#nixosConfigurations.moonarch-vm.config.system.build.toplevel +nix build .#nixosConfigurations.testvm.config.system.build.toplevel ``` Activate: ```bash -sudo nixos-rebuild switch --flake .#moonarch-vm +sudo nixos-rebuild switch --flake .#testvm ``` -## Installing on bare metal +## Installing on a machine -The disk layout is not declared here. Calamares handles partitioning, -encryption and hardware detection, and produces the one file that cannot be -written in advance: `hardware-configuration.nix` with the machine's UUIDs, -LUKS devices and kernel modules. +The disk layout is not declared here. The graphical installer handles +partitioning, encryption and hardware detection, and produces the one file that +cannot be written in advance: `hardware-configuration.nix` with the machine's +UUIDs, LUKS devices and kernel modules. -1. Boot the NixOS ISO and run Calamares. Choose `dkressler` as the user name — - that is what `hosts/moonarch/default.nix` declares. Pick btrfs, snapper - expects it. -2. Reboot into the fresh system. It runs on the configuration Calamares - generated, without Moonarch. +1. Boot the NixOS ISO and run the installer. Choose `dkressler` as the user + name — that is what `hosts/thinkpad/default.nix` declares. Pick btrfs, + snapper expects it. +2. Reboot into the fresh system. It runs on the generated configuration, + without this repo. 3. Enable flakes and install git — neither is available yet: ```bash @@ -70,10 +79,10 @@ LUKS devices and kernel modules. ```bash git clone https://gitea.moonarch.de/nevaforget/moonix.git - cp /etc/nixos/hardware-configuration.nix moonix/hosts/moonarch/ + cp /etc/nixos/hardware-configuration.nix moonix/hosts/thinkpad/ ``` -5. Add the import to `hosts/moonarch/default.nix`: +5. Add the import to `hosts/thinkpad/default.nix`: ```nix imports = [ ./hardware-configuration.nix ]; @@ -82,59 +91,77 @@ LUKS devices and kernel modules. 6. Switch and reboot: ```bash - sudo nixos-rebuild switch --flake ~/moonix#moonarch + sudo nixos-rebuild switch --flake ~/moonix#thinkpad ``` If step 6 breaks the system, pick the previous generation in the boot menu — -the Calamares install stays available as generation 1. +the installer's configuration stays available as generation 1. -Still missing afterwards: a `nixos-hardware` profile for the specific model -(firmware, power management, model quirks). It can be added at any time. +The `nixos-hardware` profile for the T14 Gen 3 (AMD) is already wired up: +firmware, power management and graphics quirks come with it. -`nixosConfigurations.moonarch` therefore evaluates but is not installable on -its own: without `hardware-configuration.nix` it has no filesystems. +Until step 5 is done, `nixosConfigurations.thinkpad` does not evaluate: + +``` +error: Failed assertions: +- The 'fileSystems' option does not specify your root file system. +``` + +That is expected — the filesystems live in `hardware-configuration.nix`. ## Version bumps -The project repos are pinned to release tags. After tagging a new version: +The application repos are pinned to release tags. After tagging a new version: ```bash # adjust the tag in flake.nix, then nix flake update moonlock ``` -## Configuration sources +## Configuration files -`moonarch/defaults/` is the single source of truth for both distributions. -Files are deployed as-is wherever possible. Three exceptions exist, each -because the file contains an FHS path that does not exist under Nix: +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, generated with the store path | -| `waypaper/config.ini` | wallpaper path, generated and seeded into `$HOME` | +| `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 | -Two files from `defaults/xdg/` are deliberately not deployed: +`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. -- `walker/` — walker is not part of this setup, the Quickshell launcher - replaced it -- `kanshi/config` — a 0-byte placeholder, unused on Arch as well; kanshi - rejects it and restarts in a loop +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/moonarch-vm` targets a QEMU guest. The launcher script lives outside -this repo at `~/VMs/moonix-vm.sh`. +`hosts/testvm` targets a QEMU guest. The launcher script lives outside this +repo at `~/VMs/moonix-vm.sh`. + +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. -## Not covered +## Known gaps -- Clipboard history and the `walker -d` menus in - `moonarch-sink-switcher`, `moonarch-vpn` and `moonarch-cpugov` — walker was - dropped without a replacement yet -- Bare metal installation (disk layout, encryption, bootloader) +Four entry points from the niri config and the helper scripts call a launcher +that is not installed here, and therefore do nothing: + +| Entry point | Function | +|---|---| +| `Super+C` | clipboard history | +| `moonarch-sink-switcher` | audio sink picker | +| `moonarch-vpn` | VPN picker | +| `moonarch-cpugov` | CPU governor picker | + +`cliphist` records the clipboard, so only the picker front-end is missing. diff --git a/flake.nix b/flake.nix index c4a21f8..f901dab 100644 --- a/flake.nix +++ b/flake.nix @@ -2,10 +2,11 @@ # ABOUTME: Pulls the Moonarch project repos as sources and builds them with Nix. { - description = "Moonarch on NixOS — packages and modules"; + description = "NixOS configuration for a niri and Quickshell Wayland desktop"; inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + nixos-hardware.url = "github:NixOS/nixos-hardware"; # Project sources. These repos are not flakes themselves, they are @@ -68,6 +69,10 @@ src = inputs.stasis; version = "1.4.1"; }; + + moonarch-scripts = pkgs.callPackage ./pkgs/moonarch-scripts.nix { + src = inputs.moonarch; + }; }; in { @@ -89,22 +94,23 @@ }; nixosConfigurations = { - # QEMU test VM. - moonarch-vm = nixpkgs.lib.nixosSystem { + # QEMU/KVM test VM. + testvm = nixpkgs.lib.nixosSystem { inherit system; modules = [ - ./hosts/moonarch-vm + ./hosts/testvm self.nixosModules.moonarch ]; }; - # Bare metal. Machine independent settings only — no disk layout and - # no hardware profile, both depend on a target machine that is not - # decided yet. Not installable as is. - moonarch = nixpkgs.lib.nixosSystem { + # ThinkPad T14 Gen 3 (AMD Ryzen 7 PRO 6850U). + # Needs hosts/thinkpad/hardware-configuration.nix from the installer + # before it evaluates — see README. + thinkpad = nixpkgs.lib.nixosSystem { inherit system; modules = [ - ./hosts/moonarch + inputs.nixos-hardware.nixosModules.lenovo-thinkpad-t14-amd-gen3 + ./hosts/thinkpad self.nixosModules.moonarch ]; }; diff --git a/hosts/moonarch-vm/default.nix b/hosts/moonarch-vm/default.nix deleted file mode 100644 index db2c529..0000000 --- a/hosts/moonarch-vm/default.nix +++ /dev/null @@ -1,55 +0,0 @@ -# ABOUTME: Host configuration for the NixOS test VM running under QEMU/KVM. -# ABOUTME: Holds everything specific to the VM so the bare-metal host stays clean. - -{ pkgs, ... }: - -{ - imports = [ ./hardware-configuration.nix ]; - - boot.loader.systemd-boot.enable = true; - boot.loader.efi.canTouchEfiVariables = true; - boot.kernelParams = [ "quiet" ]; - - networking.hostName = "moonarch-vm"; - networking.networkmanager.enable = true; - - time.timeZone = "Europe/Berlin"; - i18n.defaultLocale = "de_DE.UTF-8"; - console.keyMap = "de"; - - users.users.kresdo = { - isNormalUser = true; - description = "Dominik Kressler"; - extraGroups = [ "networkmanager" "wheel" "video" "input" ]; - shell = pkgs.zsh; - }; - - # Development access from the host via the QEMU port forward on 127.0.0.1:2222. - services.openssh = { - enable = true; - settings.PasswordAuthentication = true; - }; - - # Throwaway test VM: passwordless sudo so the host can drive rebuilds and - # read logs over SSH. Deliberately confined to this host configuration — - # the bare-metal host must not inherit it. - security.sudo.wheelNeedsPassword = false; - - # QEMU guest agent and clipboard/resolution integration. - services.qemuGuest.enable = true; - services.spice-vdagentd.enable = true; - - # Render in software. Passing the guest through to the host GPU via - # virtio-gpu 3D triggered an amdgpu hard recovery on the host, killing the - # QEMU process. Slower, but it leaves the host graphics stack alone. - environment.variables.LIBGL_ALWAYS_SOFTWARE = "1"; - - # greetd starts niri directly, without a login shell, so the variable has to - # be set on the unit as well. - systemd.services.greetd.environment.LIBGL_ALWAYS_SOFTWARE = "1"; - - nix.settings.experimental-features = [ "nix-command" "flakes" ]; - nixpkgs.config.allowUnfree = true; - - system.stateVersion = "26.05"; -} diff --git a/hosts/testvm/default.nix b/hosts/testvm/default.nix new file mode 100644 index 0000000..9071737 --- /dev/null +++ b/hosts/testvm/default.nix @@ -0,0 +1,38 @@ +# ABOUTME: Host configuration for the QEMU/KVM test VM. +# ABOUTME: Launcher script lives outside this repo at ~/VMs/moonix-vm.sh. + +{ pkgs, ... }: + +{ + imports = [ ./hardware-configuration.nix ]; + + boot.loader.systemd-boot.enable = true; + boot.loader.efi.canTouchEfiVariables = true; + boot.kernelParams = [ "quiet" ]; + + networking.hostName = "testvm"; + networking.networkmanager.enable = true; + + time.timeZone = "Europe/Berlin"; + i18n.defaultLocale = "de_DE.UTF-8"; + console.keyMap = "de"; + + users.users.kresdo = { + isNormalUser = true; + description = "Dominik Kressler"; + extraGroups = [ "networkmanager" "wheel" "video" "input" ]; + shell = pkgs.zsh; + }; + + # Development access from the host via the QEMU port forward on 127.0.0.1:2222. + services.openssh.enable = true; + + # QEMU guest agent and clipboard/resolution integration. + services.qemuGuest.enable = true; + services.spice-vdagentd.enable = true; + + nix.settings.experimental-features = [ "nix-command" "flakes" ]; + nixpkgs.config.allowUnfree = true; + + system.stateVersion = "26.05"; +} diff --git a/hosts/moonarch-vm/hardware-configuration.nix b/hosts/testvm/hardware-configuration.nix similarity index 100% rename from hosts/moonarch-vm/hardware-configuration.nix rename to hosts/testvm/hardware-configuration.nix diff --git a/hosts/moonarch/default.nix b/hosts/thinkpad/default.nix similarity index 90% rename from hosts/moonarch/default.nix rename to hosts/thinkpad/default.nix index 0a05a96..59efb71 100644 --- a/hosts/moonarch/default.nix +++ b/hosts/thinkpad/default.nix @@ -1,16 +1,15 @@ -# ABOUTME: Host configuration for the bare metal machine. -# ABOUTME: Machine independent settings only — no disk layout, no hardware profile. +# ABOUTME: Host configuration for the ThinkPad T14 Gen 3 (AMD). +# ABOUTME: hardware-configuration.nix is added during installation, see README. { pkgs, ... }: { - boot.loader.systemd-boot.enable = true; boot.loader.efi.canTouchEfiVariables = true; boot.kernelPackages = pkgs.linuxPackages_zen; boot.kernelParams = [ "quiet" ]; - networking.hostName = "moonarch"; + networking.hostName = "thinkpad"; networking.networkmanager.enable = true; time.timeZone = "Europe/Berlin"; diff --git a/modules/desktop.nix b/modules/desktop.nix index b92d95d..b93f423 100644 --- a/modules/desktop.nix +++ b/modules/desktop.nix @@ -1,5 +1,5 @@ -# ABOUTME: Deploys the Moonarch XDG configs and desktop package set. -# ABOUTME: Counterpart to the package() function in moonarch-git/PKGBUILD. +# ABOUTME: Deploys the XDG configs from the moonarch repo and the package set. +# ABOUTME: Niri, Quickshell, terminal, theming, portals. { pkgs, lib, moonarchSrc, moonarchPkgs, ... }: @@ -7,8 +7,6 @@ let xdg = "${moonarchSrc}/defaults/xdg"; etc = "${moonarchSrc}/defaults/etc"; - # The moonarch-* helper scripts from defaults/bin, installed as a single - # package. Counterpart to `install -Dm755 defaults/bin/moonarch-* -t /usr/bin/`. # waypaper calls `swww`/`swww-daemon`, but the maintained successor ships as # `awww`. Same compatibility symlinks the Arch package creates. swww-compat = pkgs.runCommand "swww-compat" { } '' @@ -25,28 +23,6 @@ let substitute ${xdg}/waypaper/config.ini "$out" \ --replace-fail /usr/share/moonarch/wallpaper.jpg ${wallpaper} ''; - - moonarch-scripts = pkgs.stdenv.mkDerivation { - pname = "moonarch-scripts"; - version = "0-unstable"; - src = moonarchSrc; - - dontBuild = true; - installPhase = '' - runHook preInstall - # moonarch-waybar* are excluded: waybar is not part of this setup, the - # Quickshell bar replaced it. - for f in defaults/bin/moonarch-*; do - case "$(basename "$f")" in - moonarch-waybar*) continue ;; - esac - install -Dm755 "$f" -t "$out/bin/" - done - runHook postInstall - ''; - - meta.description = "Moonarch helper scripts for Waybar, power and devices"; - }; in { # --- XDG configs -> /etc/xdg/ --- @@ -54,8 +30,7 @@ in # Deployed verbatim from the moonarch repo; defaults/ stays the single # source of truth for both distros. # - # Deliberately excluded: defaults/xdg/walker/ — walker is not part of this - # setup, the Quickshell launcher replaced it. + # defaults/xdg/walker/ is not deployed: the Quickshell launcher covers it. environment.etc = { # niri reads /etc/niri, not /etc/xdg/niri. Both are provided: the former # is what niri actually loads, the latter keeps the layout consistent with @@ -172,12 +147,12 @@ in ]; environment.systemPackages = with pkgs; [ - # Moonarch's own programs + # Own programs moonarchPkgs.moonlock moonarchPkgs.moonset moonarchPkgs.sweet-cursors moonarchPkgs.stasis - moonarch-scripts + moonarchPkgs.moonarch-scripts # Compositor extras xwayland-satellite diff --git a/modules/services.nix b/modules/services.nix index 6dcad98..037d5b1 100644 --- a/modules/services.nix +++ b/modules/services.nix @@ -1,5 +1,5 @@ -# ABOUTME: systemd user and system services for the Moonarch session. -# ABOUTME: Rebuilt from defaults/etc/systemd/ because the units use FHS paths. +# ABOUTME: systemd system and user services for the desktop session. +# ABOUTME: Rebuilt from moonarch/defaults/etc/systemd/, whose units use FHS paths. { pkgs, lib, moonarchPkgs, ... }: @@ -45,6 +45,52 @@ }; }; + # --- System services from defaults/etc/systemd/system/ --- + # + # Rebuilt rather than copied, same reason as the user units: the originals + # reference /usr/bin. + + systemd.services.moonarch-batsaver = { + description = "Restore battery conservation mode threshold"; + after = [ "sysinit.target" ]; + wantedBy = [ "multi-user.target" ]; + unitConfig.ConditionPathExists = [ + "/sys/class/power_supply/BAT0/charge_control_end_threshold" + "/var/lib/moonarch/batsaver-threshold" + ]; + serviceConfig = { + Type = "oneshot"; + ExecStart = "${moonarchPkgs.moonarch-scripts}/bin/moonarch-batsaver-restore"; + NoNewPrivileges = true; + ProtectHome = true; + PrivateTmp = true; + ProtectKernelModules = true; + ProtectControlGroups = true; + RestrictNamespaces = true; + RestrictRealtime = true; + LockPersonality = true; + }; + }; + + systemd.services.moonarch-camera = { + description = "Switch off all cameras on boot"; + after = [ "sysinit.target" ]; + wantedBy = [ "multi-user.target" ]; + unitConfig.ConditionPathExistsGlob = "/sys/class/video4linux/video*"; + serviceConfig = { + Type = "oneshot"; + ExecStart = "${moonarchPkgs.moonarch-scripts}/bin/moonarch-camera-boot"; + NoNewPrivileges = true; + ProtectHome = true; + PrivateTmp = true; + ProtectKernelModules = true; + ProtectControlGroups = true; + RestrictNamespaces = true; + RestrictRealtime = true; + LockPersonality = true; + }; + }; + # Upstream ships a unit in the Arch package but not in the tarball, so it is # declared here. Mirrors /usr/lib/systemd/user/stasis.service. systemd.user.services.stasis = { @@ -89,7 +135,7 @@ # Night light stays disabled by default: it is a user toggle. Enabling it # here would create a system-scope symlink that overrides any - # `systemctl --user disable` — same reasoning as in moonarch-git/PKGBUILD. + # `systemctl --user disable`. systemd.user.services.wlsunset = { description = "Wlsunset night light (blue light filter)"; partOf = [ "graphical-session.target" ]; diff --git a/pkgs/moonarch-scripts.nix b/pkgs/moonarch-scripts.nix new file mode 100644 index 0000000..e2d70b6 --- /dev/null +++ b/pkgs/moonarch-scripts.nix @@ -0,0 +1,39 @@ +# ABOUTME: Helper scripts for battery conservation and camera control. +# ABOUTME: Installs the subset of moonarch/defaults/bin that is still called. + +{ lib, stdenvNoCC, src }: + +let + # Only the scripts something still calls. The rest of defaults/bin is either + # referenced solely by the waybar config, which this setup does not use + # (cpugov, sink-switcher, nightlight — all three covered by Quickshell + # popouts), or unreferenced anywhere (btnote, capsnote, vpn, waybar-hidpp). + scripts = [ + "moonarch-batsaver-apply" # called by -toggle + "moonarch-batsaver-restore" # called by moonarch-batsaver.service + "moonarch-batsaver-toggle" # called by BatteryPopout.qml + "moonarch-camera-apply" # called by -toggle + "moonarch-camera-boot" # called by moonarch-camera.service + "moonarch-camera-list" # called by CameraService.qml + "moonarch-camera-toggle" # called by CameraService.qml + ]; +in +stdenvNoCC.mkDerivation { + pname = "moonarch-scripts"; + version = "0-unstable"; + inherit src; + + dontBuild = true; + dontConfigure = true; + + installPhase = '' + runHook preInstall + install -Dm755 ${lib.concatMapStringsSep " " (s: "defaults/bin/${s}") scripts} -t "$out/bin/" + runHook postInstall + ''; + + meta = { + description = "Moonarch helper scripts for battery and camera control"; + platforms = lib.platforms.linux; + }; +}