feat: Tier 1 system features — snapper, hibernate, containers, libvirt, keyring
Five opt-in modules lifted from bernardo/nixos and adapted to Nomarchy's nomarchy.system.* option namespace. All default off (except keyring which defaults on); evaluation of the existing VM/ISO is unchanged when the toggles are unset. - core/system/snapper.nix: BTRFS timeline snapshots (5h/7d), nixos-rebuild-snap wrapper that pre-snaps before each switch using the running hostname. Auto-skips when / isn't BTRFS so impermanence/non-BTRFS hosts are safe. - core/system/hibernate.nix: suspend-then-hibernate on lid/idle/power-key with configurable idleMinutes (default 30). Description warns swap is required. - core/system/containers.nix: rootless Podman with dockerCompat + dns + podman-compose, podman-tui, dive. Better default than the docker daemon for a desktop distro. - core/system/virtualization.nix: extends the existing uwsm/Hyprland file with a libvirt + virt-manager + OVMF branch behind nomarchy.system.virtualization.libvirt.enable. - core/system/pam.nix: GNOME Keyring auto-unlock at SDDM/login/hyprlock plus gcr-ssh-agent so SSH keys flow through the keyring instead of a separate ssh-agent. Default on. - core/system/options.nix: declares the five new options. - core/system/default.nix: imports the four new files. - installer/install.sh: surfaces all five toggles as commented one-liners in the "Optional Nomarchy modules" section of the generated system.nix. Verified via the existing dry-run / generator smoke test. Verified each toggle lights up the right NixOS option (services.snapper, logind IdleAction, virtualisation.podman/libvirtd, pam.sddm.enableGnomeKeyring) via nix eval against extendModules. VM and live-ISO toplevels still build. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
23
core/system/containers.nix
Normal file
23
core/system/containers.nix
Normal file
@@ -0,0 +1,23 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
cfg = config.nomarchy.system.containers;
|
||||
in
|
||||
{
|
||||
config = lib.mkIf cfg.enable {
|
||||
virtualisation.podman = {
|
||||
enable = true;
|
||||
# `docker` and `docker-compose` invocations transparently route to
|
||||
# podman. Pairs cleanly with rootless mode.
|
||||
dockerCompat = true;
|
||||
defaultNetwork.settings.dns_enabled = true;
|
||||
};
|
||||
|
||||
environment.systemPackages = with pkgs; [
|
||||
podman
|
||||
podman-compose
|
||||
podman-tui
|
||||
dive
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -18,6 +18,11 @@
|
||||
./impermanence.nix
|
||||
./browser.nix
|
||||
./makima.nix
|
||||
# Tier 1 system features (all opt-in via nomarchy.system.*).
|
||||
./snapper.nix
|
||||
./hibernate.nix
|
||||
./containers.nix
|
||||
./pam.nix
|
||||
../../themes/engine/plymouth.nix
|
||||
../../themes/engine/sddm.nix
|
||||
];
|
||||
|
||||
24
core/system/hibernate.nix
Normal file
24
core/system/hibernate.nix
Normal file
@@ -0,0 +1,24 @@
|
||||
{ config, lib, ... }:
|
||||
|
||||
let
|
||||
cfg = config.nomarchy.system.hibernation;
|
||||
in
|
||||
{
|
||||
config = lib.mkIf cfg.enable {
|
||||
# Wait this long after suspend before hibernating, and use the same
|
||||
# delay as the idle-action timeout so the two paths agree.
|
||||
systemd.sleep.extraConfig = ''
|
||||
HibernateDelaySec=${toString (cfg.idleMinutes * 60)}
|
||||
'';
|
||||
|
||||
services.logind = {
|
||||
settings.Login = {
|
||||
HandleLidSwitch = lib.mkDefault "suspend-then-hibernate";
|
||||
HandleLidSwitchExternalPower = lib.mkDefault "suspend";
|
||||
HandlePowerKey = "hibernate";
|
||||
IdleAction = "suspend-then-hibernate";
|
||||
IdleActionSec = toString (cfg.idleMinutes * 60);
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -51,5 +51,56 @@
|
||||
default = "summer-night";
|
||||
description = "Selected system theme.";
|
||||
};
|
||||
|
||||
# ----- Tier 1 system features (all opt-in, no behavioural change off) ---
|
||||
|
||||
snapper = {
|
||||
enable = lib.mkEnableOption ''
|
||||
Snapper-driven BTRFS timeline snapshots of `/`. Auto-disables when
|
||||
`/` isn't BTRFS. Includes a `nixos-rebuild-snap` wrapper that takes
|
||||
a "Pre-rebuild" snapshot before each switch.
|
||||
'';
|
||||
};
|
||||
|
||||
hibernation = {
|
||||
enable = lib.mkEnableOption ''
|
||||
suspend-then-hibernate (lid close, idle, power button). NOTE: this
|
||||
requires a disk swap device or swapfile sized to at least RAM —
|
||||
zRAM alone is not enough.
|
||||
'';
|
||||
idleMinutes = lib.mkOption {
|
||||
type = lib.types.int;
|
||||
default = 30;
|
||||
description = "Idle minutes before suspend-then-hibernate fires.";
|
||||
};
|
||||
};
|
||||
|
||||
containers = {
|
||||
enable = lib.mkEnableOption ''
|
||||
Rootless Podman with Docker compatibility (`docker` → `podman`),
|
||||
plus podman-compose, podman-tui and dive.
|
||||
'';
|
||||
};
|
||||
|
||||
virtualization = {
|
||||
libvirt = {
|
||||
enable = lib.mkEnableOption ''
|
||||
libvirt daemon + virt-manager + OVMF. The user must be in the
|
||||
`libvirtd` group.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
keyring = {
|
||||
enable = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Auto-unlock GNOME Keyring at SDDM/Hyprland login and route SSH
|
||||
keys through `gcr-ssh-agent`. Default on — near-universal QoL
|
||||
improvement.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
28
core/system/pam.nix
Normal file
28
core/system/pam.nix
Normal file
@@ -0,0 +1,28 @@
|
||||
{ config, lib, ... }:
|
||||
|
||||
let
|
||||
cfg = config.nomarchy.system.keyring;
|
||||
in
|
||||
{
|
||||
config = lib.mkIf cfg.enable {
|
||||
# Auto-unlock GNOME Keyring at SDDM autologin and at login. hyprlock
|
||||
# gets the same treatment so the session keyring stays unlocked when
|
||||
# the screen lock disengages.
|
||||
security.pam.services = {
|
||||
login.enableGnomeKeyring = true;
|
||||
sddm.enableGnomeKeyring = true;
|
||||
hyprlock.enableGnomeKeyring = true;
|
||||
};
|
||||
|
||||
# Run the keyring + the gcr SSH agent. Disabling `programs.ssh.startAgent`
|
||||
# ensures keys flow through the keyring's agent (so unlock-on-login
|
||||
# carries over to ssh) instead of a separate ssh-agent process.
|
||||
services.gnome.gnome-keyring.enable = true;
|
||||
services.gnome.gcr-ssh-agent.enable = true;
|
||||
programs.ssh.startAgent = lib.mkForce false;
|
||||
|
||||
# Point downstream tooling at the gcr socket so `ssh` / `git` / etc.
|
||||
# find the keyring's keys without per-user shell config.
|
||||
environment.sessionVariables.SSH_AUTH_SOCK = "$XDG_RUNTIME_DIR/gcr/ssh";
|
||||
};
|
||||
}
|
||||
42
core/system/snapper.nix
Normal file
42
core/system/snapper.nix
Normal file
@@ -0,0 +1,42 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
cfg = config.nomarchy.system.snapper;
|
||||
rootIsBtrfs = (config.fileSystems."/".fsType or "") == "btrfs";
|
||||
active = cfg.enable && rootIsBtrfs;
|
||||
in
|
||||
{
|
||||
config = lib.mkIf active {
|
||||
# `nixos-rebuild-snap`: take a Snapper pre-rebuild snapshot, then run
|
||||
# `nixos-rebuild switch` against the current host. The hostname is read
|
||||
# from the running config so this script works on every machine without
|
||||
# editing.
|
||||
environment.systemPackages = [
|
||||
(pkgs.writeShellScriptBin "nixos-rebuild-snap" ''
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo "This script must be run as root (use sudo)" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "Creating pre-rebuild snapshot..."
|
||||
${pkgs.snapper}/bin/snapper -c root create \
|
||||
-d "Pre-rebuild $(date +'%Y-%m-%d %H:%M:%S')" \
|
||||
--cleanup-algorithm number
|
||||
echo "Rebuilding..."
|
||||
nixos-rebuild switch --flake .#${config.networking.hostName} "$@"
|
||||
'')
|
||||
];
|
||||
|
||||
services.snapper.configs = {
|
||||
root = {
|
||||
SUBVOLUME = "/";
|
||||
TIMELINE_CREATE = true;
|
||||
TIMELINE_CLEANUP = true;
|
||||
TIMELINE_LIMIT_HOURLY = "5";
|
||||
TIMELINE_LIMIT_DAILY = "7";
|
||||
TIMELINE_LIMIT_WEEKLY = "0";
|
||||
TIMELINE_LIMIT_MONTHLY = "0";
|
||||
TIMELINE_LIMIT_YEARLY = "0";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -1,6 +1,11 @@
|
||||
{ lib, ... }:
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
libvirt = config.nomarchy.system.virtualization.libvirt.enable;
|
||||
in
|
||||
{
|
||||
# uwsm + Hyprland session — present on every Nomarchy install regardless
|
||||
# of the optional libvirt branch below.
|
||||
programs.uwsm = {
|
||||
enable = lib.mkDefault true;
|
||||
waylandCompositors.hyprland = {
|
||||
@@ -8,4 +13,14 @@
|
||||
prettyName = "Hyprland";
|
||||
};
|
||||
};
|
||||
|
||||
# Optional: libvirt + virt-manager + OVMF. Toggle with
|
||||
# `nomarchy.system.virtualization.libvirt.enable = true;`. The user must
|
||||
# be in the `libvirtd` group to drive virsh / virt-manager.
|
||||
virtualisation.libvirtd.enable = lib.mkIf libvirt true;
|
||||
environment.systemPackages = lib.mkIf libvirt (with pkgs; [
|
||||
virt-manager
|
||||
qemu
|
||||
OVMF
|
||||
]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user