Surface active rate-limiting via deliveryStatus throttled and retryAfterMs
deliveryStatus previously only signalled failure-driven suspension, so a client could not tell server-side throttling apart from a quiet stream or a broken endpoint. Refresh responses can now carry throttled: true with an advisory retryAfterMs hint; gap envelopes still cover events skipped outright.
This commit is contained in:
parent
b8f75563af
commit
21be9c31c9
1 changed files with 19 additions and 0 deletions
|
|
@ -476,8 +476,27 @@ The `events/subscribe` response MAY include a `deliveryStatus` object when refre
|
|||
}
|
||||
```
|
||||
|
||||
```jsonc
|
||||
// Subscription being actively rate-limited (deliveries delayed, not failing)
|
||||
{
|
||||
"id": "sub_a3f1c8e2b0d49f7e",
|
||||
"refreshBefore": "2026-02-19T17:00:00Z",
|
||||
"cursor": "cursor_xyz",
|
||||
"truncated": false,
|
||||
"deliveryStatus": {
|
||||
"active": true,
|
||||
"lastDeliveryAt": "2026-02-19T16:28:00Z",
|
||||
"lastError": null,
|
||||
"throttled": true,
|
||||
"retryAfterMs": 60000
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`deliveryStatus` is OPTIONAL — servers MAY omit it entirely. When present, `active` indicates whether the server is currently delivering events (`false` means it has suspended retries after repeated failures; the refresh that returned this status has just reactivated it). `lastError` MUST be a server-generated category string — one of `connection_refused`, `timeout`, `tls_error`, `http_4xx`, `http_5xx`, `challenge_failed` — and MUST NOT include raw response bodies, headers, or status lines from the endpoint, to avoid serving as a response oracle for attacker-chosen URLs. The client can use this information to diagnose connectivity or authentication issues with the webhook endpoint. The same categories also appear as `data.reason` on a `-32015 CallbackEndpointError` returned synchronously from `events/subscribe`.
|
||||
|
||||
`throttled` (optional boolean) distinguishes active rate-limiting from failure-driven suspension: `true` means the server is currently limiting outbound deliveries for this subscription — deliveries are being delayed or coalesced, not failing — so the client should not read reduced traffic as "nothing is happening" or as an endpoint problem. `retryAfterMs` (optional integer) accompanies it as a hint for how long the client should expect throttling to last before normal delivery resumes. Both fields are advisory; servers that do not rate-limit outbound deliveries never send them, and a `gap` envelope still covers the case where throttling caused events to be skipped outright.
|
||||
|
||||
#### Webhook Security
|
||||
|
||||
**SSRF prevention.** The server MUST validate callback URLs. Servers SHOULD reject URLs whose resolved IP is not globally routable per the [IANA IPv4](https://www.iana.org/assignments/iana-ipv4-special-registry/) and [IPv6](https://www.iana.org/assignments/iana-ipv6-special-registry/) Special-Purpose Address Registries (illustrative examples: `127.0.0.0/8`, `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`, `169.254.0.0/16`, `::1`, `fc00::/7`, `fe80::/10`) unless explicitly configured to allow them. To prevent DNS rebinding, this validation MUST be performed at delivery time, not only at subscribe time: the server resolves the hostname, checks the resolved IP against the blocklist, and connects directly to that validated IP (sending the original hostname in the `Host` header / TLS SNI) so the address cannot change between check and connect. Subscribe-time validation alone is insufficient — a rebinding attacker returns a public IP at subscribe time and a private IP at delivery time. Webhook delivery requests MUST NOT follow HTTP redirects, since a redirect can target an internal address that bypasses the blocklist. Servers MAY additionally maintain an allowlist of permitted callback URL patterns.
|
||||
|
|
|
|||
Loading…
Reference in a new issue