Commit 67499e2
Changed files (10)
modules
core
disk
hosts
tribios
roles
modules/core/impermanence/btrfs.nix
@@ -0,0 +1,94 @@
+{
+ den,
+ lib,
+ ...
+}: {
+ den.aspects.core.impermanence.btrfs = {
+ settings = {
+ partId = lib.mkOption {
+ type = lib.types.str;
+ default = "";
+ description = ''
+ Disk part id (e.g., "/dev/disk/by-partuuid/...").
+ If not set, auto-detect when aspects.disk.disko is included.
+ '';
+ };
+ preserveTime = lib.mkOption {
+ type = lib.types.ints.positive;
+ default = 30;
+ description = ''
+ Time old root/home subvolumes preserved.
+ '';
+ };
+ };
+
+ nixos = {host, ...}: let
+ cfg = host.settings.core.impermanence.btrfs;
+
+ partDevice =
+ if cfg.partId != ""
+ then
+ if lib.hasPrefix "/dev/" cfg.partId
+ then cfg.partId
+ else "/dev/disk/by-partuuid/" + cfg.partId
+ else if (host.hasAspect den.aspects.disk.disko)
+ then "/dev/disk/by-partlabel/disk-main-nixos"
+ else abort "Host not included disk.disko aspect, Please set settings.core.impermanence.btrfs.partId.";
+
+ partDeviceUnit =
+ lib.removePrefix "-" (
+ lib.replaceStrings ["-" "/"] ["\\x2d" "-"] partDevice
+ )
+ + ".device";
+
+ script = subvol: ''
+ mkdir -p /mnt
+
+ mount ${partDevice} /mnt
+
+ mkdir -p /mnt/${subvol}_old
+ timestamp=$(date "+%Y-%m-%-d_%H:%M:%S")
+ rm -rf "/mnt/${subvol}_old/$timestamp"
+ mv /mnt/${subvol} /mnt/${subvol}_old/$timestamp
+
+ delete_subvolume_recursively() {
+ IFS=$'\n'
+ for i in $(btrfs subvolume list -o "$1" | cut -f 9- -d ' '); do
+ delete_subvolume_recursively "/mnt/$i"
+ done
+ btrfs subvolume delete "$1"
+ }
+
+ for i in $(find /mnt/${subvol}_old -maxdepth 1 -mtime +${toString cfg.preserveTime}); do
+ delete_subvolume_recursively "$i"
+ done
+
+ btrfs subvolume create /mnt/${subvol}
+ umount /mnt
+ '';
+ in {
+ boot.initrd.systemd.services = lib.mkIf (host.hasAspect den.aspects.disk.btrfs) {
+ rollback-btrfs-root = {
+ description = "Rollback and clean old btrfs root subvolumes";
+ wantedBy = ["initrd.target"];
+ before = ["sysroot.mount"];
+ after = [partDeviceUnit];
+ requires = [partDeviceUnit];
+ unitConfig.DefaultDependencies = false;
+ serviceConfig.Type = "oneshot";
+ script = script "root";
+ };
+ rollback-btrfs-home = {
+ description = "Rollback and clean old btrfs home subvolumes";
+ wantedBy = ["initrd.target"];
+ before = ["home.mount"];
+ after = [partDeviceUnit];
+ requires = [partDeviceUnit];
+ unitConfig.DefaultDependencies = false;
+ serviceConfig.Type = "oneshot";
+ script = script "home";
+ };
+ };
+ };
+ };
+}
modules/core/impermanence/default.nix
@@ -0,0 +1,54 @@
+{
+ den,
+ inputs,
+ ...
+}: {
+ flake-file.inputs.impermanence = {
+ url = "github:nix-community/impermanence";
+ inputs.nixpkgs.follows = "nixpkgs";
+ inputs.home-manager.follows = "home-manager";
+ };
+
+ den.aspects.core.impermanence = {
+ includes = with den.aspects.core.impermanence; [
+ persist-collector
+ persist-user-collector
+ persist-home-collector
+
+ btrfs
+ ];
+
+ persist = {
+ directories = [
+ "/etc/NetworkManager/system-connections"
+ ];
+ files = [
+ "/root/.bash_history"
+ ];
+ };
+ cache = {
+ directories = [
+ "/var/lib/nixos"
+ "/srv"
+ ];
+ };
+ persistUser = {
+ files = [
+ ".bash_history"
+ ];
+ };
+
+ nixos = {lib, ...}: {
+ imports = [inputs.impermanence.nixosModules.impermanence];
+
+ environment.persistence."/persist" = {
+ enable = true;
+ hideMounts = lib.mkDefault true;
+ };
+ environment.persistence."/cache" = {
+ enable = true;
+ hideMounts = lib.mkDefault true;
+ };
+ };
+ };
+}
modules/core/impermanence/quirks.nix
@@ -0,0 +1,81 @@
+{
+ den,
+ lib,
+ ...
+}: let
+ # Shared collector body for the home-manager persistence quirks. Reused by
+ # two distinct aspects so that the same logic runs at both the host scope
+ # (projected onto users via the host-aspects battery) and the user scope.
+ homeCollector = {
+ persistHome,
+ cacheHome,
+ config,
+ ...
+ }: let
+ _removeHomePrefix = path: lib.removePrefix "${config.home.homeDirectory}/" path;
+ removeHomePrefix = path:
+ if (lib.typeOf path == "string")
+ then _removeHomePrefix path
+ else path // {directory = _removeHomePrefix path.directory;};
+ removeHomePrefixes = paths: lib.map removeHomePrefix paths;
+ mergePersist = entries: {
+ directories = lib.unique (lib.concatMap (e: removeHomePrefixes (e.directories or [])) entries);
+ files = lib.unique (lib.concatMap (e: removeHomePrefixes (e.files or [])) entries);
+ };
+ in {
+ home.persistence."/persist" = mergePersist persistHome;
+ home.persistence."/cache" = mergePersist cacheHome;
+ };
+in {
+ den.quirks.persist.description = "Persistent directories/files collected from aspects (host)";
+ den.quirks.cache.description = "Cache directories/files collected from aspects (host)";
+ den.quirks.persistUser.description = "Persistent directories/files collected from aspects (user)";
+ den.quirks.cacheUser.description = "Cache directories/files collected from aspects (user)";
+ den.quirks.persistHome.description = "Persistent directories/files collected from aspects (home manager)";
+ den.quirks.cacheHome.description = "Cache directories/files collected from aspects (home manager)";
+
+ den.aspects.core.impermanence.persist-collector = {
+ nixos = {
+ persist,
+ cache,
+ ...
+ }: let
+ mergePersist = entries: {
+ directories = lib.unique (lib.concatMap (e: e.directories or []) entries);
+ files = lib.unique (lib.concatMap (e: e.files or []) entries);
+ };
+ in {
+ environment.persistence."/persist" = mergePersist persist;
+ environment.persistence."/cache" = mergePersist cache;
+ };
+ };
+
+ den.aspects.core.impermanence.persist-user-collector = {user, ...}: {
+ nixos = {
+ persistUser,
+ cacheUser,
+ ...
+ }: let
+ mergePersist = entries: {
+ directories = lib.unique (lib.concatMap (e: e.directories or []) entries);
+ files = lib.unique (lib.concatMap (e: e.files or []) entries);
+ };
+ in {
+ environment.persistence."/persist".users.${user.userName} = mergePersist persistUser;
+ environment.persistence."/cache".users.${user.userName} = mergePersist cacheUser;
+ };
+ };
+
+ den.aspects.core.impermanence.persist-home-collector.homeManager = homeCollector;
+ den.aspects.core.impermanence.persist-home-user-collector.homeManager = homeCollector;
+
+ den.default.includes = (
+ den.lib.policy.when
+ ({host, ...}:
+ host.hasAspect {
+ name = "impermanence";
+ meta.provider = ["core"];
+ })
+ [den.aspects.core.impermanence.persist-home-user-collector]
+ );
+}
modules/core/openssh.nix
@@ -1,5 +1,20 @@
{lib, ...}: {
den.aspects.core.openssh = {
+ persist = {config, ...}: {
+ files =
+ builtins.concatMap
+ (key: [key.path] ++ ["${key.path}.pub"])
+ config.services.openssh.hostKeys;
+ };
+ persistUser = {
+ directories = [
+ {
+ directory = ".ssh";
+ mode = "0700";
+ }
+ ];
+ };
+
nixos = {
services.openssh = {
generateHostKeys = true;
modules/core/systemd.nix
@@ -1,5 +1,28 @@
{
den.aspects.core.systemd = {
+ persist = {
+ files = [
+ "/etc/machine-id"
+ ];
+ };
+ cache = {
+ directories = [
+ "/var/lib/systemd/coredump"
+ "/var/lib/systemd/timers"
+ "/var/lib/systemd/catalog"
+ {
+ directory = "/var/lib/systemd/network";
+ mode = "0755";
+ user = "systemd-network";
+ group = "systemd-network";
+ }
+ "/var/log"
+ ];
+ files = [
+ "/var/lib/lastlog/lastlog2.db"
+ ];
+ };
+
nixos = {
systemd.tmpfiles.rules = [
"d /var/lib/systemd/coredump 0755 root root 7d"
modules/disk/btrfs-disko.nix
@@ -110,11 +110,11 @@
mountpoint = "/nix";
mountOptions = defaultBtrfsOpts ++ ["noatime"];
};
- "/persist" = {
+ "/persist" = lib.mkIf (host.hasAspect den.aspects.core.impermanence) {
mountpoint = "/persist";
mountOptions = defaultBtrfsOpts;
};
- "/cache" = {
+ "/cache" = lib.mkIf (host.hasAspect den.aspects.core.impermanence) {
mountpoint = "/cache";
mountOptions = defaultBtrfsOpts;
};
modules/hosts/tribios/file-system.nix
@@ -1,4 +1,8 @@
{den, ...}: {
+ den.hosts.aarch64-linux.tribios = {
+ settings.core.impermanence.btrfs.partId = "/dev/disk/by-uuid/9886235f-5a74-41e8-919b-9e4096a7cb9e";
+ };
+
den.aspects.tribios = {
# Manually manage file system.
# Since tribios has edk2-rk3588 firmware in main storage.
modules/roles/default.nix
@@ -13,6 +13,7 @@
core.hardware.thunderbolt
core.editor
+ core.impermanence
core.localization
core.nameservers
core.nftables
flake.lock
@@ -123,6 +123,29 @@
"type": "github"
}
},
+ "impermanence": {
+ "inputs": {
+ "home-manager": [
+ "home-manager"
+ ],
+ "nixpkgs": [
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1769548169,
+ "narHash": "sha256-03+JxvzmfwRu+5JafM0DLbxgHttOQZkUtDWBmeUkN8Y=",
+ "owner": "nix-community",
+ "repo": "impermanence",
+ "rev": "7b1d382faf603b6d264f58627330f9faa5cba149",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-community",
+ "repo": "impermanence",
+ "type": "github"
+ }
+ },
"import-tree": {
"locked": {
"lastModified": 1778781969,
@@ -163,6 +186,7 @@
"flake-file": "flake-file",
"flake-parts": "flake-parts",
"home-manager": "home-manager",
+ "impermanence": "impermanence",
"import-tree": "import-tree",
"nixpkgs": "nixpkgs"
}
flake.nix
@@ -25,6 +25,13 @@
url = "github:nix-community/home-manager";
inputs.nixpkgs.follows = "nixpkgs";
};
+ impermanence = {
+ url = "github:nix-community/impermanence";
+ inputs = {
+ home-manager.follows = "home-manager";
+ nixpkgs.follows = "nixpkgs";
+ };
+ };
import-tree.url = "github:denful/import-tree";
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
};