diff --git a/README.md b/README.md index fcc04cf..08a9aa1 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ Personal NixOS configuration using the [den](https://github.com/vic/den) framewo |------------|----------------------|--------| | `desktop` | Gaming + development | Nvidia | | `notebook` | Portable development | Intel | +| `work` | Portable development | AMD + Nvidia (PRIME) | ## Prerequisites @@ -43,6 +44,64 @@ just gc just diff ``` +## Installing on a new machine (notebook) + +Only the `notebook` host supports automated installation via the installer ISO. +No hardware-specific preparation is needed beforehand — the installer detects the +hardware automatically using `nixos-generate-config`. + +### 1. Build and flash the ISO + +The ISO embeds the flake at build time, so **all changes must be committed first**. + +```bash +# Commit any pending changes — the ISO only includes git-tracked files +git add -A && git commit -m "pre-install snapshot" + +just iso +sudo dd if=result/iso/*.iso of=/dev/sdX bs=4M status=progress && sync +``` + +### 2. Boot and install + +Boot the ISO on the target machine. The installer logs in as root automatically. + +**If you need Wi-Fi**, the script will prompt you to connect via `nmtui` before proceeding. + +Run the installer: +```bash +nixos-install-host +``` + +The script will: +1. Prompt to set up network (Wi-Fi via `nmtui` if needed) +2. Ask which host to install (`notebook`) +3. **Auto-detect hardware** with `nixos-generate-config` and inject the result into the flake +4. Show available disks and confirm the target +5. Ask for the age key (`keys.txt`) — copy it to a USB or provide the path manually +6. Partition the disk with LUKS + btrfs via disko (prompts for LUKS passphrase) +7. Run `nixos-install` +8. Copy the flake (with detected hardware config) to `~/repos/nixos` on the new system + +### 3. Age key + +The age key used to encrypt `secrets/secrets.yaml` must be available during installation. +Bring it on a USB drive — the installer will find it automatically at `/run/media/*/keys.txt`. + +### 4. After the first boot + +Commit the auto-detected hardware config so future ISO builds include it: + +```bash +cd ~/repos/nixos +git add modules/_hardware/notebook-hardware.nix +git commit -m "add notebook hardware config" +git push +``` + +> **Note:** GRUB will ask for the LUKS passphrase once during boot, and the initrd will ask +> again to mount the root filesystem. This is expected behavior with `enableCryptodisk`. + ## Secrets (SOPS + age) Secrets are stored encrypted in `secrets/secrets.yaml` and decrypted at boot by [sops-nix](https://github.com/Mic92/sops-nix). diff --git a/installer/default.nix b/installer/default.nix index ea525df..1639f42 100644 --- a/installer/default.nix +++ b/installer/default.nix @@ -14,23 +14,71 @@ let warn() { echo -e "''${YELLOW}[warn]''${NC} $1"; } die() { echo -e "''${RED}[error]''${NC} $1"; exit 1; } - FLAKE="path:/etc/nixos-config" + # ------------------------------------------------------------------ # + # Network # + # ------------------------------------------------------------------ # + + header "Network" + echo "An internet connection is required to download packages during installation." + echo "If you need Wi-Fi, connect now with: nmtui" + echo "" + read -p "Press Enter when the network is ready (or Ctrl-C to abort)..." + + # ------------------------------------------------------------------ # + # Host selection # + # ------------------------------------------------------------------ # header "NixOS Installer" + echo "Evaluating flake..." + HOSTS_JSON=$(nix eval "path:/etc/nixos-config#nixosConfigurations" \ + --apply builtins.attrNames --json) \ + || die "Failed to evaluate the flake at /etc/nixos-config." + echo "Available hosts:" - nix eval "$FLAKE#nixosConfigurations" --apply builtins.attrNames --json 2>/dev/null \ - | ${pkgs.jq}/bin/jq -r '.[]' | grep -v installer | sed 's/^/ /' + echo "$HOSTS_JSON" | ${pkgs.jq}/bin/jq -r '.[]' | grep -v installer | sed 's/^/ /' echo "" read -p "Host to install: " HOST [ -z "$HOST" ] && die "No host specified." + # Verify the selected host has disko configured before touching anything. + # local.notebook.disk is only defined for hosts that include notebook-disko.nix. + echo "Checking that '$HOST' supports automated installation..." + nix eval "path:/etc/nixos-config#nixosConfigurations.$HOST.config.local.notebook.disk" \ + --raw > /dev/null 2>&1 \ + || die "Host '$HOST' does not have a disko configuration. Only 'notebook' is supported." + ok "Host '$HOST' is supported." + + # ------------------------------------------------------------------ # + # Hardware detection # + # ------------------------------------------------------------------ # + + header "Detecting hardware" + echo "Running nixos-generate-config..." + DETECTED_HW=$(nixos-generate-config --no-filesystems --show-hardware-config 2>/dev/null) \ + || die "Failed to detect hardware. Is nixos-generate-config available?" + ok "Hardware detected." + + # Build a writable copy of the embedded flake and inject the detected + # hardware config so both disko and nixos-install use the right modules. + echo "Preparing installation flake..." + rm -rf /tmp/nixos-flake + cp -rL /etc/nixos-config /tmp/nixos-flake + echo "$DETECTED_HW" > /tmp/nixos-flake/modules/_hardware/notebook-hardware.nix + ok "Hardware config written to flake." + + FLAKE="path:/tmp/nixos-flake" + + # ------------------------------------------------------------------ # + # Disk selection # + # ------------------------------------------------------------------ # + header "Available disks" lsblk -d -o NAME,SIZE,MODEL --noheadings | grep -v loop echo "" - CONFIGURED_DISK=$(nix eval "$FLAKE#nixosConfigurations.$HOST.config.local.notebook.disk" --raw 2>/dev/null \ - || echo "/dev/nvme0n1") + CONFIGURED_DISK=$(nix eval "$FLAKE#nixosConfigurations.$HOST.config.local.notebook.disk" \ + --raw 2>/dev/null || echo "/dev/nvme0n1") echo -e "Disk configured for this host: ''${BOLD}$CONFIGURED_DISK''${NC}" read -p "Target disk [$CONFIGURED_DISK]: " DISK_INPUT DISK="''${DISK_INPUT:-$CONFIGURED_DISK}" @@ -39,13 +87,23 @@ let if [ "$DISK" != "$CONFIGURED_DISK" ]; then warn "Disk $DISK differs from configured $CONFIGURED_DISK." - warn "Edit modules/_hardware/-disko.nix and rebuild the ISO if it doesn't match." + warn "The disko config will use $DISK regardless, but update local.notebook.disk in" + warn "modules/_hardware/notebook-disko.nix and commit it after installation." fi + # ------------------------------------------------------------------ # + # Age key # + # ------------------------------------------------------------------ # + header "Age key (secrets decryption)" KEYS_PATH="" - for candidate in /run/media/nixos/*/keys.txt /run/media/*/keys.txt /tmp/keys.txt; do + # Search already-mounted media (standard auto-mount paths) + for candidate in \ + /run/media/nixos/*/keys.txt \ + /run/media/*/keys.txt \ + /run/media/*/*/keys.txt \ + /tmp/keys.txt; do if [ -f "$candidate" ]; then KEYS_PATH="$candidate" ok "Found at $KEYS_PATH" @@ -53,16 +111,48 @@ let fi done + # When booting via Ventoy the USB data partition (label "Ventoy") is not + # auto-mounted in the live environment — only the ISO loop device is set up. + # Detect it by label, mount read-only, copy keys.txt to /tmp, then unmount. + if [ -z "$KEYS_PATH" ]; then + VENTOY_DEV=$(${pkgs.util-linux}/bin/blkid -t LABEL="Ventoy" -o device 2>/dev/null | head -1 || true) + if [ -n "$VENTOY_DEV" ]; then + warn "keys.txt not found in mounted media. Trying Ventoy partition ($VENTOY_DEV)..." + VENTOY_MNT=$(mktemp -d) + if mount -o ro "$VENTOY_DEV" "$VENTOY_MNT" 2>/dev/null; then + if [ -f "$VENTOY_MNT/keys.txt" ]; then + cp "$VENTOY_MNT/keys.txt" /tmp/keys.txt + chmod 600 /tmp/keys.txt + KEYS_PATH="/tmp/keys.txt" + ok "Found on Ventoy USB — copied to /tmp/keys.txt" + else + warn "Ventoy partition mounted but no keys.txt found at its root." + echo "Contents of Ventoy root:" + ls "$VENTOY_MNT" | sed 's/^/ /' + fi + umount "$VENTOY_MNT" 2>/dev/null || true + rm -rf "$VENTOY_MNT" + else + warn "Could not mount Ventoy partition $VENTOY_DEV." + fi + fi + fi + if [ -z "$KEYS_PATH" ]; then warn "Age key not found automatically." echo "Options:" - echo " 1. Copy keys.txt to a USB, mount it, and it will be found at /run/media/*" - echo " 2. Enter the path manually below" + echo " 1. Place keys.txt at the root of the Ventoy USB (alongside the .iso)" + echo " 2. Mount another USB and it will be found at /run/media/*" + echo " 3. Enter the path manually below" read -p "Path to keys.txt: " KEYS_PATH fi [ ! -f "$KEYS_PATH" ] && die "Age key not found at $KEYS_PATH" + # ------------------------------------------------------------------ # + # Confirmation # + # ------------------------------------------------------------------ # + header "Confirmation" echo -e " Host : ''${BOLD}$HOST''${NC}" echo -e " Disk : ''${BOLD}$DISK''${NC} (ALL DATA WILL BE ERASED)" @@ -71,6 +161,10 @@ let read -p "Type 'yes' to continue: " CONFIRM [ "$CONFIRM" != "yes" ] && { echo "Aborted."; exit 1; } + # ------------------------------------------------------------------ # + # Installation # + # ------------------------------------------------------------------ # + header "Partitioning and formatting" echo "(You will be prompted to set the LUKS passphrase.)" disko --mode destroy,format,mount --flake "$FLAKE#$HOST" @@ -83,14 +177,38 @@ let header "Installing NixOS" nixos-install --root /mnt --flake "$FLAKE#$HOST" --no-root-passwd + # ------------------------------------------------------------------ # + # Post-install: save config to the new system # + # ------------------------------------------------------------------ # + + header "Saving configuration" + # Copy the flake (including the detected hardware config) to the user's + # working directory on the installed system so they can commit it. + mkdir -p /mnt/home/zoty/repos + cp -r /tmp/nixos-flake /mnt/home/zoty/repos/nixos + # UID 1000 is the first normal user, matching the zoty account. + chown -R 1000:1000 /mnt/home/zoty/repos/nixos + ok "Configuration saved to ~/repos/nixos on the installed system." + echo "" ok "Installation complete! Remove the USB drive and reboot." + echo "" + echo "After the first boot, commit the detected hardware config:" + echo " cd ~/repos/nixos" + echo " git add modules/_hardware/notebook-hardware.nix" + echo " git commit -m 'add notebook hardware config'" + echo " git push" ''; in { imports = [ "${modulesPath}/installer/cd-dvd/installation-cd-minimal.nix" ]; + # Ventoy's initrd does not support zstd (the NixOS 25.11 default), which + # causes a -110 timeout and "unable to read id index table" on boot. + # xz is universally supported by Ventoy across all versions. + isoImage.squashfsCompression = "xz -Xdict-size 100%"; + services.getty.autologinUser = lib.mkForce "root"; nix.settings = { @@ -112,7 +230,9 @@ in { pkgs.neovim ]; - # Embed the flake source so the install script can reference it at path:/etc/nixos-config + # Embed the flake source so the install script can copy it at install time. + # The script copies it to /tmp/nixos-flake (writable) and injects the + # detected hardware config before running disko and nixos-install. environment.etc."nixos-config".source = ../.; documentation.enable = lib.mkForce false; diff --git a/modules/_hardware/notebook-hardware.nix b/modules/_hardware/notebook-hardware.nix index 2775cc9..14e109c 100644 --- a/modules/_hardware/notebook-hardware.nix +++ b/modules/_hardware/notebook-hardware.nix @@ -1,17 +1,28 @@ -# Hardware-specific configuration for the notebook. -# fileSystems and swap are declared by disko (notebook-disko.nix). -# Regenerate this file with: nixos-generate-config --no-filesystems --show-hardware-config -{ config, lib, modulesPath, ... }: +# Notebook hardware configuration. +# +# This file is REPLACED automatically during installation. +# The installer runs: +# nixos-generate-config --no-filesystems --show-hardware-config +# and writes the output here before calling nixos-install. +# +# After installation, commit the generated file: +# git add modules/_hardware/notebook-hardware.nix +# git commit -m "add notebook hardware config" +# +# To regenerate on the installed system: +# nixos-generate-config --no-filesystems --show-hardware-config \ +# > ~/repos/nixos/modules/_hardware/notebook-hardware.nix +{ lib, modulesPath, ... }: { imports = [ (modulesPath + "/installer/scan/not-detected.nix") ]; - boot.initrd.availableKernelModules = [ "xhci_pci" "ehci_pci" "ahci" "usb_storage" "ums_realtek" "sd_mod" "sr_mod" ]; - boot.kernelModules = [ "kvm-intel" ]; + # Filled in by the installer. These are placeholder values that allow + # the flake to evaluate before the real hardware is detected. + boot.initrd.availableKernelModules = [ "nvme" "xhci_pci" "ahci" "usbhid" "usb_storage" "sd_mod" ]; + boot.kernelModules = []; boot.extraModulePackages = []; - networking.useDHCP = lib.mkDefault true; - nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; - hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; } diff --git a/modules/_hardware/work-hardware.nix b/modules/_hardware/work-hardware.nix index 7a65b27..830fe29 100644 --- a/modules/_hardware/work-hardware.nix +++ b/modules/_hardware/work-hardware.nix @@ -9,7 +9,7 @@ ]; boot.initrd.availableKernelModules = [ "nvme" "xhci_pci" "ahci" "usbhid" "usb_storage" "sd_mod" ]; - boot.initrd.kernelModules = [ ]; + boot.initrd.kernelModules = [ "amdgpu" ]; boot.kernelModules = [ "kvm-amd" ]; boot.extraModulePackages = [ ]; diff --git a/modules/den.nix b/modules/den.nix index faf9bf8..6e603c2 100644 --- a/modules/den.nix +++ b/modules/den.nix @@ -72,6 +72,7 @@ in { den.aspects.prismlauncher den.aspects.androidcam den.aspects.winvm + den.aspects.leisure ]; nixos = { system.stateVersion = "25.11"; diff --git a/modules/leisure.nix b/modules/leisure.nix new file mode 100644 index 0000000..1522fd3 --- /dev/null +++ b/modules/leisure.nix @@ -0,0 +1,24 @@ +{ den, inputs, ... }: { + den.aspects.leisure.nixos = { pkgs, ... }: + let + pkgs-unstable = import inputs.nixpkgs-unstable { + system = pkgs.system; + config.allowUnfree = true; + }; + in { + environment.systemPackages = with pkgs-unstable; [ + # Streaming + stremio-linux-shell + + # Torrenting + qbittorrent + + # Media playback + mpv + yt-dlp + + # Social + discord + ]; + }; +} diff --git a/modules/notebook.nix b/modules/notebook.nix index 6b90a5a..af645cf 100644 --- a/modules/notebook.nix +++ b/modules/notebook.nix @@ -14,10 +14,11 @@ services.thermald.enable = true; boot = { - initrd.availableKernelModules = [ "xhci_pci" "ahci" "usbhid" "sd_mod" ]; - initrd.kernelModules = [ "dm-crypt" ]; - kernelModules = [ "kvm-intel" ]; - initrd.compressor = "zstd"; + # Hardware-specific modules (kvm-intel/kvm-amd, nvme, etc.) come from + # modules/_hardware/notebook-hardware.nix, generated during installation + # by nixos-generate-config --no-filesystems --show-hardware-config. + initrd.kernelModules = [ "dm-crypt" ]; + initrd.compressor = "zstd"; loader = { grub = {