Secrets — sops-nix + age¶
Secrets are managed with sops-nix and age identities. The
encrypted store nixos/secrets.yaml is safe to commit; it is decrypted at activation into
/run/secrets/<name>.
Configuration¶
From nixos/configuration.nix:
sops = {
defaultSopsFile = ./secrets.yaml;
defaultSopsFormat = "yaml";
age.sshKeyPaths = [ "/persist/etc/ssh/ssh_host_ed25519_key" ];
secrets = {
user_password = { neededForUsers = true; };
root_password = { neededForUsers = true; };
gemini_api_key = { owner = "lowcache"; };
github_token = { owner = "lowcache"; };
};
};
| Secret | Decryption target | Used by |
|---|---|---|
user_password |
user creation (neededForUsers) |
users.users.lowcache.hashedPasswordFile |
root_password |
user creation (neededForUsers) |
users.users.root.hashedPasswordFile |
gemini_api_key |
/run/secrets/gemini_api_key |
exported as GEMINI_API_KEY in Fish |
github_token |
/run/secrets/github_token |
exported as GITHUB_TOKEN in Fish |
The API keys are exported at shell start in
home/shell.nix only if the runtime
secret file is readable:
Keys¶
- Host key — decrypts at boot:
/persist/etc/ssh/ssh_host_ed25519_key(converted to age viassh-to-age). - User editing key —
~/.config/sops/age/keys.txt(mode600, persisted under/persist).SOPS_AGE_KEY_FILEis set in the Fish environment, so editing needs no prefix:
The two-place rule¶
Secrets live in exactly two places
- sops-encrypted —
nixos/secrets.yaml(committable because encrypted). /persist— never git-tracked.
They are never placed under dots/, which is published publicly. As a safety net,
.gitignore excludes nixos/*.yaml and the credential files under dots/gemini/.
Adding a secret: add it to nixos/secrets.yaml → declare sops.secrets.<name> in
configuration.nix → consume it (e.g. export in home/shell.nix).