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 = {
 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    };
 27
 28    nixos = {
 29      config,
 30      host,
 31      ...
 32    }: let
 33      cfg = host.settings.disk.btrfs-disko;
 34
 35      diskDevice =
 36        if cfg.deviceId != ""
 37        then
 38          if lib.hasPrefix "/dev/" cfg.deviceId
 39          then cfg.deviceId
 40          else "/dev/disk/by-id/" + cfg.deviceId
 41        else let
 42          native-disks = builtins.filter (f: f.driver != "usb-storage") config.hardware.facter.report.hardware.disk;
 43          disk-labels =
 44            map (
 45              disk:
 46                builtins.head (
 47                  builtins.filter (f: builtins.substring 0 16 f == "/dev/disk/by-id/") disk.unix_device_names
 48                )
 49            )
 50            native-disks;
 51        in
 52          if (builtins.length disk-labels == 1)
 53          then (builtins.head disk-labels)
 54          else
 55            abort (
 56              "Multiple disks found. Please set settings.disk.btrfs-disko.deviceId. Found: "
 57              + toString disk-labels
 58            );
 59
 60      defaultBtrfsOpts = [
 61        "defaults"
 62        "compress=zstd:1"
 63        "ssd"
 64        "discard=async"
 65      ];
 66    in {
 67      disko.devices = {
 68        disk = {
 69          main = {
 70            device = diskDevice;
 71            type = "disk";
 72            content = {
 73              type = "gpt";
 74              partitions = {
 75                boot = lib.mkIf (!config.hardware.facter.report.uefi.supported) {
 76                  label = "GRUB";
 77                  size = "1M";
 78                  type = "EF02";
 79                  priority = 0;
 80                };
 81                ESP = {
 82                  label = "boot";
 83                  name = "ESP";
 84                  size = "512M";
 85                  type = "EF00";
 86                  priority = 1;
 87                  content = {
 88                    type = "filesystem";
 89                    format = "vfat";
 90                    mountpoint = "/boot";
 91                    mountOptions = ["defaults"];
 92                  };
 93                };
 94                nixos = {
 95                  size = "100%";
 96                  priority = 2;
 97                  content = {
 98                    type = "btrfs";
 99                    subvolumes =
100                      {
101                        "/root" = {
102                          mountpoint = "/";
103                          mountOptions = defaultBtrfsOpts ++ ["noatime"];
104                        };
105                        "/home" = {
106                          mountpoint = "/home";
107                          mountOptions = defaultBtrfsOpts ++ ["noatime"];
108                        };
109                        "/nix" = {
110                          mountpoint = "/nix";
111                          mountOptions = defaultBtrfsOpts ++ ["noatime"];
112                        };
113                        "/persist" = lib.mkIf (host.hasAspect den.aspects.core.impermanence) {
114                          mountpoint = "/persist";
115                          mountOptions = defaultBtrfsOpts;
116                        };
117                        "/cache" = lib.mkIf (host.hasAspect den.aspects.core.impermanence) {
118                          mountpoint = "/cache";
119                          mountOptions = defaultBtrfsOpts;
120                        };
121                      }
122                      // lib.optionalAttrs (cfg.swapSize > 0) {
123                        "@swap" = {
124                          mountpoint = "/swap";
125                          swap.swapfile.size = "${toString cfg.swapSize}M";
126                        };
127                      };
128                  };
129                };
130              };
131            };
132          };
133        };
134      };
135
136      fileSystems = {
137        "/nix".neededForBoot = true;
138        "/home".neededForBoot = true;
139        "/persist".neededForBoot = true;
140        "/cache".neededForBoot = true;
141      };
142    };
143  };
144}