<< All versions
Skill v1.0.0
currentAutomated scan100/100fmind/dot/sops-secrets
──Details
PublishedSeptember 26, 2026 at 03:51 AM
Content Hashsha256:c922ad1324f0bd8d...
Git SHA
──Files
Files (1 file, 6.5 KB)
SKILL.md6.5 KBactive
SKILL.md · 63 lines · 6.5 KB
version: "1.0.0" name: sops-secrets description: "Manage encrypted secrets with sops and age, including Git storage and runtime delivery." license: MIT metadata: kind: task author: Médéric HURIER (Fmind) source: github.com/fmind/dot/tree/main/skills/sops-secrets created: "2026-08-07" updated: "2026-10-02"
Secrets with sops and age
Encrypted secrets live in git next to their configuration. Use environment variables or FIFOs for runtime delivery; interactive editing can write temporary plaintext. gitleaks scans for leaked credentials, and cloud-run owns runtime Secret Manager integration.
Model
- age provides the key pair: one private key per machine or human, public recipients everywhere; prefer it for solo use. Choose cloud KMS when centrally managed access is required; adding a KMS recipient alongside age does not revoke the independent age decryption path.
- sops encrypts the values of YAML, JSON, and ENV files; keys stay readable, so diffs review cleanly and
git logtells which secret changed, never what it is. - Naming: encrypted files are committed as
*.enc.yaml,*.enc.json, or*.enc.env; sops.yaml keys its rules off that suffix and plaintext siblings stay gitignored. - Policy as file:
.sops.yamlat the repo root (sops.yaml) declares which paths get encrypted and for which recipients, so no ad-hoc flags are needed.
Keys
- Resolve the key file: preserve an existing
SOPS_AGE_KEY_FILEor key. Otherwise use$XDG_CONFIG_HOME/sops/age/keys.txtwhen configured,~/.config/sops/age/keys.txton Linux, or~/Library/Application Support/sops/age/keys.txton macOS. SetSOPS_AGE_KEY_FILEexplicitly when choosing another location; see the age key lookup implementation. - Generate only when the key file is absent: create its parent directory with mode
0700, then runage-keygen -o "<key-file>". It creates a private file and refuses to overwrite an existing one. Print only the public half withage-keygen -y "<key-file>". - Distribute only the public key, as the
age:recipient in each repo's.sops.yaml. - Back up the private key in a password manager; never commit it to any dotfiles repo.
- Rotate: add the new recipient to
.sops.yaml, review the recipient diff, and runsops updatekeys --yes <file>on every encrypted file. Verify decryption through the replacement key before retiring the old one. After removing the old recipient, runsops updatekeys --yes <file>followed bysops rotate -i <file>so its previously recoverable data key cannot decrypt future values. Complete this sequence before storing replacement application credentials; urgent provider-side revocation follows the authorized incident response and need not wait for file re-encryption.
Commands
bash
sops edit secrets.enc.yaml # create or edit: decrypts to $EDITOR, re-encrypts on savesops encrypt --filename-override config.enc.yaml config.yaml > config.enc.yaml # suffix selects the creation rule; protect the existing plaintext inputsops decrypt secrets.enc.yaml # decrypt to stdout for piping, never redirect to a filesops exec-env secrets.enc.env 'mise run watch' # inject as env vars, memory-only (preferred)sops exec-file secrets.enc.json 'tool --config {}' # Unix tools get a FIFO by default, not a tmpfs guarantee
- Prefer `exec-env` and `exec-file` for runtime delivery; keep values out of logs and inspect how child processes handle them.
--no-fifowrites a regular temporary file. - CI: jobs wrap commands in
sops exec-env, and every other secret rides encrypted in the repo. The simplest decryption path stores an age private key as the singleSOPS_AGE_KEYGitHub Actions secret, but that long-lived key decrypts every file it is a recipient of. For GCP projects, prefer agcp_kmsrecipient that CI decrypts through Workload Identity Federation (Application Default Credentials fromgoogle-github-actions/auth, withroles/cloudkms.cryptoKeyDecrypteron that key only), and keep age recipients for humans; retire a former CI age recipient through Rotate. - Integrations: Flux, OpenTofu, and runtime-manager wiring lives in integrations.md.
Gotchas
- Editor plaintext:
sops editwrites a temporary plaintext file for the editor. When disk plaintext is forbidden, use an explicitly configured memory-backed temporary directory and compatible editor settings, or avoid the editor workflow. - Existing plaintext and old recipients: encryption does not erase the input, editor backups, shell history, or past commits. Removing a recipient and rotating the SOPS data key protects the new file, but cannot revoke copies or older Git revisions that recipient could decrypt. Rotate exposed application credentials at their provider within the authorized scope.
- Key names still leak: sops encrypts values, not keys, so
stripe_production_key:in a public repo is information; name keys neutrally when the repo is public. - Never edit ciphertext by hand: sops stores a MAC over the file and out-of-band edits fail decryption; go through
sops editorsops set. - Rule match is positional:
sops editpicks the firstcreation_rulesentry whosepath_regexmatches the path relative to.sops.yaml, so run sops from the repo root, and runupdatekeysafter any recipient change. - gitleaks coexists: inspect the redacted rule and location for every finding, including
*.enc.*files; the suffix does not prove that comments, keys, or excluded fields are encrypted. Confirm whether plaintext was exposed before rotating credentials per gitleaks. - Staged hook: the lefthook pre-commit runs gitleaks on staged content, catching a staged plaintext sibling of an
*.enc.*file.
Documentation
- sops · key management · age
- Releases: sops · age
- Companion skills: gitleaks, lefthook, cloud-run (runtime secrets), infra-as-code, security-review.