{ config, lib, ... }: { options.storage = let datasetSubmodule = {name, ...}: { # TODO: we need to figure out what options a dataset can have in zfs }; impermanenceDatasetSubmodules = [ datasetSubmodule ({...}: let 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 = { user = lib.mkOption { type = lib.types.str; default = "nouser"; }; permissions = pathPermissions; }; group = { group = lib.mkOption { type = lib.types.str; default = "nogroup"; }; permissions = pathPermissions; }; other = { permissions = pathPermissions; }; }; }; in { options = { files = lib.types.mkOption { type = lib.types.attrsOf (lib.types.submodule pathTypeSubmodule); default = {}; }; directories = { type = lib.types.attrsOf (lib.types.submodule pathTypeSubmodule); default = {}; }; }; }) ]; in { zfs = { # TODO: enable option implementation 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"; }; }; # TODO: we need options to configure zfs pools # we should have warnings when the configured pool is missing drives after activation # TODO: implementation of this # TODO: validations that we have at least one boot drive pool = let deviceType = lib.types.coercedTo lib.types.str (device: { device = device; boot = false; }) { device = lib.mkOption { type = lib.types.str; }; boot = lib.mkEnableOption "should this device be a boot device"; }; in { encryption = lib.mkEnableOption "Should encryption be enabled on this pool."; vdevs = lib.mkOption { type = lib.types.listOf deviceType; default = []; }; cache = lib.mkOption { type = lib.types.attrsOf deviceType; }; }; # TODO:create the root dataset automatically # 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 datasets = lib.types.attrsOf (lib.types.submodule datasetSubmodule); }; impermanence = { enable = lib.mkEnableOption "should impermanence be enabled for this system"; # TODO: enable option implementation # TODO: assertion that zfs needs to be enabled when impermanence is enabled # 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 # We should also create datasets for systemd modules that have have impermanence enabled for them datasets = lib.types.attrsOf (lib.types.submodule impermanenceDatasetSubmodules); # TODO: this should just live under home-manager.users..storage.impermanence home-manager = lib.types.attrsOf (lib.types.submodule ({name, ...}: { enable = lib.mkEnableOption "should impermanence be enabled for this user"; # We should by default create the `local/home/${name}`, and `persist/home/${name}` datasets datasets = lib.types.attrsOf (lib.types.submodule impermanenceDatasetSubmodules); })); }; # 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"; } ) ]; }