Install only the helper scripts that are still called

cpugov, sink-switcher and nightlight were referenced solely by the waybar
config; the Quickshell popouts cover all three. btnote, capsnote, vpn and
waybar-hidpp are unreferenced anywhere.

Adds the two system units for battery threshold restore and camera
shutoff, which had been missed.
This commit is contained in:
nevaforget committed 2026-08-07 17:12:53 +02:00
1 parent 553effd35c
commit bf915d66bb
9 files changed
+229 -154

No files matched your search

+80 -53
View File
@@ -1,30 +1,39 @@
# moonix # moonix
Moonarch on NixOS — packages and NixOS modules that reproduce the Moonarch NixOS configuration for a scrollable-tiling Wayland desktop: niri as the
desktop from the same sources as the Arch packages. 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 Ships Nix packages for the components that are not in nixpkgs — moonlock,
consumes `moonarch/defaults/` unchanged and builds the Rust projects with Nix moongreet, moonset, stasis, sweet-cursors — and NixOS modules that assemble
instead of `makepkg`. 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 ## Layout
``` ```
flake.nix Inputs (project repos pinned to tags) and outputs flake.nix Inputs and outputs
pkgs/ Build recipes — counterparts to the PKGBUILDs pkgs/ Build recipes
moonlock.nix moonlock.nix screen locker (GTK4, PAM, fingerprint)
moongreet.nix moongreet.nix greetd greeter
moonset.nix moonset.nix session power menu
stasis.nix stasis.nix idle manager
sweet-cursors.nix sweet-cursors.nix cursor theme
modules/ modules/
desktop.nix /etc deployment, package set, theme desktop.nix /etc deployment, package set, theme
greetd.nix greetd + moongreet greetd.nix greetd + moongreet
services.nix systemd system and user services services.nix systemd system and user services
hosts/ 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 ## Usage
Build a single package: Build a single package:
@@ -33,30 +42,30 @@ Build a single package:
nix build .#moonlock nix build .#moonlock
``` ```
Build the whole system without activating it: Build a whole system without activating it:
```bash ```bash
nix build .#nixosConfigurations.moonarch-vm.config.system.build.toplevel nix build .#nixosConfigurations.testvm.config.system.build.toplevel
``` ```
Activate: Activate:
```bash ```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, The disk layout is not declared here. The graphical installer handles
encryption and hardware detection, and produces the one file that cannot be partitioning, encryption and hardware detection, and produces the one file that
written in advance: `hardware-configuration.nix` with the machine's UUIDs, cannot be written in advance: `hardware-configuration.nix` with the machine's
LUKS devices and kernel modules. UUIDs, LUKS devices and kernel modules.
1. Boot the NixOS ISO and run Calamares. Choose `dkressler` as the user name — 1. Boot the NixOS ISO and run the installer. Choose `dkressler` as the user
that is what `hosts/moonarch/default.nix` declares. Pick btrfs, snapper name — that is what `hosts/thinkpad/default.nix` declares. Pick btrfs,
expects it. snapper expects it.
2. Reboot into the fresh system. It runs on the configuration Calamares 2. Reboot into the fresh system. It runs on the generated configuration,
generated, without Moonarch. without this repo.
3. Enable flakes and install git — neither is available yet: 3. Enable flakes and install git — neither is available yet:
```bash ```bash
@@ -70,10 +79,10 @@ LUKS devices and kernel modules.
```bash ```bash
git clone https://gitea.moonarch.de/nevaforget/moonix.git 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 ```nix
imports = [ ./hardware-configuration.nix ]; imports = [ ./hardware-configuration.nix ];
@@ -82,59 +91,77 @@ LUKS devices and kernel modules.
6. Switch and reboot: 6. Switch and reboot:
```bash ```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 — 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 The `nixos-hardware` profile for the T14 Gen 3 (AMD) is already wired up:
(firmware, power management, model quirks). It can be added at any time. firmware, power management and graphics quirks come with it.
`nixosConfigurations.moonarch` therefore evaluates but is not installable on Until step 5 is done, `nixosConfigurations.thinkpad` does not evaluate:
its own: without `hardware-configuration.nix` it has no filesystems.
```
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 ## 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 ```bash
# adjust the tag in flake.nix, then # adjust the tag in flake.nix, then
nix flake update moonlock nix flake update moonlock
``` ```
## Configuration sources ## Configuration files
`moonarch/defaults/` is the single source of truth for both distributions. Config files are deployed unchanged wherever possible. Three are generated
Files are deployed as-is wherever possible. Three exceptions exist, each instead, because they contain a path that does not exist under Nix:
because the file contains an FHS path that does not exist under Nix:
| File | Reason | | File | Reason |
|---|---| |---|---|
| `moongreet.toml` | wallpaper path, generated with the store path | | `moongreet.toml` | wallpaper path, rewritten to the store path |
| `waypaper/config.ini` | wallpaper path, generated and seeded into `$HOME` | | `waypaper/config.ini` | wallpaper path, rewritten and seeded into `$HOME` |
| fontconfig defaults | `/etc/fonts/conf.d` is owned by the NixOS module | | 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 Programs that read only from `$HOME` and have no system-wide fallback get a
replaced it copy seeded through `systemd.user.tmpfiles` with the `C` directive, which never
- `kanshi/config` — a 0-byte placeholder, unused on Arch as well; kanshi overwrites later edits.
rejects it and restarts in a loop
## Test VM ## Test VM
`hosts/moonarch-vm` targets a QEMU guest. The launcher script lives outside `hosts/testvm` targets a QEMU guest. The launcher script lives outside this
this repo at `~/VMs/moonix-vm.sh`. 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 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 import with `Error::DeviceMissing` otherwise, and software rendering is not a
workaround. On the current host that path has twice triggered an amdgpu hard 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. recovery, killing the QEMU process — shut the VM down when not testing.
## Not covered ## Known gaps
- Clipboard history and the `walker -d` menus in Four entry points from the niri config and the helper scripts call a launcher
`moonarch-sink-switcher`, `moonarch-vpn` and `moonarch-cpugov` — walker was that is not installed here, and therefore do nothing:
dropped without a replacement yet
- Bare metal installation (disk layout, encryption, bootloader) | 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.
+15 -9
View File
@@ -2,10 +2,11 @@
# ABOUTME: Pulls the Moonarch project repos as sources and builds them with Nix. # 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 = { inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
nixos-hardware.url = "github:NixOS/nixos-hardware";
# Project sources. These repos are not flakes themselves, they are # Project sources. These repos are not flakes themselves, they are
@@ -68,6 +69,10 @@
src = inputs.stasis; src = inputs.stasis;
version = "1.4.1"; version = "1.4.1";
}; };
moonarch-scripts = pkgs.callPackage ./pkgs/moonarch-scripts.nix {
src = inputs.moonarch;
};
}; };
in in
{ {
@@ -89,22 +94,23 @@
}; };
nixosConfigurations = { nixosConfigurations = {
# QEMU test VM. # QEMU/KVM test VM.
moonarch-vm = nixpkgs.lib.nixosSystem { testvm = nixpkgs.lib.nixosSystem {
inherit system; inherit system;
modules = [ modules = [
./hosts/moonarch-vm ./hosts/testvm
self.nixosModules.moonarch self.nixosModules.moonarch
]; ];
}; };
# Bare metal. Machine independent settings only — no disk layout and # ThinkPad T14 Gen 3 (AMD Ryzen 7 PRO 6850U).
# no hardware profile, both depend on a target machine that is not # Needs hosts/thinkpad/hardware-configuration.nix from the installer
# decided yet. Not installable as is. # before it evaluates — see README.
moonarch = nixpkgs.lib.nixosSystem { thinkpad = nixpkgs.lib.nixosSystem {
inherit system; inherit system;
modules = [ modules = [
./hosts/moonarch inputs.nixos-hardware.nixosModules.lenovo-thinkpad-t14-amd-gen3
./hosts/thinkpad
self.nixosModules.moonarch self.nixosModules.moonarch
]; ];
}; };
-55
View File
@@ -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";
}
+38
View File
@@ -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";
}
@@ -1,16 +1,15 @@
# ABOUTME: Host configuration for the bare metal machine. # ABOUTME: Host configuration for the ThinkPad T14 Gen 3 (AMD).
# ABOUTME: Machine independent settings only — no disk layout, no hardware profile. # ABOUTME: hardware-configuration.nix is added during installation, see README.
{ pkgs, ... }: { pkgs, ... }:
{ {
boot.loader.systemd-boot.enable = true; boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true; boot.loader.efi.canTouchEfiVariables = true;
boot.kernelPackages = pkgs.linuxPackages_zen; boot.kernelPackages = pkgs.linuxPackages_zen;
boot.kernelParams = [ "quiet" ]; boot.kernelParams = [ "quiet" ];
networking.hostName = "moonarch"; networking.hostName = "thinkpad";
networking.networkmanager.enable = true; networking.networkmanager.enable = true;
time.timeZone = "Europe/Berlin"; time.timeZone = "Europe/Berlin";
+5 -30
View File
@@ -1,5 +1,5 @@
# ABOUTME: Deploys the Moonarch XDG configs and desktop package set. # ABOUTME: Deploys the XDG configs from the moonarch repo and the package set.
# ABOUTME: Counterpart to the package() function in moonarch-git/PKGBUILD. # ABOUTME: Niri, Quickshell, terminal, theming, portals.
{ pkgs, lib, moonarchSrc, moonarchPkgs, ... }: { pkgs, lib, moonarchSrc, moonarchPkgs, ... }:
@@ -7,8 +7,6 @@ let
xdg = "${moonarchSrc}/defaults/xdg"; xdg = "${moonarchSrc}/defaults/xdg";
etc = "${moonarchSrc}/defaults/etc"; 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 # waypaper calls `swww`/`swww-daemon`, but the maintained successor ships as
# `awww`. Same compatibility symlinks the Arch package creates. # `awww`. Same compatibility symlinks the Arch package creates.
swww-compat = pkgs.runCommand "swww-compat" { } '' swww-compat = pkgs.runCommand "swww-compat" { } ''
@@ -25,28 +23,6 @@ let
substitute ${xdg}/waypaper/config.ini "$out" \ substitute ${xdg}/waypaper/config.ini "$out" \
--replace-fail /usr/share/moonarch/wallpaper.jpg ${wallpaper} --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 in
{ {
# --- XDG configs -> /etc/xdg/ --- # --- XDG configs -> /etc/xdg/ ---
@@ -54,8 +30,7 @@ in
# Deployed verbatim from the moonarch repo; defaults/ stays the single # Deployed verbatim from the moonarch repo; defaults/ stays the single
# source of truth for both distros. # source of truth for both distros.
# #
# Deliberately excluded: defaults/xdg/walker/ — walker is not part of this # defaults/xdg/walker/ is not deployed: the Quickshell launcher covers it.
# setup, the Quickshell launcher replaced it.
environment.etc = { environment.etc = {
# niri reads /etc/niri, not /etc/xdg/niri. Both are provided: the former # 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 # is what niri actually loads, the latter keeps the layout consistent with
@@ -172,12 +147,12 @@ in
]; ];
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [
# Moonarch's own programs # Own programs
moonarchPkgs.moonlock moonarchPkgs.moonlock
moonarchPkgs.moonset moonarchPkgs.moonset
moonarchPkgs.sweet-cursors moonarchPkgs.sweet-cursors
moonarchPkgs.stasis moonarchPkgs.stasis
moonarch-scripts moonarchPkgs.moonarch-scripts
# Compositor extras # Compositor extras
xwayland-satellite xwayland-satellite
+49 -3
View File
@@ -1,5 +1,5 @@
# ABOUTME: systemd user and system services for the Moonarch session. # ABOUTME: systemd system and user services for the desktop session.
# ABOUTME: Rebuilt from defaults/etc/systemd/ because the units use FHS paths. # ABOUTME: Rebuilt from moonarch/defaults/etc/systemd/, whose units use FHS paths.
{ pkgs, lib, moonarchPkgs, ... }: { 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 # 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. # declared here. Mirrors /usr/lib/systemd/user/stasis.service.
systemd.user.services.stasis = { systemd.user.services.stasis = {
@@ -89,7 +135,7 @@
# Night light stays disabled by default: it is a user toggle. Enabling it # Night light stays disabled by default: it is a user toggle. Enabling it
# here would create a system-scope symlink that overrides any # 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 = { systemd.user.services.wlsunset = {
description = "Wlsunset night light (blue light filter)"; description = "Wlsunset night light (blue light filter)";
partOf = [ "graphical-session.target" ]; partOf = [ "graphical-session.target" ];
+39
View File
@@ -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;
};
}