summaryrefslogtreecommitdiff
path: root/machines/enzo
diff options
context:
space:
mode:
authorHenry J. Webster <hwebs@hwebs.info>2026-07-30 10:38:54 -0500
committerHenry J. Webster <hwebs@hwebs.info>2026-07-30 10:38:54 -0500
commit11ba75e40728e8b5735c9c7cefc7af1631c942af (patch)
treecaef581fd56be96ec9c5338a17d0332b51b08e64 /machines/enzo
parentd47d76336a9680b661220d23c5d429d90a2f3f9e (diff)
Restructure into a multi-machine flake and add the enzo laptop
Split the single-host config into a shared minimal base plus opt-in profiles, and add a second machine (enzo laptop) alongside kusanagi. - flake: add mkMachine helper; declare nixosConfigurations.kusanagi and .enzo; add the disko input; move overlays per-machine. - common/: minimal base only (boot, networking, tailscale, locale, nix settings, neovim editor). Old configuration.nix/programs.nix removed. - profiles/: coarse opt-in bundles (desktop, dev, apps). kusanagi imports all three and stays byte-for-byte identical to the running system. - machines/kusanagi/: all workstation-only config (ROCm, Scarlett audio, optical-disc archiving, NAS mounts, kavita/immich/ollama); hardware config moved here unchanged. User stays 'henz'. - machines/enzo/: new laptop. Declarative LUKS+btrfs via disko (LVM-on-LUKS, TPM2+PIN unlock, zram + encrypted swap for suspend-then-hibernate); its own minimal niri desktop with playback-only audio (no rtkit/jack); user 'hwebs'. Assisted-by: Claude:claude-opus-4-8
Diffstat (limited to 'machines/enzo')
-rw-r--r--machines/enzo/default.nix91
-rw-r--r--machines/enzo/desktop.nix68
-rw-r--r--machines/enzo/disko.nix93
-rw-r--r--machines/enzo/hardware-configuration.nix27
4 files changed, 279 insertions, 0 deletions
diff --git a/machines/enzo/default.nix b/machines/enzo/default.nix
new file mode 100644
index 0000000..65adef6
--- /dev/null
+++ b/machines/enzo/default.nix
@@ -0,0 +1,91 @@
+# enzo — laptop. Gets the shared common/ base plus laptop-only settings.
+{ inputs, pkgs, ... }:
+
+{
+ imports = [
+ # enzo's own minimal desktop (not the shared profiles/desktop.nix). Add
+ # ../../profiles/dev.nix or apps.nix here, or one-off packages below, as needed.
+ ./desktop.nix
+
+ inputs.disko.nixosModules.disko
+ ./disko.nix
+ ./hardware-configuration.nix
+ ];
+
+ networking.hostName = "enzo";
+
+ # Set this to the NixOS release the laptop is first installed from, then
+ # leave it. (See the comment in machines/kusanagi/default.nix.)
+ system.stateVersion = "26.05";
+
+ # --- User account (hwebs) ---
+ # (Desktop groups audio/input come from ./desktop.nix.)
+ users.users.hwebs = {
+ isNormalUser = true;
+ description = "Henry Webster";
+ extraGroups = [
+ "networkmanager"
+ "wheel"
+ ];
+ packages = with pkgs; [
+ git
+ neovim
+ tmux
+ btop
+ ripgrep
+ unzip
+ xclip
+ bc
+ psmisc
+ usbutils
+ stow
+ nushell
+ ];
+ };
+
+ nix.settings.trusted-users = [
+ "root"
+ "hwebs"
+ ];
+
+ # --- Disk encryption / hibernation / swap ---
+ # systemd in initrd is required for TPM2 unlock and clean hibernate resume.
+ boot.initrd.systemd.enable = true;
+
+ # TPM2 auto-unlock. Enroll the key WITH A PIN post-install:
+ # sudo systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=7 \
+ # --tpm2-with-pin=yes /dev/nvme0n1p2
+ # The original LUKS passphrase stays as a fallback keyslot. disko already
+ # declares boot.initrd.luks.devices."crypted".device.
+ boot.initrd.luks.devices."crypted".crypttabExtraOpts = [ "tpm2-device=auto" ];
+
+ # zram for everyday paging; the encrypted LVM swap from disko.nix is the
+ # hibernation target (boot.resumeDevice comes from disko's resumeDevice=true).
+ zramSwap.enable = true;
+
+ # Suspend-to-RAM on lid close, auto-hibernate after a delay so a dying battery
+ # doesn't lose the session. Everyday resume needs only the login password; the
+ # LUKS PIN appears only on cold boot / hibernate resume.
+ services.logind.settings.Login.HandleLidSwitch = "suspend-then-hibernate";
+ systemd.sleep.settings.Sleep.HibernateDelaySec = "60min";
+
+ # btrfs + SSD upkeep.
+ services.btrfs.autoScrub.enable = true;
+ services.fstrim.enable = true;
+
+ # --- Laptop power management ---
+ # power-profiles-daemon integrates with GNOME's power settings. If you prefer
+ # finer-grained control, disable this and enable services.tlp instead.
+ services.power-profiles-daemon.enable = true;
+ powerManagement.enable = true;
+
+ # Backlight control from the CLI / keybinds (programs.light was removed from
+ # nixpkgs; acpilight provides the udev rules and brightnessctl the CLI).
+ hardware.acpilight.enable = true;
+ environment.systemPackages = [ pkgs.brightnessctl ];
+
+ # --- GPU ---
+ # TODO: adjust for the laptop's actual GPU once known. For an Intel iGPU add
+ # intel-media-driver to hardware.graphics.extraPackages; for a discrete AMD
+ # GPU you may want ROCm as in machines/kusanagi/default.nix.
+}
diff --git a/machines/enzo/desktop.nix b/machines/enzo/desktop.nix
new file mode 100644
index 0000000..8fb68ef
--- /dev/null
+++ b/machines/enzo/desktop.nix
@@ -0,0 +1,68 @@
+# Minimal niri desktop for enzo — deliberately separate from ../../profiles/desktop.nix
+# so the laptop can grow on its own terms. Add to it (or switch to the shared
+# profile) whenever you want more.
+{ pkgs, ... }:
+
+{
+ # --- Session / login ---
+ programs.niri.enable = true;
+ programs.dconf.enable = true;
+
+ services.greetd = {
+ enable = true;
+ settings.default_session = {
+ command = "${pkgs.tuigreet}/bin/tuigreet --time --remember --cmd niri-session";
+ user = "greeter";
+ };
+ };
+ # Let tuigreet write its cache (for --remember).
+ systemd.services.greetd.serviceConfig = {
+ Type = "idle";
+ StandardInput = "tty";
+ StandardOutput = "tty";
+ StandardError = "journal";
+ TTYReset = true;
+ TTYVHangup = true;
+ TTYVTDisallocate = true;
+ };
+
+ # --- Graphics ---
+ hardware.graphics.enable = true;
+
+ # --- Audio (playback only; no rtkit/jack — enzo isn't for recording).
+ # If you ever hear dropouts under load, add: security.rtkit.enable = true;
+ services.pulseaudio.enable = false;
+ services.pipewire = {
+ enable = true;
+ alsa.enable = true;
+ pulse.enable = true;
+ };
+
+ # --- Fonts (minimal) ---
+ fonts.enableDefaultPackages = true;
+ fonts.packages = with pkgs; [
+ noto-fonts
+ nerd-fonts.symbols-only
+ ];
+
+ environment.sessionVariables = {
+ XCURSOR_THEME = "Adwaita";
+ XCURSOR_SIZE = "24";
+ };
+
+ # --- User: groups + niri essentials ---
+ users.users.hwebs.extraGroups = [
+ "audio"
+ "input"
+ ];
+
+ users.users.hwebs.packages = with pkgs; [
+ ghostty # terminal
+ fuzzel # launcher
+ swaybg # wallpaper
+ xwayland-satellite # run X11 apps under niri
+ networkmanagerapplet # wifi tray
+ mako # notifications
+ libnotify # notify-send
+ ];
+}
diff --git a/machines/enzo/disko.nix b/machines/enzo/disko.nix
new file mode 100644
index 0000000..ddfa4d3
--- /dev/null
+++ b/machines/enzo/disko.nix
@@ -0,0 +1,93 @@
+# Declarative disk layout for enzo: LVM-on-LUKS with a btrfs root and an
+# encrypted swap logical volume (>= RAM) for hibernation.
+#
+# nvme0n1
+# ├─ p1 ESP 1 GiB vfat /boot (unencrypted)
+# └─ p2 LUKS2 "crypted" ── LVM vg "vg" ──
+# ├─ lv "swap" swap (hibernation target)
+# └─ lv "root" btrfs subvolumes: @ @home @nix @log @snapshots
+#
+# Apply on the laptop with (prompts for the LUKS passphrase = fallback keyslot):
+# sudo nix run github:nix-community/disko/latest -- \
+# --mode destroy,format,mount ./machines/enzo/disko.nix
+{ ... }:
+
+{
+ disko.devices = {
+ disk.main = {
+ type = "disk";
+ device = "/dev/nvme0n1"; # TODO: confirm on the laptop with `lsblk`
+ content = {
+ type = "gpt";
+ partitions = {
+ ESP = {
+ size = "1G";
+ type = "EF00";
+ content = {
+ type = "filesystem";
+ format = "vfat";
+ mountpoint = "/boot";
+ mountOptions = [ "umask=0077" ];
+ };
+ };
+ luks = {
+ size = "100%";
+ content = {
+ type = "luks";
+ name = "crypted";
+ settings = {
+ # Allow TRIM through LUKS (minor metadata leak, fine for a laptop SSD).
+ allowDiscards = true;
+ };
+ content = {
+ type = "lvm_pv";
+ vg = "vg";
+ };
+ };
+ };
+ };
+ };
+ };
+
+ lvm_vg.vg = {
+ type = "lvm_vg";
+ lvs = {
+ swap = {
+ size = "20G"; # TODO: set >= RAM (RAM + a little) for hibernation
+ content = {
+ type = "swap";
+ resumeDevice = true; # emits swapDevices + boot.resumeDevice
+ };
+ };
+ root = {
+ size = "100%FREE";
+ content = {
+ type = "btrfs";
+ extraArgs = [ "-f" ];
+ subvolumes = {
+ "@" = {
+ mountpoint = "/";
+ mountOptions = [ "compress=zstd" "noatime" ];
+ };
+ "@home" = {
+ mountpoint = "/home";
+ mountOptions = [ "compress=zstd" "noatime" ];
+ };
+ "@nix" = {
+ mountpoint = "/nix";
+ mountOptions = [ "compress=zstd" "noatime" ];
+ };
+ "@log" = {
+ mountpoint = "/var/log";
+ mountOptions = [ "compress=zstd" "noatime" ];
+ };
+ "@snapshots" = {
+ mountpoint = "/.snapshots";
+ };
+ };
+ };
+ };
+ };
+ };
+ };
+}
diff --git a/machines/enzo/hardware-configuration.nix b/machines/enzo/hardware-configuration.nix
new file mode 100644
index 0000000..30c00ee
--- /dev/null
+++ b/machines/enzo/hardware-configuration.nix
@@ -0,0 +1,27 @@
+# PLACEHOLDER — regenerate on the laptop.
+#
+# During install run: nixos-generate-config --no-filesystems --root /mnt
+# then copy the generated hardware-configuration.nix over this file. Because the
+# disk layout is declared in ./disko.nix, this file must NOT define fileSystems
+# or swapDevices (--no-filesystems omits them). It only needs to evaluate until
+# the real one is generated; it will NOT boot as-is.
+{ config, lib, pkgs, modulesPath, ... }:
+
+{
+ imports = [
+ (modulesPath + "/installer/scan/not-detected.nix")
+ ];
+
+ # nixos-generate-config fills these in on the laptop. TPM unlock in initrd
+ # needs the TPM module (tpm_tis / tpm_crb) here — usually auto-detected.
+ boot.initrd.availableKernelModules = [ ];
+ boot.initrd.kernelModules = [ ];
+ boot.kernelModules = [ ];
+ boot.extraModulePackages = [ ];
+
+ # fileSystems / swapDevices intentionally omitted — provided by ./disko.nix.
+
+ networking.useDHCP = lib.mkDefault true;
+
+ nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
+}