diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 014fdd9..47ecc96 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -264,7 +264,8 @@ "Bash(xargs cat)", "Bash(nix-instantiate --parse modules/home/rofi.nix)", "Bash(nix-instantiate --parse modules/home/shell.nix)", - "Bash(command -v nix)" + "Bash(command -v nix)", + "Bash(nix-instantiate --parse modules/home/keys.nix)" ] } } diff --git a/README.md b/README.md index 92acc4d..ecc3ad3 100644 --- a/README.md +++ b/README.md @@ -350,13 +350,15 @@ close · `SUPER+1..9` workspaces · `Print` region screenshot. also locks hyprlock, so the user types a password twice. Lock only on suspend (not hibernate), or skip the hyprlock prompt when resuming from a LUKS-encrypted hibernate — see `modules/home/idle.nix`. -- **Key agents & pinentry:** no SSH/GPG agent ships yet, so signing/auth - prompts have nowhere to go. Wire up an agent + a Wayland-native graphical - pinentry: `services.ssh-agent` (or gpg-agent's `enableSshSupport`), - `programs.gnupg.agent` with `pinentry-gnome3`/`pinentry-qt`, `SSH_AUTH_SOCK` - exported, and the pinentry styled from the palette where it can be. Decide - one-agent (gpg-agent fronting SSH) vs. two, and confirm it cooperates with - hyprlock/hypridle on unlock. +- ✓ **Key agents & pinentry:** ships `modules/home/keys.nix` + (`nomarchy.keys.enable`): one agent — `services.gpg-agent` with + `enableSshSupport` fronts SSH, so a single `pinentry-qt` (native-Wayland, + Stylix-themed Qt) handles GPG and SSH passphrases alike. `SSH_AUTH_SOCK` + comes from the agent's zsh integration; cache TTLs (30 min / 2 h) govern + re-prompting. gnome-keyring stays the Secret Service (modern versions run + no SSH agent, so no socket contention); screen lock doesn't flush the + cache. Remaining (optional): a session-level `SSH_AUTH_SOCK` export so GUI + clients launched outside a shell also see the agent. ## Known issues & follow-ups - ✓ **Yazi TOML parse error on startup:** yazi 26.x made fetchers' diff --git a/modules/home/default.nix b/modules/home/default.nix index b89a04e..4539e79 100644 --- a/modules/home/default.nix +++ b/modules/home/default.nix @@ -18,6 +18,7 @@ ./yazi.nix # flagship TUI file manager, themed + plugins ./osd.nix # swayosd volume/brightness OSD, themed ./shell.nix # zsh + starship + bat/eza/zoxide, themed + ./keys.nix # gpg-agent (fronts SSH) + pinentry-qt ]; # Clipboard history (wl-paste watcher); browsed via the SUPER+CTRL+V diff --git a/modules/home/keys.nix b/modules/home/keys.nix new file mode 100644 index 0000000..aea9840 --- /dev/null +++ b/modules/home/keys.nix @@ -0,0 +1,42 @@ +# SSH + GPG agent — one agent for everything. +# +# gpg-agent fronts SSH (enableSshSupport), so a single pinentry handles +# every passphrase: GPG signing/decryption and SSH key unlocks alike. SSH +# keys are added with `ssh-add` (gpg-agent stores them by keygrip). +# +# Pinentry is pinentry-qt — reliable native-Wayland under Hyprland (no +# GNOME session to lean on) and themed by Stylix's Qt config, so the +# passphrase dialog tracks the palette. +# +# SSH_AUTH_SOCK is exported by the agent's shell integration (zsh, on via +# home.shell.enableZshIntegration in shell.nix). Terminal git/ssh is the +# supported path; a GUI client launched outside a shell won't inherit the +# socket — revisit with a session-level export if that's ever wanted. +# +# gnome-keyring (system side) stays the Secret Service for application +# secrets; modern gnome-keyring no longer runs an SSH agent, so there is no +# SSH_AUTH_SOCK contention with gpg-agent. Screen lock (hyprlock/hypridle) +# doesn't flush the agent cache — unlocked keys persist across a lock, as +# expected; the cache TTLs below govern re-prompting instead. +{ config, lib, pkgs, ... }: + +let + cfg = config.nomarchy; +in +{ + config = lib.mkIf cfg.keys.enable { + programs.gpg.enable = true; + + services.gpg-agent = { + enable = true; + enableSshSupport = true; + pinentry.package = pkgs.pinentry-qt; + + # Cache unlocked keys for a working session, then re-prompt. + defaultCacheTtl = 1800; # 30 min since last use + maxCacheTtl = 7200; # 2 h hard cap + defaultCacheTtlSsh = 1800; + maxCacheTtlSsh = 7200; + }; + }; +} diff --git a/modules/home/options.nix b/modules/home/options.nix index 6fa3d86..234cb09 100644 --- a/modules/home/options.nix +++ b/modules/home/options.nix @@ -69,6 +69,7 @@ yazi.enable = lib.mkEnableOption "the yazi TUI file manager, themed with a curated plugin set" // { default = true; }; osd.enable = lib.mkEnableOption "swayosd on-screen display for volume/brightness/mute" // { default = true; }; shell.enable = lib.mkEnableOption "the zsh shell experience (starship prompt, bat/eza/zoxide)" // { default = true; }; + keys.enable = lib.mkEnableOption "the SSH + GPG agent (gpg-agent fronting SSH, pinentry-qt)" // { default = true; }; ghostty.enable = lib.mkEnableOption "Nomarchy's Ghostty configuration" // { default = true; }; btop.enable = lib.mkEnableOption "btop with the per-theme nomarchy theme" // { default = true; }; stylix.enable = lib.mkEnableOption "Stylix theming for the long tail of apps (GTK, Qt, cursors)" // { default = true; };