You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Background WEBHOOK_SECRET is a single environment variable used by @guildpass/webhook-utils's verifySignature to validate the x-guildpass-signature header on every incoming webhook. There is currently exactly one active secret at any time, configured purely via environment variable — there is no in-app mechanism to rotate it.
Problem
Rotating WEBHOOK_SECRET today requires updating the environment variable and restarting the dashboard, with a hard cutover: any webhook signed with the old secret and delivered after the new secret takes effect will be rejected, and there is no window to update the secret on the GuildPass core side and the dashboard side atomically. This makes routine or incident-driven (e.g. suspected leak) secret rotation operationally risky — a real gap given SECURITY.md's explicit in-scope concern about "exposure of ... API keys" and "webhook verification bypass."
Expected outcome
The dashboard can hold two active webhook secrets simultaneously (current + previous) during a rotation window, accepting signatures verified by either, with a clear admin-facing flow (e.g. in /settings) to: generate a new secret, enter the "dual-verification" rotation window, and later retire the old secret — without ever silently rejecting legitimately-signed webhooks mid-rotation, and without ever displaying a secret value once it has been generated and stored (write-once display, matching common secret-management UX conventions).
Suggested implementation
Extend the webhook verification call site (not necessarily @guildpass/webhook-utils itself, which is a generic, secret-agnostic library) to try verification against a list of currently-valid secrets (current + optionally previous) rather than a single hardcoded value, short-circuiting on the first match.
Add secret storage (requires durable mode, Issue Add a server-wide role sync command for admins #6, since secrets must persist and be admin-manageable — document this dependency clearly) with fields for the active secret, an optional "previous" secret, and a rotation-window expiry timestamp after which the previous secret is no longer accepted.
Build a /settings UI flow: "Rotate webhook secret" generates a new secret, shows it exactly once (with a copy-to-clipboard control and an explicit warning that it won't be shown again), and starts a configurable rotation window (e.g. default 24-48 hours) during which both secrets verify successfully.
Add a background or on-access check that automatically retires the previous secret once its rotation window expires.
Update SECURITY.md and packages/webhook-utils/README.md to describe the rotation flow and recommend it as the safe way to respond to a suspected secret leak.
Acceptance criteria
During an active rotation window, webhooks signed with either the current or the previous secret are accepted.
Outside the rotation window (expired), only the current secret is accepted, and the previous one is correctly rejected.
The generated secret is displayed exactly once in the UI and is not retrievable afterward (only re-rotatable).
A test simulates the full rotation lifecycle: dual-valid window → expiry → old-secret rejection.
Documentation (SECURITY.md, packages/webhook-utils/README.md) is updated to describe the rotation feature and recommend its use after a suspected leak.
pnpm typecheck, pnpm lint, and relevant tests pass.
Likely affected files/directories
Webhook handler/verification call site, apps/dashboard/app/settings/, new secret-storage schema (building on Issue #6), SECURITY.md, packages/webhook-utils/README.md.
Difficulty: Expert
Type: Security
Background
WEBHOOK_SECRETis a single environment variable used by@guildpass/webhook-utils'sverifySignatureto validate thex-guildpass-signatureheader on every incoming webhook. There is currently exactly one active secret at any time, configured purely via environment variable — there is no in-app mechanism to rotate it.Problem
Rotating
WEBHOOK_SECRETtoday requires updating the environment variable and restarting the dashboard, with a hard cutover: any webhook signed with the old secret and delivered after the new secret takes effect will be rejected, and there is no window to update the secret on the GuildPass core side and the dashboard side atomically. This makes routine or incident-driven (e.g. suspected leak) secret rotation operationally risky — a real gap givenSECURITY.md's explicit in-scope concern about "exposure of ... API keys" and "webhook verification bypass."Expected outcome
The dashboard can hold two active webhook secrets simultaneously (current + previous) during a rotation window, accepting signatures verified by either, with a clear admin-facing flow (e.g. in
/settings) to: generate a new secret, enter the "dual-verification" rotation window, and later retire the old secret — without ever silently rejecting legitimately-signed webhooks mid-rotation, and without ever displaying a secret value once it has been generated and stored (write-once display, matching common secret-management UX conventions).Suggested implementation
@guildpass/webhook-utilsitself, which is a generic, secret-agnostic library) to try verification against a list of currently-valid secrets (current + optionally previous) rather than a single hardcoded value, short-circuiting on the first match.durablemode, Issue Add a server-wide role sync command for admins #6, since secrets must persist and be admin-manageable — document this dependency clearly) with fields for the active secret, an optional "previous" secret, and a rotation-window expiry timestamp after which the previous secret is no longer accepted./settingsUI flow: "Rotate webhook secret" generates a new secret, shows it exactly once (with a copy-to-clipboard control and an explicit warning that it won't be shown again), and starts a configurable rotation window (e.g. default 24-48 hours) during which both secrets verify successfully.SECURITY.mdandpackages/webhook-utils/README.mdto describe the rotation flow and recommend it as the safe way to respond to a suspected secret leak.Acceptance criteria
SECURITY.md,packages/webhook-utils/README.md) is updated to describe the rotation feature and recommend its use after a suspected leak.pnpm typecheck,pnpm lint, and relevant tests pass.Likely affected files/directories
Webhook handler/verification call site,
apps/dashboard/app/settings/, new secret-storage schema (building on Issue #6),SECURITY.md,packages/webhook-utils/README.md.