{ config, pkgs, lib, inputs, ... }: let cfg = config.persist; takeAll = what: concatMap (x: x.${what}); persists = with cfg; [ state derivative cache ]; absoluteHomeFiles = map (x: "${cfg.homeDir}/${x}"); allHomeFiles = takeAll "homeFiles" persists; absoluteEtcFiles = map (x: "/etc/${x}"); allEtcFiles = absoluteEtcFiles (takeAll "etcFiles" persists); allDirectories = takeAll "directories" persists; inherit (builtins) concatMap; inherit (lib) mkIf; in { options = let inherit (lib) mkOption mkEnableOption; inherit (lib.types) listOf path str; common = { directories = mkOption { type = listOf path; default = [ ]; }; etcFiles = mkOption { type = listOf str; default = [ ]; }; homeFiles = mkOption { type = listOf str; default = [ ]; }; }; in { persist = { enable = mkEnableOption "a tmpfs root with explicit opt-in state"; persistRoot = mkOption { type = path; default = "/persist"; }; homeDir = mkOption { type = path; default = "/home/balsoft"; }; # Stuff that matters # TODO backups of this stuff state = { # backup = {...}; } // common; # Stuff that can be computed from declarative+state, but is never invalidated (so shouldn't be cleaned up) derivative = common; # Stuff that's just there to speed up the system # It's cleaned up regularly, to solve the cache invalidation problem once and for all cache = { clean = { enable = mkEnableOption "cleaning the cache files and directories"; dates = mkOption { type = str; default = "weekly"; description = "A systemd.time calendar description of when to clean the cache files"; }; }; } // common; }; }; imports = [ inputs.impermanence.nixosModules.impermanence # Eugh (let module = (import "${inputs.impermanence}/home-manager.nix" { inherit pkgs lib; config = lib.recursiveUpdate config.home-manager.users.balsoft { home.persistence."${cfg.persistRoot}${cfg.homeDir}" = { directories = [ ]; files = allHomeFiles; allowOther = false; removePrefixDirectory = false; }; }; }); in { config.home-manager.users.balsoft = lib.mkIf cfg.enable module.config; }) ]; config = mkIf cfg.enable { environment.persistence.${cfg.persistRoot} = { directories = allDirectories; files = allEtcFiles; }; fileSystems."/" = { device = "none"; options = [ "defaults" "size=4G" "mode=755" ]; fsType = "tmpfs"; }; boot.initrd.postMountCommands = assert config.fileSystems ? ${cfg.persistRoot} && config.fileSystems.${cfg.persistRoot}.neededForBoot; '' mkdir -p /mnt-root/nix mount --bind /mnt-root${cfg.persistRoot}/nix /mnt-root/nix chmod 755 /mnt-root ''; # Euuuugh systemd.services.persist-cache-cleanup = lib.mkIf cfg.cache.clean.enable { description = "Cleaning up cache files and directories"; script = '' ${builtins.concatStringsSep "\n" (map (x: "rm ${lib.escapeShellArg x}") (absoluteEtcFiles cfg.cache.etcFiles ++ absoluteHomeFiles cfg.cache.homeFiles))} ${builtins.concatStringsSep "\n" (map (x: "rm -rf ${lib.escapeShellArg x}") cfg.cache.directories)} ''; startAt = cfg.cache.clean.dates; }; }; }