diff --git a/configurations/home-manager/eve/packages.nix b/configurations/home-manager/eve/packages.nix index ac24fa2..6b3c2e2 100644 --- a/configurations/home-manager/eve/packages.nix +++ b/configurations/home-manager/eve/packages.nix @@ -60,7 +60,7 @@ in { bitwarden.enable = true; discord.enable = true; makemkv.enable = true; - signal-desktop.enable = true; + signal-desktop-bin.enable = true; steam.enable = true; piper.enable = hardware.piperMouse.enable; krita.enable = true; diff --git a/configurations/home-manager/leyla/default.nix b/configurations/home-manager/leyla/default.nix index 20b04c7..8a37754 100644 --- a/configurations/home-manager/leyla/default.nix +++ b/configurations/home-manager/leyla/default.nix @@ -12,7 +12,7 @@ ]; config = { - impermanence.enable = osConfig.storage.impermanence.enable; + impermanence.enable = osConfig.host.impermanence.enable; # Home Manager needs a bit of information about you and the paths it should # manage. diff --git a/configurations/home-manager/leyla/impermanence.nix b/configurations/home-manager/leyla/impermanence.nix index 8fbff41..ce81c81 100644 --- a/configurations/home-manager/leyla/impermanence.nix +++ b/configurations/home-manager/leyla/impermanence.nix @@ -4,7 +4,7 @@ ... }: { config = lib.mkIf (config.impermanence.enable) { - home.persistence."${config.impermanence.persistencePath}" = { + home.persistence."/persist/home/leyla" = { directories = [ "desktop" "downloads" @@ -14,6 +14,7 @@ ".bash_history" # keep shell history around "${config.xdg.dataHome}/recently-used.xbel" # gnome recently viewed files ]; + allowOther = true; }; }; } diff --git a/configurations/home-manager/leyla/packages/default.nix b/configurations/home-manager/leyla/packages/default.nix index 5f64742..475269d 100644 --- a/configurations/home-manager/leyla/packages/default.nix +++ b/configurations/home-manager/leyla/packages/default.nix @@ -50,7 +50,7 @@ in { android-studio.enable = true; makemkv.enable = true; discord.enable = true; - signal-desktop.enable = true; + signal-desktop-bin.enable = true; calibre.enable = true; obsidian.enable = true; jetbrains.idea-oss.enable = true; diff --git a/configurations/nixos/defiant/configuration.nix b/configurations/nixos/defiant/configuration.nix index 40adbd5..19252a5 100644 --- a/configurations/nixos/defiant/configuration.nix +++ b/configurations/nixos/defiant/configuration.nix @@ -33,6 +33,44 @@ isPrincipleUser = true; }; }; + impermanence.enable = true; + storage = { + enable = true; + encryption = true; + notifications = { + enable = true; + host = "smtp.protonmail.ch"; + port = 587; + to = "leyla@jan-leila.com"; + user = "noreply@jan-leila.com"; + tokenFile = config.sops.secrets."services/zfs_smtp_token".path; + }; + pool = { + # We are having to boot off of the nvm cache drive because I cant figure out how to boot via the HBA + bootDrives = ["nvme-Samsung_SSD_990_PRO_4TB_S7KGNU0X907881F"]; + vdevs = [ + [ + "ata-ST18000NE000-3G6101_ZVTCXVEB" + "ata-ST18000NE000-3G6101_ZVTCXWSC" + "ata-ST18000NE000-3G6101_ZVTD10EH" + "ata-ST18000NT001-3NF101_ZVTE0S3Q" + "ata-ST18000NT001-3NF101_ZVTEF27J" + "ata-ST18000NE000-3G6101_ZVTJ7359" + ] + [ + "ata-ST4000NE001-2MA101_WS2275P3" + "ata-ST4000NE001-2MA101_WS227B9F" + "ata-ST4000NE001-2MA101_WS227CEW" + "ata-ST4000NE001-2MA101_WS227CYN" + "ata-ST4000NE001-2MA101_WS23TBWV" + "ata-ST4000NE001-2MA101_WS23TC5F" + ] + ]; + cache = [ + "nvme-Samsung_SSD_990_PRO_4TB_S7KGNU0X907881F" + ]; + }; + }; network_storage = { enable = true; directories = [ @@ -66,53 +104,6 @@ }; }; - storage = { - zfs = { - enable = true; - notifications = { - enable = true; - host = "smtp.protonmail.ch"; - port = 587; - to = "leyla@jan-leila.com"; - user = "noreply@jan-leila.com"; - tokenFile = config.sops.secrets."services/zfs_smtp_token".path; - }; - pool = { - encryption = { - enable = true; - }; - vdevs = [ - [ - "ata-ST18000NE000-3G6101_ZVTCXVEB" - "ata-ST18000NE000-3G6101_ZVTCXWSC" - "ata-ST18000NE000-3G6101_ZVTD10EH" - "ata-ST18000NT001-3NF101_ZVTE0S3Q" - "ata-ST18000NT001-3NF101_ZVTEF27J" - "ata-ST18000NE000-3G6101_ZVTJ7359" - ] - [ - "ata-ST4000NE001-2MA101_WS2275P3" - "ata-ST4000NE001-2MA101_WS227B9F" - "ata-ST4000NE001-2MA101_WS227CEW" - "ata-ST4000NE001-2MA101_WS227CYN" - "ata-ST4000NE001-2MA101_WS23TBWV" - "ata-ST4000NE001-2MA101_WS23TC5F" - ] - ]; - # We are having to boot off of the nvm cache drive because I cant figure out how to boot via the HBA - cache = [ - { - device = "nvme-Samsung_SSD_990_PRO_4TB_S7KGNU0X907881F"; - boot = true; - } - ]; - }; - }; - impermanence = { - enable = true; - }; - }; - systemd.network = { enable = true; @@ -227,7 +218,6 @@ postgresql = { enable = true; adminUsers = ["leyla"]; - impermanence.enable = false; }; # temp enable desktop environment for setup @@ -246,7 +236,6 @@ reverseProxy = { enable = true; openFirewall = true; - impermanence.enable = false; acme = { enable = true; email = "jan-leila@protonmail.com"; @@ -256,7 +245,6 @@ ollama = { enable = true; exposePort = true; - impermanence.enable = false; environmentVariables = { OLLAMA_KEEP_ALIVE = "24h"; @@ -291,7 +279,6 @@ enable = true; authKeyFile = config.sops.secrets."vpn-keys/tailscale-authkey/defiant".path; useRoutingFeatures = "server"; - impermanence.enable = false; extraUpFlags = [ "--advertise-exit-node" "--advertise-routes=192.168.0.0/24" @@ -304,33 +291,24 @@ ]; }; - syncthing = { - enable = true; - impermanence.enable = false; - }; + syncthing.enable = true; - fail2ban = { - enable = true; - impermanence.enable = false; - }; + fail2ban.enable = true; jellyfin = { enable = true; domain = "media.jan-leila.com"; extraDomains = ["jellyfin.jan-leila.com"]; - impermanence.enable = false; }; immich = { enable = true; domain = "photos.jan-leila.com"; - impermanence.enable = false; }; forgejo = { enable = true; reverseProxy.domain = "git.jan-leila.com"; - impermanence.enable = false; }; searx = { @@ -341,7 +319,6 @@ actual = { enable = false; domain = "budget.jan-leila.com"; - impermanence.enable = false; }; home-assistant = { @@ -349,7 +326,6 @@ domain = "home.jan-leila.com"; openFirewall = true; postgres.enable = true; - impermanence.enable = false; extensions = { sonos.enable = true; @@ -362,13 +338,11 @@ enable = true; domain = "documents.jan-leila.com"; passwordFile = config.sops.secrets."services/paperless_password".path; - impermanence.enable = false; }; panoramax = { enable = false; openFirewall = true; - impermanence.enable = false; }; crab-hole = { @@ -376,7 +350,6 @@ port = 8085; openFirewall = true; show_doc = true; - impermanence.enable = false; downstreams = { host = { enable = true; @@ -392,38 +365,31 @@ mediaDir = "/srv/qbittorent"; openFirewall = true; webuiPort = 8084; - impermanence.enable = false; }; sonarr = { enable = true; openFirewall = true; - impermanence.enable = false; }; radarr = { enable = true; openFirewall = true; - impermanence.enable = false; }; bazarr = { enable = true; openFirewall = true; - impermanence.enable = false; }; lidarr = { enable = true; openFirewall = true; - impermanence.enable = false; }; jackett = { enable = true; openFirewall = true; - impermanence.enable = false; }; flaresolverr = { enable = true; openFirewall = true; - impermanence.enable = false; }; }; diff --git a/configurations/nixos/defiant/default.nix b/configurations/nixos/defiant/default.nix index dd2383f..3013946 100644 --- a/configurations/nixos/defiant/default.nix +++ b/configurations/nixos/defiant/default.nix @@ -4,7 +4,5 @@ ./hardware-configuration.nix ./configuration.nix ./packages.nix - ./legacy-storage.nix - ./legacy-impermanence.nix ]; } diff --git a/configurations/nixos/defiant/legacy-impermanence.nix b/configurations/nixos/defiant/legacy-impermanence.nix deleted file mode 100644 index 4cfe18b..0000000 --- a/configurations/nixos/defiant/legacy-impermanence.nix +++ /dev/null @@ -1,296 +0,0 @@ -# Legacy impermanence module for defiant -# See legacy-storage.nix for the full incremental migration plan. -# -# This file is consumed in two phases: -# -# Phase 3 (after generateBase is enabled): -# Remove the SYSTEM-LEVEL entries marked [PHASE 3] below. These will be -# handled automatically by storage.nix, ssh.nix, and the impermanence module: -# - var-lib-private-permissions activation script -# - /etc/machine-id -# - SSH host keys -# - /var/lib/nixos -# - /var/lib/systemd/coredump -# - /persist/system/var/log persistence block -# -# Phase 4 (migrate services one at a time, any order): -# For each service: -# 1. Remove the service's section marked [PHASE 4] from this file -# 2. Remove `impermanence.enable = false` for that service in configuration.nix -# For jellyfin/qbittorrent, also remove the separate media persistence blocks. -# -# Phase 5: Delete this file once empty. -{ - config, - lib, - ... -}: { - config = lib.mkIf config.storage.impermanence.enable { - # [PHASE 3] Remove this activation script after enabling generateBase - system.activationScripts = { - "var-lib-private-permissions" = { - deps = ["specialfs"]; - text = '' - mkdir -p /persist/system/root/var/lib/private - chmod 0700 /persist/system/root/var/lib/private - ''; - }; - }; - - environment.persistence."/persist/system/root" = { - enable = true; - hideMounts = true; - # [PHASE 3] Remove this files block after enabling generateBase - files = lib.mkMerge [ - ["/etc/machine-id"] - # SSH host keys - (lib.mkIf config.services.openssh.enable ( - lib.lists.flatten ( - builtins.map (hostKey: [ - hostKey.path - "${hostKey.path}.pub" - ]) - config.services.openssh.hostKeys - ) - )) - ]; - directories = lib.mkMerge [ - # [PHASE 3] Remove these system directories after enabling generateBase - [ - "/var/lib/nixos" - "/var/lib/systemd/coredump" - ] - - # [PHASE 4] PostgreSQL - (lib.mkIf config.services.postgresql.enable [ - { - directory = "/var/lib/postgresql/16"; - user = "postgres"; - group = "postgres"; - } - ]) - - # [PHASE 4] Reverse Proxy (ACME) - (lib.mkIf config.services.reverseProxy.enable [ - { - directory = "/var/lib/acme"; - user = "acme"; - group = "acme"; - } - ]) - - # [PHASE 4] Ollama - (lib.mkIf config.services.ollama.enable [ - { - directory = "/var/lib/private/ollama"; - user = config.services.ollama.user; - group = config.services.ollama.group; - mode = "0700"; - } - ]) - - # [PHASE 4] Tailscale - (lib.mkIf config.services.tailscale.enable [ - { - directory = "/var/lib/tailscale"; - user = "root"; - group = "root"; - } - ]) - - # [PHASE 4] Syncthing - (lib.mkIf config.services.syncthing.enable [ - { - directory = "/mnt/sync"; - user = "syncthing"; - group = "syncthing"; - } - { - directory = "/etc/syncthing"; - user = "syncthing"; - group = "syncthing"; - } - ]) - - # [PHASE 4] Fail2ban - (lib.mkIf config.services.fail2ban.enable [ - { - directory = "/var/lib/fail2ban"; - user = "fail2ban"; - group = "fail2ban"; - } - ]) - - # [PHASE 4] Jellyfin (data/cache only - media is on separate dataset) - (lib.mkIf config.services.jellyfin.enable [ - { - directory = "/var/lib/jellyfin"; - user = "jellyfin"; - group = "jellyfin"; - } - { - directory = "/var/cache/jellyfin"; - user = "jellyfin"; - group = "jellyfin"; - } - ]) - - # [PHASE 4] Immich - (lib.mkIf config.services.immich.enable [ - { - directory = "/var/lib/immich"; - user = "immich"; - group = "immich"; - } - ]) - - # [PHASE 4] Forgejo - (lib.mkIf config.services.forgejo.enable [ - { - directory = "/var/lib/forgejo"; - user = "forgejo"; - group = "forgejo"; - } - ]) - - # [PHASE 4] Actual - (lib.mkIf config.services.actual.enable [ - { - directory = "/var/lib/private/actual"; - user = "actual"; - group = "actual"; - } - ]) - - # [PHASE 4] Home Assistant - (lib.mkIf config.services.home-assistant.enable [ - { - directory = "/var/lib/hass"; - user = "hass"; - group = "hass"; - } - ]) - - # [PHASE 4] Paperless - (lib.mkIf config.services.paperless.enable [ - { - directory = "/var/lib/paperless"; - user = "paperless"; - group = "paperless"; - } - ]) - - # [PHASE 4] Crab-hole - (lib.mkIf config.services.crab-hole.enable [ - { - directory = "/var/lib/private/crab-hole"; - user = "crab-hole"; - group = "crab-hole"; - } - ]) - - # [PHASE 4] qBittorrent (config only - media is on separate dataset) - (lib.mkIf config.services.qbittorrent.enable [ - { - directory = "/var/lib/qBittorrent/"; - user = "qbittorrent"; - group = "qbittorrent"; - } - ]) - - # [PHASE 4] Sonarr - (lib.mkIf config.services.sonarr.enable [ - { - directory = "/var/lib/sonarr/.config/NzbDrone"; - user = "sonarr"; - group = "sonarr"; - } - ]) - - # [PHASE 4] Radarr - (lib.mkIf config.services.radarr.enable [ - { - directory = "/var/lib/radarr/.config/Radarr"; - user = "radarr"; - group = "radarr"; - } - ]) - - # [PHASE 4] Bazarr - (lib.mkIf config.services.bazarr.enable [ - { - directory = "/var/lib/bazarr"; - user = "bazarr"; - group = "bazarr"; - } - ]) - - # [PHASE 4] Lidarr - (lib.mkIf config.services.lidarr.enable [ - { - directory = "/var/lib/lidarr/.config/Lidarr"; - user = "lidarr"; - group = "lidarr"; - } - ]) - - # [PHASE 4] Jackett - (lib.mkIf config.services.jackett.enable [ - { - directory = "/var/lib/jackett/.config/Jackett"; - user = "jackett"; - group = "jackett"; - } - ]) - - # [PHASE 4] FlareSolverr - (lib.mkIf config.services.flaresolverr.enable [ - { - directory = "/var/lib/flaresolverr"; - user = "flaresolverr"; - group = "flaresolverr"; - } - ]) - ]; - }; - - # [PHASE 4 - LAST] Jellyfin media on separate dataset - # Requires Phase 2 media dataset merge before migrating (several days of data copy) - environment.persistence."/persist/system/jellyfin" = lib.mkIf config.services.jellyfin.enable { - enable = true; - hideMounts = true; - directories = [ - { - directory = config.services.jellyfin.media_directory; - user = "jellyfin"; - group = "jellyfin_media"; - mode = "1770"; - } - ]; - }; - - # [PHASE 4 - LAST] qBittorrent media on separate dataset - # Requires Phase 2 media dataset merge before migrating (several days of data copy) - environment.persistence."/persist/system/qbittorrent" = lib.mkIf config.services.qbittorrent.enable { - enable = true; - hideMounts = true; - directories = [ - { - directory = config.services.qbittorrent.mediaDir; - user = "qbittorrent"; - group = "qbittorrent"; - mode = "1775"; - } - ]; - }; - - # [PHASE 3] /var/log persistence - handled by storage.nix after generateBase - environment.persistence."/persist/system/var/log" = { - enable = true; - hideMounts = true; - directories = [ - "/var/log" - ]; - }; - }; -} diff --git a/configurations/nixos/defiant/legacy-storage.nix b/configurations/nixos/defiant/legacy-storage.nix deleted file mode 100644 index 9ab79a6..0000000 --- a/configurations/nixos/defiant/legacy-storage.nix +++ /dev/null @@ -1,218 +0,0 @@ -# Legacy storage configuration for defiant -# This file manually defines ZFS datasets matching the existing on-disk layout -# to allow incremental migration to the new storage module (generateBase = true). -# -# ============================================================================ -# INCREMENTAL MIGRATION PLAN -# ============================================================================ -# -# Current disk usage (for reference): -# rpool/local/system/nix ~26G (renamed in place, no copy) -# rpool/local/system/sops ~328K (renamed in place, no copy) -# rpool/persist/system/jellyfin ~32T (renamed in place, no copy) -# rpool/persist/system/qbittorrent ~6.5T (copied into media dataset, ~6.5T temp) -# rpool free space ~30T -# -# Phase 1: Migrate base datasets on disk (boot from live USB or rescue) -# All operations in this phase are instant renames -- no data is copied. -# -# Unlock the pool: -# zfs load-key -a -# -# Step 1a: Move nix and sops out of local/ (they go to persist/local/) -# The -p flag auto-creates the parent datasets. -# -# zfs rename -p rpool/local/system/nix rpool/persist/local/nix -# zfs rename -p rpool/local/system/sops rpool/persist/local/system/sops -# -# Step 1b: Rename local/ -> ephemeral/ (takes remaining children with it) -# zfs rename rpool/local rpool/ephemeral -# # This moves: local/system/root -> ephemeral/system/root -# # local/home/leyla -> ephemeral/home/leyla -# -# Step 1c: Recreate blank snapshots on ephemeral datasets -# zfs destroy rpool/ephemeral/system/root@blank -# zfs snapshot rpool/ephemeral/system/root@blank -# zfs destroy rpool/ephemeral/home/leyla@blank -# zfs snapshot rpool/ephemeral/home/leyla@blank -# -# Step 1d: Move persist/ children under persist/replicate/ -# zfs create -o canmount=off rpool/persist/replicate -# zfs create -o canmount=off rpool/persist/replicate/system -# zfs rename rpool/persist/system/root rpool/persist/replicate/system/root -# zfs rename rpool/persist/system/var rpool/persist/replicate/system/var -# zfs rename rpool/persist/home/leyla rpool/persist/replicate/home -# # Clean up the now-empty home parent -# zfs destroy rpool/persist/home -# # NOTE: Do NOT destroy rpool/persist/system -- it still contains -# # persist/system/jellyfin and persist/system/qbittorrent which are -# # migrated in Phase 2. -# -# Verify the new layout: -# zfs list -r rpool -o name,used,mountpoint -# -# Phase 2: Merge media into a single dataset (do this last) -# Strategy: Rename the jellyfin dataset to become the shared media dataset -# (zero copy, instant), then copy qbittorrent data into it (~6.5T copy). -# This avoids duplicating the 32T jellyfin dataset. -# -# Step 2a: Rename jellyfin dataset to the shared media name -# zfs rename rpool/persist/system/jellyfin rpool/persist/replicate/system/media -# -# Step 2b: Copy qbittorrent data into the media dataset -# This copies ~6.5T and may take several hours/days depending on disk speed. -# The qbittorrent data is not critical to back up so no snapshot needed. -# -# systemctl stop qbittorrent -# rsync -avPHAX /persist/system/qbittorrent/ /persist/replicate/system/media/ -# -# Step 2c: Verify the data and clean up -# ls -la /persist/replicate/system/media/ -# zfs destroy rpool/persist/system/qbittorrent -# # persist/system should now be empty, clean it up: -# zfs destroy rpool/persist/system -# -# Phase 3: Enable generateBase -# In the nix config: -# - Delete this file (legacy-storage.nix) and remove its import from default.nix -# - Remove [PHASE 3] entries from legacy-impermanence.nix: -# - var-lib-private-permissions activation script -# - /etc/machine-id, SSH host keys (files block) -# - /var/lib/nixos, /var/lib/systemd/coredump (directories) -# - /persist/system/var/log persistence block -# These are now handled automatically by storage.nix and ssh.nix. -# Rebuild and verify: -# sudo nixos-rebuild switch --flake .#defiant -# # Verify mounts: findmnt -t fuse.bindfs,fuse -# # Verify persist: ls /persist/replicate/system/root/var/lib/nixos -# # Verify boot: reboot and confirm system comes up cleanly -# -# Phase 4: Migrate services (one at a time, any order) -# For each service (except jellyfin/qbittorrent): -# 1. Remove the service's [PHASE 4] section from legacy-impermanence.nix -# 2. Remove `impermanence.enable = false` for that service in configuration.nix -# 3. Rebuild: sudo nixos-rebuild switch --flake .#defiant -# 4. Verify: systemctl status , check the service's data is intact -# No data migration is needed -- the data already lives on the renamed -# dataset at the new path. -# -# Migrate jellyfin and qbittorrent LAST (after Phase 2 media merge): -# 1. Remove [PHASE 4 - LAST] jellyfin entries from legacy-impermanence.nix -# 2. Remove [PHASE 4 - LAST] qbittorrent entries from legacy-impermanence.nix -# 3. Remove `impermanence.enable = false` for both in configuration.nix -# 4. Rebuild: sudo nixos-rebuild switch --flake .#defiant -# 5. Verify: systemctl status jellyfin qbittorrent -# -# Phase 5: Cleanup -# Once all services are migrated and legacy-impermanence.nix is empty: -# - Delete legacy-impermanence.nix and remove its import from default.nix -# - Rebuild: sudo nixos-rebuild switch --flake .#defiant -# -# ============================================================================ -# -# Current on-disk dataset layout: -# rpool/local/ - ephemeral parent -# rpool/local/home/leyla - ephemeral user home (rolled back on boot) -# rpool/local/system/nix - nix store -# rpool/local/system/root - root filesystem (rolled back on boot) -# rpool/local/system/sops - sops age key -# rpool/persist/ - persistent parent -# rpool/persist/home/leyla - persistent user home -# rpool/persist/system/jellyfin - jellyfin media -# rpool/persist/system/qbittorrent - qbittorrent media -# rpool/persist/system/root - persistent root data -# rpool/persist/system/var/log - log persistence -{lib, ...}: { - # Disable automatic base dataset generation so we can define them manually - storage.generateBase = false; - - # Manually define ZFS datasets matching main's structure - storage.zfs.datasets = { - # Ephemeral datasets (local/) - "local" = { - type = "zfs_fs"; - mount = null; - }; - "local/home/leyla" = { - type = "zfs_fs"; - mount = "/home/leyla"; - snapshot = { - blankSnapshot = true; - }; - }; - "local/system/nix" = { - type = "zfs_fs"; - mount = "/nix"; - atime = "off"; - relatime = "off"; - snapshot = { - autoSnapshot = false; - }; - }; - "local/system/root" = { - type = "zfs_fs"; - mount = "/"; - snapshot = { - blankSnapshot = true; - }; - }; - "local/system/sops" = { - type = "zfs_fs"; - mount = "/var/lib/sops-nix"; - }; - - # Persistent datasets (persist/) - "persist" = { - type = "zfs_fs"; - mount = null; - }; - "persist/home/leyla" = { - type = "zfs_fs"; - mount = "/persist/home/leyla"; - snapshot = { - autoSnapshot = true; - }; - }; - "persist/system/jellyfin" = { - type = "zfs_fs"; - mount = "/persist/system/jellyfin"; - atime = "off"; - relatime = "off"; - }; - "persist/system/qbittorrent" = { - type = "zfs_fs"; - mount = "/persist/system/qbittorrent"; - atime = "off"; - relatime = "off"; - }; - "persist/system/root" = { - type = "zfs_fs"; - mount = "/persist/system/root"; - snapshot = { - autoSnapshot = true; - }; - }; - "persist/system/var/log" = { - type = "zfs_fs"; - mount = "/persist/system/var/log"; - }; - }; - - # Boot commands to rollback ephemeral root and user homes on boot - boot.initrd.postResumeCommands = lib.mkAfter '' - zfs rollback -r rpool/local/system/root@blank - zfs rollback -r rpool/local/home/leyla@blank - ''; - - # FileSystems needed for boot - fileSystems = { - "/".neededForBoot = true; - "/persist/system/root".neededForBoot = true; - "/persist/system/var/log".neededForBoot = true; - "/persist/system/jellyfin".neededForBoot = true; - "/persist/system/qbittorrent".neededForBoot = true; - "/var/lib/sops-nix".neededForBoot = true; - "/persist/home/leyla".neededForBoot = true; - "/home/leyla".neededForBoot = true; - }; -} diff --git a/configurations/nixos/emergent/configuration.nix b/configurations/nixos/emergent/configuration.nix index 35ef445..958b423 100644 --- a/configurations/nixos/emergent/configuration.nix +++ b/configurations/nixos/emergent/configuration.nix @@ -59,22 +59,12 @@ hardware = { piperMouse.enable = true; }; - }; - storage = { - zfs = { + storage = { enable = true; pool = { - mode = "stripe"; - vdevs = [ - [ - { - device = "wwn-0x5000039fd0cf05eb"; - boot = true; - } - ] - ]; - cache = []; + mode = ""; + drives = ["wwn-0x5000039fd0cf05eb"]; }; }; }; diff --git a/configurations/nixos/emergent/default.nix b/configurations/nixos/emergent/default.nix index 3acaeda..452334a 100644 --- a/configurations/nixos/emergent/default.nix +++ b/configurations/nixos/emergent/default.nix @@ -3,6 +3,5 @@ imports = [ ./configuration.nix ./hardware-configuration.nix - ./legacy-storage.nix ]; } diff --git a/configurations/nixos/emergent/legacy-storage.nix b/configurations/nixos/emergent/legacy-storage.nix deleted file mode 100644 index 2b24729..0000000 --- a/configurations/nixos/emergent/legacy-storage.nix +++ /dev/null @@ -1,51 +0,0 @@ -# Legacy storage configuration for emergent -# This file manually defines ZFS datasets matching the existing on-disk layout -# to allow incremental migration to the new storage module (generateBase = true). -# -# Current on-disk dataset layout: -# rpool/local/ - parent (canmount=off) -# rpool/local/system/nix - nix store -# rpool/local/system/root - root filesystem -# -# Migration plan: -# Phase 1: Rename datasets on disk (boot from live USB) -# zfs rename -p rpool/local/system/nix rpool/persist/local/nix -# zfs rename rpool/local rpool/persist/local -# # This moves: local/system/root -> persist/local/root (need to rename after) -# # Actually, since local/system/root needs to become persist/local/root: -# zfs rename rpool/persist/local/system/root rpool/persist/local/root -# zfs destroy rpool/persist/local/system # now empty -# # Recreate blank snapshot: -# zfs destroy rpool/persist/local/root@blank -# zfs snapshot rpool/persist/local/root@blank -# -# Phase 2: Delete this file, remove its import from default.nix, rebuild. -{...}: { - # Disable automatic base dataset generation so we can define them manually - storage.generateBase = false; - - # Manually define ZFS datasets matching the existing on-disk layout - storage.zfs.datasets = { - "local" = { - type = "zfs_fs"; - mount = null; - }; - "local/system/nix" = { - type = "zfs_fs"; - mount = "/nix"; - atime = "off"; - relatime = "off"; - snapshot = { - autoSnapshot = false; - }; - }; - "local/system/root" = { - type = "zfs_fs"; - mount = "/"; - snapshot = { - blankSnapshot = true; - autoSnapshot = true; - }; - }; - }; -} diff --git a/flake.lock b/flake.lock index 14c8561..1403bb4 100644 --- a/flake.lock +++ b/flake.lock @@ -7,11 +7,11 @@ ] }, "locked": { - "lastModified": 1772867152, - "narHash": "sha256-RIFgZ4O6Eg+5ysZ8Tqb3YvcqiRaNy440GEY22ltjRrs=", + "lastModified": 1771881364, + "narHash": "sha256-A5uE/hMium5of/QGC6JwF5TGoDAfpNtW00T0s9u/PN8=", "owner": "nix-community", "repo": "disko", - "rev": "eaafb89b56e948661d618eefd4757d9ea8d77514", + "rev": "a4cb7bf73f264d40560ba527f9280469f1f081c6", "type": "github" }, "original": { @@ -28,11 +28,11 @@ }, "locked": { "dir": "pkgs/firefox-addons", - "lastModified": 1772856163, - "narHash": "sha256-xD+d1+FVhKJ+oFYMTWOdVSBoXS4yeMyVZyDjMXqWEJE=", + "lastModified": 1771888219, + "narHash": "sha256-XlA/l99y1Qilmd8ttYJ9y5BSse9GKoQlt9hnY8H+EHM=", "owner": "rycee", "repo": "nur-expressions", - "rev": "d358a550c7beac5f04fbc5a786e14af079606689", + "rev": "a347c1da78da64eeb78a0c9005bdaadace33e83c", "type": "gitlab" }, "original": { @@ -115,11 +115,32 @@ ] }, "locked": { - "lastModified": 1772845525, - "narHash": "sha256-Dp5Ir2u4jJDGCgeMRviHvEQDe+U37hMxp6RSNOoMMPc=", + "lastModified": 1771851181, + "narHash": "sha256-gFgE6mGUftwseV3DUENMb0k0EiHd739lZexPo5O/sdQ=", "owner": "nix-community", "repo": "home-manager", - "rev": "27b93804fbef1544cb07718d3f0a451f4c4cd6c0", + "rev": "9a4b494b1aa1b93d8edf167f46dc8e0c0011280c", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "home-manager", + "type": "github" + } + }, + "home-manager_2": { + "inputs": { + "nixpkgs": [ + "impermanence", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1768598210, + "narHash": "sha256-kkgA32s/f4jaa4UG+2f8C225Qvclxnqs76mf8zvTVPg=", + "owner": "nix-community", + "repo": "home-manager", + "rev": "c47b2cc64a629f8e075de52e4742de688f930dc6", "type": "github" }, "original": { @@ -129,20 +150,12 @@ } }, "impermanence": { - "inputs": { - "home-manager": [ - "home-manager" - ], - "nixpkgs": [ - "nixpkgs" - ] - }, "locked": { - "lastModified": 1769548169, - "narHash": "sha256-03+JxvzmfwRu+5JafM0DLbxgHttOQZkUtDWBmeUkN8Y=", + "lastModified": 1737831083, + "narHash": "sha256-LJggUHbpyeDvNagTUrdhe/pRVp4pnS6wVKALS782gRI=", "owner": "nix-community", "repo": "impermanence", - "rev": "7b1d382faf603b6d264f58627330f9faa5cba149", + "rev": "4b3e914cdf97a5b536a889e939fb2fd2b043a170", "type": "github" }, "original": { @@ -191,14 +204,14 @@ "mcp-nixos": { "inputs": { "flake-parts": "flake-parts", - "nixpkgs": "nixpkgs" + "nixpkgs": "nixpkgs_2" }, "locked": { - "lastModified": 1772769318, - "narHash": "sha256-RAyOW5JMXRhiREqxFPOzw80fVsYVBnOPFgBSjnJ6gbY=", + "lastModified": 1769804089, + "narHash": "sha256-Wkot1j0cTx64xxjmLXzPubTckaZBSUJFhESEdOzPYas=", "owner": "utensils", "repo": "mcp-nixos", - "rev": "60c1efbba0de1268b42f1144c904e6c8a9627dde", + "rev": "37a691ea4ea9c8bdcccfe174c6127847b8213fd3", "type": "github" }, "original": { @@ -214,11 +227,11 @@ ] }, "locked": { - "lastModified": 1772379624, - "narHash": "sha256-NG9LLTWlz4YiaTAiRGChbrzbVxBfX+Auq4Ab/SWmk4A=", + "lastModified": 1771520882, + "narHash": "sha256-9SeTZ4Pwr730YfT7V8Azb8GFbwk1ZwiQDAwft3qAD+o=", "owner": "LnL7", "repo": "nix-darwin", - "rev": "52d061516108769656a8bd9c6e811c677ec5b462", + "rev": "6a7fdcd5839ec8b135821179eea3b58092171bcf", "type": "github" }, "original": { @@ -255,11 +268,11 @@ ] }, "locked": { - "lastModified": 1772850876, - "narHash": "sha256-Ga19zlfMpakCY4GMwBSOljNLOF0nEYrYBXv0hP/d4rw=", + "lastModified": 1771901087, + "narHash": "sha256-b5eSke+C8UeR5Er+TZOzHCDStBJ68yyFlqAUc6fNBX0=", "owner": "nix-community", "repo": "nix-vscode-extensions", - "rev": "22f084d4c280dfc8a9d764f7b85af38e5d69c3dc", + "rev": "c22e7adea9adec98b3dc79be954ee17d56a232bd", "type": "github" }, "original": { @@ -270,11 +283,11 @@ }, "nixos-hardware": { "locked": { - "lastModified": 1771969195, - "narHash": "sha256-qwcDBtrRvJbrrnv1lf/pREQi8t2hWZxVAyeMo7/E9sw=", + "lastModified": 1771423359, + "narHash": "sha256-yRKJ7gpVmXbX2ZcA8nFi6CMPkJXZGjie2unsiMzj3Ig=", "owner": "NixOS", "repo": "nixos-hardware", - "rev": "41c6b421bdc301b2624486e11905c9af7b8ec68e", + "rev": "740a22363033e9f1bb6270fbfb5a9574067af15b", "type": "github" }, "original": { @@ -286,15 +299,15 @@ }, "nixpkgs": { "locked": { - "lastModified": 1767640445, - "narHash": "sha256-UWYqmD7JFBEDBHWYcqE6s6c77pWdcU/i+bwD6XxMb8A=", - "owner": "NixOS", + "lastModified": 1768564909, + "narHash": "sha256-Kell/SpJYVkHWMvnhqJz/8DqQg2b6PguxVWOuadbHCc=", + "owner": "nixos", "repo": "nixpkgs", - "rev": "9f0c42f8bc7151b8e7e5840fb3bd454ad850d8c5", + "rev": "e4bae1bd10c9c57b2cf517953ab70060a828ee6f", "type": "github" }, "original": { - "owner": "NixOS", + "owner": "nixos", "ref": "nixos-unstable", "repo": "nixpkgs", "type": "github" @@ -317,11 +330,27 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1772773019, - "narHash": "sha256-E1bxHxNKfDoQUuvriG71+f+s/NT0qWkImXsYZNFFfCs=", + "lastModified": 1767640445, + "narHash": "sha256-UWYqmD7JFBEDBHWYcqE6s6c77pWdcU/i+bwD6XxMb8A=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "9f0c42f8bc7151b8e7e5840fb3bd454ad850d8c5", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_3": { + "locked": { + "lastModified": 1771369470, + "narHash": "sha256-0NBlEBKkN3lufyvFegY4TYv5mCNHbi5OmBDrzihbBMQ=", "owner": "nixos", "repo": "nixpkgs", - "rev": "aca4d95fce4914b3892661bcb80b8087293536c6", + "rev": "0182a361324364ae3f436a63005877674cf45efb", "type": "github" }, "original": { @@ -331,7 +360,7 @@ "type": "github" } }, - "nixpkgs_3": { + "nixpkgs_4": { "locked": { "lastModified": 1759070547, "narHash": "sha256-JVZl8NaVRYb0+381nl7LvPE+A774/dRpif01FKLrYFQ=", @@ -349,7 +378,7 @@ }, "noita-entangled-worlds": { "inputs": { - "nixpkgs": "nixpkgs_3", + "nixpkgs": "nixpkgs_4", "rust-overlay": "rust-overlay", "systems": "systems_2" }, @@ -381,7 +410,7 @@ "nix-syncthing": "nix-syncthing", "nix-vscode-extensions": "nix-vscode-extensions", "nixos-hardware": "nixos-hardware", - "nixpkgs": "nixpkgs_2", + "nixpkgs": "nixpkgs_3", "noita-entangled-worlds": "noita-entangled-worlds", "secrets": "secrets", "sops-nix": "sops-nix" @@ -431,11 +460,11 @@ ] }, "locked": { - "lastModified": 1772495394, - "narHash": "sha256-hmIvE/slLKEFKNEJz27IZ8BKlAaZDcjIHmkZ7GCEjfw=", + "lastModified": 1771889317, + "narHash": "sha256-YV17Q5lEU0S9ppw08Y+cs4eEQJBuc79AzblFoHORLMU=", "owner": "Mic92", "repo": "sops-nix", - "rev": "1d9b98a29a45abe9c4d3174bd36de9f28755e3ff", + "rev": "b027513c32e5b39b59f64626b87fbe168ae02094", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index df5f6e9..ae110da 100644 --- a/flake.nix +++ b/flake.nix @@ -37,8 +37,6 @@ # delete your darlings impermanence = { url = "github:nix-community/impermanence"; - inputs.nixpkgs.follows = "nixpkgs"; - inputs.home-manager.follows = "home-manager"; }; nix-darwin = { diff --git a/modules/home-manager-modules/impermanence.nix b/modules/home-manager-modules/impermanence.nix index e8b3ec4..6c75edd 100644 --- a/modules/home-manager-modules/impermanence.nix +++ b/modules/home-manager-modules/impermanence.nix @@ -12,29 +12,21 @@ in { type = lib.types.bool; default = true; }; - persistencePath = lib.mkOption { - type = lib.types.str; - default = - if osConfig.storage.generateBase - then "/persist/replicate/home" - else "/persist"; - description = "The base path for user home persistence. The impermanence module will automatically append the user's home directory path. Automatically adapts based on whether the system uses the new dataset layout or the legacy one."; - }; }; config = lib.mkMerge [ (lib.mkIf config.impermanence.enable { assertions = [ { - assertion = osConfig.storage.impermanence.enable; + assertion = osConfig.host.impermanence.enable; message = "impermanence can not be enabled for a user when it is not enabled for the system"; } ]; }) # If impermanence is not enabled for this user but system impermanence is enabled, # persist the entire home directory as fallback - (lib.mkIf (osConfig.storage.impermanence.enable && !cfg.enable && cfg.fallbackPersistence.enable) { - home.persistence."${cfg.persistencePath}" = { + (lib.mkIf (osConfig.host.impermanence.enable && !cfg.enable && cfg.fallbackPersistence.enable) { + home.persistence."/persist/home/${config.home.username}" = { directories = ["."]; allowOther = true; }; diff --git a/modules/home-manager-modules/openssh.nix b/modules/home-manager-modules/openssh.nix index 2f44957..5f07491 100644 --- a/modules/home-manager-modules/openssh.nix +++ b/modules/home-manager-modules/openssh.nix @@ -96,7 +96,7 @@ } ) (lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { + home.persistence."/persist${config.home.homeDirectory}" = { files = lib.lists.flatten ( builtins.map (hostKey: [".ssh/${hostKey.path}" ".ssh/${hostKey.path}.pub"]) config.programs.openssh.hostKeys ); diff --git a/modules/home-manager-modules/programs/android-studio.nix b/modules/home-manager-modules/programs/android-studio.nix index 8d1e28c..0f76276 100644 --- a/modules/home-manager-modules/programs/android-studio.nix +++ b/modules/home-manager-modules/programs/android-studio.nix @@ -14,17 +14,22 @@ android-studio ]; } - ( - lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { - directories = [ - "${config.xdg.configHome}/Google/AndroidStudio" - ".android" - ".gradle" - "${config.xdg.cacheHome}/Google/AndroidStudio" - ]; - }; - } - ) + # TODO: create this + # ( + # lib.mkIf config.impermanence.enable { + # home.persistence."/persist${config.home.homeDirectory}" = { + # directories = [ + # # configuration + # "${config.xdg.configHome}/Google/AndroidStudio" + # # Android SDK + # ".android" + # # Gradle cache + # ".gradle" + # # Android Studio projects cache + # "${config.xdg.cacheHome}/Google/AndroidStudio" + # ]; + # }; + # } + # ) ]); } diff --git a/modules/home-manager-modules/programs/anki.nix b/modules/home-manager-modules/programs/anki.nix index dcabce8..c2f93ea 100644 --- a/modules/home-manager-modules/programs/anki.nix +++ b/modules/home-manager-modules/programs/anki.nix @@ -1,13 +1,15 @@ { lib, config, + osConfig, ... }: { - config = lib.mkIf (config.programs.anki.enable && config.impermanence.enable) { - home.persistence."${config.impermanence.persistencePath}" = { + config = lib.mkIf (config.programs.anki.enable && osConfig.host.impermanence.enable) { + home.persistence."/persist${config.home.homeDirectory}" = { directories = [ - ".local/share/Anki2" + "${config.xdg.dataHome}/Anki2/" ]; + allowOther = true; }; }; } diff --git a/modules/home-manager-modules/programs/bitwarden.nix b/modules/home-manager-modules/programs/bitwarden.nix index bbd2086..e305b6c 100644 --- a/modules/home-manager-modules/programs/bitwarden.nix +++ b/modules/home-manager-modules/programs/bitwarden.nix @@ -16,10 +16,11 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { + home.persistence."/persist${config.home.homeDirectory}" = { directories = [ "${config.xdg.configHome}/Bitwarden" ]; + allowOther = true; }; } ) diff --git a/modules/home-manager-modules/programs/bruno.nix b/modules/home-manager-modules/programs/bruno.nix index 7bc64b6..8ad5e63 100644 --- a/modules/home-manager-modules/programs/bruno.nix +++ b/modules/home-manager-modules/programs/bruno.nix @@ -16,10 +16,11 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { + home.persistence."/persist${config.home.homeDirectory}" = { directories = [ "${config.xdg.configHome}/bruno/" ]; + allowOther = true; }; } ) diff --git a/modules/home-manager-modules/programs/calibre.nix b/modules/home-manager-modules/programs/calibre.nix index 7174b43..608c75d 100644 --- a/modules/home-manager-modules/programs/calibre.nix +++ b/modules/home-manager-modules/programs/calibre.nix @@ -12,10 +12,11 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { + home.persistence."/persist${config.home.homeDirectory}" = { directories = [ "${config.xdg.configHome}/calibre" ]; + allowOther = true; }; } ) diff --git a/modules/home-manager-modules/programs/davinci-resolve.nix b/modules/home-manager-modules/programs/davinci-resolve.nix index 5956578..6c4526f 100644 --- a/modules/home-manager-modules/programs/davinci-resolve.nix +++ b/modules/home-manager-modules/programs/davinci-resolve.nix @@ -16,11 +16,12 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { + home.persistence."/persist${config.home.homeDirectory}" = { directories = [ "${config.xdg.dataHome}/DaVinciResolve" "${config.xdg.configHome}/blackmagic" ]; + allowOther = true; }; } ) diff --git a/modules/home-manager-modules/programs/dbeaver.nix b/modules/home-manager-modules/programs/dbeaver.nix index 1595a02..8b6c41a 100644 --- a/modules/home-manager-modules/programs/dbeaver.nix +++ b/modules/home-manager-modules/programs/dbeaver.nix @@ -16,10 +16,11 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { + home.persistence."/persist${config.home.homeDirectory}" = { directories = [ "${config.xdg.dataHome}/DBeaverData/" ]; + allowOther = true; }; } ) diff --git a/modules/home-manager-modules/programs/discord.nix b/modules/home-manager-modules/programs/discord.nix index e42367b..71b09b2 100644 --- a/modules/home-manager-modules/programs/discord.nix +++ b/modules/home-manager-modules/programs/discord.nix @@ -6,10 +6,11 @@ config = lib.mkIf config.programs.discord.enable (lib.mkMerge [ ( lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { + home.persistence."/persist${config.home.homeDirectory}" = { directories = [ "${config.xdg.configHome}/discord/" ]; + allowOther = true; }; } ) diff --git a/modules/home-manager-modules/programs/firefox.nix b/modules/home-manager-modules/programs/firefox.nix index 2756e31..8841887 100644 --- a/modules/home-manager-modules/programs/firefox.nix +++ b/modules/home-manager-modules/programs/firefox.nix @@ -22,10 +22,11 @@ # Extension configuration ".mozilla/firefox/${profile}/extension-settings.json" ]; + allowOther = true; }; in { config = lib.mkIf (config.programs.firefox.enable && config.impermanence.enable) { - home.persistence."${config.impermanence.persistencePath}" = lib.mkMerge ( + home.persistence."/persist${config.home.homeDirectory}" = lib.mkMerge ( ( lib.attrsets.mapAttrsToList (profile: _: buildProfilePersistence profile) diff --git a/modules/home-manager-modules/programs/freecad.nix b/modules/home-manager-modules/programs/freecad.nix index 50600db..89668de 100644 --- a/modules/home-manager-modules/programs/freecad.nix +++ b/modules/home-manager-modules/programs/freecad.nix @@ -16,10 +16,11 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { + home.persistence."/persist${config.home.homeDirectory}" = { directories = [ "${config.xdg.configHome}/FreeCAD" ]; + allowOther = true; }; } ) diff --git a/modules/home-manager-modules/programs/gimp.nix b/modules/home-manager-modules/programs/gimp.nix index 95c87e6..925a2d9 100644 --- a/modules/home-manager-modules/programs/gimp.nix +++ b/modules/home-manager-modules/programs/gimp.nix @@ -16,10 +16,11 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { + home.persistence."/persist${config.home.homeDirectory}" = { directories = [ "${config.xdg.configHome}/GIMP" ]; + allowOther = true; }; } ) diff --git a/modules/home-manager-modules/programs/idea.nix b/modules/home-manager-modules/programs/idea.nix index a1aebda..b193416 100644 --- a/modules/home-manager-modules/programs/idea.nix +++ b/modules/home-manager-modules/programs/idea.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { + home.persistence."/persist${config.home.homeDirectory}" = { directories = [ # configuration "${config.xdg.configHome}/JetBrains/" diff --git a/modules/home-manager-modules/programs/inkscape.nix b/modules/home-manager-modules/programs/inkscape.nix index 28eb334..a26ddec 100644 --- a/modules/home-manager-modules/programs/inkscape.nix +++ b/modules/home-manager-modules/programs/inkscape.nix @@ -16,10 +16,11 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { + home.persistence."/persist${config.home.homeDirectory}" = { directories = [ "${config.xdg.configHome}/inkscape" ]; + allowOther = true; }; } ) diff --git a/modules/home-manager-modules/programs/kdenlive.nix b/modules/home-manager-modules/programs/kdenlive.nix index 2c4bac8..05327d1 100644 --- a/modules/home-manager-modules/programs/kdenlive.nix +++ b/modules/home-manager-modules/programs/kdenlive.nix @@ -23,11 +23,12 @@ in { } ( lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { + home.persistence."/persist${config.home.homeDirectory}" = { directories = [ "${config.xdg.configHome}/kdenliverc" "${config.xdg.dataHome}/kdenlive" ]; + allowOther = true; }; } ) diff --git a/modules/home-manager-modules/programs/krita.nix b/modules/home-manager-modules/programs/krita.nix index dd7bb12..3ba5560 100644 --- a/modules/home-manager-modules/programs/krita.nix +++ b/modules/home-manager-modules/programs/krita.nix @@ -16,11 +16,12 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { + home.persistence."/persist${config.home.homeDirectory}" = { directories = [ "${config.xdg.configHome}/kritarc" "${config.xdg.dataHome}/krita" ]; + allowOther = true; }; } ) diff --git a/modules/home-manager-modules/programs/libreoffice.nix b/modules/home-manager-modules/programs/libreoffice.nix index 283c8db..93163e7 100644 --- a/modules/home-manager-modules/programs/libreoffice.nix +++ b/modules/home-manager-modules/programs/libreoffice.nix @@ -16,10 +16,11 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { + home.persistence."/persist${config.home.homeDirectory}" = { directories = [ "${config.xdg.configHome}/libreoffice" ]; + allowOther = true; }; } ) diff --git a/modules/home-manager-modules/programs/makemkv.nix b/modules/home-manager-modules/programs/makemkv.nix index f748f68..e92c3d3 100644 --- a/modules/home-manager-modules/programs/makemkv.nix +++ b/modules/home-manager-modules/programs/makemkv.nix @@ -30,7 +30,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { + home.persistence."/persist${config.home.homeDirectory}" = { directories = [ ".MakeMKV" ]; diff --git a/modules/home-manager-modules/programs/mapillary-uploader.nix b/modules/home-manager-modules/programs/mapillary-uploader.nix index 0d9ad5f..df1f093 100644 --- a/modules/home-manager-modules/programs/mapillary-uploader.nix +++ b/modules/home-manager-modules/programs/mapillary-uploader.nix @@ -17,11 +17,12 @@ in { } ( mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { + home.persistence."/persist${config.home.homeDirectory}" = { directories = [ "${config.xdg.configHome}/mapillary-uploader" "${config.xdg.dataHome}/mapillary-uploader" ]; + allowOther = true; }; } ) diff --git a/modules/home-manager-modules/programs/obs.nix b/modules/home-manager-modules/programs/obs.nix index 0a4caf7..bfdba90 100644 --- a/modules/home-manager-modules/programs/obs.nix +++ b/modules/home-manager-modules/programs/obs.nix @@ -6,10 +6,11 @@ config = lib.mkIf config.programs.obs-studio.enable (lib.mkMerge [ ( lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { + home.persistence."/persist${config.home.homeDirectory}" = { directories = [ "${config.xdg.configHome}/obs-studio" ]; + allowOther = true; }; } ) diff --git a/modules/home-manager-modules/programs/obsidian.nix b/modules/home-manager-modules/programs/obsidian.nix index 6676ecd..824563d 100644 --- a/modules/home-manager-modules/programs/obsidian.nix +++ b/modules/home-manager-modules/programs/obsidian.nix @@ -6,7 +6,7 @@ config = lib.mkIf config.programs.obsidian.enable (lib.mkMerge [ ( lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { + home.persistence."/persist${config.home.homeDirectory}" = { directories = [ "${config.xdg.configHome}/obsidian" ]; diff --git a/modules/home-manager-modules/programs/olympus.nix b/modules/home-manager-modules/programs/olympus.nix index 2d5adb6..0e38eec 100644 --- a/modules/home-manager-modules/programs/olympus.nix +++ b/modules/home-manager-modules/programs/olympus.nix @@ -23,11 +23,12 @@ in { } ( lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { + home.persistence."/persist${config.home.homeDirectory}" = { directories = [ "${config.xdg.configHome}/olympus" "${config.xdg.dataHome}/olympus" ]; + allowOther = true; }; } ) diff --git a/modules/home-manager-modules/programs/openrgb.nix b/modules/home-manager-modules/programs/openrgb.nix index c350b1e..c9d5e14 100644 --- a/modules/home-manager-modules/programs/openrgb.nix +++ b/modules/home-manager-modules/programs/openrgb.nix @@ -16,10 +16,11 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { + home.persistence."/persist${config.home.homeDirectory}" = { directories = [ "${config.xdg.configHome}/OpenRGB" ]; + allowOther = true; }; } ) diff --git a/modules/home-manager-modules/programs/picard.nix b/modules/home-manager-modules/programs/picard.nix index ffc4289..bc37b86 100644 --- a/modules/home-manager-modules/programs/picard.nix +++ b/modules/home-manager-modules/programs/picard.nix @@ -16,10 +16,11 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { + home.persistence."/persist${config.home.homeDirectory}" = { directories = [ "${config.xdg.configHome}/MusicBrainz" ]; + allowOther = true; }; } ) diff --git a/modules/home-manager-modules/programs/prostudiomasters.nix b/modules/home-manager-modules/programs/prostudiomasters.nix index d61b7e5..5345169 100644 --- a/modules/home-manager-modules/programs/prostudiomasters.nix +++ b/modules/home-manager-modules/programs/prostudiomasters.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { + home.persistence."/persist${config.home.homeDirectory}" = { directories = [ "${config.xdg.configHome}/ProStudioMasters" ]; diff --git a/modules/home-manager-modules/programs/protonvpn.nix b/modules/home-manager-modules/programs/protonvpn.nix index 5742948..513a610 100644 --- a/modules/home-manager-modules/programs/protonvpn.nix +++ b/modules/home-manager-modules/programs/protonvpn.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { + home.persistence."/persist${config.home.homeDirectory}" = { directories = [ "${config.xdg.configHome}/protonvpn" "${config.xdg.configHome}/Proton" diff --git a/modules/home-manager-modules/programs/qbittorrent.nix b/modules/home-manager-modules/programs/qbittorrent.nix index b2e0f50..61d13c0 100644 --- a/modules/home-manager-modules/programs/qbittorrent.nix +++ b/modules/home-manager-modules/programs/qbittorrent.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { + home.persistence."/persist${config.home.homeDirectory}" = { directories = [ "${config.xdg.configHome}/qBittorrent" ]; diff --git a/modules/home-manager-modules/programs/qflipper.nix b/modules/home-manager-modules/programs/qflipper.nix index bb141a4..8b42766 100644 --- a/modules/home-manager-modules/programs/qflipper.nix +++ b/modules/home-manager-modules/programs/qflipper.nix @@ -16,10 +16,11 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { + home.persistence."/persist${config.home.homeDirectory}" = { directories = [ "${config.xdg.configHome}/qFlipper" ]; + allowOther = true; }; } ) diff --git a/modules/home-manager-modules/programs/signal.nix b/modules/home-manager-modules/programs/signal.nix index a50a49e..7db23a7 100644 --- a/modules/home-manager-modules/programs/signal.nix +++ b/modules/home-manager-modules/programs/signal.nix @@ -4,19 +4,19 @@ config, ... }: { - options.programs.signal-desktop = { + options.programs.signal-desktop-bin = { enable = lib.mkEnableOption "enable signal"; }; - config = lib.mkIf config.programs.signal-desktop.enable (lib.mkMerge [ + config = lib.mkIf config.programs.signal-desktop-bin.enable (lib.mkMerge [ { home.packages = with pkgs; [ - signal-desktop + signal-desktop-bin ]; } ( lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { + home.persistence."/persist${config.home.homeDirectory}" = { directories = [ "${config.xdg.configHome}/Signal" ]; diff --git a/modules/home-manager-modules/programs/steam.nix b/modules/home-manager-modules/programs/steam.nix index 4e0644e..fd98cb6 100644 --- a/modules/home-manager-modules/programs/steam.nix +++ b/modules/home-manager-modules/programs/steam.nix @@ -18,13 +18,14 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { + home.persistence."/persist${config.home.homeDirectory}" = { directories = [ { directory = "${config.xdg.dataHome}/Steam"; method = "symlink"; } ]; + allowOther = true; }; } ) diff --git a/modules/home-manager-modules/programs/tor-browser.nix b/modules/home-manager-modules/programs/tor-browser.nix index c108805..c3b085d 100644 --- a/modules/home-manager-modules/programs/tor-browser.nix +++ b/modules/home-manager-modules/programs/tor-browser.nix @@ -16,10 +16,11 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { + home.persistence."/persist${config.home.homeDirectory}" = { directories = [ "${config.xdg.dataHome}/torbrowser" ]; + allowOther = true; }; } ) diff --git a/modules/home-manager-modules/programs/ungoogled-chromium.nix b/modules/home-manager-modules/programs/ungoogled-chromium.nix index 32f4b40..ef6a881 100644 --- a/modules/home-manager-modules/programs/ungoogled-chromium.nix +++ b/modules/home-manager-modules/programs/ungoogled-chromium.nix @@ -16,10 +16,11 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { + home.persistence."/persist${config.home.homeDirectory}" = { directories = [ "${config.xdg.configHome}/chromium" ]; + allowOther = true; }; } ) diff --git a/modules/home-manager-modules/programs/via.nix b/modules/home-manager-modules/programs/via.nix index ad6f45a..0aa58e4 100644 --- a/modules/home-manager-modules/programs/via.nix +++ b/modules/home-manager-modules/programs/via.nix @@ -16,11 +16,12 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { + home.persistence."/persist${config.home.homeDirectory}" = { directories = [ "${config.xdg.configHome}/via" "${config.xdg.dataHome}/via" ]; + allowOther = true; }; } ) diff --git a/modules/home-manager-modules/programs/vmware-workstation.nix b/modules/home-manager-modules/programs/vmware-workstation.nix index 76f260b..8e9d406 100644 --- a/modules/home-manager-modules/programs/vmware-workstation.nix +++ b/modules/home-manager-modules/programs/vmware-workstation.nix @@ -17,7 +17,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { + home.persistence."/persist${config.home.homeDirectory}" = { directories = [ { directory = ".vmware"; @@ -28,6 +28,7 @@ method = "symlink"; } ]; + allowOther = true; }; } ) diff --git a/modules/nixos-modules/default.nix b/modules/nixos-modules/default.nix index 34e041e..2ba1a58 100644 --- a/modules/nixos-modules/default.nix +++ b/modules/nixos-modules/default.nix @@ -8,13 +8,14 @@ ./desktop.nix ./ssh.nix ./i18n.nix - ./sync - ./ollama + ./sync.nix + ./impermanence.nix + ./disko.nix + ./ollama.nix ./ai.nix - ./tailscale + ./tailscale.nix ./steam.nix ./server - ./storage ]; nixpkgs.config.permittedInsecurePackages = [ diff --git a/modules/nixos-modules/disko.nix b/modules/nixos-modules/disko.nix new file mode 100644 index 0000000..a962689 --- /dev/null +++ b/modules/nixos-modules/disko.nix @@ -0,0 +1,267 @@ +{ + lib, + pkgs, + config, + inputs, + ... +}: let + # there currently is a bug with disko that causes long disk names to be generated improperly this hash function should alleviate it when used for disk names instead of what we are defaulting to + # max gpt length is 36 and disk adds formats it like disk-xxxx-zfs which means we need to be 9 characters under that + hashDisk = drive: (builtins.substring 0 27 (builtins.hashString "sha256" drive)); + + vdevs = + builtins.map ( + disks: + builtins.map (disk: lib.attrsets.nameValuePair (hashDisk disk) disk) disks + ) + config.host.storage.pool.vdevs; + cache = + builtins.map ( + disk: lib.attrsets.nameValuePair (hashDisk disk) disk + ) + config.host.storage.pool.cache; + + datasets = config.host.storage.pool.datasets // config.host.storage.pool.extraDatasets; +in { + options.host.storage = { + enable = lib.mkEnableOption "are we going create zfs disks with disko on this device"; + encryption = lib.mkEnableOption "is the vdev going to be encrypted"; + notifications = { + enable = lib.mkEnableOption "are notifications enabled"; + host = lib.mkOption { + type = lib.types.str; + description = "what is the host that we are going to send the email to"; + }; + port = lib.mkOption { + type = lib.types.port; + description = "what port is the host using to receive mail on"; + }; + to = lib.mkOption { + type = lib.types.str; + description = "what account is the email going to be sent to"; + }; + user = lib.mkOption { + type = lib.types.str; + description = "what user is the email going to be set from"; + }; + tokenFile = lib.mkOption { + type = lib.types.str; + description = "file containing the password to be used by msmtp for notifications"; + }; + }; + pool = { + mode = lib.mkOption { + type = lib.types.str; + default = "raidz2"; + description = "what level of redundancy should this pool have"; + }; + # list of drives in pool that will have a boot partition put onto them + bootDrives = lib.mkOption { + type = lib.types.listOf lib.types.str; + description = "list of disks that are going to have a boot partition installed on them"; + default = lib.lists.flatten config.host.storage.pool.vdevs; + }; + # shorthand for vdevs if you only have 1 vdev + drives = lib.mkOption { + type = lib.types.listOf lib.types.str; + description = "list of drives that are going to be in the vdev"; + default = []; + }; + # list of all drives in each vdev + vdevs = lib.mkOption { + type = lib.types.listOf (lib.types.listOf lib.types.str); + description = "list of disks that are going to be in"; + default = [config.host.storage.pool.drives]; + }; + # list of cache drives for pool + cache = lib.mkOption { + type = lib.types.listOf lib.types.str; + description = "list of drives that are going to be used as cache"; + default = []; + }; + # Default datasets that are needed to make a functioning system + datasets = lib.mkOption { + type = lib.types.attrsOf (inputs.disko.lib.subType { + types = {inherit (inputs.disko.lib.types) zfs_fs zfs_volume;}; + }); + default = { + "local" = { + type = "zfs_fs"; + options.canmount = "off"; + }; + # nix directory needs to be available pre persist and doesn't need to be snapshotted or backed up + "local/system/nix" = { + type = "zfs_fs"; + mountpoint = "/nix"; + options = { + atime = "off"; + relatime = "off"; + canmount = "on"; + }; + }; + # dataset for root that gets rolled back on every boot + "local/system/root" = { + type = "zfs_fs"; + mountpoint = "/"; + options = { + canmount = "on"; + }; + postCreateHook = '' + zfs snapshot rpool/local/system/root@blank + ''; + }; + }; + }; + extraDatasets = lib.mkOption { + type = lib.types.attrsOf (inputs.disko.lib.subType { + types = {inherit (inputs.disko.lib.types) zfs_fs zfs_volume;}; + }); + description = "List of datasets to define"; + default = {}; + }; + }; + }; + + config = lib.mkIf config.host.storage.enable { + programs.msmtp = lib.mkIf config.host.storage.notifications.enable { + enable = true; + setSendmail = true; + defaults = { + aliases = "/etc/aliases"; + port = config.host.storage.notifications.port; + tls_trust_file = "/etc/ssl/certs/ca-certificates.crt"; + tls = "on"; + auth = "login"; + tls_starttls = "off"; + }; + accounts = { + zfs_notifications = { + auth = true; + tls = true; + host = config.host.storage.notifications.host; + passwordeval = "cat ${config.host.storage.notifications.tokenFile}"; + user = config.host.storage.notifications.user; + from = config.host.storage.notifications.user; + }; + }; + }; + + services.zfs = { + autoScrub.enable = true; + autoSnapshot.enable = true; + + zed = lib.mkIf config.host.storage.notifications.enable { + enableMail = true; + + settings = { + ZED_DEBUG_LOG = "/tmp/zed.debug.log"; + ZED_EMAIL_ADDR = [config.host.storage.notifications.to]; + ZED_EMAIL_PROG = "${pkgs.msmtp}/bin/msmtp"; + ZED_EMAIL_OPTS = "-a zfs_notifications @ADDRESS@"; + + ZED_NOTIFY_INTERVAL_SECS = 3600; + ZED_NOTIFY_VERBOSE = true; + + ZED_USE_ENCLOSURE_LEDS = true; + ZED_SCRUB_AFTER_RESILVER = true; + }; + }; + }; + + disko.devices = { + disk = ( + builtins.listToAttrs ( + builtins.map + (drive: + lib.attrsets.nameValuePair (drive.name) { + type = "disk"; + device = "/dev/disk/by-id/${drive.value}"; + content = { + type = "gpt"; + partitions = { + ESP = lib.mkIf (builtins.elem drive.value config.host.storage.pool.bootDrives) { + # The 2GB here for the boot partition might be a bit overkill we probably only need like 1/4th of that but storage is cheap + size = "2G"; + type = "EF00"; + content = { + type = "filesystem"; + format = "vfat"; + mountpoint = "/boot"; + mountOptions = ["umask=0077"]; + }; + }; + zfs = { + size = "100%"; + content = { + type = "zfs"; + pool = "rpool"; + }; + }; + }; + }; + }) + ( + (lib.lists.flatten vdevs) ++ cache + ) + ) + ); + zpool = { + rpool = { + type = "zpool"; + mode = { + topology = { + type = "topology"; + vdev = ( + builtins.map (disks: { + mode = config.host.storage.pool.mode; + members = + builtins.map (disk: disk.name) disks; + }) + vdevs + ); + cache = builtins.map (disk: disk.name) cache; + }; + }; + + options = { + ashift = "12"; + autotrim = "on"; + }; + + rootFsOptions = + { + canmount = "off"; + mountpoint = "none"; + + xattr = "sa"; + acltype = "posixacl"; + relatime = "on"; + + compression = "lz4"; + + "com.sun:auto-snapshot" = "false"; + } + // ( + lib.attrsets.optionalAttrs config.host.storage.encryption { + encryption = "on"; + keyformat = "hex"; + keylocation = "prompt"; + } + ); + + datasets = lib.mkMerge [ + ( + lib.attrsets.mapAttrs (name: value: { + type = value.type; + options = value.options; + mountpoint = value.mountpoint; + postCreateHook = value.postCreateHook; + }) + datasets + ) + ]; + }; + }; + }; + }; +} diff --git a/modules/nixos-modules/impermanence.nix b/modules/nixos-modules/impermanence.nix new file mode 100644 index 0000000..60011cb --- /dev/null +++ b/modules/nixos-modules/impermanence.nix @@ -0,0 +1,134 @@ +{ + config, + lib, + ... +}: { + # options.storage = { + # zfs = { + # # TODO: enable option + # # when this option is enabled we need to configure and enable disko things + + # # TODO: we need some way of managing notifications + + # # TODO: we need options to configure zfs pools + # # we should have warnings when the configured pool is missing drives + + # # TODO: dataset option that is a submodule that adds datasets to the system + # # warnings for when a dataset was created in the past on a system but it is now missing some of the options defined for it + + # # TODO: pools and datasets need to be passed to disko + # }; + + # impermanence = { + # # TODO: enable option + + # # TODO: datasets option that is a submodule that will be used to define what datasets to add to the storage system + # # We should by default create the `local`, `local/system/nix`, `local/system/root`, `persist` `persist/system/root`, and `persist/system/var/log` datasets + # # Then we should make a dataset for user folders local and persist + # # We should also create datasets for systemd modules that have have impermanence enabled for them + # # we need to figure out what options a dataset can have in zfs + # }; + + # # TODO: we should have an impermanence module for home manager that proxies its values namespaced to the user down here that matches the same interface + + # # TODO: we should have a way of enabling impermanence for a systemd config + # # these should have an option to put their folder into their own dataset (this needs to support private vs non private) + # # options for features that can be added to the dataset + # }; + + options.host.impermanence.enable = lib.mkEnableOption "are we going to use impermanence on this device"; + + config = lib.mkMerge [ + { + assertions = [ + { + assertion = !(config.host.impermanence.enable && !config.host.storage.enable); + message = '' + Disko storage must be enabled to use impermanence. + ''; + } + ]; + } + ( + lib.mkIf config.host.impermanence.enable { + assertions = [ + { + assertion = config.host.impermanence.enable && config.host.storage.enable; + message = "Impermanence can not be used without managed host storage."; + } + ]; + + # fixes issues with /var/lib/private not having the correct permissions https://github.com/nix-community/impermanence/issues/254 + system.activationScripts."createPersistentStorageDirs".deps = ["var-lib-private-permissions" "users" "groups"]; + system.activationScripts = { + "var-lib-private-permissions" = { + deps = ["specialfs"]; + text = '' + mkdir -p /persist/system/root/var/lib/private + chmod 0700 /persist/system/root/var/lib/private + ''; + }; + }; + + programs.fuse.userAllowOther = true; + + boot.initrd.postResumeCommands = lib.mkAfter '' + zfs rollback -r rpool/local/system/root@blank + ''; + + fileSystems = { + "/".neededForBoot = true; + "/persist/system/root".neededForBoot = true; + "/persist/system/var/log".neededForBoot = true; + }; + + host.storage.pool.extraDatasets = { + # persist datasets are datasets that contain information that we would like to keep around + "persist" = { + type = "zfs_fs"; + options.canmount = "off"; + options = { + "com.sun:auto-snapshot" = "true"; + }; + }; + # this is where root data actually lives + "persist/system/root" = { + type = "zfs_fs"; + mountpoint = "/persist/system/root"; + }; + "persist/system/var/log" = { + type = "zfs_fs"; + mountpoint = "/persist/system/var/log"; + # logs should be append only so we shouldn't need to snapshot them + options = { + "com.sun:auto-snapshot" = "false"; + }; + }; + }; + + environment.persistence."/persist/system/var/log" = { + enable = true; + hideMounts = true; + directories = [ + "/var/log" + ]; + }; + + environment.persistence."/persist/system/root" = { + enable = true; + hideMounts = true; + directories = [ + "/var/lib/nixos" + "/var/lib/systemd/coredump" + ]; + files = [ + "/etc/machine-id" + ]; + }; + + # TODO: this should live in leylas home manager configuration + security.sudo.extraConfig = "Defaults lecture=never"; + } + ) + ]; +} diff --git a/modules/nixos-modules/ollama/ollama.nix b/modules/nixos-modules/ollama.nix similarity index 63% rename from modules/nixos-modules/ollama/ollama.nix rename to modules/nixos-modules/ollama.nix index dc7cdd9..99819bf 100644 --- a/modules/nixos-modules/ollama/ollama.nix +++ b/modules/nixos-modules/ollama.nix @@ -27,6 +27,20 @@ allowedUDPPorts = ports; }; })) + (lib.mkIf config.host.impermanence.enable { + environment.persistence."/persist/system/root" = { + enable = true; + hideMounts = true; + directories = [ + { + directory = "/var/lib/private/ollama"; + user = config.services.ollama.user; + group = config.services.ollama.group; + mode = "0700"; + } + ]; + }; + }) ] ); } diff --git a/modules/nixos-modules/ollama/default.nix b/modules/nixos-modules/ollama/default.nix deleted file mode 100644 index 896526a..0000000 --- a/modules/nixos-modules/ollama/default.nix +++ /dev/null @@ -1,6 +0,0 @@ -{...}: { - imports = [ - ./ollama.nix - ./storage.nix - ]; -} diff --git a/modules/nixos-modules/ollama/storage.nix b/modules/nixos-modules/ollama/storage.nix deleted file mode 100644 index 6ab0fc8..0000000 --- a/modules/nixos-modules/ollama/storage.nix +++ /dev/null @@ -1,37 +0,0 @@ -{ - config, - lib, - ... -}: { - options = { - services.ollama.impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.ollama.enable && config.storage.impermanence.enable; - }; - }; - - config = lib.mkIf (config.services.ollama.enable) { - storage.datasets.replicate."system/root" = { - directories."/var/lib/private/ollama" = lib.mkIf config.services.ollama.impermanence.enable { - enable = true; - owner.name = config.services.ollama.user; - group.name = config.services.ollama.group; - owner.permissions = { - read = true; - write = true; - execute = false; - }; - group.permissions = { - read = false; - write = false; - execute = false; - }; - other.permissions = { - read = false; - write = false; - execute = false; - }; - }; - }; - }; -} diff --git a/modules/nixos-modules/server/actual/default.nix b/modules/nixos-modules/server/actual/default.nix index 99778af..b59517b 100644 --- a/modules/nixos-modules/server/actual/default.nix +++ b/modules/nixos-modules/server/actual/default.nix @@ -3,6 +3,6 @@ ./actual.nix ./proxy.nix ./fail2ban.nix - ./storage.nix + ./impermanence.nix ]; } diff --git a/modules/nixos-modules/server/actual/impermanence.nix b/modules/nixos-modules/server/actual/impermanence.nix new file mode 100644 index 0000000..d870789 --- /dev/null +++ b/modules/nixos-modules/server/actual/impermanence.nix @@ -0,0 +1,37 @@ +{ + lib, + config, + ... +}: let + const = import ./const.nix; + dataDirectory = const.dataDirectory; +in { + options.services.actual = { + impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.actual.enable && config.host.impermanence.enable; + }; + }; + + config = lib.mkIf config.services.actual.impermanence.enable { + assertions = [ + { + assertion = config.services.actual.settings.dataDir == dataDirectory; + message = "actual data location does not match persistence\nconfig directory: ${config.services.actual.settings.dataDir}\npersistence directory: ${dataDirectory}"; + } + { + assertion = config.systemd.services.actual.serviceConfig.DynamicUser or false; + message = "actual systemd service must have DynamicUser enabled to use private directory"; + } + ]; + environment.persistence."/persist/system/root" = { + directories = [ + { + directory = dataDirectory; + user = "actual"; + group = "actual"; + } + ]; + }; + }; +} diff --git a/modules/nixos-modules/server/actual/storage.nix b/modules/nixos-modules/server/actual/storage.nix deleted file mode 100644 index d6b904e..0000000 --- a/modules/nixos-modules/server/actual/storage.nix +++ /dev/null @@ -1,22 +0,0 @@ -{ - lib, - config, - ... -}: let - const = import ./const.nix; - dataDirectory = const.dataDirectory; -in { - options.services.actual.impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.actual.enable && config.storage.impermanence.enable; - }; - - config = lib.mkIf config.services.actual.enable { - storage.datasets.replicate."system/root" = { - directories."${dataDirectory}" = lib.mkIf config.services.actual.impermanence.enable { - owner.name = "actual"; - group.name = "actual"; - }; - }; - }; -} diff --git a/modules/nixos-modules/server/bazarr/default.nix b/modules/nixos-modules/server/bazarr/default.nix index cb2a5f0..86dbb4b 100644 --- a/modules/nixos-modules/server/bazarr/default.nix +++ b/modules/nixos-modules/server/bazarr/default.nix @@ -1,5 +1,5 @@ {...}: { imports = [ - ./storage.nix + ./impermanence.nix ]; } diff --git a/modules/nixos-modules/server/bazarr/impermanence.nix b/modules/nixos-modules/server/bazarr/impermanence.nix new file mode 100644 index 0000000..70a45d1 --- /dev/null +++ b/modules/nixos-modules/server/bazarr/impermanence.nix @@ -0,0 +1,33 @@ +{ + lib, + config, + ... +}: let + bazarr_data_directory = "/var/lib/bazarr"; +in { + options.services.bazarr = { + impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.bazarr.enable && config.host.impermanence.enable; + }; + }; + + config = lib.mkIf config.services.bazarr.impermanence.enable { + assertions = [ + { + assertion = config.services.bazarr.dataDir == bazarr_data_directory; + message = "bazarr data directory does not match persistence"; + } + ]; + + environment.persistence."/persist/system/root" = { + directories = [ + { + directory = bazarr_data_directory; + user = "bazarr"; + group = "bazarr"; + } + ]; + }; + }; +} diff --git a/modules/nixos-modules/server/bazarr/storage.nix b/modules/nixos-modules/server/bazarr/storage.nix deleted file mode 100644 index a243d4c..0000000 --- a/modules/nixos-modules/server/bazarr/storage.nix +++ /dev/null @@ -1,21 +0,0 @@ -{ - lib, - config, - ... -}: let - bazarr_data_directory = "/var/lib/bazarr"; -in { - options.services.bazarr.impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.bazarr.enable && config.storage.impermanence.enable; - }; - - config = lib.mkIf config.services.bazarr.enable { - storage.datasets.replicate."system/root" = { - directories."${bazarr_data_directory}" = lib.mkIf config.services.bazarr.impermanence.enable { - owner.name = "bazarr"; - group.name = "bazarr"; - }; - }; - }; -} diff --git a/modules/nixos-modules/server/crab-hole/default.nix b/modules/nixos-modules/server/crab-hole/default.nix index 9f990c5..158a851 100644 --- a/modules/nixos-modules/server/crab-hole/default.nix +++ b/modules/nixos-modules/server/crab-hole/default.nix @@ -1,6 +1,6 @@ {...}: { imports = [ ./crab-hole.nix - ./storage.nix + ./impermanence.nix ]; } diff --git a/modules/nixos-modules/server/crab-hole/impermanence.nix b/modules/nixos-modules/server/crab-hole/impermanence.nix new file mode 100644 index 0000000..51efc0c --- /dev/null +++ b/modules/nixos-modules/server/crab-hole/impermanence.nix @@ -0,0 +1,33 @@ +{ + lib, + config, + ... +}: let + workingDirectory = "/var/lib/private/crab-hole"; +in { + options.services.crab-hole = { + impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.crab-hole.enable && config.host.impermanence.enable; + }; + }; + + config = lib.mkIf config.services.crab-hole.impermanence.enable { + assertions = [ + { + assertion = + config.systemd.services.crab-hole.serviceConfig.WorkingDirectory == (builtins.replaceStrings ["/private"] [""] workingDirectory); + message = "crab-hole working directory does not match persistence"; + } + ]; + environment.persistence."/persist/system/root" = { + directories = [ + { + directory = workingDirectory; + user = "crab-hole"; + group = "crab-hole"; + } + ]; + }; + }; +} diff --git a/modules/nixos-modules/server/crab-hole/storage.nix b/modules/nixos-modules/server/crab-hole/storage.nix deleted file mode 100644 index 827fb25..0000000 --- a/modules/nixos-modules/server/crab-hole/storage.nix +++ /dev/null @@ -1,21 +0,0 @@ -{ - lib, - config, - ... -}: let - workingDirectory = "/var/lib/private/crab-hole"; -in { - options.services.crab-hole.impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.crab-hole.enable && config.storage.impermanence.enable; - }; - - config = lib.mkIf config.services.crab-hole.enable { - storage.datasets.replicate."system/root" = { - directories."${workingDirectory}" = lib.mkIf config.services.crab-hole.impermanence.enable { - owner.name = "crab-hole"; - group.name = "crab-hole"; - }; - }; - }; -} diff --git a/modules/nixos-modules/server/fail2ban/default.nix b/modules/nixos-modules/server/fail2ban/default.nix index 84a46d4..30fca99 100644 --- a/modules/nixos-modules/server/fail2ban/default.nix +++ b/modules/nixos-modules/server/fail2ban/default.nix @@ -1,6 +1,6 @@ {...}: { imports = [ ./fail2ban.nix - ./storage.nix + ./impermanence.nix ]; } diff --git a/modules/nixos-modules/server/fail2ban/impermanence.nix b/modules/nixos-modules/server/fail2ban/impermanence.nix new file mode 100644 index 0000000..6e214b3 --- /dev/null +++ b/modules/nixos-modules/server/fail2ban/impermanence.nix @@ -0,0 +1,34 @@ +{ + lib, + config, + ... +}: let + dataFolder = "/var/lib/fail2ban"; + dataFile = "fail2ban.sqlite3"; +in { + options.services.fail2ban = { + impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.fail2ban.enable && config.host.impermanence.enable; + }; + }; + + config = lib.mkIf config.services.fail2ban.impermanence.enable { + assertions = [ + { + assertion = config.services.fail2ban.daemonSettings.Definition.dbfile == "${dataFolder}/${dataFile}"; + message = "fail2ban data file does not match persistence"; + } + ]; + + environment.persistence."/persist/system/root" = { + directories = [ + { + directory = dataFolder; + user = "fail2ban"; + group = "fail2ban"; + } + ]; + }; + }; +} diff --git a/modules/nixos-modules/server/fail2ban/storage.nix b/modules/nixos-modules/server/fail2ban/storage.nix deleted file mode 100644 index 1ef02c7..0000000 --- a/modules/nixos-modules/server/fail2ban/storage.nix +++ /dev/null @@ -1,22 +0,0 @@ -{ - lib, - config, - ... -}: let - dataFolder = "/var/lib/fail2ban"; - dataFile = "fail2ban.sqlite3"; -in { - options.services.fail2ban.impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.fail2ban.enable && config.storage.impermanence.enable; - }; - - config = lib.mkIf config.services.fail2ban.enable { - storage.datasets.replicate."system/root" = { - directories."${dataFolder}" = lib.mkIf config.services.fail2ban.impermanence.enable { - owner.name = "fail2ban"; - group.name = "fail2ban"; - }; - }; - }; -} diff --git a/modules/nixos-modules/server/flaresolverr/default.nix b/modules/nixos-modules/server/flaresolverr/default.nix index cb2a5f0..86dbb4b 100644 --- a/modules/nixos-modules/server/flaresolverr/default.nix +++ b/modules/nixos-modules/server/flaresolverr/default.nix @@ -1,5 +1,5 @@ {...}: { imports = [ - ./storage.nix + ./impermanence.nix ]; } diff --git a/modules/nixos-modules/server/flaresolverr/impermanence.nix b/modules/nixos-modules/server/flaresolverr/impermanence.nix new file mode 100644 index 0000000..4544e75 --- /dev/null +++ b/modules/nixos-modules/server/flaresolverr/impermanence.nix @@ -0,0 +1,26 @@ +{ + lib, + config, + ... +}: { + options.services.flaresolverr = { + impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.flaresolverr.enable && config.host.impermanence.enable; + }; + }; + + config = lib.mkIf config.services.flaresolverr.impermanence.enable { + # FlareSolverr typically doesn't need persistent storage as it's a proxy service + # but we'll add basic structure in case it's needed for logs or configuration + environment.persistence."/persist/system/root" = { + directories = [ + { + directory = "/var/lib/flaresolverr"; + user = "flaresolverr"; + group = "flaresolverr"; + } + ]; + }; + }; +} diff --git a/modules/nixos-modules/server/flaresolverr/storage.nix b/modules/nixos-modules/server/flaresolverr/storage.nix deleted file mode 100644 index 919318c..0000000 --- a/modules/nixos-modules/server/flaresolverr/storage.nix +++ /dev/null @@ -1,19 +0,0 @@ -{ - lib, - config, - ... -}: { - options.services.flaresolverr.impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.flaresolverr.enable && config.storage.impermanence.enable; - }; - - config = lib.mkIf config.services.flaresolverr.enable { - storage.datasets.replicate."system/root" = { - directories."/var/lib/flaresolverr" = lib.mkIf config.services.flaresolverr.impermanence.enable { - owner.name = "flaresolverr"; - group.name = "flaresolverr"; - }; - }; - }; -} diff --git a/modules/nixos-modules/server/forgejo/default.nix b/modules/nixos-modules/server/forgejo/default.nix index c990e57..4333f69 100644 --- a/modules/nixos-modules/server/forgejo/default.nix +++ b/modules/nixos-modules/server/forgejo/default.nix @@ -4,6 +4,6 @@ ./proxy.nix ./database.nix ./fail2ban.nix - ./storage.nix + ./impermanence.nix ]; } diff --git a/modules/nixos-modules/server/forgejo/impermanence.nix b/modules/nixos-modules/server/forgejo/impermanence.nix new file mode 100644 index 0000000..6fe3de8 --- /dev/null +++ b/modules/nixos-modules/server/forgejo/impermanence.nix @@ -0,0 +1,35 @@ +{ + lib, + config, + ... +}: let + stateDir = "/var/lib/forgejo"; +in { + options.services.forgejo = { + impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.forgejo.enable && config.host.impermanence.enable; + }; + }; + + config = lib.mkIf config.services.forgejo.impermanence.enable { + assertions = [ + { + assertion = config.services.forgejo.stateDir == stateDir; + message = "forgejo state directory does not match persistence"; + } + ]; + + environment.persistence."/persist/system/root" = { + enable = true; + hideMounts = true; + directories = [ + { + directory = stateDir; + user = "forgejo"; + group = "forgejo"; + } + ]; + }; + }; +} diff --git a/modules/nixos-modules/server/forgejo/storage.nix b/modules/nixos-modules/server/forgejo/storage.nix deleted file mode 100644 index da30ed9..0000000 --- a/modules/nixos-modules/server/forgejo/storage.nix +++ /dev/null @@ -1,21 +0,0 @@ -{ - lib, - config, - ... -}: let - stateDir = "/var/lib/forgejo"; -in { - options.services.forgejo.impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.forgejo.enable && config.storage.impermanence.enable; - }; - - config = lib.mkIf config.services.forgejo.enable { - storage.datasets.replicate."system/root" = { - directories."${stateDir}" = lib.mkIf config.services.forgejo.impermanence.enable { - owner.name = "forgejo"; - group.name = "forgejo"; - }; - }; - }; -} diff --git a/modules/nixos-modules/server/home-assistant/default.nix b/modules/nixos-modules/server/home-assistant/default.nix index d213964..b6f9356 100644 --- a/modules/nixos-modules/server/home-assistant/default.nix +++ b/modules/nixos-modules/server/home-assistant/default.nix @@ -4,7 +4,7 @@ ./proxy.nix ./database.nix ./fail2ban.nix - ./storage.nix + ./impermanence.nix ./extensions ]; } diff --git a/modules/nixos-modules/server/home-assistant/impermanence.nix b/modules/nixos-modules/server/home-assistant/impermanence.nix new file mode 100644 index 0000000..8c056a1 --- /dev/null +++ b/modules/nixos-modules/server/home-assistant/impermanence.nix @@ -0,0 +1,26 @@ +{ + lib, + config, + ... +}: let + configDir = "/var/lib/hass"; +in + lib.mkIf (config.host.impermanence.enable && config.services.home-assistant.enable) { + assertions = [ + { + assertion = config.services.home-assistant.configDir == configDir; + message = "home assistant config directory does not match persistence"; + } + ]; + environment.persistence."/persist/system/root" = { + enable = true; + hideMounts = true; + directories = [ + { + directory = configDir; + user = "hass"; + group = "hass"; + } + ]; + }; + } diff --git a/modules/nixos-modules/server/home-assistant/storage.nix b/modules/nixos-modules/server/home-assistant/storage.nix deleted file mode 100644 index 60e5085..0000000 --- a/modules/nixos-modules/server/home-assistant/storage.nix +++ /dev/null @@ -1,21 +0,0 @@ -{ - lib, - config, - ... -}: let - configDir = "/var/lib/hass"; -in { - options.services.home-assistant.impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.home-assistant.enable && config.storage.impermanence.enable; - }; - - config = lib.mkIf config.services.home-assistant.enable { - storage.datasets.replicate."system/root" = { - directories."${configDir}" = lib.mkIf config.services.home-assistant.impermanence.enable { - owner.name = "hass"; - group.name = "hass"; - }; - }; - }; -} diff --git a/modules/nixos-modules/server/immich/default.nix b/modules/nixos-modules/server/immich/default.nix index 75ae2fd..4d93c0b 100644 --- a/modules/nixos-modules/server/immich/default.nix +++ b/modules/nixos-modules/server/immich/default.nix @@ -3,7 +3,7 @@ ./proxy.nix ./database.nix ./fail2ban.nix - ./storage.nix + ./impermanence.nix ]; # NOTE: This shouldn't be needed now that we are out of testing diff --git a/modules/nixos-modules/server/immich/impermanence.nix b/modules/nixos-modules/server/immich/impermanence.nix new file mode 100644 index 0000000..56e51d0 --- /dev/null +++ b/modules/nixos-modules/server/immich/impermanence.nix @@ -0,0 +1,32 @@ +{ + lib, + config, + ... +}: let + mediaLocation = "/var/lib/immich"; +in { + options.services.immich = { + impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.immich.enable && config.host.impermanence.enable; + }; + }; + + config = lib.mkIf config.services.immich.impermanence.enable { + assertions = [ + { + assertion = config.services.immich.mediaLocation == mediaLocation; + message = "immich media location does not match persistence"; + } + ]; + environment.persistence."/persist/system/root" = { + directories = [ + { + directory = mediaLocation; + user = "immich"; + group = "immich"; + } + ]; + }; + }; +} diff --git a/modules/nixos-modules/server/immich/storage.nix b/modules/nixos-modules/server/immich/storage.nix deleted file mode 100644 index de24329..0000000 --- a/modules/nixos-modules/server/immich/storage.nix +++ /dev/null @@ -1,21 +0,0 @@ -{ - lib, - config, - ... -}: let - mediaLocation = "/var/lib/immich"; -in { - options.services.immich.impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.immich.enable && config.storage.impermanence.enable; - }; - - config = lib.mkIf config.services.immich.enable { - storage.datasets.replicate."system/root" = { - directories."${mediaLocation}" = lib.mkIf config.services.immich.impermanence.enable { - owner.name = "immich"; - group.name = "immich"; - }; - }; - }; -} diff --git a/modules/nixos-modules/server/jackett/default.nix b/modules/nixos-modules/server/jackett/default.nix index 5043814..e70a929 100644 --- a/modules/nixos-modules/server/jackett/default.nix +++ b/modules/nixos-modules/server/jackett/default.nix @@ -1,6 +1,6 @@ {...}: { imports = [ - ./storage.nix + ./impermanence.nix ]; config = { diff --git a/modules/nixos-modules/server/jackett/impermanence.nix b/modules/nixos-modules/server/jackett/impermanence.nix new file mode 100644 index 0000000..24fc5e6 --- /dev/null +++ b/modules/nixos-modules/server/jackett/impermanence.nix @@ -0,0 +1,33 @@ +{ + lib, + config, + ... +}: let + jackett_data_directory = "/var/lib/jackett/.config/Jackett"; +in { + options.services.jackett = { + impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.jackett.enable && config.host.impermanence.enable; + }; + }; + + config = lib.mkIf config.services.jackett.impermanence.enable { + assertions = [ + { + assertion = config.services.jackett.dataDir == jackett_data_directory; + message = "jackett data directory does not match persistence"; + } + ]; + + environment.persistence."/persist/system/root" = { + directories = [ + { + directory = jackett_data_directory; + user = "jackett"; + group = "jackett"; + } + ]; + }; + }; +} diff --git a/modules/nixos-modules/server/jackett/storage.nix b/modules/nixos-modules/server/jackett/storage.nix deleted file mode 100644 index 5f202e6..0000000 --- a/modules/nixos-modules/server/jackett/storage.nix +++ /dev/null @@ -1,21 +0,0 @@ -{ - lib, - config, - ... -}: let - jackett_data_directory = "/var/lib/jackett/.config/Jackett"; -in { - options.services.jackett.impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.jackett.enable && config.storage.impermanence.enable; - }; - - config = lib.mkIf config.services.jackett.enable { - storage.datasets.replicate."system/root" = { - directories."${jackett_data_directory}" = lib.mkIf config.services.jackett.impermanence.enable { - owner.name = "jackett"; - group.name = "jackett"; - }; - }; - }; -} diff --git a/modules/nixos-modules/server/jellyfin/default.nix b/modules/nixos-modules/server/jellyfin/default.nix index 4770ae1..2dbdcfd 100644 --- a/modules/nixos-modules/server/jellyfin/default.nix +++ b/modules/nixos-modules/server/jellyfin/default.nix @@ -3,6 +3,6 @@ ./jellyfin.nix ./proxy.nix ./fail2ban.nix - ./storage.nix + ./impermanence.nix ]; } diff --git a/modules/nixos-modules/server/jellyfin/impermanence.nix b/modules/nixos-modules/server/jellyfin/impermanence.nix new file mode 100644 index 0000000..cbcb54f --- /dev/null +++ b/modules/nixos-modules/server/jellyfin/impermanence.nix @@ -0,0 +1,73 @@ +{ + lib, + config, + ... +}: let + jellyfin_data_directory = "/var/lib/jellyfin"; + jellyfin_cache_directory = "/var/cache/jellyfin"; +in { + options.services.jellyfin = { + impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.jellyfin.enable && config.host.impermanence.enable; + }; + }; + + config = lib.mkIf config.services.jellyfin.impermanence.enable { + fileSystems."/persist/system/jellyfin".neededForBoot = true; + + host.storage.pool.extraDatasets = { + # sops age key needs to be available to pre persist for user generation + "persist/system/jellyfin" = { + type = "zfs_fs"; + mountpoint = "/persist/system/jellyfin"; + options = { + atime = "off"; + relatime = "off"; + canmount = "on"; + }; + }; + }; + + assertions = [ + { + assertion = config.services.jellyfin.dataDir == jellyfin_data_directory; + message = "jellyfin data directory does not match persistence"; + } + { + assertion = config.services.jellyfin.cacheDir == jellyfin_cache_directory; + message = "jellyfin cache directory does not match persistence"; + } + ]; + + environment.persistence = { + "/persist/system/root" = { + directories = [ + { + directory = jellyfin_data_directory; + user = "jellyfin"; + group = "jellyfin"; + } + { + directory = jellyfin_cache_directory; + user = "jellyfin"; + group = "jellyfin"; + } + ]; + }; + + "/persist/system/jellyfin" = { + enable = true; + hideMounts = true; + directories = [ + { + directory = config.services.jellyfin.media_directory; + user = "jellyfin"; + group = "jellyfin_media"; + mode = "1770"; + } + ]; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/jellyfin/storage.nix b/modules/nixos-modules/server/jellyfin/storage.nix deleted file mode 100644 index 5cff3e8..0000000 --- a/modules/nixos-modules/server/jellyfin/storage.nix +++ /dev/null @@ -1,56 +0,0 @@ -{ - lib, - config, - ... -}: let - jellyfin_data_directory = "/var/lib/jellyfin"; - jellyfin_cache_directory = "/var/cache/jellyfin"; -in { - options.services.jellyfin.impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.jellyfin.enable && config.storage.impermanence.enable; - }; - - config = lib.mkIf config.services.jellyfin.enable { - storage.datasets.replicate = { - "system/root" = { - directories = { - "${jellyfin_data_directory}" = lib.mkIf config.services.jellyfin.impermanence.enable { - enable = true; - owner.name = "jellyfin"; - group.name = "jellyfin"; - }; - "${jellyfin_cache_directory}" = lib.mkIf config.services.jellyfin.impermanence.enable { - enable = true; - owner.name = "jellyfin"; - group.name = "jellyfin"; - }; - }; - }; - "system/media" = { - mount = "/persist/replicate/system/media"; - - directories."${config.services.jellyfin.media_directory}" = lib.mkIf config.services.jellyfin.impermanence.enable { - enable = true; - owner.name = "jellyfin"; - group.name = "jellyfin_media"; - owner.permissions = { - read = true; - write = true; - execute = true; - }; - group.permissions = { - read = true; - write = true; - execute = true; - }; - other.permissions = { - read = false; - write = false; - execute = false; - }; - }; - }; - }; - }; -} diff --git a/modules/nixos-modules/server/lidarr/default.nix b/modules/nixos-modules/server/lidarr/default.nix index cb2a5f0..86dbb4b 100644 --- a/modules/nixos-modules/server/lidarr/default.nix +++ b/modules/nixos-modules/server/lidarr/default.nix @@ -1,5 +1,5 @@ {...}: { imports = [ - ./storage.nix + ./impermanence.nix ]; } diff --git a/modules/nixos-modules/server/lidarr/impermanence.nix b/modules/nixos-modules/server/lidarr/impermanence.nix new file mode 100644 index 0000000..5d3aa3f --- /dev/null +++ b/modules/nixos-modules/server/lidarr/impermanence.nix @@ -0,0 +1,33 @@ +{ + lib, + config, + ... +}: let + lidarr_data_directory = "/var/lib/lidarr/.config/Lidarr"; +in { + options.services.lidarr = { + impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.lidarr.enable && config.host.impermanence.enable; + }; + }; + + config = lib.mkIf config.services.lidarr.impermanence.enable { + assertions = [ + { + assertion = config.services.lidarr.dataDir == lidarr_data_directory; + message = "lidarr data directory does not match persistence"; + } + ]; + + environment.persistence."/persist/system/root" = { + directories = [ + { + directory = lidarr_data_directory; + user = "lidarr"; + group = "lidarr"; + } + ]; + }; + }; +} diff --git a/modules/nixos-modules/server/lidarr/storage.nix b/modules/nixos-modules/server/lidarr/storage.nix deleted file mode 100644 index c4c020e..0000000 --- a/modules/nixos-modules/server/lidarr/storage.nix +++ /dev/null @@ -1,21 +0,0 @@ -{ - lib, - config, - ... -}: let - lidarr_data_directory = "/var/lib/lidarr/.config/Lidarr"; -in { - options.services.lidarr.impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.lidarr.enable && config.storage.impermanence.enable; - }; - - config = lib.mkIf config.services.lidarr.enable { - storage.datasets.replicate."system/root" = { - directories."${lidarr_data_directory}" = lib.mkIf config.services.lidarr.impermanence.enable { - owner.name = "lidarr"; - group.name = "lidarr"; - }; - }; - }; -} diff --git a/modules/nixos-modules/server/network_storage/network_storage.nix b/modules/nixos-modules/server/network_storage/network_storage.nix index b9d0446..ebc3bee 100644 --- a/modules/nixos-modules/server/network_storage/network_storage.nix +++ b/modules/nixos-modules/server/network_storage/network_storage.nix @@ -74,7 +74,7 @@ in { ); } # (lib.mkIf config.host.impermanence.enable { - # environment.persistence."/persist/replicate/system/root" = { + # environment.persistence."/persist/system/root" = { # enable = true; # hideMounts = true; # directories = [ diff --git a/modules/nixos-modules/server/panoramax/default.nix b/modules/nixos-modules/server/panoramax/default.nix index f5a514f..4c6b9ea 100644 --- a/modules/nixos-modules/server/panoramax/default.nix +++ b/modules/nixos-modules/server/panoramax/default.nix @@ -2,7 +2,7 @@ imports = [ ./proxy.nix ./fail2ban.nix - ./storage.nix + ./impermanence.nix ./panoramax.nix ./database.nix ]; diff --git a/modules/nixos-modules/server/panoramax/impermanence.nix b/modules/nixos-modules/server/panoramax/impermanence.nix new file mode 100644 index 0000000..e25ef92 --- /dev/null +++ b/modules/nixos-modules/server/panoramax/impermanence.nix @@ -0,0 +1,20 @@ +{ + lib, + config, + ... +}: { + options.services.panoramax = { + impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.panoramax.enable && config.host.impermanence.enable; + }; + }; + + config = lib.mkIf config.services.panoramax.impermanence.enable { + # TODO: configure impermanence for panoramax data + # This would typically include directories like: + # - /var/lib/panoramax + # - panoramax storage directories + # - any cache or temporary directories that need to persist + }; +} diff --git a/modules/nixos-modules/server/panoramax/storage.nix b/modules/nixos-modules/server/panoramax/storage.nix deleted file mode 100644 index b36e087..0000000 --- a/modules/nixos-modules/server/panoramax/storage.nix +++ /dev/null @@ -1,19 +0,0 @@ -{ - lib, - config, - ... -}: { - options.services.panoramax.impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.panoramax.enable && config.storage.impermanence.enable; - }; - - config = lib.mkIf config.services.panoramax.enable { - storage.datasets.replicate."system/root" = { - directories."/var/lib/panoramax" = lib.mkIf config.services.panoramax.impermanence.enable { - owner.name = "panoramax"; - group.name = "panoramax"; - }; - }; - }; -} diff --git a/modules/nixos-modules/server/paperless/default.nix b/modules/nixos-modules/server/paperless/default.nix index f7a5aa7..7e5e16b 100644 --- a/modules/nixos-modules/server/paperless/default.nix +++ b/modules/nixos-modules/server/paperless/default.nix @@ -4,6 +4,6 @@ ./proxy.nix ./database.nix ./fail2ban.nix - ./storage.nix + ./impermanence.nix ]; } diff --git a/modules/nixos-modules/server/paperless/impermanence.nix b/modules/nixos-modules/server/paperless/impermanence.nix new file mode 100644 index 0000000..fc87ea7 --- /dev/null +++ b/modules/nixos-modules/server/paperless/impermanence.nix @@ -0,0 +1,32 @@ +{ + config, + lib, + ... +}: let + dataDir = "/var/lib/paperless"; +in { + options.services.paperless = { + impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.paperless.enable && config.host.impermanence.enable; + }; + }; + + config = lib.mkIf config.services.paperless.impermanence.enable { + assertions = [ + { + assertion = config.services.paperless.dataDir == dataDir; + message = "paperless data location does not match persistence"; + } + ]; + environment.persistence."/persist/system/root" = { + directories = [ + { + directory = dataDir; + user = "paperless"; + group = "paperless"; + } + ]; + }; + }; +} diff --git a/modules/nixos-modules/server/paperless/storage.nix b/modules/nixos-modules/server/paperless/storage.nix deleted file mode 100644 index 6e17bc2..0000000 --- a/modules/nixos-modules/server/paperless/storage.nix +++ /dev/null @@ -1,21 +0,0 @@ -{ - config, - lib, - ... -}: let - dataDir = "/var/lib/paperless"; -in { - options.services.paperless.impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.paperless.enable && config.storage.impermanence.enable; - }; - - config = lib.mkIf config.services.paperless.enable { - storage.datasets.replicate."system/root" = { - directories."${dataDir}" = lib.mkIf config.services.paperless.impermanence.enable { - owner.name = "paperless"; - group.name = "paperless"; - }; - }; - }; -} diff --git a/modules/nixos-modules/server/postgres/default.nix b/modules/nixos-modules/server/postgres/default.nix index 50d90d4..abf4ade 100644 --- a/modules/nixos-modules/server/postgres/default.nix +++ b/modules/nixos-modules/server/postgres/default.nix @@ -1,6 +1,6 @@ {...}: { imports = [ ./postgres.nix - ./storage.nix + ./impermanence.nix ]; } diff --git a/modules/nixos-modules/server/postgres/impermanence.nix b/modules/nixos-modules/server/postgres/impermanence.nix new file mode 100644 index 0000000..a67fb1a --- /dev/null +++ b/modules/nixos-modules/server/postgres/impermanence.nix @@ -0,0 +1,27 @@ +{ + config, + lib, + ... +}: let + dataDir = "/var/lib/postgresql/16"; +in { + config = lib.mkIf (config.services.postgresql.enable && config.host.impermanence.enable) { + assertions = [ + { + assertion = config.services.postgresql.dataDir == dataDir; + message = "postgres data directory does not match persistence"; + } + ]; + environment.persistence."/persist/system/root" = { + enable = true; + hideMounts = true; + directories = [ + { + directory = dataDir; + user = "postgres"; + group = "postgres"; + } + ]; + }; + }; +} diff --git a/modules/nixos-modules/server/postgres/storage.nix b/modules/nixos-modules/server/postgres/storage.nix deleted file mode 100644 index 58a84a6..0000000 --- a/modules/nixos-modules/server/postgres/storage.nix +++ /dev/null @@ -1,21 +0,0 @@ -{ - config, - lib, - ... -}: let - dataDir = "/var/lib/postgresql/16"; -in { - options.services.postgresql.impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.postgresql.enable && config.storage.impermanence.enable; - }; - - config = lib.mkIf config.services.postgresql.enable { - storage.datasets.replicate."system/root" = { - directories."${dataDir}" = lib.mkIf config.services.postgresql.impermanence.enable { - owner.name = "postgres"; - group.name = "postgres"; - }; - }; - }; -} diff --git a/modules/nixos-modules/server/qbittorent/default.nix b/modules/nixos-modules/server/qbittorent/default.nix index 11cc449..f7511e6 100644 --- a/modules/nixos-modules/server/qbittorent/default.nix +++ b/modules/nixos-modules/server/qbittorent/default.nix @@ -1,6 +1,6 @@ {...}: { imports = [ ./qbittorent.nix - ./storage.nix + ./impermanence.nix ]; } diff --git a/modules/nixos-modules/server/qbittorent/impermanence.nix b/modules/nixos-modules/server/qbittorent/impermanence.nix new file mode 100644 index 0000000..1489e7d --- /dev/null +++ b/modules/nixos-modules/server/qbittorent/impermanence.nix @@ -0,0 +1,61 @@ +{ + lib, + config, + ... +}: let + qbittorent_profile_directory = "/var/lib/qBittorrent/"; +in { + options.services.qbittorrent = { + impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.qbittorrent.enable && config.host.impermanence.enable; + }; + }; + + config = lib.mkIf config.services.qbittorrent.impermanence.enable { + fileSystems."/persist/system/qbittorrent".neededForBoot = true; + + host.storage.pool.extraDatasets = { + # sops age key needs to be available to pre persist for user generation + "persist/system/qbittorrent" = { + type = "zfs_fs"; + mountpoint = "/persist/system/qbittorrent"; + options = { + canmount = "on"; + }; + }; + }; + + assertions = [ + { + assertion = config.services.qbittorrent.profileDir == qbittorent_profile_directory; + message = "qbittorrent data directory does not match persistence"; + } + ]; + + environment.persistence = { + "/persist/system/root" = { + directories = [ + { + directory = qbittorent_profile_directory; + user = "qbittorrent"; + group = "qbittorrent"; + } + ]; + }; + + "/persist/system/qbittorrent" = { + enable = true; + hideMounts = true; + directories = [ + { + directory = config.services.qbittorrent.mediaDir; + user = "qbittorrent"; + group = "qbittorrent"; + mode = "1775"; + } + ]; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/qbittorent/storage.nix b/modules/nixos-modules/server/qbittorent/storage.nix deleted file mode 100644 index da82bcc..0000000 --- a/modules/nixos-modules/server/qbittorent/storage.nix +++ /dev/null @@ -1,46 +0,0 @@ -{ - lib, - config, - ... -}: let - qbittorent_profile_directory = "/var/lib/qBittorrent/"; -in { - options.services.qbittorrent.impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.qbittorrent.enable && config.storage.impermanence.enable; - }; - - config = lib.mkIf config.services.qbittorrent.enable { - storage.datasets.replicate = { - "system/root" = { - directories."${qbittorent_profile_directory}" = lib.mkIf config.services.qbittorrent.impermanence.enable { - owner.name = "qbittorrent"; - group.name = "qbittorrent"; - }; - }; - "system/media" = { - mount = "/persist/replicate/system/media"; - - directories."${config.services.qbittorrent.mediaDir}" = lib.mkIf config.services.qbittorrent.impermanence.enable { - owner.name = "qbittorrent"; - group.name = "qbittorrent"; - owner.permissions = { - read = true; - write = true; - execute = true; - }; - group.permissions = { - read = true; - write = true; - execute = true; - }; - other.permissions = { - read = true; - write = false; - execute = true; - }; - }; - }; - }; - }; -} diff --git a/modules/nixos-modules/server/radarr/default.nix b/modules/nixos-modules/server/radarr/default.nix index cb2a5f0..86dbb4b 100644 --- a/modules/nixos-modules/server/radarr/default.nix +++ b/modules/nixos-modules/server/radarr/default.nix @@ -1,5 +1,5 @@ {...}: { imports = [ - ./storage.nix + ./impermanence.nix ]; } diff --git a/modules/nixos-modules/server/radarr/impermanence.nix b/modules/nixos-modules/server/radarr/impermanence.nix new file mode 100644 index 0000000..c948e3a --- /dev/null +++ b/modules/nixos-modules/server/radarr/impermanence.nix @@ -0,0 +1,33 @@ +{ + lib, + config, + ... +}: let + radarr_data_directory = "/var/lib/radarr/.config/Radarr"; +in { + options.services.radarr = { + impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.radarr.enable && config.host.impermanence.enable; + }; + }; + + config = lib.mkIf config.services.radarr.impermanence.enable { + assertions = [ + { + assertion = config.services.radarr.dataDir == radarr_data_directory; + message = "radarr data directory does not match persistence"; + } + ]; + + environment.persistence."/persist/system/root" = { + directories = [ + { + directory = radarr_data_directory; + user = "radarr"; + group = "radarr"; + } + ]; + }; + }; +} diff --git a/modules/nixos-modules/server/radarr/storage.nix b/modules/nixos-modules/server/radarr/storage.nix deleted file mode 100644 index 8f991c0..0000000 --- a/modules/nixos-modules/server/radarr/storage.nix +++ /dev/null @@ -1,21 +0,0 @@ -{ - lib, - config, - ... -}: let - radarr_data_directory = "/var/lib/radarr/.config/Radarr"; -in { - options.services.radarr.impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.radarr.enable && config.storage.impermanence.enable; - }; - - config = lib.mkIf config.services.radarr.enable { - storage.datasets.replicate."system/root" = { - directories."${radarr_data_directory}" = lib.mkIf config.services.radarr.impermanence.enable { - owner.name = "radarr"; - group.name = "radarr"; - }; - }; - }; -} diff --git a/modules/nixos-modules/server/reverseProxy/default.nix b/modules/nixos-modules/server/reverseProxy/default.nix index 336e28b..5d57175 100644 --- a/modules/nixos-modules/server/reverseProxy/default.nix +++ b/modules/nixos-modules/server/reverseProxy/default.nix @@ -1,6 +1,6 @@ {...}: { imports = [ ./reverseProxy.nix - ./storage.nix + ./impermanence.nix ]; } diff --git a/modules/nixos-modules/server/reverseProxy/impermanence.nix b/modules/nixos-modules/server/reverseProxy/impermanence.nix new file mode 100644 index 0000000..7af55df --- /dev/null +++ b/modules/nixos-modules/server/reverseProxy/impermanence.nix @@ -0,0 +1,21 @@ +{ + lib, + config, + ... +}: let + dataDir = "/var/lib/acme"; +in { + config = lib.mkIf (config.host.impermanence.enable && config.services.reverseProxy.enable) { + environment.persistence."/persist/system/root" = { + enable = true; + hideMounts = true; + directories = [ + { + directory = dataDir; + user = "acme"; + group = "acme"; + } + ]; + }; + }; +} diff --git a/modules/nixos-modules/server/reverseProxy/storage.nix b/modules/nixos-modules/server/reverseProxy/storage.nix deleted file mode 100644 index 62b5451..0000000 --- a/modules/nixos-modules/server/reverseProxy/storage.nix +++ /dev/null @@ -1,21 +0,0 @@ -{ - lib, - config, - ... -}: let - dataDir = "/var/lib/acme"; -in { - options.services.reverseProxy.impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.reverseProxy.enable && config.storage.impermanence.enable; - }; - - config = lib.mkIf config.services.reverseProxy.enable { - storage.datasets.replicate."system/root" = { - directories."${dataDir}" = lib.mkIf config.services.reverseProxy.impermanence.enable { - owner.name = "acme"; - group.name = "acme"; - }; - }; - }; -} diff --git a/modules/nixos-modules/server/sonarr/default.nix b/modules/nixos-modules/server/sonarr/default.nix index cb2a5f0..86dbb4b 100644 --- a/modules/nixos-modules/server/sonarr/default.nix +++ b/modules/nixos-modules/server/sonarr/default.nix @@ -1,5 +1,5 @@ {...}: { imports = [ - ./storage.nix + ./impermanence.nix ]; } diff --git a/modules/nixos-modules/server/sonarr/impermanence.nix b/modules/nixos-modules/server/sonarr/impermanence.nix new file mode 100644 index 0000000..5b90ee9 --- /dev/null +++ b/modules/nixos-modules/server/sonarr/impermanence.nix @@ -0,0 +1,33 @@ +{ + lib, + config, + ... +}: let + sonarr_data_directory = "/var/lib/sonarr/.config/NzbDrone"; +in { + options.services.sonarr = { + impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.sonarr.enable && config.host.impermanence.enable; + }; + }; + + config = lib.mkIf config.services.sonarr.impermanence.enable { + assertions = [ + { + assertion = config.services.sonarr.dataDir == sonarr_data_directory; + message = "sonarr data directory does not match persistence"; + } + ]; + + environment.persistence."/persist/system/root" = { + directories = [ + { + directory = sonarr_data_directory; + user = "sonarr"; + group = "sonarr"; + } + ]; + }; + }; +} diff --git a/modules/nixos-modules/server/sonarr/storage.nix b/modules/nixos-modules/server/sonarr/storage.nix deleted file mode 100644 index 8587751..0000000 --- a/modules/nixos-modules/server/sonarr/storage.nix +++ /dev/null @@ -1,21 +0,0 @@ -{ - lib, - config, - ... -}: let - sonarr_data_directory = "/var/lib/sonarr/.config/NzbDrone"; -in { - options.services.sonarr.impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.sonarr.enable && config.storage.impermanence.enable; - }; - - config = lib.mkIf config.services.sonarr.enable { - storage.datasets.replicate."system/root" = { - directories."${sonarr_data_directory}" = lib.mkIf config.services.sonarr.impermanence.enable { - owner.name = "sonarr"; - group.name = "sonarr"; - }; - }; - }; -} diff --git a/modules/nixos-modules/server/wyoming.nix b/modules/nixos-modules/server/wyoming.nix index 1df6877..c9a1474 100644 --- a/modules/nixos-modules/server/wyoming.nix +++ b/modules/nixos-modules/server/wyoming.nix @@ -48,7 +48,7 @@ systemd.services."wyoming-faster-whisper-en".serviceConfig.ProcSubset = lib.mkForce "all"; } (lib.mkIf config.host.impermanence.enable { - environment.persistence."/persist/replicate/system/root" = { + environment.persistence."/persist/system/root" = { enable = true; hideMounts = true; directories = [ diff --git a/modules/nixos-modules/ssh.nix b/modules/nixos-modules/ssh.nix index 6fe8e5c..0a82116 100644 --- a/modules/nixos-modules/ssh.nix +++ b/modules/nixos-modules/ssh.nix @@ -3,42 +3,28 @@ config, ... }: { - options = { - services.openssh.impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.openssh.enable && config.storage.impermanence.enable; - }; - }; - - config = { - services = { - openssh = { - enable = true; - ports = [22]; - settings = { - PasswordAuthentication = false; - UseDns = true; - X11Forwarding = false; + config = lib.mkMerge [ + { + services = { + openssh = { + enable = true; + ports = [22]; + settings = { + PasswordAuthentication = false; + UseDns = true; + X11Forwarding = false; + }; }; }; - }; - storage.datasets.replicate."system/root" = { - files = lib.mkIf config.services.openssh.impermanence.enable (builtins.listToAttrs ( - lib.lists.flatten ( - builtins.map (hostKey: [ - { - name = hostKey.path; - value = {enable = true;}; - } - { - name = "${hostKey.path}.pub"; - value = {enable = true;}; - } - ]) - config.services.openssh.hostKeys - ) - )); - }; - }; + programs.ssh.kexAlgorithms = config.services.openssh.settings.KexAlgorithms; + } + (lib.mkIf config.host.impermanence.enable { + environment.persistence."/persist/system/root" = { + files = lib.lists.flatten ( + builtins.map (hostKey: [hostKey.path "${hostKey.path}.pub"]) config.services.openssh.hostKeys + ); + }; + }) + ]; } diff --git a/modules/nixos-modules/storage/default.nix b/modules/nixos-modules/storage/default.nix deleted file mode 100644 index ebf990a..0000000 --- a/modules/nixos-modules/storage/default.nix +++ /dev/null @@ -1,13 +0,0 @@ -{...}: { - # TODO: we should have an impermanence module for home manager that proxies its values namespaced to the user down here that matches the same interface - - # TODO: we should have a way of enabling impermanence for a systemd config - # these should have an option to put their folder into their own dataset (this needs to support private vs non private) - # options for features that can be added to the dataset - - imports = [ - ./impermanence.nix - ./zfs.nix - ./storage.nix - ]; -} diff --git a/modules/nixos-modules/storage/impermanence.nix b/modules/nixos-modules/storage/impermanence.nix deleted file mode 100644 index 4fdf803..0000000 --- a/modules/nixos-modules/storage/impermanence.nix +++ /dev/null @@ -1,142 +0,0 @@ -args @ { - lib, - config, - ... -}: let - datasetSubmodules = (import ./submodules/dataset.nix) args; - impermanenceDatasetSubmodule = (import ./submodules/impermanenceDataset.nix) args; - - permissionsToMode = permissions: let - permSetToDigit = permSet: - ( - if permSet.read - then 4 - else 0 - ) - + ( - if permSet.write - then 2 - else 0 - ) - + ( - if permSet.execute - then 1 - else 0 - ); - - ownerDigit = permSetToDigit permissions.owner.permissions; - groupDigit = permSetToDigit permissions.group.permissions; - otherDigit = permSetToDigit permissions.other.permissions; - in - toString ownerDigit + toString groupDigit + toString otherDigit; - - # Get the option names from both submodules to automatically determine which are impermanence-specific - regularDatasetEval = lib.evalModules { - modules = [datasetSubmodules]; - specialArgs = args; - }; - impermanenceDatasetEval = lib.evalModules { - modules = [impermanenceDatasetSubmodule]; - specialArgs = args; - }; - - regularDatasetOptions = builtins.attrNames regularDatasetEval.options; - impermanenceDatasetOptions = builtins.attrNames impermanenceDatasetEval.options; - - # Find options that are only in impermanence datasets (not in regular ZFS datasets) - impermanenceOnlyOptions = lib.lists.subtractLists regularDatasetOptions impermanenceDatasetOptions; -in { - options.storage = { - impermanence = { - enable = lib.mkEnableOption "should impermanence be enabled for this system"; - - datasets = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule impermanenceDatasetSubmodule); - default = {}; - }; - }; - }; - - config = lib.mkIf config.storage.impermanence.enable (lib.mkMerge [ - { - assertions = [ - { - assertion = config.storage.zfs.enable; - message = "storage.impermanence can not be used without storage.zfs."; - } - ]; - - system.activationScripts = { - # fixes issues with /var/lib/private not having the correct permissions https://github.com/nix-community/impermanence/issues/254 - "createPersistentStorageDirs".deps = ["var-lib-private-permissions" "users" "groups"]; - - "var-lib-private-permissions" = lib.mkIf config.storage.generateBase { - deps = ["specialfs"]; - text = '' - mkdir -p /persist/replicate/system/root/var/lib/private - chmod 0700 /persist/replicate/system/root/var/lib/private - ''; - }; - }; - - programs.fuse.userAllowOther = true; - - # Suppress sudo lecture on every boot since impermanence wipes the lecture status file - security.sudo.extraConfig = "Defaults lecture=never"; - - fileSystems = - lib.mapAttrs' ( - datasetName: dataset: - lib.nameValuePair "/${datasetName}" { - device = "rpool/${datasetName}"; - fsType = "zfs"; - neededForBoot = true; - } - ) - (lib.filterAttrs ( - datasetName: dataset: dataset.impermanence.enable - ) - config.storage.impermanence.datasets); - - environment.persistence = - lib.mapAttrs (datasetName: dataset: { - enable = true; - hideMounts = true; - persistentStoragePath = "/${datasetName}"; - directories = lib.mapAttrsToList (path: dirConfig: { - directory = path; - user = dirConfig.owner.name; - group = dirConfig.group.name; - mode = permissionsToMode dirConfig; - }) (lib.filterAttrs (_: dirConfig: dirConfig.enable) dataset.directories); - files = lib.mapAttrsToList (path: fileConfig: { - file = path; - parentDirectory = { - user = fileConfig.owner.name; - group = fileConfig.group.name; - mode = permissionsToMode fileConfig; - }; - }) (lib.filterAttrs (_: fileConfig: fileConfig.enable) dataset.files); - }) - (lib.filterAttrs ( - datasetName: dataset: let - enabledDirectories = lib.filterAttrs (_: dirConfig: dirConfig.enable) dataset.directories; - enabledFiles = lib.filterAttrs (_: fileConfig: fileConfig.enable) dataset.files; - in - (enabledDirectories != {}) || (enabledFiles != {}) - ) - (lib.filterAttrs ( - datasetName: dataset: dataset.impermanence.enable - ) - config.storage.impermanence.datasets)); - } - (lib.mkIf config.storage.zfs.enable { - storage.zfs.datasets = - lib.mapAttrs ( - datasetName: dataset: - builtins.removeAttrs dataset impermanenceOnlyOptions - ) - config.storage.impermanence.datasets; - }) - ]); -} diff --git a/modules/nixos-modules/storage/storage.nix b/modules/nixos-modules/storage/storage.nix deleted file mode 100644 index 771d661..0000000 --- a/modules/nixos-modules/storage/storage.nix +++ /dev/null @@ -1,216 +0,0 @@ -args @ { - lib, - config, - ... -}: let - datasetSubmodule = (import ./submodules/dataset.nix) args; - impermanenceDatasetSubmodule = (import ./submodules/impermanenceDataset.nix) args; - - # Get the option names from both submodules to automatically determine which are impermanence-specific - regularDatasetEval = lib.evalModules { - modules = [datasetSubmodule]; - specialArgs = args; - }; - impermanenceDatasetEval = lib.evalModules { - modules = [impermanenceDatasetSubmodule]; - specialArgs = args; - }; - - regularDatasetOptions = builtins.attrNames regularDatasetEval.options; - impermanenceDatasetOptions = builtins.attrNames impermanenceDatasetEval.options; - - # Find options that are only in impermanence datasets (not in regular ZFS datasets) - impermanenceOnlyOptions = lib.lists.subtractLists regularDatasetOptions impermanenceDatasetOptions; -in { - options.storage = { - generateBase = lib.mkOption { - type = lib.types.bool; - default = true; - description = '' - When enabled, enables automatic generation of base datasets (ephemeral, local, replicate roots). - This allows manual definition of datasets matching an existing system layout for migration purposes. - ''; - }; - datasets = { - ephemeral = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule datasetSubmodule); - default = {}; - }; - local = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule impermanenceDatasetSubmodule); - default = {}; - }; - replicate = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule impermanenceDatasetSubmodule); - default = {}; - }; - }; - }; - - config = lib.mkMerge [ - (lib.mkIf (config.storage.zfs.enable && config.storage.generateBase) { - # Create ZFS datasets based on storage.datasets configuration - storage.datasets = { - local = { - "nix" = { - impermanence.enable = false; - type = "zfs_fs"; - mount = "/nix"; - snapshot = { - autoSnapshot = false; - }; - atime = "off"; - relatime = "off"; - }; - }; - }; - }) - (lib.mkIf (config.storage.zfs.enable && config.storage.impermanence.enable && config.storage.generateBase) { - storage.datasets = { - ephemeral = { - "" = { - type = "zfs_fs"; - mount = null; - }; - "system/root" = { - type = "zfs_fs"; - mount = "/"; - snapshot = { - blankSnapshot = true; - }; - }; - }; - # TODO: can we auto set the mount points on these to just be `"/persist/local/${name}"` - local = { - "" = { - mount = "/persist/local"; - }; - }; - # TODO: can we auto set the mount points on these to just be `"/persist/replicate/${name}"` - replicate = { - "" = { - mount = "/persist/replicate"; - }; - "system/root" = { - mount = "/persist/replicate/system/root"; - snapshot = { - autoSnapshot = true; - }; - directories = { - "/var/lib/nixos".enable = true; - "/var/lib/systemd/coredump".enable = true; - }; - files = { - "/etc/machine-id".enable = true; - }; - }; - "home" = { - mount = "/persist/replicate/home"; - snapshot = { - autoSnapshot = true; - }; - }; - "system/var/log" = { - type = "zfs_fs"; - directories = { - "/var/log".enable = true; - }; - }; - }; - }; - - storage.zfs.datasets = lib.mkMerge [ - (lib.mapAttrs' (name: dataset: { - name = - if name == "" - then "ephemeral" - else "ephemeral/${name}"; - value = dataset; - }) - config.storage.datasets.ephemeral) - ]; - - boot.initrd.postResumeCommands = lib.mkAfter '' - zfs rollback -r rpool/ephemeral/system/root@blank - ''; - - storage.impermanence.datasets = lib.mkMerge [ - (lib.mapAttrs' (name: dataset: { - name = - if name == "" - then "persist/local" - else "persist/local/${name}"; - value = dataset; - }) - config.storage.datasets.local) - (lib.mapAttrs' (name: dataset: { - name = - if name == "" - then "persist/replicate" - else "persist/replicate/${name}"; - value = dataset; - }) - config.storage.datasets.replicate) - ]; - }) - (lib.mkIf (config.storage.zfs.enable && !config.storage.impermanence.enable && config.storage.generateBase) { - storage.datasets = { - # Base organizational datasets (only needed when impermanence is disabled) - local = { - "" = { - type = "zfs_fs"; - mount = null; - }; - "root" = { - type = "zfs_fs"; - mount = "/"; - compression = "lz4"; - acltype = "posixacl"; - relatime = "on"; - xattr = "sa"; - snapshot = { - autoSnapshot = true; - blankSnapshot = true; - }; - }; - }; - replicate = { - "" = { - type = "zfs_fs"; - mount = null; - }; - "system/var/log" = { - type = "zfs_fs"; - mount = "/var/log"; - }; - }; - }; - - storage.zfs.datasets = lib.mkMerge [ - (lib.mapAttrs' (name: dataset: { - name = - if name == "" - then "persist/local" - else "persist/local/${name}"; - value = builtins.removeAttrs dataset impermanenceOnlyOptions; - }) - config.storage.datasets.local) - (lib.mapAttrs' (name: dataset: { - name = - if name == "" - then "persist/replicate" - else "persist/replicate/${name}"; - value = builtins.removeAttrs dataset impermanenceOnlyOptions; - }) - config.storage.datasets.replicate) - ]; - }) - ]; - - # TODO: set up datasets for systemd services that want a dataset created - # TODO: home-manager.users..storage.impermanence.enable - # is false then persist the entire directory of the user - # if true persist home-manager.users..storage.impermanence.datasets - # TODO: systemd.services..storage.datasets persists - # TODO: configure other needed storage modes here -} diff --git a/modules/nixos-modules/storage/submodules/dataset.nix b/modules/nixos-modules/storage/submodules/dataset.nix deleted file mode 100644 index 2a45552..0000000 --- a/modules/nixos-modules/storage/submodules/dataset.nix +++ /dev/null @@ -1,86 +0,0 @@ -{lib, ...}: {name, ...}: { - options = { - type = lib.mkOption { - type = lib.types.enum ["zfs_fs" "zfs_volume"]; - default = "zfs_fs"; - description = "Type of ZFS dataset (filesystem or volume)"; - }; - - acltype = lib.mkOption { - type = lib.types.nullOr (lib.types.enum ["off" "nfsv4" "posixacl"]); - default = null; - description = "Access control list type"; - }; - - relatime = lib.mkOption { - type = lib.types.nullOr (lib.types.enum ["on" "off"]); - default = null; - description = "Controls when access time is updated"; - }; - - atime = lib.mkOption { - type = lib.types.nullOr (lib.types.enum ["on" "off"]); - default = null; - description = "Controls whether access time is updated"; - }; - - xattr = lib.mkOption { - type = lib.types.nullOr (lib.types.enum ["on" "off" "sa" "dir"]); - default = null; - description = "Extended attribute storage method"; - }; - - compression = lib.mkOption { - type = lib.types.nullOr (lib.types.enum ["on" "off" "lz4" "gzip" "zstd" "lzjb" "zle"]); - default = null; - description = "Compression algorithm to use"; - }; - - sync = lib.mkOption { - type = lib.types.nullOr (lib.types.enum ["standard" "always" "disabled"]); - default = null; - description = "Synchronous write behavior"; - }; - - mount = lib.mkOption { - type = lib.types.nullOr lib.types.str; - description = "Controls the mount point used for this file system"; - default = null; - }; - - encryption = { - enable = lib.mkEnableOption "should encryption be enabled"; - type = lib.mkOption { - type = lib.types.enum ["aes-128-ccm" "aes-192-ccm" "aes-256-ccm" "aes-128-gcm" "aes-192-gcm" "aes-256-gcm"]; - description = "What encryption type to use"; - }; - keyformat = lib.mkOption { - type = lib.types.enum ["raw" "hex" "passphrase"]; - description = "Format of the encryption key"; - }; - keylocation = lib.mkOption { - type = lib.types.str; - description = "Location of the encryption key"; - }; - }; - - snapshot = { - # This option should set this option flag - autoSnapshot = lib.mkEnableOption "Enable automatic snapshots for this dataset"; - # Creates a blank snapshot in the post create hook for rollback purposes - blankSnapshot = lib.mkEnableOption "Should a blank snapshot be auto created in the post create hook"; - }; - - recordSize = lib.mkOption { - type = lib.types.nullOr lib.types.str; - default = null; - description = "Suggested block size for files in the file system"; - }; - - postCreateHook = lib.mkOption { - type = lib.types.str; - default = ""; - description = "Script to run after dataset creation"; - }; - }; -} diff --git a/modules/nixos-modules/storage/submodules/impermanenceDataset.nix b/modules/nixos-modules/storage/submodules/impermanenceDataset.nix deleted file mode 100644 index e4d3584..0000000 --- a/modules/nixos-modules/storage/submodules/impermanenceDataset.nix +++ /dev/null @@ -1,56 +0,0 @@ -args @ {lib, ...}: {name, ...}: let - datasetSubmodule = (import ./dataset.nix) args; - pathPermissions = { - read = lib.mkEnableOption "should the path have read permissions"; - write = lib.mkEnableOption "should the path have read permissions"; - execute = lib.mkEnableOption "should the path have read permissions"; - }; - pathTypeSubmodule = {name, ...}: { - options = { - enable = lib.mkOption { - type = lib.types.bool; - default = true; - }; - owner = { - name = lib.mkOption { - type = lib.types.str; - default = "root"; - }; - permissions = pathPermissions; - }; - group = { - name = lib.mkOption { - type = lib.types.str; - default = "root"; - }; - permissions = pathPermissions; - }; - other = { - permissions = pathPermissions; - }; - }; - }; -in { - imports = [ - datasetSubmodule - ]; - - options = { - files = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule pathTypeSubmodule); - default = {}; - }; - directories = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule pathTypeSubmodule); - default = {}; - }; - impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = true; - }; - }; - - config = { - mount = lib.mkDefault "/${name}"; - }; -} diff --git a/modules/nixos-modules/storage/zfs.nix b/modules/nixos-modules/storage/zfs.nix deleted file mode 100644 index 2fc6cb4..0000000 --- a/modules/nixos-modules/storage/zfs.nix +++ /dev/null @@ -1,347 +0,0 @@ -args @ { - lib, - pkgs, - config, - ... -}: let - datasetSubmodule = (import ./submodules/dataset.nix) args; - - # Hash function for disk names (max 27 chars to fit GPT limitations) - hashDisk = drive: (builtins.substring 0 27 (builtins.hashString "sha256" drive)); - - # Map "stripe" to "" for disko compatibility (disko uses "" for stripe mode) - diskoPoolMode = - if config.storage.zfs.pool.mode == "stripe" - then "" - else config.storage.zfs.pool.mode; - - # Helper to flatten vdevs into list of devices with names - allVdevDevices = lib.lists.flatten (builtins.map ( - vdev: - builtins.map ( - device: - lib.attrsets.nameValuePair (hashDisk device.device) device - ) - vdev - ) - config.storage.zfs.pool.vdevs); - - # Cache devices with names - allCacheDevices = builtins.map ( - device: - lib.attrsets.nameValuePair (hashDisk device.device) device - ) (config.storage.zfs.pool.cache); - - # All devices (vdevs + cache) - allDevices = allVdevDevices ++ allCacheDevices; - - # Boot devices - filter devices that have boot = true - bootDevices = builtins.filter (device: device.value.boot) allDevices; - - # Helper function to convert dataset options to ZFS properties - datasetToZfsOptions = dataset: let - baseOptions = - (lib.attrsets.optionalAttrs (dataset.acltype != null) {acltype = dataset.acltype;}) - // (lib.attrsets.optionalAttrs (dataset.relatime != null) {relatime = dataset.relatime;}) - // (lib.attrsets.optionalAttrs (dataset.atime != null) {atime = dataset.atime;}) - // (lib.attrsets.optionalAttrs (dataset.xattr != null) {xattr = dataset.xattr;}) - // (lib.attrsets.optionalAttrs (dataset.compression != null) {compression = dataset.compression;}) - // (lib.attrsets.optionalAttrs (dataset.sync != null) {sync = dataset.sync;}) - // (lib.attrsets.optionalAttrs (dataset.recordSize != null) {recordSize = dataset.recordSize;}); - - encryptionOptions = lib.attrsets.optionalAttrs (dataset.encryption.enable) ( - (lib.attrsets.optionalAttrs (dataset.encryption ? type) {encryption = dataset.encryption.type;}) - // (lib.attrsets.optionalAttrs (dataset.encryption ? keyformat) {keyformat = dataset.encryption.keyformat;}) - // (lib.attrsets.optionalAttrs (dataset.encryption ? keylocation) {keylocation = dataset.encryption.keylocation;}) - ); - - mountOptions = lib.attrsets.optionalAttrs (dataset ? mount && dataset.mount ? enable) ( - if builtins.isBool dataset.mount.enable - then { - canmount = - if dataset.mount.enable - then "on" - else "off"; - } - else {canmount = dataset.mount.enable;} - ); - - snapshotOptions = lib.attrsets.optionalAttrs (dataset ? snapshot && dataset.snapshot ? autoSnapshot) { - "com.sun:auto-snapshot" = - if dataset.snapshot.autoSnapshot - then "true" - else "false"; - }; - in - baseOptions // encryptionOptions // mountOptions // snapshotOptions; - - # Helper to generate post create hooks - generatePostCreateHook = name: dataset: - dataset.postCreateHook - + (lib.optionalString dataset.snapshot.blankSnapshot '' - zfs snapshot rpool/${name}@blank - ''); - - # Convert datasets to disko format - convertedDatasets = builtins.listToAttrs ( - (lib.attrsets.mapAttrsToList ( - name: dataset: - lib.attrsets.nameValuePair name { - type = dataset.type; - options = datasetToZfsOptions dataset; - mountpoint = dataset.mount or null; - postCreateHook = generatePostCreateHook name dataset; - } - ) - config.storage.zfs.datasets) - ++ (lib.optional (config.storage.zfs.rootDataset != null) ( - lib.attrsets.nameValuePair "" { - type = config.storage.zfs.rootDataset.type; - options = datasetToZfsOptions config.storage.zfs.rootDataset; - mountpoint = config.storage.zfs.rootDataset.mount or null; - postCreateHook = generatePostCreateHook "" config.storage.zfs.rootDataset; - } - )) - ); -in { - options.storage = { - zfs = { - enable = lib.mkEnableOption "Should zfs be enabled on this system."; - - notifications = { - enable = lib.mkEnableOption "are notifications enabled"; - host = lib.mkOption { - type = lib.types.str; - description = "what is the host that we are going to send the email to"; - }; - port = lib.mkOption { - type = lib.types.port; - description = "what port is the host using to receive mail on"; - }; - to = lib.mkOption { - type = lib.types.str; - description = "what account is the email going to be sent to"; - }; - user = lib.mkOption { - type = lib.types.str; - description = "what user is the email going to be set from"; - }; - tokenFile = lib.mkOption { - type = lib.types.str; - description = "file containing the password to be used by msmtp for notifications"; - }; - }; - - pool = let - deviceType = - lib.types.coercedTo lib.types.str (device: { - device = device; - boot = false; - }) (lib.types.submodule { - options = { - device = lib.mkOption { - type = lib.types.str; - }; - boot = lib.mkEnableOption "should this device be a boot device"; - }; - }); - in { - encryption = { - enable = lib.mkEnableOption "Should encryption be enabled on this pool."; - keyformat = lib.mkOption { - type = lib.types.enum ["raw" "hex" "passphrase"]; - default = "hex"; - description = "Format of the encryption key"; - }; - keylocation = lib.mkOption { - type = lib.types.str; - default = "prompt"; - description = "Location of the encryption key"; - }; - }; - mode = lib.mkOption { - type = lib.types.enum ["stripe" "mirror" "raidz1" "raidz2" "raidz3"]; - default = "raidz2"; - description = "ZFS redundancy mode for the pool"; - }; - bootPartitionSize = lib.mkOption { - type = lib.types.str; - default = "2G"; - description = "Size of the boot partition on boot drives"; - }; - vdevs = lib.mkOption { - type = lib.types.listOf (lib.types.listOf deviceType); - default = []; - description = "List of vdevs, where each vdev is a list of devices"; - }; - cache = lib.mkOption { - type = lib.types.listOf deviceType; - default = []; - }; - }; - - rootDataset = lib.mkOption { - type = lib.types.nullOr (lib.types.submodule datasetSubmodule); - description = "Root ZFS dataset to create"; - default = null; - }; - - datasets = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule datasetSubmodule); - description = "Additional ZFS datasets to create"; - default = {}; - }; - }; - }; - - config = lib.mkIf config.storage.zfs.enable (lib.mkMerge [ - { - # Assertion that we have at least one boot device - assertions = [ - { - assertion = (builtins.length bootDevices) > 0; - message = "ZFS configuration requires at least one boot device. Set boot = true for at least one device in your vdevs or cache."; - } - ]; - - # # Warning about disk/dataset mismatches - these would be runtime checks - # warnings = let - # configuredDisks = builtins.map (device: device.device) (builtins.map (dev: dev.value) allDevices); - # diskWarnings = - # lib.optional (config.storage.zfs.enable) - # "ZFS: Please ensure the following disks are available on your system: ${builtins.concatStringsSep ", " configuredDisks}"; - - # configuredDatasets = builtins.attrNames config.storage.zfs.datasets; - # datasetWarnings = - # lib.optional (config.storage.zfs.enable && (builtins.length configuredDatasets) > 0) - # "ZFS: Configured datasets: ${builtins.concatStringsSep ", " configuredDatasets}. Ensure these match your intended ZFS layout."; - # in - # diskWarnings ++ datasetWarnings; - - services.zfs = { - autoScrub.enable = true; - autoSnapshot.enable = true; - }; - - # # Configure disko for ZFS setup - disko.devices = { - disk = builtins.listToAttrs ( - builtins.map ( - drive: - lib.attrsets.nameValuePair (drive.name) { - type = "disk"; - device = "/dev/disk/by-id/${drive.value.device}"; - content = { - type = "gpt"; - partitions = { - ESP = lib.mkIf drive.value.boot { - size = config.storage.zfs.pool.bootPartitionSize; - type = "EF00"; - content = { - type = "filesystem"; - format = "vfat"; - mountpoint = "/boot"; - mountOptions = ["umask=0077"]; - }; - }; - zfs = { - size = "100%"; - content = { - type = "zfs"; - pool = "rpool"; - }; - }; - }; - }; - } - ) - allDevices - ); - - zpool = { - rpool = { - type = "zpool"; - mode = { - topology = { - type = "topology"; - vdev = - builtins.map (vdev: { - mode = diskoPoolMode; - members = builtins.map (device: hashDisk device.device) vdev; - }) - config.storage.zfs.pool.vdevs; - cache = builtins.map (device: hashDisk device.device) config.storage.zfs.pool.cache; - }; - }; - - options = { - ashift = "12"; - autotrim = "on"; - }; - - rootFsOptions = - { - canmount = "off"; - mountpoint = "none"; - xattr = "sa"; - acltype = "posixacl"; - relatime = "on"; - compression = "lz4"; - "com.sun:auto-snapshot" = "false"; - } - // (lib.attrsets.optionalAttrs config.storage.zfs.pool.encryption.enable { - encryption = "on"; - keyformat = config.storage.zfs.pool.encryption.keyformat; - keylocation = config.storage.zfs.pool.encryption.keylocation; - }); - - datasets = convertedDatasets; - }; - }; - }; - } - (lib.mkIf config.storage.zfs.notifications.enable { - programs.msmtp = { - enable = true; - setSendmail = true; - defaults = { - aliases = "/etc/aliases"; - port = config.storage.zfs.notifications.port; - tls_trust_file = "/etc/ssl/certs/ca-certificates.crt"; - tls = "on"; - auth = "login"; - tls_starttls = "off"; - }; - accounts = { - zfs_notifications = { - auth = true; - tls = true; - host = config.storage.zfs.notifications.host; - passwordeval = "cat ${config.storage.zfs.notifications.tokenFile}"; - user = config.storage.zfs.notifications.user; - from = config.storage.zfs.notifications.user; - }; - }; - }; - - services.zfs = { - zed = { - enableMail = true; - - settings = { - ZED_DEBUG_LOG = "/tmp/zed.debug.log"; - ZED_EMAIL_ADDR = [config.storage.zfs.notifications.to]; - ZED_EMAIL_PROG = "${pkgs.msmtp}/bin/msmtp"; - ZED_EMAIL_OPTS = "-a zfs_notifications @ADDRESS@"; - - ZED_NOTIFY_INTERVAL_SECS = 3600; - ZED_NOTIFY_VERBOSE = true; - - ZED_USE_ENCLOSURE_LEDS = true; - ZED_SCRUB_AFTER_RESILVER = true; - }; - }; - }; - }) - ]); -} diff --git a/modules/nixos-modules/sync.nix b/modules/nixos-modules/sync.nix new file mode 100644 index 0000000..96f54d5 --- /dev/null +++ b/modules/nixos-modules/sync.nix @@ -0,0 +1,69 @@ +{ + config, + lib, + syncthingConfiguration, + ... +}: let + mountDir = "/mnt/sync"; + configDir = "/etc/syncthing"; +in { + config = lib.mkMerge [ + { + systemd = lib.mkIf config.services.syncthing.enable { + tmpfiles.rules = [ + "A ${mountDir} - - - - u:syncthing:rwX,g:syncthing:rwX,o::-" + "d ${mountDir} 2755 syncthing syncthing -" + "d ${config.services.syncthing.dataDir} 775 syncthing syncthing -" + "d ${config.services.syncthing.configDir} 755 syncthing syncthing -" + ]; + }; + } + (lib.mkIf config.services.syncthing.enable (lib.mkMerge [ + { + services.syncthing = { + user = "syncthing"; + group = "syncthing"; + dataDir = "${mountDir}/default"; + configDir = configDir; + overrideDevices = true; + overrideFolders = true; + configuration = syncthingConfiguration; + deviceName = config.networking.hostName; + }; + } + + (lib.mkIf config.host.impermanence.enable { + assertions = + [ + { + assertion = config.services.syncthing.configDir == configDir; + message = "syncthing config dir does not match persistence"; + } + ] + ++ lib.attrsets.mapAttrsToList (_: folder: { + assertion = lib.strings.hasPrefix mountDir folder.path; + message = "syncthing folder ${folder.label} is stored at ${folder.path} which not under the persisted path of ${mountDir}"; + }) + config.services.syncthing.settings.folders; + environment.persistence = { + "/persist/system/root" = { + enable = true; + hideMounts = true; + directories = [ + { + directory = mountDir; + user = "syncthing"; + group = "syncthing"; + } + { + directory = configDir; + user = "syncthing"; + group = "syncthing"; + } + ]; + }; + }; + }) + ])) + ]; +} diff --git a/modules/nixos-modules/sync/default.nix b/modules/nixos-modules/sync/default.nix deleted file mode 100644 index 5640417..0000000 --- a/modules/nixos-modules/sync/default.nix +++ /dev/null @@ -1,6 +0,0 @@ -{...}: { - imports = [ - ./sync.nix - ./storage.nix - ]; -} diff --git a/modules/nixos-modules/sync/storage.nix b/modules/nixos-modules/sync/storage.nix deleted file mode 100644 index 61bf855..0000000 --- a/modules/nixos-modules/sync/storage.nix +++ /dev/null @@ -1,32 +0,0 @@ -{ - config, - lib, - ... -}: let - mountDir = "/mnt/sync"; - configDir = "/etc/syncthing"; -in { - options = { - services.syncthing.impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.syncthing.enable && config.storage.impermanence.enable; - }; - }; - - config = lib.mkIf config.services.syncthing.enable { - storage.datasets.replicate."system/root" = { - directories = { - "${mountDir}" = lib.mkIf config.services.syncthing.impermanence.enable { - enable = true; - owner.name = "syncthing"; - group.name = "syncthing"; - }; - "${configDir}" = lib.mkIf config.services.syncthing.impermanence.enable { - enable = true; - owner.name = "syncthing"; - group.name = "syncthing"; - }; - }; - }; - }; -} diff --git a/modules/nixos-modules/sync/sync.nix b/modules/nixos-modules/sync/sync.nix deleted file mode 100644 index 28b6e38..0000000 --- a/modules/nixos-modules/sync/sync.nix +++ /dev/null @@ -1,36 +0,0 @@ -{ - config, - lib, - syncthingConfiguration, - ... -}: let - mountDir = "/mnt/sync"; - configDir = "/etc/syncthing"; -in { - config = lib.mkMerge [ - { - systemd = lib.mkIf config.services.syncthing.enable { - tmpfiles.rules = [ - "A ${mountDir} - - - - u:syncthing:rwX,g:syncthing:rwX,o::-" - "d ${mountDir} 2755 syncthing syncthing -" - "d ${config.services.syncthing.dataDir} 775 syncthing syncthing -" - "d ${config.services.syncthing.configDir} 755 syncthing syncthing -" - ]; - }; - } - (lib.mkIf config.services.syncthing.enable (lib.mkMerge [ - { - services.syncthing = { - user = "syncthing"; - group = "syncthing"; - dataDir = "${mountDir}/default"; - configDir = configDir; - overrideDevices = true; - overrideFolders = true; - configuration = syncthingConfiguration; - deviceName = config.networking.hostName; - }; - } - ])) - ]; -} diff --git a/modules/nixos-modules/tailscale.nix b/modules/nixos-modules/tailscale.nix new file mode 100644 index 0000000..db664e8 --- /dev/null +++ b/modules/nixos-modules/tailscale.nix @@ -0,0 +1,34 @@ +{ + config, + lib, + ... +}: let + tailscale_data_directory = "/var/lib/tailscale"; +in { + options.host.tailscale = { + enable = lib.mkEnableOption "should tailscale be enabled on this computer"; + }; + + config = lib.mkIf config.services.tailscale.enable ( + lib.mkMerge [ + { + # any configs we want shared between all machines + } + (lib.mkIf config.host.impermanence.enable { + environment.persistence = { + "/persist/system/root" = { + enable = true; + hideMounts = true; + directories = [ + { + directory = tailscale_data_directory; + user = "root"; + group = "root"; + } + ]; + }; + }; + }) + ] + ); +} diff --git a/modules/nixos-modules/tailscale/default.nix b/modules/nixos-modules/tailscale/default.nix deleted file mode 100644 index 7a283e8..0000000 --- a/modules/nixos-modules/tailscale/default.nix +++ /dev/null @@ -1,6 +0,0 @@ -{...}: { - imports = [ - ./tailscale.nix - ./storage.nix - ]; -} diff --git a/modules/nixos-modules/tailscale/storage.nix b/modules/nixos-modules/tailscale/storage.nix deleted file mode 100644 index 7ac7e9a..0000000 --- a/modules/nixos-modules/tailscale/storage.nix +++ /dev/null @@ -1,24 +0,0 @@ -{ - config, - lib, - ... -}: let - tailscale_data_directory = "/var/lib/tailscale"; -in { - options = { - services.tailscale.impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.tailscale.enable && config.storage.impermanence.enable; - }; - }; - - config = lib.mkIf config.services.tailscale.enable { - storage.datasets.replicate."system/root" = { - directories."${tailscale_data_directory}" = lib.mkIf config.services.tailscale.impermanence.enable { - enable = true; - owner.name = "root"; - group.name = "root"; - }; - }; - }; -} diff --git a/modules/nixos-modules/tailscale/tailscale.nix b/modules/nixos-modules/tailscale/tailscale.nix deleted file mode 100644 index 06899b1..0000000 --- a/modules/nixos-modules/tailscale/tailscale.nix +++ /dev/null @@ -1,19 +0,0 @@ -{ - config, - lib, - ... -}: { - options = { - host.tailscale = { - enable = lib.mkEnableOption "should tailscale be enabled on this computer"; - }; - }; - - config = lib.mkIf config.services.tailscale.enable ( - lib.mkMerge [ - { - # any configs we want shared between all machines - } - ] - ); -} diff --git a/modules/nixos-modules/users.nix b/modules/nixos-modules/users.nix index 9cef952..bee8ca3 100644 --- a/modules/nixos-modules/users.nix +++ b/modules/nixos-modules/users.nix @@ -373,60 +373,79 @@ in { }; }; } - (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ - { - # sops age key needs to be available to pre persist for user generation - storage.datasets.local."system/sops" = { - type = "zfs_fs"; - mount = SOPS_AGE_KEY_DIRECTORY; - atime = "off"; - relatime = "off"; - impermanence.enable = false; - }; - } - (lib.mkIf (!config.storage.impermanence.enable) { - storage.datasets.replicate = lib.mkMerge ( - builtins.map (user: { - "home/${user.name}" = { + (lib.mkIf config.host.impermanence.enable { + boot.initrd.postResumeCommands = lib.mkAfter ( + lib.strings.concatLines (builtins.map (user: "zfs rollback -r rpool/local/home/${user.name}@blank") + normalUsers) + ); + + systemd = { + tmpfiles.rules = + builtins.map ( + user: "d /persist/home/${user.name} 700 ${user.name} ${user.name} -" + ) + normalUsers; + }; + + fileSystems = lib.mkMerge [ + { + ${SOPS_AGE_KEY_DIRECTORY}.neededForBoot = true; + } + ( + builtins.listToAttrs ( + builtins.map (user: + lib.attrsets.nameValuePair "/persist/home/${user.name}" { + neededForBoot = true; + }) + normalUsers + ) + ) + ( + builtins.listToAttrs ( + builtins.map (user: + lib.attrsets.nameValuePair "/home/${user.name}" { + neededForBoot = true; + }) + normalUsers + ) + ) + ]; + + host.storage.pool.extraDatasets = lib.mkMerge ( + [ + { + # sops age key needs to be available to pre persist for user generation + "local/system/sops" = { type = "zfs_fs"; - mount = "/home/${user.name}"; - snapshot.autoSnapshot = true; + mountpoint = SOPS_AGE_KEY_DIRECTORY; + options = { + atime = "off"; + relatime = "off"; + canmount = "on"; + }; + }; + } + ] + ++ ( + builtins.map (user: { + "local/home/${user.name}" = { + type = "zfs_fs"; + mountpoint = "/home/${user.name}"; + options = { + canmount = "on"; + }; + postCreateHook = '' + zfs snapshot rpool/local/home/${user.name}@blank + ''; + }; + "persist/home/${user.name}" = { + type = "zfs_fs"; + mountpoint = "/persist/home/${user.name}"; }; }) normalUsers - ); - }) - (lib.mkIf config.storage.impermanence.enable { - storage.datasets.ephemeral = lib.mkMerge ( - builtins.map (user: { - "home/${user.name}" = { - type = "zfs_fs"; - mount = "/home/${user.name}"; - snapshot.blankSnapshot = true; - }; - }) - normalUsers - ); - - # Post resume commands to rollback user home datasets to blank snapshots - # Only add these when generateBase is true -- when false, the legacy - # storage config is responsible for providing rollback commands with - # the correct (old) dataset paths. - boot.initrd.postResumeCommands = lib.mkIf config.storage.generateBase (lib.mkAfter ( - lib.strings.concatLines (builtins.map (user: "zfs rollback -r rpool/ephemeral/home/${user.name}@blank") - normalUsers) - )); - - # TODO: I don't think we need this anymore but I have not tested it - # Create persist home directories with proper permissions - # systemd = { - # tmpfiles.rules = - # builtins.map ( - # user: "d /persist/replicate/home/${user.name} 700 ${user.name} ${user.name} -" - # ) - # normalUsers; - # }; - }) - ])) + ) + ); + }) ]; } diff --git a/util/default.nix b/util/default.nix index d72d00d..246543d 100644 --- a/util/default.nix +++ b/util/default.nix @@ -29,6 +29,7 @@ common-modules ++ [ sops-nix.homeManagerModules.sops + impermanence.homeManagerModules.impermanence ../modules/home-manager-modules ];