# Credential Rotation Checklist

**Last audited**: March 2026  
**Purpose**: Prioritized list of credentials that should be rotated, with exact locations and steps.

> After rotating any credential, update it in **Vaultwarden** (collection: Homelab) as the source of truth before updating the compose file or Portainer stack.

---

## Priority Legend

| Symbol | Meaning |
|--------|---------|
| 🔴 CRITICAL | Live credential exposed in git — rotate immediately |
| 🟠 HIGH | Sensitive secret that should be rotated soon |
| 🟡 MEDIUM | Lower-risk but should be updated as part of routine rotation |
| 🟢 LOW | Default/placeholder values — change before putting service in production |

---

## 🔴 CRITICAL — Rotate Immediately

### 1. OpenAI API Key
- **File**: `hosts/vms/homelab-vm/hoarder.yaml:15`
- **Service**: Hoarder AI tagging
- **Rotation steps**:
  1. Go to [platform.openai.com/api-keys](https://platform.openai.com/api-keys)
  2. Delete the old key
  3. Create a new key
  4. Update `hosts/vms/homelab-vm/hoarder.yaml` — `OPENAI_API_KEY`
  5. Save new key in Vaultwarden → Homelab → Hoarder
  6. Redeploy hoarder stack via Portainer

### 2. Gmail App Password — Authentik + Joplin SMTP (see Vaultwarden → Homelab → Gmail App Passwords)
- **Files**: 
  - `hosts/synology/calypso/authentik/docker-compose.yaml` (SMTP password)
  - `hosts/synology/atlantis/joplin.yml` (SMTP password)
- **Rotation steps**:
  1. Go to [myaccount.google.com/apppasswords](https://myaccount.google.com/apppasswords)
  2. Revoke the old app password
  3. Create a new app password (label: "Homelab SMTP")
  4. Update both files above with the new password
  5. Save in Vaultwarden → Homelab → Gmail App Passwords
  6. Redeploy both stacks

### 3. Gmail App Password — Vaultwarden SMTP (see Vaultwarden → Homelab → Gmail App Passwords)
- **File**: `hosts/synology/atlantis/vaultwarden.yaml`
- **Rotation steps**: Same as above — create a separate app password per service
  1. Revoke old, create new
  2. Update `hosts/synology/atlantis/vaultwarden.yaml` — `SMTP_PASSWORD`
  3. Redeploy vaultwarden stack

### 4. Gmail App Password — Documenso SMTP (see Vaultwarden → Homelab → Gmail App Passwords)
- **File**: `hosts/synology/atlantis/documenso/documenso.yaml:47`
- **Rotation steps**: Same pattern — revoke, create new, update compose, redeploy

### 5. Gmail App Password — Reactive Resume SMTP (see Vaultwarden → Homelab → Gmail App Passwords)
- **File**: `hosts/synology/calypso/reactive_resume_v5/docker-compose.yml`
- **Rotation steps**: Same pattern

### 6. Gitea PAT — retro-site.yaml (now removed)
- **Status**: ✅ Hardcoded token removed from `retro-site.yaml` — now uses `${GIT_TOKEN}` env var
- **Action**: Revoke the old token `REDACTED_GITEA_TOKEN` in Gitea
  1. Go to `https://git.vish.gg/user/settings/applications`
  2. Revoke the token associated with `retro-site.yaml`
  3. The stack now uses the `GIT_TOKEN` Gitea secret — no file update needed

### 7. Gitea PAT — Ansible Playbook (now removed)
- **Status**: ✅ Hardcoded token removed from `ansible/automation/playbooks/setup_gitea_runner.yml`
- **Action**: Revoke the old token `REDACTED_GITEA_TOKEN` in Gitea
  1. Go to `https://git.vish.gg/user/settings/applications`
  2. Revoke the associated token
  3. Future runs of the playbook will prompt for the token interactively

---

## 🟠 HIGH — Rotate Soon

### 8. Authentik Secret Key
- **File**: `hosts/synology/calypso/authentik/docker-compose.yaml:58,89`
- **Impact**: Rotating this invalidates **all active sessions** — do during a maintenance window
- **Rotation steps**:
  1. Generate a new 50-char random key: `openssl rand -base64 50`
  2. Update `AUTHENTIK_SECRET_KEY` in the compose file
  3. Save in Vaultwarden → Homelab → Authentik
  4. Redeploy — all users will need to re-authenticate

### 9. Mastodon SECRET_KEY_BASE + OTP_SECRET
- **File**: `hosts/synology/atlantis/mastodon.yml:67-68`
- **Impact**: Rotating breaks **all active sessions and 2FA tokens** — coordinate with users
- **Rotation steps**:
  1. Generate new values:
     ```bash
     docker run --rm tootsuite/mastodon bundle exec rake secret
     docker run --rm tootsuite/mastodon bundle exec rake secret
     ```
  2. Update `SECRET_KEY_BASE` and `OTP_SECRET` in `mastodon.yml`
  3. Save in Vaultwarden → Homelab → Mastodon
  4. Redeploy

### 10. Grafana OAuth Client Secret (Authentik Provider)
- **File**: `hosts/vms/homelab-vm/monitoring.yaml:986`
- **Rotation steps**:
  1. Go to Authentik → Applications → Providers → Grafana provider
  2. Edit → regenerate client secret
  3. Copy the new secret
  4. Update `GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET` in `monitoring.yaml`
  5. Save in Vaultwarden → Homelab → Grafana OAuth
  6. Redeploy monitoring stack

---

## 🟡 MEDIUM — Routine Rotation

### 11. Watchtower HTTP API Token (`REDACTED_WATCHTOWER_TOKEN`)
- **Files** (must update all at once):
  - `hosts/synology/atlantis/watchtower.yml`
  - `hosts/synology/atlantis/grafana_prometheus/prometheus.yml`
  - `hosts/synology/atlantis/grafana_prometheus/prometheus_mariushosting.yml`
  - `hosts/synology/calypso/grafana_prometheus/prometheus.yml`
  - `hosts/synology/setillo/prometheus/prometheus.yml`
  - `hosts/synology/calypso/watchtower.yaml`
  - `common/watchtower-enhanced.yaml`
  - `common/watchtower-full.yaml`
- **Rotation steps**:
  1. Choose a new token: `openssl rand -hex 32`
  2. Update `WATCHTOWER_HTTP_API_TOKEN` in all watchtower stack files
  3. Update `bearer_token` in all prometheus.yml scrape configs
  4. Save in Vaultwarden → Homelab → Watchtower
  5. Redeploy all affected stacks (watchtower first, then prometheus)

### 12. Shlink API Key
- **File**: `hosts/vms/homelab-vm/shlink.yml:41`
- **Rotation steps**:
  1. Log into Shlink admin UI
  2. Generate a new API key
  3. Update `DEFAULT_API_KEY` in `shlink.yml`
  4. Save in Vaultwarden → Homelab → Shlink
  5. Redeploy shlink stack

### 13. Spotify Client ID + Secret (YourSpotify)
- **Files**:
  - `hosts/physical/concord-nuc/yourspotify.yaml`
  - `hosts/vms/bulgaria-vm/yourspotify.yml`
- **Rotation steps**:
  1. Go to [developer.spotify.com/dashboard](https://developer.spotify.com/dashboard)
  2. Select the app → Settings → Rotate client secret
  3. Update both files with new `SPOTIFY_CLIENT_ID` and `SPOTIFY_CLIENT_SECRET`
  4. Save in Vaultwarden → Homelab → Spotify API
  5. Redeploy both stacks

### 14. SNMPv3 Auth + Priv Passwords
- **Files**:
  - `hosts/synology/atlantis/grafana_prometheus/snmp.yml` (exporter config)
  - `hosts/vms/homelab-vm/monitoring.yaml` (prometheus scrape config)
- **Note**: Must match the SNMPv3 credentials configured on the target devices (Synology NAS, switches)
- **Rotation steps**:
  1. Change the SNMPv3 user credentials on each monitored device (DSM → Terminal & SNMP)
  2. Update `auth_password` and `priv_password` in `snmp.yml`
  3. Update the corresponding values in `monitoring.yaml`
  4. Save in Vaultwarden → Homelab → SNMP
  5. Redeploy monitoring stack

---

## 🟢 LOW — Change Before Production Use

These are clearly placeholder/default values that exist in stacks but are either:
- Not currently deployed in production, or
- Low-impact internal-only services

| Service | File | Credential | Value to Replace |
|---------|------|-----------|-----------------|
| NetBox | `hosts/synology/atlantis/netbox.yml` | Superuser password | see Vaultwarden |
| Paperless | `hosts/synology/calypso/paperless/docker-compose.yml` | Admin password | see Vaultwarden |
| Seafile | `hosts/synology/calypso/seafile-server.yaml` | Admin password | see Vaultwarden |
| Gotify | `hosts/vms/homelab-vm/gotify.yml` | Admin password | `REDACTED_PASSWORD` |
| Invidious (old) | `hosts/physical/concord-nuc/invidious/invidious_old/invidious.yaml` | PO token | Rotate if service is active |

---

## Post-Rotation Checklist

After rotating any credential:

- [ ] New value saved in Vaultwarden under correct collection/folder
- [ ] Compose file updated in git repo
- [ ] Stack redeployed via Portainer (or `docker compose up -d --force-recreate`)
- [ ] Service verified healthy (check Uptime Kuma / Portainer logs)
- [ ] Old credential revoked at the source (Google, OpenAI, Gitea, etc.)
- [ ] `.secrets.baseline` updated if detect-secrets flags the new value:
  ```bash
  detect-secrets scan --baseline .secrets.baseline
  git add .secrets.baseline && git commit -m "chore: update secrets baseline after rotation"
  ```

---

## Related Documentation

- [Secrets Management Strategy](secrets-management.md)
- [Headscale Operations](../services/individual/headscale.md)
- [B2 Backup Status](b2-backup-status.md)
