diff --git a/flake.lock b/flake.lock index 39566af..bdde15f 100755 --- a/flake.lock +++ b/flake.lock @@ -46,6 +46,26 @@ "type": "github" } }, + "disko": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1780290312, + "narHash": "sha256-eTAlX0CwgB84Ts3GaBd944A3DRXVMzgA0EqroZBISUo=", + "owner": "nix-community", + "repo": "disko", + "rev": "115e5211780054d8a890b41f0b7734cafad54dfe", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "disko", + "type": "github" + } + }, "flake-parts": { "inputs": { "nixpkgs-lib": "nixpkgs-lib" @@ -285,6 +305,7 @@ "root": { "inputs": { "caelestia-shell": "caelestia-shell", + "disko": "disko", "flake-parts": "flake-parts", "flake-utils": "flake-utils", "home-manager": "home-manager", diff --git a/flake.nix b/flake.nix index 5d51296..ed6a709 100644 --- a/flake.nix +++ b/flake.nix @@ -27,6 +27,10 @@ url = "github:caelestia-dots/shell"; inputs.nixpkgs.follows = "nixpkgs-unstable"; }; + disko = { + url = "github:nix-community/disko"; + inputs.nixpkgs.follows = "nixpkgs"; + }; microPlugins-vivify = { url = "git+https://codeberg.org/gibbert/micro-vivify"; flake = false; diff --git a/modules/applications/hyprland.nix b/modules/applications/hyprland.nix index c4de24a..e9e25d4 100644 --- a/modules/applications/hyprland.nix +++ b/modules/applications/hyprland.nix @@ -48,6 +48,13 @@ ]; }; }; + flake.homeModules.hyprland-desktop = _: { + wayland.windowManager.hyprland.settings = { + monitor = [ + ", highres@highrr, auto, 1" + ]; + }; + }; flake.homeModules.hyprland = {pkgs-unstable, ...}: let wallpaper = ../../assets/wallpaper1.jpg; in { diff --git a/modules/applications/zsh.nix b/modules/applications/zsh.nix index b63a7db..6e82f18 100644 --- a/modules/applications/zsh.nix +++ b/modules/applications/zsh.nix @@ -7,10 +7,18 @@ }; }; }; - flake.homeModules.zsh = {pkgs, ...}: { + flake.homeModules.zsh-desktop = _: { + programs.zsh = { + shellAliases = { + snrt = "git -C ~/nixos add -A && time sudo nixos-rebuild test --flake ~/nixos/#desktop && pkill shell || true && pkill caelestia-shell || true && caelestia-shell -n > /dev/null 2>&1 & disown"; + snrs = "git -C ~/nixos add -A && time sudo nixos-rebuild switch --flake ~/nixos/#desktop && pkill shell || true && pkill caelestia-shell || true && caelestia-shell -n > /dev/null 2>&1 & disown"; + }; + }; + }; + flake.homeModules.zsh = {pkgs, pkgs-unstable, ...}: { home.packages = [ - self.packages.${pkgs.system}.dejaManuallyDerived - #pkgs-unstable.deja + #self.packages.${pkgs.system}.dejaManuallyDerived + pkgs-unstable.deja ]; programs.zsh = { diff --git a/modules/hardware/bootloader.nix b/modules/hardware/bootloader.nix index bfd1f1a..54b6a3d 100644 --- a/modules/hardware/bootloader.nix +++ b/modules/hardware/bootloader.nix @@ -9,6 +9,20 @@ _: { theme = inputs.nixos-grub-themes.packages.${pkgs.system}.nixos; # if you want to use nixos grub theme }; + # Bootloader. + boot.loader.systemd-boot.enable = true; + boot.loader.efi.canTouchEfiVariables = true; + }; + flake.nixosModules.bootloader-desktop = { + inputs, + pkgs, + ... + }: { + # grub theme + boot.loader.grub = { + theme = inputs.nixos-grub-themes.packages.${pkgs.system}.nixos; # if you want to use nixos grub theme + }; + # Bootloader. boot.loader.systemd-boot.enable = true; boot.loader.efi.canTouchEfiVariables = true; diff --git a/modules/hardware/disko/README.md b/modules/hardware/disko/README.md new file mode 100644 index 0000000..d9cf7fc --- /dev/null +++ b/modules/hardware/disko/README.md @@ -0,0 +1,241 @@ +Note : ChatGPT helped me with the disko setup + +This is the layout I'd start with. It's not fully copy-paste ready because you'll need to fill in the actual disk IDs after booting the installer, but it captures the architecture you described: + +* SSD: LUKS → Btrfs +* HDD: LUKS → ext4 +* One password entered at boot +* HDD unlocked using a keyfile stored on the SSD +* Suitable for a desktop host + +# disko/desktop.nix +``` + +{ +disko.devices = { +disk = { +ssd = { +# Replace with actual disk id from: +# ls -l /dev/disk/by-id +device = "/dev/disk/by-id/REPLACE_SSD_ID"; + + type = "disk"; + + content = { + type = "gpt"; + + partitions = { + ESP = { + size = "1G"; + type = "EF00"; + + content = { + type = "filesystem"; + format = "vfat"; + mountpoint = "/boot"; + }; + }; + + luks = { + size = "100%"; + + content = { + type = "luks"; + name = "cryptroot"; + + # You will type this password at boot. + settings.allowDiscards = true; + + content = { + type = "btrfs"; + + extraArgs = [ "-f" ]; + + subvolumes = { + "@root" = { + mountpoint = "/"; + mountOptions = [ + "compress=zstd" + "noatime" + ]; + }; + + "@home" = { + mountpoint = "/home"; + mountOptions = [ + "compress=zstd" + "noatime" + ]; + }; + + "@nix" = { + mountpoint = "/nix"; + mountOptions = [ + "compress=zstd" + "noatime" + ]; + }; + + "@snapshots" = { + mountpoint = "/.snapshots"; + }; + + # Keyfile storage. + # This subvolume will contain the key used + # to unlock the HDD automatically. + "@keys" = { + mountpoint = "/keys"; + }; + }; + }; + }; + }; + }; + }; + }; + + hdd = { + # Replace with actual disk id + device = "/dev/disk/by-id/REPLACE_HDD_ID"; + + type = "disk"; + + content = { + type = "gpt"; + + partitions = { + luks = { + size = "100%"; + + content = { + type = "luks"; + + name = "cryptdata"; + + # IMPORTANT: + # This keyfile will be created after installation. + # The SSD unlocks first. + # Then this keyfile unlocks the HDD automatically. + settings = { + keyFile = "/keys/hdd.key"; + }; + + content = { + type = "filesystem"; + format = "ext4"; + mountpoint = "/data"; + }; + }; + }; + }; + }; + }; +}; +``` + +}; +} + +A few notes: + +### 1. You'll need the disk IDs + +On the installer: + +```bash +ls -l /dev/disk/by-id +``` + +Then replace: + +```text +REPLACE_SSD_ID +REPLACE_HDD_ID +``` + +with the actual values. + +--- + +### 2. The HDD keyfile is a second step + +After installation: + +```bash +sudo mkdir -p /keys +sudo chmod 700 /keys + +sudo dd if=/dev/urandom of=/keys/hdd.key bs=4096 count=1 +sudo chmod 000 /keys/hdd.key +``` + +Then add the key to the HDD LUKS container: + +```bash +sudo cryptsetup luksAddKey /dev/disk/by-id/ACTUAL_HDD_PARTITION /keys/hdd.key +``` + +Now: + +```text +boot +↓ +enter SSD password +↓ +SSD unlocks +↓ +keyfile becomes available +↓ +HDD unlocks automatically +``` + +One password. + +--- + +### 3. Btrfs subvolumes + +I intentionally kept it simple: + +```text +/ +/home +/nix +/.snapshots +``` + +This is a very common NixOS layout. + +--- + +### 4. Swap + +I haven't included swap because there are a few valid choices: + +* swapfile on Btrfs +* swap partition inside the SSD LUKS container +* zram only + +I'd probably use **zram + a small swapfile** unless you have a very specific workload. + +--- + +### 5. Dendritic adaptation + +The actual Disko definition above won't change much. + +The dendritic part is mostly wrapping it in your top-level module structure, something like: + +```nix +{ + flake.diskoConfigurations.desktop = { + imports = [ + ./desktop-disko.nix + ]; + }; +} +``` + +or whatever convention your repository uses. + +The storage design itself remains the same. diff --git a/modules/hardware/disko/disko-config.nix b/modules/hardware/disko/disko-config.nix new file mode 100644 index 0000000..65d5c14 --- /dev/null +++ b/modules/hardware/disko/disko-config.nix @@ -0,0 +1,122 @@ +_: { + flake.diskoConfigurations.desktop = _: { +disko.devices = { +disk = { + ssd = { + # Replace with actual disk id from: + # ls -l /dev/disk/by-id + device = "/dev/disk/by-id/REPLACE_SSD_ID"; + + type = "disk"; + + content = { + type = "gpt"; + + partitions = { + ESP = { + size = "1G"; + type = "EF00"; + + content = { + type = "filesystem"; + format = "vfat"; + mountpoint = "/boot"; + }; + }; + + luks = { + size = "100%"; + + content = { + type = "luks"; + name = "cryptroot"; + + # You will type this password at boot. + settings.allowDiscards = true; + + content = { + type = "btrfs"; + + extraArgs = [ "-f" ]; + + subvolumes = { + "@root" = { + mountpoint = "/"; + mountOptions = [ + "compress=zstd" + "noatime" + ]; + }; + + "@home" = { + mountpoint = "/home"; + mountOptions = [ + "compress=zstd" + "noatime" + ]; + }; + + "@nix" = { + mountpoint = "/nix"; + mountOptions = [ + "compress=zstd" + "noatime" + ]; + }; + + "@snapshots" = { + mountpoint = "/.snapshots"; + }; + + # Keyfile storage. + # This subvolume will contain the key used + # to unlock the HDD automatically. + "@keys" = { + mountpoint = "/keys"; + }; + }; + }; + }; + }; + }; + }; + }; + + hdd = { + # Replace with actual disk id + device = "/dev/disk/by-id/REPLACE_HDD_ID"; + + type = "disk"; + + content = { + type = "gpt"; + + partitions = { + luks = { + size = "100%"; + + content = { + type = "luks"; + + name = "cryptdata"; + + # IMPORTANT: + # This keyfile will be created after installation. + # The SSD unlocks first. + # Then this keyfile unlocks the HDD automatically. + settings = { + keyFile = "/keys/hdd.key"; + }; + + content = { + type = "filesystem"; + format = "ext4"; + mountpoint = "/data"; + }; + }; + }; + }; + }; + }; +}; +} diff --git a/modules/hosts/desktop.nix b/modules/hosts/desktop.nix new file mode 100644 index 0000000..6d348d8 --- /dev/null +++ b/modules/hosts/desktop.nix @@ -0,0 +1,137 @@ +# Note : i should add a way to communicate, annote which commits i should cherry-pick between computers +{ + inputs, + self, + ... +}: { + flake.nixosConfigurations.desktop = inputs.nixpkgs.lib.nixosSystem { + #system = "x86_64-linux"; + specialArgs = { + inherit (self) pkgs-unstable; + }; + modules = with self.nixosModules; [ + # important do not remove + home-manager-desktop + desktop + {nixpkgs.pkgs = self.pkgs;} + # keep this alphabetically organised + anki + audioAndMedia + autoCleanup-desktop + autoUpdate-desktop + bluetooth + bootloader-desktop + browsers + caelestia + development + documentation + disk # this only installs some disk utilities. disko will format the drives + fonts + #hardware-configuration-desktop + hyprland + IO + #kdrive-laptop # we will setup this later + latex + ly + mullvad + networking + notifications + office + #ollama + otherUtils + printer + rss + udev + user + ]; + }; + flake.homeModules.desktop = {...}: { + imports = with self.homeModules; [ + inputs.caelestia-shell.homeManagerModules.default + anki + caelestia + cursor + development + eza + fastfetch + git + gtk + hyprland + hyprland-desktop + kitty + librewolf + mullvad + neovim + notewrapper + oh-my-zsh + ripgrep + rofi + sbb-tui + ssh + thunderbird + tomasr + vivify + zoxide + zsh + zsh-desktop + ]; + }; + flake.nixosModules.desktop = _: { + # This value determines the NixOS release from which the default + # settings for stateful data, like file locations and database versions + # on your system were taken. It‘s perfectly fine and recommended to leave + # this value at the release version of the first install of this system. + # Before changing this value read the documentation for this option + # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). + system.stateVersion = "25.11"; # Did you read the comment? + nix.settings.experimental-features = [ + "nix-command" + "flakes" + ]; + }; + flake.nixosModules.home-manager-desktop = {pkgs, ...}: { + imports = [ + inputs.home-manager.nixosModules.default # import official home-manager NixOS module + ]; + + # Warning. Git is used in case I break everything up. It already saved me once + environment.systemPackages = [pkgs.git]; + home-manager = { + useGlobalPkgs = true; + useUserPackages = true; + + extraSpecialArgs = { + inherit (self) pkgs-unstable; + inherit self; + }; + }; + + users.users.tomasr = { + isNormalUser = true; + }; + home-manager.users.tomasr = self.homeModules.desktop; + }; + # this is a copy of what is in laptop.nix + /*flake.homeModules.tomasr = _: { + home.username = "tomasr"; + home.homeDirectory = "/home/tomasr"; + + home.sessionVariables = { + EDITOR = "neovim"; + TERMINAL = "kitty"; + }; + + # Required for Home Manager + home.stateVersion = "25.11"; # match your Home Manager release + # This value determines the Home Manager release that your configuration is + # compatible with. This helps avoid breakage when a new Home Manager release + # introduces backwards incompatible changes. + # + # You should not change this value, even if you update Home Manager. If you do + # want to update the value, then make sure to first check the Home Manager + # release notes. + + # Let Home Manager install and manage itself. + programs.home-manager.enable = true; + };*/ +} diff --git a/modules/parts.nix b/modules/parts.nix index dfc6adb..ed5411c 100644 --- a/modules/parts.nix +++ b/modules/parts.nix @@ -3,6 +3,7 @@ imports = [ # adds home-manager options to flake-parts inputs.home-manager.flakeModules.home-manager + inputs.disko.flakeModules.default ]; config.systems = [ diff --git a/modules/utilities/autoCleanup.nix b/modules/utilities/autoCleanup.nix index 21641f9..d11d824 100644 --- a/modules/utilities/autoCleanup.nix +++ b/modules/utilities/autoCleanup.nix @@ -117,6 +117,130 @@ fi git -C "$FLAKE_DIR" tag -d "pre-cleanup-$TIME" # removes the tag + rm -f "$ERROR_FILE" + ''; + }; + }; + +## Desktop +flake.nixosModules.autoCleanup-desktop = { + pkgs, + pkgs-unstable, + ... + }: { + # Ensure your script is available system-wide + environment.systemPackages = [ + self.packages.${pkgs.system}.custom-cleanNix-desktop + pkgs-unstable.statix + ]; + users.users.tomasr = { + linger = true; # lingering is required + }; + + systemd.user.services.custom-cleanNix = { + description = "NixOS configuration auto cleanup"; + + serviceConfig = { + Type = "oneshot"; + + ExecStart = "/run/current-system/sw/bin/custom-cleanNix"; + + # safety for long rebuilds + TimeoutStartSec = "45min"; + TimeoutStopSec = "10min"; + + # avoid overlap + RemainAfterExit = true; + + # tweaks that should make the system run normally during the rebuilds + Nice = 10; + IOSchedulingClass = "best-effort"; + IOSchedulingPriority = 7; + }; + }; + + # Systemd USER timer + systemd.user.timers.custom-cleanNix = { + wantedBy = ["timers.target"]; + + timerConfig = { + OnCalendar = "Sat *-*-* 20:00:00"; # runs saturday night. If for whatever reason something breaks. I have whole sunday to fix it. + + Persistent = true; # if it happens during shutted down + + # avoids thundering herd on boot + RandomizedDelaySec = "2h"; + }; + }; + }; + perSystem = {pkgs, ...}: { + packages.custom-cleanNix-desktop = pkgs.writeShellApplication { + name = "custom-cleanNix"; + + runtimeInputs = with pkgs; [ + git + nixos-rebuild + matrix-commander-rs + libnotify + alejandra + deadnix + ]; + + text = '' + set -e + + FLAKE_DIR="/home/tomasr/nixos" + FLAKE="$FLAKE_DIR#desktop" + TIME=$(date -u +"%Y-%m-%dT%H-%M-%SZ") + + ERROR_FILE=$(mktemp) + + # snapshot current state + git -C "$FLAKE_DIR" add -A + git -C "$FLAKE_DIR" commit --allow-empty -m "snapshot pre-cleanup-$TIME" + git -C "$FLAKE_DIR" tag "pre-cleanup-$TIME" HEAD + + # the echos are to separate what each one is doing. Just for curiosity + echo "deadnix scans your Nix code and removes or reports unused (dead) variables and bindings" + deadnix --edit "$FLAKE_DIR" # removes unused code + echo "--------------------------------------------------------------" + echo "statix lints your Nix code to find stylistic issues, bad patterns, and potential mistakes." + statix fix "$FLAKE_DIR" # check other linting issues + echo "--------------------------------------------------------------" + echo "alejandra formats your Nix code consistently according to a strict, opinionated style." + alejandra "$FLAKE_DIR" # formats the config + echo "--------------------------------------------------------------" + + if sudo /run/current-system/sw/bin/nixos-rebuild switch --flake "$FLAKE" 2> "$ERROR_FILE"; then # use /run/.../bin/ uses the sudoless rule + + if ! git -C "$FLAKE_DIR" diff --quiet HEAD; then + git -C "$FLAKE_DIR" add -A + git -C "$FLAKE_DIR" commit -m "Auto: cleanup-$TIME" + git -C "$FLAKE_DIR" push + + notify-send "Nix auto cleanup" "Everything OK" + + matrix-commander-rs --verbose -m "Nix auto cleanup. Everything OK.
@notificationbot_0000" \ + --html \ + -r "\!7j-78_02dHROeLj4Ns8F12eo4IiZGv4zNsQ_1-WlyIU" + else + notify-send "Nix auto cleanup" "No changes" + git -C "$FLAKE_DIR" push + fi + + else + ERROR_MSG=$(cat "$ERROR_FILE") + + notify-send -u critical "Nix auto cleanup" "FAILED" + + matrix-commander-rs --verbose -m "Nix auto cleanup failed.
Error:
$ERROR_MSG

@notificationbot_0000" \ + --html \ + -r "\!7j-78_02dHROeLj4Ns8F12eo4IiZGv4zNsQ_1-WlyIU" + + git -C "$FLAKE_DIR" reset --hard "pre-cleanup-$TIME" + fi + git -C "$FLAKE_DIR" tag -d "pre-cleanup-$TIME" # removes the tag + rm -f "$ERROR_FILE" ''; }; diff --git a/modules/utilities/autoUpdate.nix b/modules/utilities/autoUpdate.nix index 14d78fe..c4b0e2b 100644 --- a/modules/utilities/autoUpdate.nix +++ b/modules/utilities/autoUpdate.nix @@ -103,6 +103,115 @@ git -C "$FLAKE_DIR" tag -d "pre-autoupdate-$TIME" # removes the tag + rm -f "$ERROR_FILE" + ''; + }; + }; + ## desktop + flake.nixosModules.autoUpdate-desktop = {pkgs, ...}: { + # Ensure your script is available system-wide + environment.systemPackages = [ + self.packages.${pkgs.system}.custom-autoupdate-desktop + ]; + users.users.tomasr = { + linger = true; # lingering is required + }; + + systemd.user.services.custom-autoupdate = { + description = "NixOS flake auto update"; + + serviceConfig = { + Type = "oneshot"; + + ExecStart = "/run/current-system/sw/bin/custom-autoupdate"; + + # safety for long rebuilds + TimeoutStartSec = "45min"; + TimeoutStopSec = "10min"; + + # avoid overlap + RemainAfterExit = true; + + # tweaks that should make the system run normally during the rebuilds + Nice = 10; + IOSchedulingClass = "best-effort"; + IOSchedulingPriority = 7; + }; + }; + + # Systemd USER timer + systemd.user.timers.custom-autoupdate = { + wantedBy = ["timers.target"]; + + timerConfig = { + OnCalendar = "Fri *-*-* 20:00:00"; # runs friday night. If for whatever reason something breaks. I have whole week-end to fix it. + + Persistent = true; # if it happens during shutted down + + # avoids thundering herd on boot + RandomizedDelaySec = "2h"; + }; + }; + }; + perSystem = {pkgs, ...}: { + packages.custom-autoupdate-desktop = pkgs.writeShellApplication { + name = "custom-autoupdate"; + + runtimeInputs = with pkgs; [ + git + nixos-rebuild + matrix-commander-rs + libnotify + ]; + + text = '' + set -e + + FLAKE_DIR="/home/tomasr/nixos" + FLAKE="$FLAKE_DIR#desktop" + TIME=$(date -u +"%Y-%m-%dT%H-%M-%SZ") + + ERROR_FILE=$(mktemp) + + # snapshot current state + git -C "$FLAKE_DIR" add -A + git -C "$FLAKE_DIR" commit --allow-empty -m "snapshot pre-autoupdate-$TIME" + git -C "$FLAKE_DIR" tag "pre-autoupdate-$TIME" HEAD + + # update lock only + nix flake update --flake "$FLAKE_DIR" + + if sudo /run/current-system/sw/bin/nixos-rebuild switch --flake "$FLAKE" 2> "$ERROR_FILE"; then # use /run/.../bin/ uses the sudoless rule + + if ! git -C "$FLAKE_DIR" diff --quiet -- flake.lock; then + git -C "$FLAKE_DIR" add flake.lock + git -C "$FLAKE_DIR" commit -m "flake.lock: autoupdate-$TIME" + git -C "$FLAKE_DIR" push + + notify-send "Flake autoupdate" "Rebuild OK" + + matrix-commander-rs --verbose -m "Flake rebuild succesfull.
@notificationbot_0000" \ + --html \ + -r "\!7j-78_02dHROeLj4Ns8F12eo4IiZGv4zNsQ_1-WlyIU" + else + notify-send "Flake autoupdate" "No changes" + git -C "$FLAKE_DIR" push + fi + + else + ERROR_MSG=$(cat "$ERROR_FILE") + + notify-send -u critical "Flake autoupdate" "FAILED" + + matrix-commander-rs --verbose -m "Flake rebuild failed.
Error:
$ERROR_MSG

@notificationbot_0000" \ + --html \ + -r "\!7j-78_02dHROeLj4Ns8F12eo4IiZGv4zNsQ_1-WlyIU" + + git -C "$FLAKE_DIR" reset --hard "pre-autoupdate-$TIME" + fi + + git -C "$FLAKE_DIR" tag -d "pre-autoupdate-$TIME" # removes the tag + rm -f "$ERROR_FILE" ''; };