<!-- Copyright (c) 2026 defconxt. All rights reserved. -->
<!-- Licensed under AGPL-3.0 — see LICENSE file for details. -->

# Secrets Management Reference (SOPS, Ansible Vault, Borgmatic)

## SOPS with age (Air-Gapped Friendly)

```bash
# Generate age key pair
age-keygen -o ~/.config/sops/age/keys.txt  # keep private key offline

# .sops.yaml config in repo root
creation_rules:
  - path_regex: secrets/.*\.yaml$
    age: >-
      age1s3cqcks5genc6ru8chl0hkkd04zmxvczsvdxq99ekffe4gmvjpzsedk23c

# Encrypt new file
sops --encrypt secrets/prod.yaml > secrets/prod.enc.yaml

# Edit encrypted file (opens $EDITOR with plaintext)
SOPS_AGE_KEY_FILE=~/.config/sops/age/keys.txt sops secrets/prod.enc.yaml

# Inject secrets into process env without writing plaintext
sops exec-env secrets/prod.enc.yaml './app --config /etc/app.conf'
```

## SOPS with AWS KMS (CI/CD)

`export SOPS_KMS_ARN="arn:aws:kms:us-east-1:123456789:key/abc-def"` then `sops --encrypt --kms "$SOPS_KMS_ARN" secrets.yaml`. CI decrypts via IAM role — no static keys.

## Ansible Vault

Rotation: `ansible-vault rekey --new-vault-password-file ~/.new_vault_pass vault.yml`

Inline value: `ansible-vault encrypt_string 'supersecret' --name 'db_password'`

## Borgmatic Encrypted Backup

```yaml
# /etc/borgmatic/config.yaml
source_directories: [/home, /etc, /var/lib/postgresql]
repositories:
  - path: ssh://user@backup.host/./repo
    label: offsite
  - path: /mnt/local/backup.borg
    label: local
encryption_passphrase: "{{ lookup('env', 'BORG_PASSPHRASE') }}"
keep_daily: 7
keep_weekly: 4
keep_monthly: 6
checks: [{name: repository}, {name: archives, frequency: '2 weeks'}]
postgresql_databases: [{name: all}]  # dumps all DBs before backup
before_backup: [systemctl stop myapp]
after_backup: [systemctl start myapp]
```

```bash
borgmatic --config /etc/borgmatic/config.yaml create --stats --list
borgmatic extract --archive latest --path /etc/postgresql/ --destination /tmp/restore/
```

## Anti-Patterns

- Committing `.sops.yaml` with plaintext age public keys is fine — the private key never touches the repo.
- Using sops without `creation_rules` path_regex — results in inconsistent key selection across environments.
