fix(sms): replayed Twilio webhooks process again after high inbound traffic (#101107)

* fix(sms): replayed Twilio webhooks process again after high inbound traffic

* fix(sms): harden Twilio replay saturation

* test(sms): expose response header spy

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
xingzhou
2026-07-11 00:58:53 +08:00
committed by GitHub
parent baea671ef3
commit 8d5f7e27b2
5 changed files with 224 additions and 70 deletions

View File

@@ -343,7 +343,7 @@ The first message should create a pairing request. The second message should rec
## Webhook security
By default, OpenClaw validates `X-Twilio-Signature` using `publicWebhookUrl` and `authToken`. Keep `publicWebhookUrl` byte-for-byte aligned with the URL configured in Twilio, including scheme, host, path, and query string.
By default, OpenClaw validates `X-Twilio-Signature` using `publicWebhookUrl` and `authToken`. Keep the endpoint portion of `publicWebhookUrl` byte-for-byte aligned with the URL configured in Twilio, including scheme, host, path, and query string. OpenClaw excludes Twilio [connection-override](https://www.twilio.com/docs/usage/webhooks/webhooks-connection-overrides) fragments (`#...`) from signature computation, as Twilio requires.
The webhook route also enforces, independent of signature validation:
@@ -351,8 +351,11 @@ The webhook route also enforces, independent of signature validation:
- Rate limit of 30 requests per minute per source IP (HTTP 429 above that).
- The payload `AccountSid` must match the configured `accountSid` (HTTP 403 otherwise).
- Replayed `MessageSid` values are deduplicated for 10 minutes.
- Each SMS account's replay cache retains up to 10,000 live message SIDs. When every slot is live, new webhooks for that account fail closed with HTTP 429 and a `Retry-After` header until the oldest slot expires.
- Request bodies over 32 KB are rejected.
Twilio does not retry HTTP 429 by default or document support for `Retry-After`. The `#rp=4xx` and `#rp=all` connection overrides opt into 4xx retries, but Twilio caps the complete retry transaction at 15 seconds, so retries can still finish before a replay-cache slot expires. Configure a fallback URL when another handler must receive failed deliveries; treat a 429 as a fail-closed rejection, not reliable backpressure.
For local tunnel testing only, you can set:
```json5