Skip to main content
This week brings native bot message decryption, constant-memory streaming uploads, and a host of WhatsApp Web parity fixes.

New features

Bot message decryption (msmsg) Meta AI and other fbid bot replies are now decrypted automatically and dispatched as a normal Event::Message. No special handling required — just listen for messages as usual. See the Signal API reference. Constant-memory streaming uploads Audio and video uploads can now stream from disk without loading the full file into memory, and produce a per-64 KiB HMAC sidecar that enables progressive playback for recipients. Ideal for large media on memory-constrained hosts. See Upload. Inline edit decryption Secret-encrypted edits (message edits, poll edits, event edits, and poll-add-option) are now decrypted automatically on receive. Most apps no longer need to call the manual decryption helpers. See Polls. Typed event subscriptions Event handlers can now declare which event kinds they care about via EventHandler::interest() and on_event_for(&[EventKind], …), letting the runtime skip work for events you don’t consume. See Events and Bot. messageSecret retention policy A new configurable retention policy (Managed, BotOnly, Full, Disabled) controls how long messageSecret values are kept per message class, with sensible defaults (30d / 90d / 30d). History seeding and a fallback resolver are also available. See Bot.

Updates

Longer retry receipt window The default sent_message_ttl_secs has been bumped from 5 minutes to 2 hours so retry receipts from slow or briefly offline recipients still resolve to the original message. Persisted sender-key chains on disconnect disconnect() now flushes the Signal cache to your backend before clearing it, so advanced sender-key chains survive reconnects and avoid unnecessary SKDM re-fanouts. Renamed ConnectFailureReason::MainDeviceGoneAccountLocked Matches WhatsApp Web’s REASON_LOCKED (HTTP 403) and better reflects the actual condition. Update any match arms accordingly. TransportEvent::Disconnected now carries a reason Custom transports now report DisconnectReason (ServerClose, StreamEnded, ReadError, or Unknown) so reconnect logic can branch on cause. This is a breaking change for custom Transport implementations — see Transport and Custom backends. Batched app-state mutation MACs AppSyncStore::get_mutation_macs provides a batched lookup that backends can override with a SQL IN query, eliminating an N+1 during app-state sync. See Store. CompactString for LID lookups get_current_lid and SendContextResolver::get_lid_for_phone now return Option<CompactString> instead of Option<String> for lower allocation pressure. See Storage. Spawnable marker trait A new Spawnable marker (Send + 'static on native, 'static on wasm32) makes generic spawn helpers easier to write across targets. See Custom backends. WhatsApp Web parity
  • Uniform plaintext padding (1..=16 bytes) replaces the previous biased scheme.
  • Full device set included on every send for participant-hash calculation, with standard base64 alphabet and persisted group metadata for not-modified group queries.
  • Sender-key chains are now serialized per (group, sender) pair.
  • DM sends now fail fast when every per-device encrypt fails instead of silently producing an empty stanza.
  • Stanza classification fixes: poll-add-option is classified as poll, and album as text.
  • unwrap_message now peels a wider set of FutureProofMessage wrappers (including groupStatusV2 and spoiler).

Bug fixes

decrypt-fail=hide coverage conditional_reveal_message, secret_encrypted_message, and PollAddOption are now correctly hidden on decrypt failure, and SenderRevoke is excluded from hide alongside AdminRevoke. Peer edit sender resolution MessageEdit events from peers now resolve the original sender from the envelope rather than the edit target, fixing mis-attribution on multi-device accounts. Session-recreate cooldown is bounded The session-recreate history is now a bounded TTL cache (~256 entries, 1h) with atomic per-peer check-and-stamp, so long-running clients no longer accumulate unbounded state. Per-peer cooldown behavior is unchanged. SKDM-only decrypt acking Sender-key distribution-only decrypts are now acked so they drain from the offline queue instead of being redelivered.