From 28ed3fcf743acacf54e3df7c9bcc58da5fe05c14 Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Sun, 19 Oct 2025 16:43:02 +0200 Subject: [PATCH 1/6] fix(nb): resolve keyboard/touchpad and btrfs read-only issues after suspend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit addresses two critical suspend/resume issues on the nb host: 1. Keyboard and touchpad not working after suspend - Added i2c_hid_acpi kernel module - Created systemd service to reload the module after resume - Excluded input devices from TLP USB autosuspend 2. /nix/persist becoming read-only after suspend - Moved swap from /nix/persist to dedicated @swap subvolume - Added systemd service to remount /nix/persist if needed - Separated swap from persistent data to prevent btrfs corruption Changes: - Created hosts/nb/modules/suspend-fixes.nix with resume hooks - Updated swap path from /nix/persist/swapfile to /swap/swapfile - Added /swap filesystem mount for @swap btrfs subvolume - Added USB_EXCLUDE_INPUT=1 to TLP configuration Note: Manual step required before deployment - create @swap subvolume. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- hosts/nb/configuration.nix | 3 ++- hosts/nb/hardware-configuration.nix | 10 +++++++++ hosts/nb/modules/desktop/default.nix | 1 + hosts/nb/modules/suspend-fixes.nix | 32 ++++++++++++++++++++++++++++ 4 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 hosts/nb/modules/suspend-fixes.nix diff --git a/hosts/nb/configuration.nix b/hosts/nb/configuration.nix index 4f42a9c..070cf9d 100644 --- a/hosts/nb/configuration.nix +++ b/hosts/nb/configuration.nix @@ -30,6 +30,7 @@ in { ./modules/ollama.nix ./modules/qdrant.nix ./modules/battery-brightness.nix + ./modules/suspend-fixes.nix ./cachix.nix ./users @@ -79,7 +80,7 @@ in { services.irqbalance.enable = false; swapDevices = [ { - device = "/nix/persist/swapfile"; + device = "/swap/swapfile"; size = 96 * 1024; # Size is in megabytes (96GB for full hibernation with 92GB RAM) } ]; diff --git a/hosts/nb/hardware-configuration.nix b/hosts/nb/hardware-configuration.nix index a1538fb..a287335 100644 --- a/hosts/nb/hardware-configuration.nix +++ b/hosts/nb/hardware-configuration.nix @@ -92,6 +92,16 @@ ]; }; + fileSystems."/swap" = { + device = "/dev/mapper/root"; + fsType = "btrfs"; + neededForBoot = true; + options = [ + "subvol=@swap" + "noatime" + ]; + }; + swapDevices = [ ]; # Enables DHCP on each ethernet and wireless interface. In case of scripted networking diff --git a/hosts/nb/modules/desktop/default.nix b/hosts/nb/modules/desktop/default.nix index e7e7005..d1e2372 100644 --- a/hosts/nb/modules/desktop/default.nix +++ b/hosts/nb/modules/desktop/default.nix @@ -159,6 +159,7 @@ in { USB_EXCLUDE_PHONE = 0; USB_EXCLUDE_PRINTER = 1; USB_EXCLUDE_WWAN = 0; + USB_EXCLUDE_INPUT = 1; # Exclude keyboard/touchpad to prevent suspend issues # Audio power saving SOUND_POWER_SAVE_ON_AC = 0; diff --git a/hosts/nb/modules/suspend-fixes.nix b/hosts/nb/modules/suspend-fixes.nix new file mode 100644 index 0000000..430890d --- /dev/null +++ b/hosts/nb/modules/suspend-fixes.nix @@ -0,0 +1,32 @@ +{ config, pkgs, lib, ... }: + +{ + # Add i2c_hid_acpi kernel module for proper input device support + boot.kernelModules = [ "i2c_hid_acpi" ]; + + # Systemd service to reload i2c_hid_acpi module after resume + # This fixes keyboard and touchpad not working after suspend + systemd.services.reload-i2c-hid-after-resume = { + description = "Reload i2c_hid_acpi module after resume to fix keyboard/touchpad"; + after = [ "suspend.target" "hibernate.target" "hybrid-sleep.target" ]; + wantedBy = [ "suspend.target" "hibernate.target" "hybrid-sleep.target" ]; + serviceConfig = { + Type = "oneshot"; + ExecStart = "${pkgs.bash}/bin/bash -c '${pkgs.kmod}/bin/rmmod i2c_hid_acpi; ${pkgs.kmod}/bin/modprobe i2c_hid_acpi'"; + }; + }; + + # Systemd service to remount /nix/persist if it becomes read-only after resume + # This fixes the btrfs read-only issue that can occur with LUKS + btrfs on suspend/resume + systemd.services.remount-persist-after-resume = { + description = "Remount /nix/persist read-write if needed after resume"; + after = [ "suspend.target" "hibernate.target" "hybrid-sleep.target" ]; + wantedBy = [ "suspend.target" "hibernate.target" "hybrid-sleep.target" ]; + serviceConfig = { + Type = "oneshot"; + ExecStart = "${pkgs.util-linux}/bin/mount -o remount,rw /nix/persist"; + # Don't fail if already mounted rw + SuccessExitStatus = [ 0 32 ]; + }; + }; +} From 7d5294e7b9644dbec76bf52841b18038f9810960 Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Sun, 19 Oct 2025 18:14:40 +0200 Subject: [PATCH 2/6] fix: hibernate resume for nb --- hosts/nb/configuration.nix | 12 +++++++-- hosts/nb/hardware-configuration.nix | 1 - hosts/nb/modules/suspend-fixes.nix | 40 ++++++++++++----------------- 3 files changed, 26 insertions(+), 27 deletions(-) diff --git a/hosts/nb/configuration.nix b/hosts/nb/configuration.nix index 070cf9d..e1b6db5 100644 --- a/hosts/nb/configuration.nix +++ b/hosts/nb/configuration.nix @@ -72,6 +72,14 @@ in { theme = "steeef"; # Set theme plugins = [ "git" ]; # Add plugins }; + interactiveShellInit = '' + # Bind Shift+Return to insert newline (foot terminal sends \e[27;2;13~) + insert-newline() { + LBUFFER="''${LBUFFER}"$'\n' + } + zle -N insert-newline + bindkey '^[[27;2;13~' insert-newline + ''; }; users.defaultUserShell = pkgs.zsh; @@ -93,8 +101,8 @@ in { # Battery optimization - increase dirty writeback time to batch writes "vm.dirty_writeback_centisecs" = 3000; # 30 seconds (default: 500 = 5s) "vm.dirty_expire_centisecs" = 3000; # 30 seconds (default: 3000) - # Enable laptop mode for aggressive disk power management - "vm.laptop_mode" = 5; + # Enable laptop mode for disk power management (2 = balanced, less aggressive than 5) + "vm.laptop_mode" = 2; }; # nixos cross building qemu diff --git a/hosts/nb/hardware-configuration.nix b/hosts/nb/hardware-configuration.nix index a287335..e9fc49d 100644 --- a/hosts/nb/hardware-configuration.nix +++ b/hosts/nb/hardware-configuration.nix @@ -28,7 +28,6 @@ "snd_hda_intel.power_save=1" "transparent_hugepage=madvise" "pcie_aspm=force" - "nvme.noacpi=1" ]; fileSystems."/" = { diff --git a/hosts/nb/modules/suspend-fixes.nix b/hosts/nb/modules/suspend-fixes.nix index 430890d..c6bb147 100644 --- a/hosts/nb/modules/suspend-fixes.nix +++ b/hosts/nb/modules/suspend-fixes.nix @@ -4,29 +4,21 @@ # Add i2c_hid_acpi kernel module for proper input device support boot.kernelModules = [ "i2c_hid_acpi" ]; - # Systemd service to reload i2c_hid_acpi module after resume - # This fixes keyboard and touchpad not working after suspend - systemd.services.reload-i2c-hid-after-resume = { - description = "Reload i2c_hid_acpi module after resume to fix keyboard/touchpad"; - after = [ "suspend.target" "hibernate.target" "hybrid-sleep.target" ]; - wantedBy = [ "suspend.target" "hibernate.target" "hybrid-sleep.target" ]; - serviceConfig = { - Type = "oneshot"; - ExecStart = "${pkgs.bash}/bin/bash -c '${pkgs.kmod}/bin/rmmod i2c_hid_acpi; ${pkgs.kmod}/bin/modprobe i2c_hid_acpi'"; - }; - }; + # Commands to run after resume from suspend/hibernate + # This is the NixOS-native way to ensure proper execution timing + powerManagement.resumeCommands = '' + # Reload i2c_hid_acpi module to fix keyboard/touchpad after suspend + ${pkgs.kmod}/bin/rmmod i2c_hid_acpi || true + ${pkgs.kmod}/bin/modprobe i2c_hid_acpi - # Systemd service to remount /nix/persist if it becomes read-only after resume - # This fixes the btrfs read-only issue that can occur with LUKS + btrfs on suspend/resume - systemd.services.remount-persist-after-resume = { - description = "Remount /nix/persist read-write if needed after resume"; - after = [ "suspend.target" "hibernate.target" "hybrid-sleep.target" ]; - wantedBy = [ "suspend.target" "hibernate.target" "hybrid-sleep.target" ]; - serviceConfig = { - Type = "oneshot"; - ExecStart = "${pkgs.util-linux}/bin/mount -o remount,rw /nix/persist"; - # Don't fail if already mounted rw - SuccessExitStatus = [ 0 32 ]; - }; - }; + # Sync filesystem to ensure all pending writes are committed + ${pkgs.util-linux}/bin/sync + + # Remount all btrfs subvolumes read-write if they became read-only + # This fixes the issue where LUKS + btrfs can remount read-only after suspend + ${pkgs.util-linux}/bin/mount -o remount,rw /nix || true + ${pkgs.util-linux}/bin/mount -o remount,rw /nix/store || true + ${pkgs.util-linux}/bin/mount -o remount,rw /nix/persist || true + ${pkgs.util-linux}/bin/mount -o remount,rw /swap || true + ''; } From 19d0946e06fa5fd6e28406700495fcb5658435c0 Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Wed, 22 Oct 2025 09:52:40 +0200 Subject: [PATCH 3/6] feat: add sops to vim --- .../modules/development/nvim/config/sops.lua | 144 ++++++++++++++++++ hosts/nb/modules/development/nvim/default.nix | 1 + 2 files changed, 145 insertions(+) create mode 100644 hosts/nb/modules/development/nvim/config/sops.lua diff --git a/hosts/nb/modules/development/nvim/config/sops.lua b/hosts/nb/modules/development/nvim/config/sops.lua new file mode 100644 index 0000000..6d95957 --- /dev/null +++ b/hosts/nb/modules/development/nvim/config/sops.lua @@ -0,0 +1,144 @@ +-- SOPS integration for automatic encryption/decryption of secrets files +-- This module sets up autocmds to handle .secrets.yaml files transparently + +local sops_group = vim.api.nvim_create_augroup("SopsEncryption", { clear = true }) + +-- Pattern matching for secrets files +local secrets_patterns = { + "*/secrets.yaml", + "*secrets*.yaml", +} + +-- Helper function to check if file matches secrets pattern +local function is_secrets_file(filepath) + for _, pattern in ipairs(secrets_patterns) do + if vim.fn.match(filepath, vim.fn.glob2regpat(pattern)) ~= -1 then + return true + end + end + return false +end + +-- Decrypt file after reading +vim.api.nvim_create_autocmd("BufReadPost", { + group = sops_group, + pattern = secrets_patterns, + callback = function(args) + local filepath = vim.fn.expand("%:p") + + -- Only decrypt if file exists and has content + if vim.fn.filereadable(filepath) == 1 and vim.fn.getfsize(filepath) > 0 then + -- Save cursor position + local cursor_pos = vim.api.nvim_win_get_cursor(0) + + -- Decrypt file content + local result = vim.fn.system("sops --decrypt " .. vim.fn.shellescape(filepath)) + + if vim.v.shell_error == 0 then + -- Replace buffer content with decrypted content + vim.api.nvim_buf_set_lines(0, 0, -1, false, vim.split(result, "\n")) + + -- Mark buffer as not modified (since we just loaded it) + vim.bo.modified = false + + -- Restore cursor position + pcall(vim.api.nvim_win_set_cursor, 0, cursor_pos) + + -- Disable swap, backup, and undo files for security + vim.bo.swapfile = false + vim.bo.backup = false + vim.bo.writebackup = false + vim.bo.undofile = false + + -- Set filetype to yaml if not already set + if vim.bo.filetype == "" then + vim.bo.filetype = "yaml" + end + + vim.notify("SOPS: File decrypted successfully", vim.log.levels.INFO) + else + vim.notify("SOPS: Failed to decrypt file: " .. result, vim.log.levels.ERROR) + end + end + end, +}) + +-- Encrypt file before writing +vim.api.nvim_create_autocmd("BufWritePre", { + group = sops_group, + pattern = secrets_patterns, + callback = function(args) + local filepath = vim.fn.expand("%:p") + + if is_secrets_file(filepath) then + -- Get current buffer content + local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false) + local content = table.concat(lines, "\n") + + -- Encrypt content using SOPS + local encrypted = vim.fn.system("sops --encrypt /dev/stdin", content) + + if vim.v.shell_error == 0 then + -- Write encrypted content directly to file + local file = io.open(filepath, "w") + if file then + file:write(encrypted) + file:close() + + -- Mark buffer as saved (prevent Vim from writing again) + vim.bo.modified = false + + vim.notify("SOPS: File encrypted and saved successfully", vim.log.levels.INFO) + else + vim.notify("SOPS: Failed to write encrypted file", vim.log.levels.ERROR) + end + else + vim.notify("SOPS: Failed to encrypt file: " .. encrypted, vim.log.levels.ERROR) + -- Prevent write on encryption failure + return true + end + + -- Prevent default write behavior since we handled it + return true + end + end, +}) + +-- Re-decrypt after writing to show plaintext in buffer +vim.api.nvim_create_autocmd("BufWritePost", { + group = sops_group, + pattern = secrets_patterns, + callback = function(args) + local filepath = vim.fn.expand("%:p") + + if is_secrets_file(filepath) and vim.fn.filereadable(filepath) == 1 then + -- Decrypt and reload buffer content + local result = vim.fn.system("sops --decrypt " .. vim.fn.shellescape(filepath)) + + if vim.v.shell_error == 0 then + -- Save cursor position + local cursor_pos = vim.api.nvim_win_get_cursor(0) + + -- Replace buffer with decrypted content + vim.api.nvim_buf_set_lines(0, 0, -1, false, vim.split(result, "\n")) + + -- Mark as not modified + vim.bo.modified = false + + -- Restore cursor position + pcall(vim.api.nvim_win_set_cursor, 0, cursor_pos) + end + end + end, +}) + +-- Warn when leaving a secrets buffer with unsaved changes +vim.api.nvim_create_autocmd("BufLeave", { + group = sops_group, + pattern = secrets_patterns, + callback = function(args) + if vim.bo.modified then + vim.notify("Warning: Unsaved changes in secrets file!", vim.log.levels.WARN) + end + end, +}) diff --git a/hosts/nb/modules/development/nvim/default.nix b/hosts/nb/modules/development/nvim/default.nix index 233414f..17f7d5c 100644 --- a/hosts/nb/modules/development/nvim/default.nix +++ b/hosts/nb/modules/development/nvim/default.nix @@ -102,6 +102,7 @@ in "utils" "bufferline" "which-key" + "sops" ]); in '' lua << EOF From 7d2f818fca211f7cd2c6b3a87062383dcdcab927 Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Wed, 22 Oct 2025 11:58:58 +0200 Subject: [PATCH 4/6] feat: nb add env variable for sops --- hosts/nb/users/dominik.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hosts/nb/users/dominik.nix b/hosts/nb/users/dominik.nix index 979a4e4..918ca77 100644 --- a/hosts/nb/users/dominik.nix +++ b/hosts/nb/users/dominik.nix @@ -165,6 +165,7 @@ in programs.zsh = { shellInit = '' export OPENAI_API_KEY=$(cat ${config.sops.secrets.openai_api_key.path}) + export SOPS_AGE_KEY_FILE="$HOME/.config/sops/age/key.age" ''; }; @@ -175,6 +176,7 @@ in home.enableNixpkgsReleaseCheck = false; home.sessionVariables = { MOZ_ENABLE_WAYLAND = "1"; + SOPS_AGE_KEY_FILE = "$HOME/.config/sops/age/key.age"; }; nixpkgs.config.allowUnfree = true; From 5758b3a32092f28897644562004632a2cca47201 Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Wed, 22 Oct 2025 12:29:10 +0200 Subject: [PATCH 5/6] fix(nvim): enable yaml syntax highlighting for sops files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add BufReadPre autocmd to set filetype=yaml before buffer is loaded, ensuring syntax highlighting works immediately when opening encrypted sops files. Also updated BufReadPost to unconditionally set yaml filetype after decryption. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../nb/modules/development/nvim/config/sops.lua | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/hosts/nb/modules/development/nvim/config/sops.lua b/hosts/nb/modules/development/nvim/config/sops.lua index 6d95957..662f09a 100644 --- a/hosts/nb/modules/development/nvim/config/sops.lua +++ b/hosts/nb/modules/development/nvim/config/sops.lua @@ -19,6 +19,16 @@ local function is_secrets_file(filepath) return false end +-- Set filetype before reading to enable syntax highlighting +vim.api.nvim_create_autocmd("BufReadPre", { + group = sops_group, + pattern = secrets_patterns, + callback = function(args) + -- Set filetype to yaml before the file is read so syntax highlighting works + vim.bo.filetype = "yaml" + end, +}) + -- Decrypt file after reading vim.api.nvim_create_autocmd("BufReadPost", { group = sops_group, @@ -50,10 +60,8 @@ vim.api.nvim_create_autocmd("BufReadPost", { vim.bo.writebackup = false vim.bo.undofile = false - -- Set filetype to yaml if not already set - if vim.bo.filetype == "" then - vim.bo.filetype = "yaml" - end + -- Ensure filetype is set to yaml for syntax highlighting + vim.bo.filetype = "yaml" vim.notify("SOPS: File decrypted successfully", vim.log.levels.INFO) else From 7499a21cbd89665282c73a3a5f3b92cc5a7a4658 Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Wed, 22 Oct 2025 12:31:53 +0200 Subject: [PATCH 6/6] feat: nb add wim-api project --- hosts/nb/users/configs/project_history | 2 +- hosts/nb/users/dominik.nix | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hosts/nb/users/configs/project_history b/hosts/nb/users/configs/project_history index b74f02b..64a3845 100644 --- a/hosts/nb/users/configs/project_history +++ b/hosts/nb/users/configs/project_history @@ -46,7 +46,7 @@ /home/dominik/projects/epicenter.works/epicenter.works-website /home/dominik/projects/epicenter.works/epicenter-nixos /home/dominik/projects/epicenter.works/spenden.akvorrat.at -/home/dominik/projects/epicenter.works/eidas.monitor +/home/dominik/projects/epicenter.works/whoidentifies.me/wim-api /home/dominik/projects/cloonar/lena-schilling-website /home/dominik/projects/cloonar/dialog-relations-website diff --git a/hosts/nb/users/dominik.nix b/hosts/nb/users/dominik.nix index 918ca77..6387807 100644 --- a/hosts/nb/users/dominik.nix +++ b/hosts/nb/users/dominik.nix @@ -627,7 +627,7 @@ in git clone git@github.com:AKVorrat/epicenter.works-website.git ${persistHome}/projects/epicenter.works/epicenter.works-website 2>/dev/null git clone git@github.com:AKVorrat/spenden.akvorrat.at.git ${persistHome}/projects/epicenter.works/spenden.akvorrat.at 2>/dev/null git clone git@github.com:AKVorrat/dearmep-website.git ${persistHome}/projects/epicenter.works/dearmep-website 2>/dev/null - git clone gitea@git.cloonar.com:Cloonar/eidas.monitor.git ${persistHome}/projects/epicenter.works/eidas.monitor 2>/dev/null + git clone git@github.com:whoidentifies-me/api.git ${persistHome}/projects/epicenter.works/whoidentifies.me/wim-api 2>/dev/null set -eu '';