feat(services): openrgb + restic toggles; close out the opt-in services item

Finishing the four remaining "opt-in services" candidates — but two of
them aren't service-shaped, so the package-vs-toggle principle applies:

  - Nextcloud client  → a bare tray app; already in the app-suite menu,
                        not a service.
  - WireGuard         → NetworkManager (on distro-wide) imports .conf
                        tunnels natively, so there's no daemon to "just
                        enable". Only the wg/wg-quick CLI is missing →
                        wireguard-tools added to the app-suite menu.

The two that ARE config-backed become nomarchy.services.* toggles:

  - openrgb → services.hardware.openrgb (daemon + device udev rules).
  - restic  → a small scaffolded surface (repository / passwordFile /
              paths) over services.restic.backups: daily timer, 7/4/6
              retention, --exclude-caches, repo auto-init. Asserts that
              repository + passwordFile are set when enabled. passwordFile
              is typed `str`, not `path`, so a flake-relative secret can't
              be copied into the world-readable store.

README option table and ROADMAP (services now "Seventeen shipped";
cloud-sync / networking / backup / devices areas updated) + the downstream
system.nix examples. Both branches eval-verified; the restic assertions
confirmed firing when repo/password are unset.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Bernardo Magri
2026-06-17 21:02:20 +01:00
parent c45fb80b79
commit 964a5c98b7
5 changed files with 95 additions and 10 deletions

View File

@@ -76,6 +76,47 @@ in
printing.enable = lib.mkEnableOption ''
CUPS printing with Avahi/mDNS, so network printers are auto-discovered
(add vendor drivers via `services.printing.drivers`)'';
openrgb.enable = lib.mkEnableOption ''
the OpenRGB daemon and GUI for controlling RGB lighting on peripherals
and (where supported) motherboards installs the udev rules for device
access. Some motherboard controllers also need `hardware.i2c.enable`'';
restic = {
enable = lib.mkEnableOption ''
a scheduled (daily) restic backup headless, complementing the Pika
GUI. Backs up `paths` with sane retention (7 daily / 4 weekly /
6 monthly); needs a `repository` and a `passwordFile` (below)'';
repository = lib.mkOption {
type = lib.types.str;
default = "";
example = "/mnt/backup/restic";
description = ''
The restic repository as a path or URL e.g. `/mnt/backup/restic`,
`sftp:user@host:/srv/restic`, `b2:bucket:/path`. Required when
`restic.enable` is set.
'';
};
passwordFile = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
example = "/etc/nomarchy/restic-password";
description = ''
Path to a file holding the repository password. Give an absolute
path that lives ON THE MACHINE not a `./file` in the flake, which
would copy the secret into the world-readable Nix store. Required
when `restic.enable` is set.
'';
};
paths = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ "/home" ];
description = "Paths to include in the backup.";
};
};
};
config = lib.mkMerge [
@@ -204,5 +245,34 @@ in
openFirewall = true;
};
})
(lib.mkIf cfg.openrgb.enable {
services.hardware.openrgb.enable = true;
})
(lib.mkIf cfg.restic.enable {
assertions = [
{
assertion = cfg.restic.repository != "";
message = "nomarchy.services.restic.repository must be set when restic is enabled.";
}
{
assertion = cfg.restic.passwordFile != null;
message = "nomarchy.services.restic.passwordFile must be set when restic is enabled.";
}
];
services.restic.backups.nomarchy = {
repository = cfg.restic.repository;
passwordFile = cfg.restic.passwordFile;
paths = cfg.restic.paths;
initialize = true; # create the repo on first run if it doesn't exist
extraBackupArgs = [ "--exclude-caches" ];
pruneOpts = [ "--keep-daily 7" "--keep-weekly 4" "--keep-monthly 6" ];
timerConfig = {
OnCalendar = "daily";
Persistent = true; # catch up a missed run after downtime
};
};
})
];
}