← knowledge.oriz.in

age — modern file encryption (X25519 + ChaCha20-Poly1305)

service servicesecurityencryptionagesops-backend

age — modern file encryption

What it is

A 2019-era file-encryption tool by Filippo Valsorda (Go-language cryptographer, ex-Google, ex-Cloudflare). Designed as a modern replacement for gpg --encrypt with a minimal feature set.

Why we use it (instead of PGP/KMS)

Concern age GPG AWS KMS
Setup time 30 sec (age-keygen) 30 min (keyring + web of trust) hours (IAM + KMS policies)
Cloud dependency None None AWS account
Failure modes Lose the key file GPG agent issues, expired subkeys, web-of-trust corruption IAM misconfig, KMS region outage, billing
Cost $0 $0 $0.03 / 10K decrypt requests
Multi-recipient Yes (additive) Yes (additive) Yes (KMS grants)
Offline decryption Yes, forever Yes, forever Requires AWS reachable

For a solo dev with no compliance requirement, age wins on simplicity. The cost is real but small: it has no integration with hardware tokens, no revocation list, no formal trust model. Trade-offs we accept.

Our setup

Where the keys live:

Generate (one-time, only if rotating):

age-keygen -o ~/.config/sops/age/keys.txt    # generates new keypair
# Copy the public age1... line into .sops.yaml
# Copy the AGE-SECRET-KEY-1... into Bitwarden CLI

On a fresh machine:

bw get item age-key | jq -r .notes > ~/.config/sops/age/keys.txt
export SOPS_AGE_KEY_FILE=~/.config/sops/age/keys.txt
# Now sops -d works in any submodule

CI (GitHub Actions):

- name: Decrypt secrets
  env:
    SOPS_AGE_KEY: ${{ secrets.SOPS_AGE_KEY }}    # the private key as a single env var
  run: sops decrypt -i .env.enc

Key rotation

Rotation is a Bitwarden CLI update + a re-encrypt of every .env.enc family-wide. ~5 min if all submodules are cloned locally. We have not rotated yet (and have no scheduled rotation) — the cost of a real rotation only justifies if the current key is suspected compromised. See [[runbooks/rotate-age-key]] (write when first needed).

Anti-patterns

Related