Files
claude-foundations/best-practices/secrets-management.md
Paul O'Reilly e7c8214499 Add statusline scripts, context-load improvements, and prior distill updates
- Add statusline.sh and set-topic.sh for per-session status line topics
- Update context-load with improved directory walking and output format
- Update CLAUDE.md with status line docs and early-call safety note
- Update MEMORY.md and README.md with new script/skill entries
- Add memory files: script-statusline, skill-decompose, skill-orchestrate, gotchas-gitea
- Add networking.md best practice (nftables, systemd sockets, Docker forwarding, TLS)
- Update best practices from prior distill: documentation, kubernetes, scripting,
  secrets-management, skills-development
- Prune reflected session logs, add new session logs
- Update reflection state

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 11:11:40 +13:00

3.0 KiB

Secrets Management

SOPS + age

SOPS with age encryption is the standard across all projects. A single .sops.yaml at the repo root defines path-based encryption rules.

File Naming

  • .sops.yaml path-based rules match specific filename patterns (e.g., **/*secret*.yaml)
  • Non-secret files must NOT contain secret in their name, or the pre-commit hook will encrypt them
  • KSOPS generator files should be named ksops-generator.yaml, not secret-generator.yaml

encrypted_regex Gotcha

When using encrypted_regex for selective field encryption (e.g., Ansible group_vars), variable names must contain a keyword that matches the regex (e.g., password|private_key|api_key|secret|token). Arbitrary key names are silently left unencrypted.

SOPS Vars Plugin

Each Ansible project needs vars_plugins_enabled = host_group_vars,community.sops.sops in ansible.cfg. Files in group_vars/ must be named after a group (e.g., all.sops.yaml), not arbitrary names.

Interactive Editor Pitfalls

  • sops <file> opens an interactive editor — fails in non-interactive sessions
  • sops -e /tmp/file fails when the temp path doesn't match .sops.yaml rules
  • Multiple sops --set calls can corrupt files — use the interactive editor for multi-field edits

Credential Handling

  • Never pass secrets via command-line arguments — visible in ps output
  • Use @file references, environment variables sourced at runtime, or stdin
  • For Ansible, use temp files with trap rm cleanup: -e "@${tmpfile}"
  • Read secrets at execution time and use them ephemerally — never cache or persist values
  • Reference the existence of a secret file in docs, never its contents

Bootstrap Secrets

Some secrets are chicken-and-egg (e.g., the age decryption key for ArgoCD's KSOPS). These must be created manually as a bootstrap step and documented clearly.

Credential Lifecycle Management

  • Track credential expiry dates. OAuth client secrets, API tokens, and certificates have expiry dates that can cause silent failures. Document expiry dates when creating credentials.
  • Set alerts before expiry. For long-lived credentials (e.g., 720-day OAuth client secrets), set calendar reminders or automated monitoring alerts well before they expire.
  • Rotation plan. Know the rotation procedure before you need it — some credential types (e.g., Azure app registrations) require coordinated updates across multiple systems.

Multi-Field Secret Files

Secret files that contain multiple fields (e.g., repo URL, token, username) cannot be used as bare values. Consumers must parse individual fields (e.g., grep + awk or structured YAML/JSON parsing).

The multi-field format is preferable because it's self-documenting — all related credentials live together. But any automation reading the file needs extraction logic, not just cat.

Backup Considerations

Backup plans must include encryption keys (age private keys, etc.) so that encrypted data in Git repos remains recoverable.