render.completed event so you ship something they didn’t pay for.
Three defenses, in order:
- Verify the HMAC signature on every request. Reject anything that doesn’t match.
- Rotate secrets periodically and immediately after any suspected leak.
- Treat the body as untrusted until verification passes.
How signing works
For every delivery, Kynva computes:X-RenderForge-Signature header, prefixed with sha256=:
signPayload() in src/webhooks/signer.ts.
Verification rules
- Use the raw request body, not a parsed-and-restringified copy. Even a whitespace change breaks the signature.
- Compare in constant time — use
crypto.timingSafeEqual(Node) orhmac.compare_digest(Python). String equality is vulnerable to timing attacks. - Strip the
sha256=prefix before comparing. - Verify before processing anything from the body, including the event type.
Rotating secrets
When to rotate:- On a schedule (we recommend every 90 days).
- Immediately if you suspect a leak.
- When a team member with access to the secret leaves.
Replay protection
A captured legitimate webhook can be replayed. Two strategies:Dedupe on X-RenderForge-Delivery
Every delivery has a unique ID. Store the ones you’ve seen for at least 24h (e.g., in Redis with a TTL) and reject duplicates:
Reject stale timestamps
CompareX-RenderForge-Timestamp to “now” and reject anything older than 5 minutes:
Network controls
Optional but useful:- Allowlist Kynva egress IPs at your firewall. The current set is published at status.kynva.ai/network.
- Require TLS on your webhook URL —
https://.... We refuse to create webhooks against plain HTTP in production. - Don’t put your webhook URL on the open internet under a guessable path. Add a long random segment, e.g.
https://your-app.com/webhooks/kynva/8c4a....