Security
Key Rotation Guide
This page tracks two distinct key lifecycles:
- Disclosure key — the GPG key reporters use to encrypt vulnerability reports to the maintainer. Single key, public, rotated annually or on compromise. Source of truth for the active fingerprint is
SECURITY.md. - Secrets encryption keys — the Age and SOPS keys that protect encrypted dotfiles in this repo. Multiple keys, per-machine, rotated annually or on personnel / device change.
Both sections live here so a single audit can confirm the project's posture across encrypted-disclosure-in and encrypted-secrets-at-rest.
Disclosure Key (GPG)
Closes the rotation half of #870.
Active key
| Field | Value |
|---|---|
| Identity | [email protected] |
| Fingerprint | 55AFAD364FD9DB3819E61F0C8D688FAFA9144693 |
| Algorithm | ED25519 signing primary + CV25519 encryption subkey |
| Created | 2026-05-16 |
| Expires | 2029-05-15 (3 years from creation) |
| Keyserver | https://keys.openpgp.org/search?q=sebastian.rousseau%40gmail.com |
| WKD URL | https://sebastienrousseau.com/.well-known/openpgpkey/hu/qpzqfwauiwxnu1xrf5h47bunsho44p6f — not yet published; the path returns 404, so fetch the key from the keyserver above until WKD is served. |
| Cross-reference | matches the SSH signing key in dot_config/git/allowed_signers.tmpl |
Rotation triggers
| Trigger | Response time | Action |
|---|---|---|
| Annual cadence | Within 30 days of expiry | Generate new key, publish, sign transition statement with the old key, archive old key below. |
| Suspected compromise | Same day | Revoke immediately, publish revocation cert to WKD, replay in-flight encrypted disclosures with the new key. |
| Algorithm deprecation | Within 90 days of advisory | Generate replacement on a stronger algorithm; follow annual-cadence steps. |
| Maintainer change | Same day as handover | Old maintainer signs a transition statement; old key revoked 30 days after handover. |
Rotation procedure
-
Generate the new key (air-gapped where possible):
-
Sign a transition statement with the OLD key:
-
Publish the new key to:
sebastienrousseau.com/.well-known/openpgpkey/hu/(WKD)- the maintainer's keyoxide profile if applicable
-
Update the "Active key" table above with the new fingerprint and move the previous row into the "Historical disclosure keys" table below with the actual revocation date.
-
Commit, signing with both old and new keys during the overlap:
-
After the 30-day overlap, revoke the old key and publish the revocation cert to the same WKD endpoint.
Historical disclosure keys
| From | Until | Fingerprint | Reason for rotation |
|---|---|---|---|
| (none yet — first key) | — | — | — |
Age Key Rotation
When to Rotate
- Annually: As a preventive measure
- After compromise: If you suspect key exposure
- Personnel change: When team members leave
- Device loss: If a device with the key is lost/stolen
Rotation Procedure
1. Generate New Key
# Create new age key
# Note the public key (starts with "age1...")
|
2. Re-encrypt All Secrets
# List all encrypted files
# For each encrypted file, decrypt with old key and re-encrypt with new
for; do
# Decrypt with old key
# Re-encrypt with new key (update chezmoi config first)
# See step 3
done
3. Update Chezmoi Configuration
Edit ~/.config/chezmoi/chezmoi.toml:
[age]
identity = "~/.config/chezmoi/key-new.txt"
recipient = "age1your_new_public_key_here"
4. Re-add Encrypted Files
# Re-add each secret with new encryption
5. Verify and Clean Up
# Verify decryption works
# Archive old key (store securely offline)
# Rename new key
# Commit changes
Emergency Rotation (Compromised Key)
# 1. Immediately generate new key
# 2. Re-encrypt ALL secrets (prioritize most sensitive)
# 3. Revoke/rotate any API keys, tokens that were encrypted
# 4. Update any shared secrets with team members
# 5. Document the incident
SOPS Key Rotation
Overview
SOPS supports multiple key types. This covers Age keys (recommended) and GPG keys.
SOPS with Age Keys
Generate New Key
Update .sops.yaml
creation_rules:
- path_regex: \.env$
age: >-
age1new_key_here,
age1old_key_here # Keep old key temporarily for decryption
Re-encrypt Files
# Rotate key for a single file
# Or re-encrypt entirely
|
Remove Old Key
After confirming all files are re-encrypted:
- Remove old key from
.sops.yaml - Archive old key securely
- Commit changes
SOPS with GPG Keys
# Generate new GPG key
# Export fingerprint
# Update .sops.yaml with new fingerprint
# Re-encrypt files
Automation Scripts
Check Key Age
#!/usr/bin/env bash
# check-key-age.sh - Alert if keys are older than 365 days
KEY_FILE="/.config/chezmoi/key.txt"
MAX_AGE_DAYS=365
if ; then
key_age=
if ; then
else
fi
fi
Automated Backup Before Rotation
#!/usr/bin/env bash
# backup-before-rotation.sh
BACKUP_DIR="/.local/share/dotfiles/key-backups"
# Backup current key
# Encrypt backup with passphrase
# Remove unencrypted backup
Best Practices
- Never commit unencrypted keys - Use
.gitignoreproperly - Store backups offline - USB drive in secure location
- Use hardware keys when possible - YubiKey with age-plugin-yubikey
- Document rotation dates - Keep a log of when keys were rotated
- Test decryption - Always verify after rotation
- Notify team members - If using shared secrets
Recovery Procedures
If you lose access to your key:
- Check backups - Offline storage, password manager
- Check other machines - Key may exist on another device
- Re-create secrets - As last resort, regenerate API keys, tokens, etc.
Related Documentation
- SECRETS.md - Secrets management overview
- SECURITY.md - Security hardening guide
- Age documentation
- SOPS documentation