# borrows from: # https://xeiaso.net/blog/paranoid-nixos-2021-07-18 # https://elis.nu/blog/2020/05/nixos-tmpfs-as-root/ # https://github.com/nix-community/impermanence { config, lib, pkgs, utils, sane-lib, ... }: with lib; let path = sane-lib.path; sane-types = sane-lib.types; cfg = config.sane.persist; storeType = types.submodule { options = { storeDescription = mkOption { type = types.nullOr types.str; default = null; description = '' an optional description of the store, which is rendered like {store.name}: {store.storeDescription} for example, a store named "private" could have description "ecnrypted to the user's password and decrypted on login". ''; }; origin = mkOption { type = types.str; }; prefix = mkOption { type = types.str; default = "/"; description = '' optional prefix to strip from children when stored here. for example, prefix="/var/private" and mountpoint="/mnt/crypt/private" would cause /var/private/www/root to be stored at /mnt/crypt/private/www/root instead of /mnt/crypt/private/var/private/www/root. ''; }; defaultMethod = mkOption { type = types.enum [ "bind" "symlink" ]; default = "bind"; description = '' preferred way to link items from the store into the fs ''; }; defaultOrdering.wantedBeforeBy = mkOption { type = types.listOf types.str; default = [ "local-fs.target" ]; description = '' list of units or targets which would prefer that everything in this store be initialized before they run, but failing to do so should not error the items in this list. ''; }; defaultOrdering.wantedBy = mkOption { type = types.listOf types.str; default = [ ]; description = '' list of units or targets which, upon activation, should activate all units in this store. ''; }; }; }; # allows a user to specify the store either by name or as an attrset coercedToStore = types.coercedTo types.str (s: cfg.stores."${s}") storeType; # options common to all entries, whether they're keyed by path or store entryOpts = { options = { acl = mkOption { type = sane-types.aclOverride; default = {}; }; method = mkOption { type = types.nullOr (types.enum [ "bind" "symlink" ]); default = null; description = '' how to link the store entry into the fs ''; }; type = mkOption { type = types.enum [ "dir" "file" ]; default = "dir"; description = '' whether the thing being persisted is a whole directory, or just one file. ''; }; }; }; # options for a single mountpoint / persistence where the store is specified externally entryInStore = types.submodule [ entryOpts { options = { path = mkOption { type = types.str; }; }; } ]; # allow "bar/baz" as shorthand for { path = "bar/baz"; } entryInStoreOrShorthand = types.coercedTo types.str (d: { path = d; }) entryInStore; # allow the user to provide the `acl` field inline: we pop acl sub-attributes placed at the # toplevel and move them into an `acl` attribute. convertInlineAcl = to: types.coercedTo types.attrs (orig: lib.recursiveUpdate (builtins.removeAttrs orig ["user" "group" "mode" ]) { acl = sane-lib.filterByName ["user" "group" "mode"] orig; } ) to; # entry where the path is specified externally entryAtPath = types.submodule [ entryOpts { options = { store = mkOption { type = coercedToStore; }; }; } ]; # this submodule creates one attr per store, so that the user can specify something like: #