A rotated webhook secret takes effect within a minute
The problem
The inbound webhook handler cached each server's signing secret with no time limit. After a rotation, the old secret kept working and the new one was rejected for an unbounded time.
Acceptance criteria
- On a signature mismatch, drop the cached secret, fetch it once more and re-check before rejecting.
- Cached secrets expire after 60 seconds, so a rotated-out secret stops working even without a mismatch.
- A wrong secret costs exactly one extra fetch, and a failed fetch fails closed.
Failing, then passing
The rotation tests were run against the old handler first and failed: a rejection where an accept was expected, and one fetch where two were expected. After the change the next request after a rotation accepts the new secret and rejects the old one.
Review findings
- Fresh-context review: nothing bounded how long a revoked secret stayed valid. We added the 60-second expiry and a test for it.
- It also spotted the same cache shape in the webhook settings, which got its own pull request.
Blast radius and rollback
Blast radius: the webhook handler only, at most one extra secret read per failed signature and one per server per minute. Rollback: revert the commit; the cache lives in memory, so there is nothing to unwind.
Merge
CI green on the final commit, merged once every review finding was answered, then proven on a staging server by rotating a secret and checking the old one is refused.