3.6 KiB
Secrets with sops-nix
This repo wires sops-nix into the Home Manager profile so secrets are decrypted on-demand into ~/.config/secrets. The module is enabled by default and auto-generates an Age key if one does not already exist. Only the encrypted payloads under secrets/*.sops.yaml are meant to live in git; decrypted files never leave your machine.
One-time setup
- Ensure
ageandsopsare available (e.g.nix developornix profile install nixpkgs#age nixpkgs#sops). - Generate or import an Age key. Either let Home Manager create one automatically on first activation or run
age-keygen -o ~/.config/sops/age/keys.txtyourself. - Capture the public half with
age-keygen -y -f ~/.config/sops/age/keys.txtand add it to therecipientslist in each encrypted file (age1…). Commit the public key under version control or share it through your password manager so other hosts can decrypt. - (Optional) Store the private key securely in macOS Keychain or 1Password (
security add-generic-password …) so rebuilds work without manual prompts.
Managing secrets
- Declare secrets in Home Manager under
rsydn.secrets.secrets. Each entry is forwarded tosops.secrets.<name>and defaults to writing~/.config/secrets/<name>with mode0400. - Structure encrypted data however you like (
YAML,JSON,.env). Only the encrypted file is tracked; plaintext never leavessops. - Keep sensitive values scoped. Per-project secrets usually live in
.env.sops+direnv; long-lived device secrets belong here.
Example snippet for modules/home/rsydn/secrets.nix consumers:
rsydn.secrets.secrets = {
"openai-api-key" = {
sopsFile = ./secrets.sops.yaml;
path = "${config.xdg.configHome}/secrets/openai-api-key";
};
};
Create or update secrets by running:
SOPS_AGE_KEY_FILE=$HOME/.config/sops/age/keys.txt \
sops secrets/secrets.sops.yaml
When Home Manager activates, the secret is decrypted to the target path. Source it in Nushell with something like:
let-openai-key = (open $env.XDG_CONFIG_HOME ++ "/secrets/openai-api-key" | str trim)
Because the secret stays on disk, prefer reading it only when needed (inside commands/functions) instead of exporting it globally. For project workflows, you can also load it into env vars temporarily:
def with-openai-key [cmd] {
let key = open $env.XDG_CONFIG_HOME ++ "/secrets/openai-api-key" | str trim
with-env { OPENAI_API_KEY: $key } { nu -c $cmd }
}
To attach multiple secrets at once, use structured data (e.g. a YAML mapping) and parse it with Nushell's from yaml.
Rotating keys
If you regenerate your Age key, re-encrypt the file with the new recipient (sops updatekeys secrets/secrets.sops.yaml) and re-run darwin-rebuild --dry-run --flake .#macbook-pro to verify the deployment. Remember to remove old recipients so machines without access can no longer decrypt.
Frequently asked questions
- Where do encrypted files live? In this repo under
secrets/*.sops.yaml; they are safe to commit. - Where does plaintext live? At runtime under
~/.config/secrets/*(managed by Home Manager). Keep permissions tight and never add these paths to git. - How do I share secrets with another machine? Copy the Age public key from that machine into the
recipientslist, re-runsops updatekeys, commit, then pull and rebuild on the other host. - Can I keep using
.env? Yes—encrypt it (e.g.secrets/project.env.sops) and load it withsops exec-envinsidedirenvso per-project shells receive the decrypted variables without storing them on disk.