Playbook: Discord Credential Rotation
Covers: DISCORD_BOT_TOKEN · discord_radieu_password · DISCORD_WEBHOOK_URL
Overview
| Key | SOPS file(s) | Purpose |
|---|---|---|
DISCORD_BOT_TOKEN | monitoring | Discord bot for infra alerts; token reset requires 2FA |
discord_radieu_password | monitoring | Account password for the radieu Discord user |
DISCORD_WEBHOOK_URL | n8n-bms4, vps-h1 | Incoming webhook for script error notifications |
DISCORD_BOT_TOKEN and discord_radieu_password are Tier 3 Manual — no rotation API exists
for either (bot token reset and account password change are both Developer Portal / account-
settings UI operations gated by 2FA).
DISCORD_WEBHOOK_URL is Tier 1 Fully Autonomous, not Tier 3 — corrected twice on 2026-08-04
(#2889/#2666). First correction claimed Tier 2 via Playwright; that was superseded the same day
after a live test found Playwright’s login hits an unsolvable hCaptcha challenge, while the
REST API path (DISCORD_BOT_TOKEN + the bot holding Manage Webhooks on the channel) worked
end-to-end with zero login/2FA/CAPTCHA involved — see docs/playbooks/discord-provisioning.md §Step 2b. No TOTP prerequisite, no human-supervised session, no browser at all — genuinely Tier 1
once the bot has been granted Manage Webhooks on a given channel (a one-time per-channel human
grant, not part of the rotation itself). The Playwright path in that same playbook remains
documented as a fallback but is currently non-functional (hCaptcha). The steps below are kept as
the manual UI fallback for when neither automated path is available, and for the two genuinely
Tier 3 keys in this file.
When to rotate
DISCORD_BOT_TOKEN: On exposure immediately. Scheduled: annually.discord_radieu_password: On exposure; scheduled: annually.DISCORD_WEBHOOK_URL: Only if compromised or channel deleted. Webhook URLs do not expire.
Pre-rotation checklist
- 2FA app accessible (bot token reset requires Discord 2FA)
- No active bot operations in flight (bot reconnects automatically after token reset)
Rotation steps
DISCORD_BOT_TOKEN
1. Get new token (human required)
https://discord.com/developers/applications→ select the p24-infra bot- Bot tab → Reset Token → confirm 2FA prompt
- Copy the new token — shown once only
2. Distribute (Claude handles)
Update secrets/monitoring.env.sops — follow docs/playbooks/sops-edit-operations.md:
$env:SOPS_AGE_KEY_FILE = "C:\Users\konar\.age\p24-infra-keys.txt"
# Edit DISCORD_BOT_TOKEN in monitoring.env.sops per sops-edit-operations.md
# Canary after write:
sops --decrypt --input-type dotenv --output-type dotenv secrets\monitoring.env.sops | Out-Null
if ($LASTEXITCODE -ne 0) { throw "SOPS corrupt — do NOT commit" }No container restart needed — the Discord bot reconnects on the next event cycle.
3. Verify
The bot will show as Online in its Discord server within 60 seconds.
discord_radieu_password
1. Change password (human required)
Discord app → User Settings → My Account → Change Password → set new strong password.
2. Distribute (Claude handles)
Update secrets/monitoring.env.sops — key: discord_radieu_password.
No service restarts required.
DISCORD_WEBHOOK_URL
Rotate only if the webhook is compromised or its channel is deleted.
1. Create new webhook — REST API (preferred, fully autonomous, no login)
Use docs/playbooks/discord-provisioning.md §Step 2b:
python3 discord-provisioning.py --action create-webhook \
--webhook-name "<name>" --guild <GUILD_ID> --channel <CHANNEL_ID> \
--sops-file secrets/n8n-bms4.env.sops --sops-file2 secrets/vps-h1.env.sops \
--secret-key DISCORD_WEBHOOK_URL --issue <ISSUE_NUM>This tries the REST API automatically (no flag needed) and writes both SOPS files + the rotation
log in one step — skip §2 below when it succeeds. Prerequisite: DISCORD_BOT_TOKEN valid in
secrets/monitoring.env.sops and the bot holding Manage Webhooks on the target channel (a
one-time per-channel human grant — Server Settings → Roles → the bot’s role → Manage Webhooks).
No login, no TOTP, no human-supervised session needed for this path.
Fallback if REST 403s (bot lacks the permission on this channel) — Playwright: the same CLI
command falls back automatically, but as of 2026-08-04 this path is known blocked by an
unsolvable hCaptcha challenge at login (see discord-provisioning.md status note) — expect it
to fail, not just require supervision. Grant the bot Manage Webhooks on the channel instead of
relying on this fallback.
Fallback — manual (human required): if neither automated path works: Discord server → channel Settings → Integrations → Webhooks → New Webhook → copy URL.
2. Distribute (manual-path only — the Playwright path in step 1 already does this)
Update secrets/n8n-bms4.env.sops and secrets/vps-h1.env.sops: key DISCORD_WEBHOOK_URL.
Run canary on both files before git add.
Restart consumers that cache the URL at startup:
ssh root@100.86.202.85 "cd /opt/n8n && docker compose restart n8n"3. Revoke old webhook
REST API (preferred): discord-provisioning.py --action delete-webhook --webhook-name "<old-name>" --guild <GUILD_ID> --channel <CHANNEL_ID> — only after the new URL is confirmed live everywhere.
Falls back to Playwright automatically (currently hCaptcha-blocked, see §1 above) if REST fails.
Manual fallback: Discord → channel Settings → Integrations → Webhooks → find old entry → Delete.
Log rotation
Append to docs/secrets-rotation-log.md:
| YYYY-MM-DD HH:MM UTC | #ISSUE | DISCORD_BOT_TOKEN | reason | radieu | monitoring.env.sops |
Recovery
- Bot token reset portal error: wait 5 minutes, retry. Old token remains valid until explicitly reset.
- Webhook alerts stopped after URL change: check n8n workflow logs on bms-4 for HTTP 404/410.
References
secret-rotation-access-matrix.md— Tier classification (Tier 1 forDISCORD_WEBHOOK_URLvia REST API, Tier 3 for the other two keys in this file), cross-file duplication tabledocs/playbooks/discord-provisioning.md— REST API (primary) + Playwright (hCaptcha-blocked fallback) for webhook create/deletedocs/playbooks/sops-edit-operations.md— SOPS write pattern