1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
use Jid;
/// Opt-in policy that decides whether an inbound group or status retry receipt
/// enters the repair path, or is dropped before any work runs.
///
/// WhatsApp Web has no volume-based throttle on inbound retry receipts: it
/// serializes them per chat, refuses past `MAX_RETRY`, and otherwise processes
/// every one (`markForgetSenderKey`, key-bundle processing, resend). This SDK
/// mirrors that by default. In a large group a cohort of members whose pairwise
/// sessions never establish can send a retry for every single message, driving
/// that repair path at storm rate. A single-user WA Web client never sends at
/// the volume that produces this; a bot does.
///
/// A `RetryAdmission` policy is the seam for bot-scale deployments that want to
/// bound that cost, without the SDK itself diverging from WA Web. Registering a
/// policy is a deliberate, operator-owned decision to drop some eligible repair
/// requests; leaving it unset keeps exact WA Web behavior. The check is a single
/// [`std::sync::OnceLock::get`] on the receive path, so an unset policy costs
/// nothing.
///
/// [`admit`](RetryAdmission::admit) is called inline on the receive path, so it
/// must be a fast, local decision (a token-bucket check, an atomic counter) with
/// no I/O or blocking. It is deliberately synchronous: an admission verdict that
/// could `.await` would let a slow policy stall retry processing for the pending
/// key, and a gate never needs to wait.
///
/// Scope: the policy is consulted only for group and `status@broadcast` retry
/// receipts from other accounts. Retries from our own companion devices
/// (`is_peer`) and all DM retries are always admitted and never reach the
/// policy, because dropping those has no safe SKDM fallback. When the policy
/// returns `false`, the receipt is dropped before the group-info fetch, the
/// unknown-device `rotateKey` block, `markForgetSenderKey`, key-bundle
/// processing and the resend. A dropped receipt is not queued: the requester
/// re-requests on its own timer, so a policy should refill over time to keep
/// genuine recovery possible.
///
/// See `examples/retry_quarantine.rs` for a token-bucket implementation keyed by
/// (chat, requester).