{...}: { config.flake.modules.homeManager.encryption = { lib, config, pkgs, ... }: { options.azos.encryption.enable = lib.mkOption { default = true; type = lib.types.bool; }; config = lib.mkIf config.azos.encryption.enable { programs.password-store = { enable = true; settings = { PASSWORD_STORE_KEY = "26B851F81F8F7C55"; }; }; home.packages = with pkgs; [ yubikey-personalization ]; programs.gpg = { enable = true; }; programs.browserpass = { enable = true; browsers = ["chromium"]; }; services.gpg-agent = { enable = true; enableSshSupport = true; grabKeyboardAndMouse = false; pinentry.package = pkgs.pinentry-emacs; extraConfig = '' allow-emacs-pinentry allow-loopback-pinentry ''; }; # pinentry-emacs needs INSIDE_EMACS set, but NOT on gpg-agent itself: # gpg-agent calls gnupg_unsetenv("INSIDE_EMACS") on its own process at # startup (agent/gpg-agent.c, both supervised and daemon mode) -- by # design, it expects this to arrive per-connection from the CLIENT, # not statically from its own daemon environment. Each gpg/git # invocation reads INSIDE_EMACS from its own env and forwards it to # gpg-agent via "OPTION putenv=INSIDE_EMACS=..." (common/asshelp.c: # send_pinentry_environment); gpg-agent stores that per-session and # copies it into the forked pinentry's actual env for that one # request (agent/call-pinentry.c:atfork_core). So the var has to be # set on whatever shell/process runs git or gpg -- a prior attempt # at this set it on gpg-agent.service's systemd Environment instead, # which gpg-agent silently discards before it can ever be read. # # Only safe to set globally when Emacs is structurally guaranteed to # be up and responsive -- i.e. under EXWM, where Emacs *is* the # window manager. Elsewhere Emacs may not be running (fine: the # loopback socket connect fails instantly, same fast fallback as # today) or may be running but transiently busy (not fine: pinentry # then blocks on the Assuan handshake for up to its 60s timeout # before falling back, which is strictly worse than today's instant # failure). home.sessionVariables = lib.mkIf config.azos.suites.exwm.enable { INSIDE_EMACS = "t"; }; home.file.".ssh/config".source = ./ssh-config; home.file.".ssh/gpg-as-ssh.pub".source = ./gpg-as-ssh.pub; home.file.".gnupg/sshcontrol".source = ./sshcontrol; # home-manager creates ~/.gnupg (via the files above) with the default # umask, which can leave it group/other-readable. gpg refuses to trust # a homedir like that and prints an "unsafe permissions" warning on # every invocation, which pollutes anything that captures gpg/pass # output (e.g. shell-command-to-string). Enforce 700 on every switch. home.activation.gnupgPerms = lib.hm.dag.entryAfter ["writeBoundary"] '' $DRY_RUN_CMD chmod 700 "$HOME/.gnupg" ''; }; }; }