passage.nix (3135B)
1 # modules/home/passage.nix — `passage`: a password store, keyed by the age key 2 # the flake already has. 3 # 4 # passage init not needed; the recipients come from the environment 5 # passage insert web/gitlab 6 # passage show web/gitlab passage -c web/gitlab (clipboard, 45s) 7 # passage generate web/newsite 32 8 # passage ls / passage find gitlab / passage grep token 9 # passage git init && passage git remote add ... 10 # 11 # Why passage and not pass: `pass` is GnuPG, which means the agent, pinentry 12 # and a keyring. passage is the same tool reimplemented on age, and this 13 # machine already keeps an age key for sops-nix. One key, one backup. 14 # 15 # Why it reuses ~/.config/sops/age/keys.txt rather than its own identity: 16 # that file already decrypts every secret the flake has, so pointing passage 17 # at it adds no exposure that is not already there, and -- more importantly -- 18 # no SECOND 189-byte file whose loss is unrecoverable. The alternative, a 19 # dedicated passphrase-protected identity, is in the vault note; it is a real 20 # improvement in compartmentalisation and a real increase in what you must 21 # back up. Switch by changing PASSAGE_IDENTITIES_FILE below. 22 # 23 # This is deliberately NOT where machine secrets live. Anything the system or a 24 # service needs at activation belongs in secrets/secrets.yaml via sops-nix 25 # (modules/home/sops.nix) -- passage needs an interactive shell and your 26 # clipboard, which activation has neither of. passage is for the logins you 27 # type, sops for the values the machine reads. 28 { ... }: 29 { 30 flake.homeModules.passage = 31 { config, pkgs, ... }: 32 let 33 ageKey = "${config.home.homeDirectory}/.config/sops/age/keys.txt"; 34 # The public half of that key. Public by definition -- it is already 35 # committed in ~/NixDaemon/.sops.yaml as the only recipient. Kept here as a 36 # literal for the same reason gpg.nix pins a fingerprint: so a wrong key 37 # cannot be picked up silently from a file that happens to be on disk. 38 recipient = "age1pu5wcvqqh92xr6ces8zmj4xvs52v7yfm4jsfcxm3z82a32u9n97qesldfv"; 39 in 40 { 41 home.packages = [ pkgs.passage ]; 42 43 home.sessionVariables = { 44 # Defaults are ~/.passage/store and ~/.passage/identities; both are set 45 # explicitly so the store can move without surprises. 46 PASSAGE_DIR = "${config.xdg.dataHome}/passage/store"; 47 PASSAGE_IDENTITIES_FILE = ageKey; 48 49 # Recipients as an environment variable rather than a .age-recipients 50 # file in the store: it keeps the store a plain directory of .age files 51 # with nothing to commit by accident, and makes the recipient part of the 52 # flake rather than per-checkout state. For per-subdirectory recipients 53 # (sharing one folder with someone else) drop a .age-recipients in that 54 # folder instead -- passage walks up from the entry to find one, and a 55 # file found that way wins over this. 56 PASSAGE_RECIPIENTS = recipient; 57 58 # pass-compatible knobs passage still honours. 59 PASSWORD_STORE_CLIP_TIME = "45"; # seconds before the clipboard is wiped 60 PASSWORD_STORE_GENERATED_LENGTH = "25"; 61 }; 62 } 63 ; 64 }