From d485783cf754c3d707434fbe080a4f9f94fc5e2e Mon Sep 17 00:00:00 2001 From: nevaforget <> Date: Fri, 7 Aug 2026 12:26:46 +0200 Subject: [PATCH] Add NixOS modules for desktop, greetd and services --- flake.nix | 35 +++- hosts/moonarch-vm/default.nix | 41 +++++ hosts/moonarch-vm/hardware-configuration.nix | 44 +++++ modules/desktop.nix | 166 +++++++++++++++++++ modules/greetd.nix | 45 +++++ modules/services.nix | 104 ++++++++++++ 6 files changed, 432 insertions(+), 3 deletions(-) create mode 100644 hosts/moonarch-vm/default.nix create mode 100644 hosts/moonarch-vm/hardware-configuration.nix create mode 100644 modules/desktop.nix create mode 100644 modules/greetd.nix create mode 100644 modules/services.nix diff --git a/flake.nix b/flake.nix index f380bda..52c6046 100644 --- a/flake.nix +++ b/flake.nix @@ -9,6 +9,10 @@ # Project sources. These repos are not flakes themselves, they are # consumed as plain source trees pinned to a release tag. + moonarch = { + url = "git+https://gitea.moonarch.de/nevaforget/moonarch.git"; + flake = false; + }; moonlock = { url = "git+https://gitea.moonarch.de/nevaforget/moonlock.git?ref=refs/tags/v0.6.20"; flake = false; @@ -28,9 +32,8 @@ let system = "x86_64-linux"; pkgs = nixpkgs.legacyPackages.${system}; - in - { - packages.${system} = { + + moonarchPackages = { moonlock = pkgs.callPackage ./pkgs/moonlock.nix { src = inputs.moonlock; version = "0.6.20"; @@ -46,6 +49,32 @@ version = "0.9.1"; }; }; + in + { + packages.${system} = moonarchPackages; + + # Makes the packages and the moonarch source tree available to every + # module without threading arguments through by hand. + nixosModules.moonarch = { ... }: { + imports = [ + ./modules/desktop.nix + ./modules/greetd.nix + ./modules/services.nix + ]; + + _module.args = { + moonarchSrc = inputs.moonarch; + moonarchPkgs = moonarchPackages; + }; + }; + + nixosConfigurations.moonarch-vm = nixpkgs.lib.nixosSystem { + inherit system; + modules = [ + ./hosts/moonarch-vm + self.nixosModules.moonarch + ]; + }; devShells.${system}.default = pkgs.mkShell { packages = with pkgs; [ nixpkgs-fmt ]; diff --git a/hosts/moonarch-vm/default.nix b/hosts/moonarch-vm/default.nix new file mode 100644 index 0000000..5554f04 --- /dev/null +++ b/hosts/moonarch-vm/default.nix @@ -0,0 +1,41 @@ +# 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; + }; + + # 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/moonarch-vm/hardware-configuration.nix new file mode 100644 index 0000000..183b3b7 --- /dev/null +++ b/hosts/moonarch-vm/hardware-configuration.nix @@ -0,0 +1,44 @@ +# Do not modify this file! It was generated by ‘nixos-generate-config’ +# and may be overwritten by future invocations. Please make changes +# to /etc/nixos/configuration.nix instead. +{ config, lib, pkgs, modulesPath, ... }: + +{ + imports = + [ (modulesPath + "/profiles/qemu-guest.nix") + ]; + + boot.initrd.availableKernelModules = [ "ata_piix" "virtio_pci" "floppy" "sr_mod" "virtio_blk" ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-amd" ]; + boot.extraModulePackages = [ ]; + + fileSystems."/" = + { device = "/dev/disk/by-uuid/b88b75df-3999-4110-9e37-b8b76bf8206d"; + fsType = "btrfs"; + }; + + fileSystems."/home" = + { device = "/dev/disk/by-uuid/b88b75df-3999-4110-9e37-b8b76bf8206d"; + fsType = "btrfs"; + options = [ "subvol=home" ]; + }; + + fileSystems."/nix" = + { device = "/dev/disk/by-uuid/b88b75df-3999-4110-9e37-b8b76bf8206d"; + fsType = "btrfs"; + options = [ "subvol=nix" ]; + }; + + fileSystems."/boot" = + { device = "/dev/disk/by-uuid/77FC-6FE3"; + fsType = "vfat"; + options = [ "fmask=0077" "dmask=0077" ]; + }; + + swapDevices = + [ { device = "/dev/disk/by-uuid/47747ffb-51b7-44c5-a2f3-6db564497c5c"; } + ]; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; +} diff --git a/modules/desktop.nix b/modules/desktop.nix new file mode 100644 index 0000000..8458082 --- /dev/null +++ b/modules/desktop.nix @@ -0,0 +1,166 @@ +# ABOUTME: Deploys the Moonarch XDG configs and desktop package set. +# ABOUTME: Counterpart to the package() function in moonarch-git/PKGBUILD. + +{ pkgs, lib, moonarchSrc, moonarchPkgs, ... }: + +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/`. + moonarch-scripts = pkgs.stdenv.mkDerivation { + pname = "moonarch-scripts"; + version = "0-unstable"; + src = moonarchSrc; + + dontBuild = true; + installPhase = '' + runHook preInstall + install -Dm755 defaults/bin/moonarch-* -t "$out/bin/" + runHook postInstall + ''; + + meta.description = "Moonarch helper scripts for Waybar, power and devices"; + }; +in +{ + # --- XDG configs -> /etc/xdg/ --- + # + # 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. + environment.etc = { + "xdg/niri/config.kdl".source = "${xdg}/niri/config.kdl"; + "xdg/quickshell/moonarch".source = "${xdg}/quickshell/moonarch"; + "xdg/waybar".source = "${xdg}/waybar"; + "xdg/swaync".source = "${xdg}/swaync"; + "xdg/foot/foot.ini".source = "${xdg}/foot/foot.ini"; + "xdg/fastfetch/config.jsonc".source = "${xdg}/fastfetch/config.jsonc"; + "xdg/kanshi/config".source = "${xdg}/kanshi/config"; + "xdg/Kvantum/kvantum.kvconfig".source = "${xdg}/Kvantum/kvantum.kvconfig"; + "xdg/qt6ct/qt6ct.conf".source = "${xdg}/qt6ct/qt6ct.conf"; + "xdg/gtk-3.0/settings.ini".source = "${xdg}/gtk-3.0/settings.ini"; + "xdg/gtk-4.0/settings.ini".source = "${xdg}/gtk-4.0/settings.ini"; + "xdg/pipewire/pipewire.conf.d/99-input-denoising.conf".source = + "${xdg}/pipewire/pipewire.conf.d/99-input-denoising.conf"; + + # stasis reads /etc/stasis/ as its system-wide fallback, never /etc/xdg/. + "stasis/stasis.rune".source = "${xdg}/stasis/stasis.rune"; + + # Configs outside /etc/xdg/ in the Arch package as well. + "mpv/mpv.conf".source = "${etc}/mpv/mpv.conf"; + "fonts/conf.d/65-moonarch-fonts.conf".source = + "${etc}/fonts/conf.d/65-moonarch-fonts.conf"; + "xdg-desktop-portal/niri-portals.conf".source = + "${etc}/xdg-desktop-portal/niri-portals.conf"; + }; + + # --- Compositor --- + programs.niri.enable = true; + + # --- Shell --- + programs.zsh.enable = true; + + # --- Theme, systemwide instead of the gsettings calls in post-install.sh --- + programs.dconf = { + enable = true; + profiles.user.databases = [{ + settings."org/gnome/desktop/interface" = { + gtk-theme = "Colloid-Grey-Dark-Catppuccin"; + icon-theme = "Colloid-Grey-Catppuccin-Dark"; + color-scheme = "prefer-dark"; + font-name = "UbuntuSans Nerd Font 11"; + }; + }]; + }; + + # --- Portals --- + xdg.portal = { + enable = true; + extraPortals = with pkgs; [ xdg-desktop-portal-gnome xdg-desktop-portal-gtk ]; + }; + + fonts.packages = with pkgs; [ + nerd-fonts.ubuntu-sans + openmoji-color + ]; + + environment.systemPackages = with pkgs; [ + # Moonarch's own programs + moonarchPkgs.moonlock + moonarchPkgs.moonset + moonarch-scripts + + # Compositor extras + xwayland-satellite + kanshi + + # Bar, notifications, clipboard + waybar + quickshell + swaynotificationcenter + libnotify + cliphist + wl-clipboard + + # Display and wallpaper + wlsunset + nwg-look + brightnessctl + awww + + # Terminal + foot + + # Audio + pavucontrol + alsa-utils + + # Network and bluetooth + networkmanagerapplet + blueman + + # File manager stack + gvfs + udisks2 + ntfs3g + + # Theming + (colloid-gtk-theme.override { + themeVariants = [ "grey" ]; + colorVariants = [ "dark" ]; + tweaks = [ "catppuccin" ]; + }) + colloid-icon-theme + libsForQt5.qtstyleplugin-kvantum + qt6Packages.qtstyleplugin-kvantum + qt6ct + qt6.qt5compat + + # CLI tools referenced by zshrc and the helper scripts + bat + btop + eza + fastfetch + fd + fzf + jq + lazygit + ripgrep + neovim + git + + # System + fwupd + upower + polkit_gnome + wdisplays + wl-color-picker + ]; + + # Polkit agent for privilege prompts inside the session. + security.polkit.enable = true; +} diff --git a/modules/greetd.nix b/modules/greetd.nix new file mode 100644 index 0000000..b64b13c --- /dev/null +++ b/modules/greetd.nix @@ -0,0 +1,45 @@ +# ABOUTME: Sets up greetd with moongreet as the login greeter. +# ABOUTME: Counterpart to defaults/etc/greetd/ in the moonarch repo. + +{ pkgs, lib, moonarchSrc, moonarchPkgs, ... }: + +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-size = 24 + ''; + + greeterNiriConfig = "${moonarchSrc}/defaults/etc/greetd/niri-greeter.kdl"; +in +{ + environment.etc = { + "greetd/niri-greeter.kdl".source = greeterNiriConfig; + "moongreet/moongreet.toml".source = moongreetConfig; + }; + + 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"; + }; + }; + }; + + # 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/modules/services.nix b/modules/services.nix new file mode 100644 index 0000000..9ce5e4b --- /dev/null +++ b/modules/services.nix @@ -0,0 +1,104 @@ +# ABOUTME: systemd user and system services for the Moonarch session. +# ABOUTME: Rebuilt from defaults/etc/systemd/ because the units use FHS paths. + +{ pkgs, lib, ... }: + +{ + # --- Audio --- + services.pipewire = { + enable = true; + alsa.enable = true; + alsa.support32Bit = true; + pulse.enable = true; + jack.enable = true; + }; + + # --- Hardware --- + hardware.bluetooth.enable = true; + services.blueman.enable = true; + services.udisks2.enable = true; + services.upower.enable = true; + services.gvfs.enable = true; + services.fwupd.enable = true; + + # --- Firewall, replaces the ufw calls in post-install.sh --- + networking.firewall.enable = true; + + # --- Session services --- + # + # Rebuilt rather than copied: the upstream units reference /usr/bin/, + # which does not exist under Nix. + + systemd.user.services.kanshi = { + description = "Dynamic display configuration"; + partOf = [ "graphical-session.target" ]; + after = [ "graphical-session.target" ]; + wantedBy = [ "graphical-session.target" ]; + serviceConfig = { + Type = "simple"; + ExecStart = lib.getExe pkgs.kanshi; + Restart = "on-failure"; + RestartSec = 3; + }; + }; + + systemd.user.services.cliphist-text = { + description = "Clipboard history manager (text)"; + partOf = [ "graphical-session.target" ]; + wantedBy = [ "graphical-session.target" ]; + serviceConfig = { + Type = "simple"; + ExecStartPre = "${pkgs.runtimeShell} -c 'mkdir -p $XDG_RUNTIME_DIR/cliphist && ${pkgs.cliphist}/bin/cliphist wipe'"; + ExecStart = "${pkgs.wl-clipboard}/bin/wl-paste --watch ${pkgs.cliphist}/bin/cliphist -db-path %t/cliphist/db store"; + Restart = "on-failure"; + RestartSec = 3; + }; + }; + + systemd.user.services.cliphist-image = { + description = "Clipboard history manager (image)"; + partOf = [ "graphical-session.target" ]; + after = [ "cliphist-text.service" ]; + wantedBy = [ "graphical-session.target" ]; + serviceConfig = { + Type = "simple"; + ExecStart = "${pkgs.wl-clipboard}/bin/wl-paste --type image --watch ${pkgs.cliphist}/bin/cliphist -db-path %t/cliphist/db store"; + Restart = "on-failure"; + RestartSec = 3; + }; + }; + + # 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. + systemd.user.services.wlsunset = { + description = "Wlsunset night light (blue light filter)"; + partOf = [ "graphical-session.target" ]; + after = [ "graphical-session.target" "kanshi.service" ]; + wantedBy = [ ]; + serviceConfig = { + Type = "simple"; + ExecStartPre = "${pkgs.coreutils}/bin/sleep 2"; + ExecStart = "${lib.getExe pkgs.wlsunset} -T 6500 -t 5000 -S 00:00 -s 00:01"; + # Leading '-' keeps a missing waybar non-fatal, otherwise pkill's exit 1 + # fails the unit and Restart=on-failure spins it in a flicker loop. + ExecStartPost = "-${pkgs.procps}/bin/pkill -RTMIN+11 waybar"; + Restart = "on-failure"; + RestartSec = 3; + }; + }; + + # Polkit authentication agent for the graphical session. + systemd.user.services.polkit-gnome-authentication-agent-1 = { + description = "polkit-gnome authentication agent"; + partOf = [ "graphical-session.target" ]; + after = [ "graphical-session.target" ]; + wantedBy = [ "graphical-session.target" ]; + serviceConfig = { + Type = "simple"; + ExecStart = "${pkgs.polkit_gnome}/libexec/polkit-gnome-authentication-agent-1"; + Restart = "on-failure"; + RestartSec = 1; + }; + }; +}