age

Operational patterns for age encryption in the domus secrets workflow.

Identity Management

Generate a new age identity and store it in the standard location
age-keygen -o ~/.age/identities/personal.key

The output prints the public key as a comment. Extract it into a recipients file for reuse:

Build a recipients file from your identity
age-keygen -y ~/.age/identities/personal.key > ~/.age/recipients/self.txt
Directory structure — keep identities and recipients separated
~/.age/
├── identities/       # Private keys — chmod 600, never committed
│   └── personal.key
└── recipients/       # Public keys — safe to share and commit
    ├── self.txt
    └── team.txt      # One public key per line

Encrypt Configuration Files

The primary use case: encrypting dotfiles and configs before committing to git.

Encrypt SSH config before committing — the domus-captures workflow
age -e -R ~/.age/recipients/self.txt -o ssh/.ssh/config.age ssh/.ssh/config
Decrypt on a new machine
age -d -i ~/.age/identities/personal.key ssh/.ssh/config.age > ssh/.ssh/config
chmod 600 ssh/.ssh/config
Encrypt a secrets file for multiple recipients — any one can decrypt
age -e -R ~/.age/recipients/self.txt -R ~/.age/recipients/team.txt \
    -o secrets.age secrets.env

Pipeline Integration

Decrypt and pipe to another tool — never touches disk in plaintext
age -d -i ~/.age/identities/personal.key secrets.json.age | jq '.api_key'
Encrypt from stdin — useful in scripts
vault kv get -field=password kv/infra/db | age -e -R ~/.age/recipients/self.txt -o db-pass.age
Encrypt a compressed directory archive
tar czf - ~/.gnupg/ | age -e -R ~/.age/recipients/self.txt -o gnupg-backup.tar.gz.age
Decrypt and extract archive
age -d -i ~/.age/identities/personal.key gnupg-backup.tar.gz.age | tar xzf -

SSH Key as Recipient

age can use SSH public keys directly — no age-keygen needed on the recipient’s side.

Encrypt using an SSH ed25519 public key
age -r "ssh-ed25519 AAAA..." -o secret.age plaintext.txt
Encrypt using a GitHub user’s SSH keys — fetched live
age -R <(curl -s https://github.com/evanusmodestus.keys) -o secret.age plaintext.txt
Decrypt using SSH private key as identity
age -d -i ~/.ssh/id_ed25519 secret.age > plaintext.txt

Passphrase Mode

When you need to share a secret without pre-exchanging keys.

Encrypt with passphrase — interactive prompt
age -p -o secret.age plaintext.txt
ASCII-armored output — PEM-like text, safe for email or paste
age -a -p -o secret.age.txt plaintext.txt

SOPS Integration

SOPS encrypts individual YAML/JSON values while leaving keys readable — ideal for GitOps.

Encrypt a YAML file with SOPS using age
sops --age age1... -e secrets.yaml > secrets.enc.yaml
Decrypt with SOPS — requires AGE_KEY_FILE or SOPS_AGE_KEY_FILE
SOPS_AGE_KEY_FILE=~/.age/identities/personal.key sops -d secrets.enc.yaml

Batch Operations

Re-encrypt all .age files after rotating identity
find . -name '*.age' -exec sh -c '
    age -d -i ~/.age/identities/old.key "$1" | \
    age -e -R ~/.age/recipients/self.txt -o "$1.tmp" && \
    mv "$1.tmp" "$1"
' _ {} \;
Verify all .age files are decryptable — test after key rotation
find . -name '*.age' -exec sh -c '
    age -d -i ~/.age/identities/personal.key "$1" > /dev/null 2>&1 && \
    echo "OK: $1" || echo "FAIL: $1"
' _ {} \;