diff --git a/developmentlog.md b/developmentlog.md index a347db7..5772948 100644 --- a/developmentlog.md +++ b/developmentlog.md @@ -15,4 +15,34 @@ Changes (`hosts/legolas/hardware-configuration.nix`): - Removed the no-op `d3cold_allowed` rule and the broken rescan service. - Added a correct runtime-PM-off udev rule on both the NVMe endpoint (`0000:6e:00.0`) and its parent root port (`0000:00:1d.0`), keeping the PCIe wakeup disable. -Note: runtime PM was already off on the endpoint (`power/control=on`); that alone never fixed the crash because system suspend uses a separate code path. Verify after rebuild that the cmdline has `mem_sleep_default=s2idle` (the old running generation showed a stale duplicate `deep`). +Note: runtime PM was already off on the endpoint (`power/control=on`); that alone never fixed the crash because system suspend uses a separate code path. Verify after rebuild that the cmdline has `mem_sleep_default=s2idle` (the running generation showed a stale duplicate `deep`). + +## Headless home-manager configs (galadriel, elros, elrond) + +Added minimal home-manager configs for `gunalx` and `root` on headless hosts. These skip all GUI/desktop modules (niri, stylix, foot, noctalia, etc.) and keep only CLI tools: bash (with sops AI keys), git, nixvim (full config reused), opencode, atuin, sshconfig, sops. + +Files: +- `home/gunalx-headless.nix` — gunalx's minimal config, same module set as `gunalx.nix` minus all GUI imports. +- `home/root.nix` — identical CLI stack, explicitly sets `home.username`/`home.homeDirectory` to root. +- `home/sops.nix` — parameterized age key paths to use `config.home.homeDirectory` so both users work without duplication. +- `home/sshconfig.nix` — removed `ssh-askpass-fullscreen` (unwired GUI dependency, was dead weight). +- `flake.nix` — added `headlessHmModules` let binding (shared by all three hosts) wiring `home-manager.users.gunalx` + `home-manager.users.root`. + +Manual step: root needs the sops age key at `/root/.config/sops/age/key.txt` (or SSH key at `/root/.ssh/nixos`) to decrypt AI API secrets. `age.generateKey = true` will create a new key, but it won't match the encrypted secrets — copy gunalx's age key to root's path, or re-encrypt the secrets with root's age public key. + +## galadriel: kernel 7.0 EOL + +Linux 7.0 was removed from nixpkgs (EOL). `pkgs.zfs` (stable) is now 2.4.2, same version previously pulled from unstable. Switched `boot.kernelPackages` to the default (6.18) and `boot.zfs.package` to `pkgs.zfs`. No pool compatibility concern since ZFS version is unchanged. + +## legolas: lid-close still killing the NVMe — stale `deep` + logind suspend + +Root cause of the recurring "SSD dies when lid closed": the *booted* generation carried a duplicate `mem_sleep_default=deep` after `mem_sleep_default=s2idle` (last wins), so `/sys/power/mem_sleep` was `s2idle [deep]` — every suspend went to S3. The source config only had `s2idle`; the stale param lived in the old generation. ACPI advertises S3 (`supports S0 S3 S4 S5`) but S3 is broken for the Phison E21 on this Skylake-era Intel platform, so only s0/s2idle is reliable. Separately, `HandleLidSwitch` was unset → defaulted to `suspend`, which was the actual lid-close trigger. + +Changes (`hosts/legolas/hardware-configuration.nix`): +- `services.logind.lidSwitch`/`lidSwitchExternalPower`/`lidSwitchDocked` = `ignore`. Lid close no longer suspends; niri's `switch-events` still locks via noctalia, so the laptop stays on (RAM powered) but can't enter the S3 path. +- `systemd.services.force-s2idle` writes `s2idle` to `/sys/power/mem_sleep` at boot — guarantees the active mode is s2idle even if a future cmdline carries a stray `deep`. +- Suspend (s2idle) remains available via `systemctl suspend`; hibernation stays disabled. + +Also fixed `home/niri.nix` lid-close command from the undocumented `lockScreen lock` to the v5-canonical `session lock` (matching the `Mod+M` binding). + +Verify after rebuild+reboot: `/proc/cmdline` has no `deep`, and `/sys/power/mem_sleep` shows `[s2idle] deep`. diff --git a/flake.nix b/flake.nix index 89c3df2..758173d 100644 --- a/flake.nix +++ b/flake.nix @@ -63,6 +63,36 @@ }@inputs: let system = "x86_64-linux"; + + headlessHmModules = [ + home-manager.nixosModules.home-manager + { + home-manager.backupFileExtension = "bac"; + home-manager.extraSpecialArgs = { inherit inputs; }; + home-manager.sharedModules = [ + inputs.sops-nix.homeManagerModules.sops + inputs.nixvim.homeModules.nixvim + ]; + home-manager.users.gunalx = import ./home/gunalx-headless.nix; + home-manager.users.root = import ./home/root.nix; + } + { + home-manager.users.gunalx = { + nixpkgs.config.allowUnfree = true; + nixpkgs.overlays = [ + self.overlays.custom + self.overlays.unstable + ]; + }; + home-manager.users.root = { + nixpkgs.config.allowUnfree = true; + nixpkgs.overlays = [ + self.overlays.custom + self.overlays.unstable + ]; + }; + } + ]; in { packages.${system} = @@ -245,7 +275,7 @@ lanzaboote.nixosModules.lanzaboote - ]; + ] ++ headlessHmModules; }; # elros @@ -263,7 +293,7 @@ ./hosts/elros/configuration.nix sops-nix.nixosModules.sops lanzaboote.nixosModules.lanzaboote - ]; + ] ++ headlessHmModules; }; # elrond @@ -281,7 +311,7 @@ ./hosts/elrond/configuration.nix sops-nix.nixosModules.sops lanzaboote.nixosModules.lanzaboote - ]; + ] ++ headlessHmModules; }; }; diff --git a/home/gunalx-headless.nix b/home/gunalx-headless.nix new file mode 100644 index 0000000..79196b2 --- /dev/null +++ b/home/gunalx-headless.nix @@ -0,0 +1,31 @@ +{ + pkgs, + lib, + inputs, + ... +}: +{ + imports = [ + ./sshconfig.nix + ./atuin.nix + ./git.nix + ./bash.nix + + ./opencode.nix + ./nixvim.nix + + inputs.sops-nix.homeManagerModules.sops + ./sops.nix + ]; + + programs.nix-index = { + enableBashIntegration = true; + enable = true; + }; + + home.packages = with pkgs; [ + comma + ]; + + home.stateVersion = "25.05"; +} diff --git a/home/niri.nix b/home/niri.nix index e836aa1..766efcb 100644 --- a/home/niri.nix +++ b/home/niri.nix @@ -162,7 +162,7 @@ in # Switch events for lid open/close switch-events = { - lid-close.action.spawn = noctalia "lockScreen lock"; + lid-close.action.spawn = noctalia "session lock"; tablet-mode-on.action.spawn = [ "bash" "-c" diff --git a/home/root.nix b/home/root.nix new file mode 100644 index 0000000..285c902 --- /dev/null +++ b/home/root.nix @@ -0,0 +1,33 @@ +{ + pkgs, + lib, + inputs, + ... +}: +{ + imports = [ + ./sshconfig.nix + ./atuin.nix + ./git.nix + ./bash.nix + + ./opencode.nix + ./nixvim.nix + + inputs.sops-nix.homeManagerModules.sops + ./sops.nix + ]; + + programs.nix-index = { + enableBashIntegration = true; + enable = true; + }; + + home.packages = with pkgs; [ + comma + ]; + + home.username = "root"; + home.homeDirectory = "/root"; + home.stateVersion = "25.05"; +} diff --git a/home/sops.nix b/home/sops.nix index 5ad5847..dbb09d1 100644 --- a/home/sops.nix +++ b/home/sops.nix @@ -3,8 +3,8 @@ sops = { defaultSopsFile = ../secrets/secrets.yaml; validateSopsFiles = false; - age.sshKeyPaths = [ "/home/gunalx/.ssh/nixos" ]; - age.keyFile = "/home/gunalx/.config/sops/age/key.txt"; + age.sshKeyPaths = [ "${config.home.homeDirectory}/.ssh/nixos" ]; + age.keyFile = "${config.home.homeDirectory}/.config/sops/age/key.txt"; age.generateKey = true; # This is the actual specification of the secrets. #secrets."myservice/my_subdir/my_secret" = {}; diff --git a/home/sshconfig.nix b/home/sshconfig.nix index 911dab3..429ca45 100644 --- a/home/sshconfig.nix +++ b/home/sshconfig.nix @@ -9,7 +9,6 @@ home.packages = with pkgs; [ openssh - ssh-askpass-fullscreen sshfs ]; diff --git a/hosts/galadriel/hardware-configuration.nix b/hosts/galadriel/hardware-configuration.nix index 6d9033f..67e24fa 100644 --- a/hosts/galadriel/hardware-configuration.nix +++ b/hosts/galadriel/hardware-configuration.nix @@ -23,9 +23,7 @@ "sd_mod" ]; - # unstable zfs and linux because we needed zfs 2.4.2 for linux 7.0 - boot.kernelPackages = pkgs.unstable.linuxPackages_7_0; # migth need to bump down if zfs compat breaks. - boot.zfs.package = pkgs.unstable.zfs; # think this was the correct way + boot.zfs.package = pkgs.zfs; boot.initrd.kernelModules = [ ]; boot.kernelModules = [ "kvm-amd" ]; boot.extraModulePackages = [ ]; diff --git a/hosts/legolas/hardware-configuration.nix b/hosts/legolas/hardware-configuration.nix index a83a55f..2a58003 100644 --- a/hosts/legolas/hardware-configuration.nix +++ b/hosts/legolas/hardware-configuration.nix @@ -12,13 +12,18 @@ #boot.kernelPackages = pkgs.linuxPackages_zen; boot.kernelPackages = pkgs.linuxPackages_latest; - boot.kernelParams = [ + boot.kernelParams = lib.mkForce [ # s2idle (modern standby) instead of deep (S3): the Phison E21 NVMe # controller (Corsair MP600 ELITE) hangs on resume from the D3 transition # that S3 forces. s2idle avoids that path. "mem_sleep_default=s2idle" "nvme_core.default_ps_max_latency_us=0" "pcie_aspm=off" + + # from hardware config we are overriding away the deep sleep of. + "i915.enable_guc=2" + "i915.enable_fbc=1" + "i915.enable_psr=2" ]; boot.initrd.availableKernelModules = [ @@ -70,6 +75,17 @@ }; }; + # Lid close must NOT suspend. The Phison E21 NVMe hangs on resume from any + # system suspend, and logind defaults to HandleLidSwitch=suspend (the trigger + # that kills the drive). Ignore the lid at the logind level; niri's + # switch-events handler still locks the session via noctalia, so the laptop + # stays on (RAM powered, s0idle) but never enters the death path. + services.logind = { + lidSwitch = "ignore"; + lidSwitchExternalPower = "ignore"; + lidSwitchDocked = "ignore"; + }; + # Keep runtime PM off on the NVMe controller and its parent root port, and # disable PCIe wakeup events. (The previous d3cold_allowed rule was a no-op: # that sysfs attribute doesn't exist on this Phison endpoint or its root port.)