ClientBuilder or BotBuilder before building. Use with_retry_admission and with_decrypt_failure_admission for owned implementations, or their _arc variants for shared trait objects. The last registration for each policy wins. Policies remain fixed after assembly; keep live tuning inside the policy, for example with atomics. These hooks need no plugin feature.
Both traits use MaybeSendSync: native policies require Send + Sync, while wasm32 policies can keep local state. Callbacks run synchronously and must return promptly without I/O or blocking. A panic is not converted into a fallback decision; it follows the application’s panic strategy.
Inbound retry repair
RetryAdmission::decide(&RetryContext<'_>) -> RetryDecision gates eligible group and status retry receipts from other accounts. Its borrowed context contains chat, the requesting device’s requester JID, and retry_count. You may share a budget across a user’s devices.
Without a policy, eligible receipts enter repair. Own-account devices, DMs and broadcast-list retries bypass this gate. Routing and unknown-device validation run first. RetryDecision::Drop stops key-bundle installation, sender-key repair and resend. Nothing is queued for later. Allow recovery over time when imposing a budget.
RetryDecision::Accept permits repair. The client installs the retry key bundle and marks the requester for a fresh sender key before recent-message lookup. An admitted request can repair the session even when the original message has left the recent-message cache; resending that original message still needs its content.
examples/retry_quarantine.rs implements a bounded token bucket keyed by chat and requester user, with refill over time and a fail-open tracked-pair cap.
Automatic decrypt-failure requests
DecryptFailureAdmission::decide(&DecryptFailureContext<'_>) -> DecryptFailureDecision gates automatic outbound retry receipts after an eligible decrypt failure. The context borrows chat and sender; retry_count is the attempt after per-message accounting, including refused attempts.
Without a policy, eligible failures request a retry up to the per-message cap. Own messages, explicit retry requests, permanently suppressed bot retries and attempts beyond the cap bypass the policy.
AcknowledgeWithoutRetry skips the retry receipt but keeps the transport ACK, undecryptable event and first-attempt phone recovery. Refusal consumes an attempt. The ACK can stop server redelivery, so refusal can lose recovery of that message. This policy is independent of inbound retry repair and pre-decrypt durability admission.
Match the non-exhaustive decision enums with a fallback arm when inspecting them. Contexts are non-exhaustive borrowed values supplied by the client, not externally constructed request objects.