main
  1{
  2  lib,
  3  den,
  4  ...
  5}: {
  6  den.aspects.disk.btrfs-disko = {
  7    includes = [
  8      den.aspects.disk.disko
  9      den.aspects.disk.btrfs
 10    ];
 11
 12    settings.host = {
 13      deviceId = lib.mkOption {
 14        type = lib.types.str;
 15        default = "";
 16        description = ''
 17          Disk device id (e.g., "ata-..." or "/dev/disk/by-id/...").
 18          If not set, auto-detects a single non-USB disk via facter.
 19        '';
 20      };
 21      swapSize = lib.mkOption {
 22        type = lib.types.int;
 23        default = 0;
 24        description = "Size of swap in MiB, 0 disables swap.";
 25      };
 26      grubPart = lib.mkOption {
 27        type = lib.types.nullOr lib.types.bool;
 28        default = null;
 29        description = "Whether to creat 1M part for GRUB. Set null to auto detect";
 30      };
 31    };
 32
 33    nixos = {
 34      config,
 35      host,
 36      ...
 37    }: let
 38      cfg = host.settings.disk.btrfs-disko;
 39
 40      diskDevice =
 41        if cfg.deviceId != ""
 42        then
 43          if lib.hasPrefix "/dev/" cfg.deviceId
 44          then cfg.deviceId
 45          else "/dev/disk/by-id/" + cfg.deviceId
 46        else let
 47          native-disks = builtins.filter (f: f.driver != "usb-storage") config.hardware.facter.report.hardware.disk;
 48          disk-labels =
 49            map (
 50              disk:
 51                builtins.head (
 52                  builtins.filter (f: builtins.substring 0 16 f == "/dev/disk/by-id/") disk.unix_device_names
 53                )
 54            )
 55            native-disks;
 56        in
 57          if (builtins.length disk-labels == 1)
 58          then (builtins.head disk-labels)
 59          else
 60            abort (
 61              "Multiple disks found. Please set settings.disk.btrfs-disko.deviceId. Found: "
 62              + toString disk-labels
 63            );
 64
 65      defaultBtrfsOpts = [
 66        "defaults"
 67        "compress=zstd:1"
 68        "ssd"
 69        "discard=async"
 70      ];
 71    in {
 72      disko.devices = {
 73        disk = {
 74          main = {
 75            device = diskDevice;
 76            type = "disk";
 77            content = {
 78              type = "gpt";
 79              partitions = {
 80                boot =
 81                  lib.mkIf
 82                  (
 83                    if cfg.grubPart == null
 84                    then !config.hardware.facter.detected.uefi.supported
 85                    else cfg.grubPart
 86                  )
 87                  {
 88                    label = "GRUB";
 89                    size = "1M";
 90                    type = "EF02";
 91                    priority = 0;
 92                  };
 93                ESP = {
 94                  label = "boot";
 95                  name = "ESP";
 96                  size = "1G";
 97                  type = "EF00";
 98                  priority = 1;
 99                  content = {
100                    type = "filesystem";
101                    format = "vfat";
102                    mountpoint = "/boot";
103                    mountOptions = ["defaults" "umask=0077"];
104                  };
105                };
106                nixos = {
107                  size = "100%";
108                  priority = 2;
109                  content = {
110                    type = "btrfs";
111                    subvolumes =
112                      {
113                        "/root" = {
114                          mountpoint = "/";
115                          mountOptions = defaultBtrfsOpts ++ ["noatime"];
116                        };
117                        "/home" = {
118                          mountpoint = "/home";
119                          mountOptions = defaultBtrfsOpts ++ ["noatime"];
120                        };
121                        "/nix" = {
122                          mountpoint = "/nix";
123                          mountOptions = defaultBtrfsOpts ++ ["noatime"];
124                        };
125                        "/persist" = lib.mkIf (host.hasAspect den.aspects.disk.preservation) {
126                          mountpoint = "/persist";
127                          mountOptions = defaultBtrfsOpts;
128                        };
129                        "/cache" = lib.mkIf (host.hasAspect den.aspects.disk.preservation) {
130                          mountpoint = "/cache";
131                          mountOptions = defaultBtrfsOpts;
132                        };
133                      }
134                      // lib.optionalAttrs (cfg.swapSize > 0) {
135                        "@swap" = {
136                          mountpoint = "/swap";
137                          swap.swapfile.size = "${toString cfg.swapSize}M";
138                        };
139                      };
140                  };
141                };
142              };
143            };
144          };
145        };
146      };
147
148      fileSystems = {
149        "/nix".neededForBoot = true;
150        "/home".neededForBoot = true;
151        "/persist".neededForBoot = true;
152        "/cache".neededForBoot = true;
153      };
154    };
155  };
156}