Files
moonix/modules/services.nix
T
nevaforget b1ed5d087b refactor(stasis): take the package from nixpkgs
nixpkgs carries stasis 1.5.1, so the pinned input and pkgs/stasis.nix go. It
ships no unit and has no NixOS module, so modules/services.nix keeps declaring
one; its actions run as `sh -lc` and it spawns niri and pactl directly, hence
the unit path.

stasis prefers a user config over /etc and generates its own when none exists,
so moonarch's is seeded into the home directory instead of only reaching /etc.
2026-08-31 11:58:33 +02:00

166 lines
5.9 KiB
Nix

# 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, ... }:
{
# --- 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;
# power-profiles-daemon over auto-cpufreq: on amd-pstate in active mode the
# platform decides the frequency from the min/max and EPP registers alone, so
# the governor is the wrong knob. ppd writes EPP and the ACPI platform_profile;
# where neither exists it falls back to a driver that touches no hardware.
services.power-profiles-daemon.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" ];
# Without a config kanshi exits with a parse error and, with
# Restart=on-failure, spins. Monitor profiles are per machine.
unitConfig.ConditionPathExists = "%h/.config/kanshi/config";
serviceConfig = {
Type = "simple";
ExecStart = lib.getExe pkgs.kanshi;
Restart = "on-failure";
RestartSec = 3;
};
};
# --- 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;
};
};
# nixpkgs ships the package but no module, and the package carries no unit of
# its own, so it is declared here. Mirrors /usr/lib/systemd/user/stasis.service.
systemd.user.services.stasis = {
description = "Stasis Wayland Idle Manager";
partOf = [ "graphical-session.target" ];
after = [ "graphical-session.target" ];
wantedBy = [ "graphical-session.target" ];
# stasis runs its actions as `sh -lc <command>` and spawns niri and pactl
# directly, all resolved against this PATH. pulseaudio is here for pactl
# alone; it stays off systemPackages, see modules/desktop.nix.
path = with pkgs; [ bash swaylock brightnessctl niri pulseaudio ];
serviceConfig = {
Type = "simple";
ExecStart = "${lib.getExe pkgs.stasis}";
ExecReload = "${lib.getExe pkgs.stasis} reload";
Restart = "on-failure";
RestartSec = 2;
};
};
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`.
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";
Restart = "on-failure";
RestartSec = 3;
};
};
# The polkit agent is not a service here: niri spawns it via
# spawn-at-startup in defaults/xdg/niri/config.kdl. Only the FHS path it
# references has to exist — see modules/desktop.nix.
}