From 93d1ce7e58bc32e971a579d6245a05892c2fab0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Lucas?= <55464917+jlucaso1@users.noreply.github.com> Date: Thu, 6 Aug 2026 22:29:27 -0300 Subject: [PATCH 1/5] chore(release): prepare 0.7.0 Bumps the eight published crates from 0.6.0 to 0.7.0, which is what the release workflow reads to tag and publish. It also clears the advisory Semver Checks job: cargo-semver-checks compares the manifest version against the last published one, and with both at 0.6.0 every intentional break since then reads as "requires new major version" (43 across wacore, wacore-binary and waproto). Pre-1.0, 0.6 to 0.7 is that major bump. whatsapp-rust-chat-store is held at 0.1.0 with publish = false. It would otherwise be published by --workspace as a brand new crate, and its schema and query surface are still moving. The GitHub release now prefers .github/release-notes/v.md over --generate-notes, and 0.7.0's notes are added there. Generated notes for this release would be 488 unstructured bullets. docs/ is gitignored, so the notes live under .github/ next to the workflow that reads them. --- .github/release-notes/v0.7.0.md | 703 ++++++++++++++++++++++++++ .github/workflows/release.yml | 17 +- Cargo.lock | 24 +- Cargo.toml | 22 +- examples/voip-cli/Cargo.toml | 2 +- http_clients/ureq-client/Cargo.toml | 2 +- storages/chat-store/Cargo.toml | 5 +- storages/sqlite-storage/Cargo.toml | 2 +- transports/tokio-transport/Cargo.toml | 2 +- wacore/Cargo.toml | 2 +- wacore/appstate/Cargo.toml | 2 +- wacore/binary/Cargo.toml | 2 +- wacore/derive/Cargo.toml | 2 +- wacore/libsignal/Cargo.toml | 2 +- wacore/noise/Cargo.toml | 2 +- waproto/Cargo.toml | 2 +- 16 files changed, 752 insertions(+), 41 deletions(-) create mode 100644 .github/release-notes/v0.7.0.md diff --git a/.github/release-notes/v0.7.0.md b/.github/release-notes/v0.7.0.md new file mode 100644 index 000000000..5e7360261 --- /dev/null +++ b/.github/release-notes/v0.7.0.md @@ -0,0 +1,703 @@ +488 pull requests since 0.6.0, over roughly three months. The big items are VoIP (1:1 voice, 1:1 video, group calls and call links, with the media plane and not only the signaling), the switch of protobuf codegen from prost to buffa, a pre-1.0 pass over the public API (typed errors, sealed event payloads, `#[non_exhaustive]`), a plugin host, an optional SQLite chat store, opt-in tracing and metrics, and a per-message round trip about a third cheaper than 0.6.0's. + +A lot of APIs changed. See [Breaking changes](#breaking-changes) below. The two invasive ones are buffa's generated proto surface and the anyhow to typed-error migration, and both touch nearly every call site in a downstream crate. They are taken now because breaking changes are still cheap before 1.0. + +## Highlights + +### VoIP + +- **1:1 voice calls** ([#918](https://github.com/oxidezap/whatsapp-rust/pull/918)). Incoming and outgoing, against the official WhatsApp app, with the full media path: mic capture, encode, encrypt, relay, decrypt, decode, playout. Includes a pure-Rust MLow codec (WhatsApp's proprietary voice codec, ported from the reverse-engineered reference and pinned by a byte-exact golden roundtrip test), E2E-SRTP with the WARP integrity tag and the SFrame layer, and a sans-IO `CallEngine` that owns no socket, clock or thread. No codec FFI, so it still builds for wasm and embedded targets. +- **1:1 video calls** ([#1024](https://github.com/oxidezap/whatsapp-rust/pull/1024)). Start with video, upgrade from audio, downgrade back, and accept the same transitions from an official client. Codec-neutral: consumers pass complete H.264 Annex-B access units through `VideoSource` / `VideoSink`. Authenticated PLI/FIR is treated as a decoder resynchronization boundary, which is what made real webcam video render on the Android peer. +- **Group calls and call links** ([#1130](https://github.com/oxidezap/whatsapp-rust/pull/1130)). Group-bound calls, ad-hoc promotion of an active direct call, reusable call links, 128-user waiting rooms with admission and administrator controls, roster-driven subscriptions, atomic epoch rotation and key fan-out, participant-attributed audio, video, RTCP and data, plus reactions, hand raising and screen sharing. +- **Encoded audio and native Opus** ([#1050](https://github.com/oxidezap/whatsapp-rust/pull/1050)). Applications can supply raw MLOW or Opus packets without linking a codec. Full-duplex native Opus negotiation (PT120/16 kHz as the production profile, PT111/48 kHz as an explicit variant). MLOW stays the compatibility default. +- tctoken on outgoing call offers ([#970](https://github.com/oxidezap/whatsapp-rust/pull/970)), zero-copy RTP/STUN parsing ([#957](https://github.com/oxidezap/whatsapp-rust/pull/957)), and a tail of live-tested signaling fixes ([#1102](https://github.com/oxidezap/whatsapp-rust/pull/1102), [#1104](https://github.com/oxidezap/whatsapp-rust/pull/1104), [#1107](https://github.com/oxidezap/whatsapp-rust/pull/1107), [#1128](https://github.com/oxidezap/whatsapp-rust/pull/1128)). + +```rust +let handle = client.voip() + .call(peer) + .audio(mic_source, speaker_sink) + .video(h264_source, h264_sink) // optional + .start() + .await?; + +handle.wait_ended().await; +``` + +All of it sits behind optional features (`voip`, `voip-runtime`, `voip-mlow`, `voip-libopus`, `voip-encoded`). A default build pulls none of it. + +### Extensibility + +- **Plugin architecture** ([#1061](https://github.com/oxidezap/whatsapp-rust/pull/1061)). A build-time native plugin host behind the `plugins` feature, with a generation-scoped extension lifecycle (`client-lifecycle`), typed plugin APIs, capabilities, dependency ordering, scoped tasks with completion barriers, and a bounded event router keyed by `(plugin_id, topic)`. +- **`whatsapp-rust-chat-store`** ([#1014](https://github.com/oxidezap/whatsapp-rust/pull/1014)). An opt-in crate that materializes the client's event stream into queryable SQLite tables, in the same database file as the device store, so a UI or a stateful bot survives a restart without re-syncing. Event-sourced write-behind writer, encoded `wa::Message` as the source of truth, keyset pagination, a `StoreChange` broadcast for invalidation, and FTS5 search. +- **Optional tracing** ([#733](https://github.com/oxidezap/whatsapp-rust/pull/733)): around 172 spans under a `wa.{conn,recv,send,iq,appstate,pair,media,receipt,retry,pdo,notif,session,bot}.*` taxonomy, OTel-ready, with phone numbers redacted by default. **Optional metrics** ([#734](https://github.com/oxidezap/whatsapp-rust/pull/734)) through the `metrics` facade, plus a `whatsapp-rust-plugin-metrics` plugin. Both are off by default, with no dependency and no overhead when disabled. +- Per-session resource attribution in `stats()` ([#967](https://github.com/oxidezap/whatsapp-rust/pull/967)). +- Typed legacy session interop ([#1072](https://github.com/oxidezap/whatsapp-rust/pull/1072)) behind `legacy-session-interop`, for importing externally produced auth state. +- A SQLite connection-init hook that runs before pragmas and migrations ([#1074](https://github.com/oxidezap/whatsapp-rust/pull/1074)). + +### Delivery guarantees + +- **Opt-in inbound durability hook** ([#920](https://github.com/oxidezap/whatsapp-rust/pull/920)) turns the inbound consumer from at-most-once into at-least-once. The transport ack is deferred until the hook commits, and the decrypted message is buffered durably before the Signal ratchet flushes. With no hook registered the behaviour is unchanged. +- **Ordered and bounded event delivery** ([#981](https://github.com/oxidezap/whatsapp-rust/pull/981)). `EventDelivery::Ordered { capacity }` delivers callbacks in arrival order through one bounded mailbox, matching WA Web's `preserveOrder: true`. A full mailbox drops the event and counts it in `StatsSnapshot::events_dropped` instead of growing without limit. +- **Batched inbound commit during the offline drain** ([#961](https://github.com/oxidezap/whatsapp-rust/pull/961)). WA Web commits durability, signal-store writes and aggregate receipts once per snapshot; we were doing all of it per message. A 421-message backlog was spending about 1.2 s in per-message round-trips. +- An opt-in `RetryAdmission` hook ([#985](https://github.com/oxidezap/whatsapp-rust/pull/985)) and an observe-only `ServerAck` event ([#989](https://github.com/oxidezap/whatsapp-rust/pull/989)). + +### Messaging surface + +- **Passkey companion linking** ([#928](https://github.com/oxidezap/whatsapp-rust/pull/928)): WhatsApp's SHORTCAKE_PASSKEY WebAuthn device-link gate, end to end, with a `PasskeyAuthenticator` seam for the one step that cannot be reproduced in-tree. +- Reactions ([#720](https://github.com/oxidezap/whatsapp-rust/pull/720)), message forwarding ([#738](https://github.com/oxidezap/whatsapp-rust/pull/738)), keep-in-chat ([#740](https://github.com/oxidezap/whatsapp-rust/pull/740)), quiz polls ([#754](https://github.com/oxidezap/whatsapp-rust/pull/754)), events with RSVP ([#758](https://github.com/oxidezap/whatsapp-rust/pull/758)), chat labels ([#715](https://github.com/oxidezap/whatsapp-rust/pull/715)), a 1:1 disappearing-message timer ([#745](https://github.com/oxidezap/whatsapp-rust/pull/745)), `mark_as_played` for voice and video notes ([#737](https://github.com/oxidezap/whatsapp-rust/pull/737)), group profile pictures ([#739](https://github.com/oxidezap/whatsapp-rust/pull/739)), `save_contact` ([#742](https://github.com/oxidezap/whatsapp-rust/pull/742)), the `clearChat` ([#755](https://github.com/oxidezap/whatsapp-rust/pull/755)) and `userStatusMute` ([#760](https://github.com/oxidezap/whatsapp-rust/pull/760)) app-state actions, and newsletter mute/unmute ([#757](https://github.com/oxidezap/whatsapp-rust/pull/757)). +- Encrypted channel (CAG) reactions and comments, both directions ([#830](https://github.com/oxidezap/whatsapp-rust/pull/830)). +- Meta AI and fbid bot replies via `` ([#650](https://github.com/oxidezap/whatsapp-rust/pull/650)). +- Secret-encrypted envelopes: poll-edit, poll-add-option and event-edit ([#645](https://github.com/oxidezap/whatsapp-rust/pull/645)), and message edits on receive ([#665](https://github.com/oxidezap/whatsapp-rust/pull/665), [#667](https://github.com/oxidezap/whatsapp-rust/pull/667), [#762](https://github.com/oxidezap/whatsapp-rust/pull/762)). +- Constant-memory streaming upload with a media sidecar ([#684](https://github.com/oxidezap/whatsapp-rust/pull/684)), high-level media builders from `UploadResponse` ([#764](https://github.com/oxidezap/whatsapp-rust/pull/764)), `ContextInfo` in media options ([#931](https://github.com/oxidezap/whatsapp-rust/pull/931)), and media download without a connected session ([#1194](https://github.com/oxidezap/whatsapp-rust/pull/1194)). +- First-party sticker packs from the CDN ([#644](https://github.com/oxidezap/whatsapp-rust/pull/644)), and the verified business name on messages and usync ([#741](https://github.com/oxidezap/whatsapp-rust/pull/741), [#1081](https://github.com/oxidezap/whatsapp-rust/pull/1081)). + +### Generated protocol registries + +Hand-maintained protocol tables were replaced with registries generated from the [whatspec](https://github.com/oxidezap/whatspec) IR, so refreshing them is a file copy instead of an audit. + +- mex operations ([#728](https://github.com/oxidezap/whatsapp-rust/pull/728)), with typed `Variables` and `Response` per op. This also fixed two doc-ids the current WA Web bundle no longer ships, which had been failing silently. +- A/B props ([#729](https://github.com/oxidezap/whatsapp-rust/pull/729)): 2100 typed flags replacing 11 magic `u32`s, at no binary cost for the ones nothing references. +- App-state (syncd) schemas ([#716](https://github.com/oxidezap/whatsapp-rust/pull/716)). Every action now carries its own `version`, `scope` and index shape; we had been stamping `version = 1` on all of them. +- Proto bumped to WhatsApp Web 2.3000.1040878135 ([#726](https://github.com/oxidezap/whatsapp-rust/pull/726)), then 2.3000.1042742319 ([#1001](https://github.com/oxidezap/whatsapp-rust/pull/1001)). + +## Performance + +The allocation work from 0.6.0 continued, with one change in method: CodSpeed now runs deterministic instruction-count and memory instruments on every PR ([#828](https://github.com/oxidezap/whatsapp-rust/pull/828), [#898](https://github.com/oxidezap/whatsapp-rust/pull/898), [#950](https://github.com/oxidezap/whatsapp-rust/pull/950)), so a regression is caught before merge and every number below was measured rather than estimated. + +### Per-message round trip + +The ping-pong allocation series ([#1015](https://github.com/oxidezap/whatsapp-rust/pull/1015) to [#1022](https://github.com/oxidezap/whatsapp-rust/pull/1022)) took 32.3% off the bytes allocated per cycle, from a 46,088 B baseline, across five PRs: hot-path allocations, a persistent receipt worker, sync fast paths for the store adapter, exact stanza sizing, per-branch boxed send futures and an inlined marshal hint cache. + +A second sweep removed another dozen-plus allocations from the DM round trip ([#1114](https://github.com/oxidezap/whatsapp-rust/pull/1114), [#1116](https://github.com/oxidezap/whatsapp-rust/pull/1116), [#1122](https://github.com/oxidezap/whatsapp-rust/pull/1122), [#1131](https://github.com/oxidezap/whatsapp-rust/pull/1131), [#1137](https://github.com/oxidezap/whatsapp-rust/pull/1137)), coalesced queued noise frames into one transport write ([#1119](https://github.com/oxidezap/whatsapp-rust/pull/1119), [#1120](https://github.com/oxidezap/whatsapp-rust/pull/1120), [#1121](https://github.com/oxidezap/whatsapp-rust/pull/1121)) and memoized the DM device fan-out per recipient ([#1118](https://github.com/oxidezap/whatsapp-rust/pull/1118)). + +### Signal and crypto + +- Counter leasing ([#1026](https://github.com/oxidezap/whatsapp-rust/pull/1026), [#1048](https://github.com/oxidezap/whatsapp-rust/pull/1048)): warm group encryption went from 84.69 ms to 23.98 ms (-71.7%), warm participant fan-out from 49.16 ms to 8.69 ms (-82.3%). +- The sender-key backlog is stored as `Copy` pairs ([#953](https://github.com/oxidezap/whatsapp-rust/pull/953)), taking worst-case out-of-order group decrypt from 163.7 µs to 113.3 µs (-31%), and is shared behind an `Arc` ([#881](https://github.com/oxidezap/whatsapp-rust/pull/881)), roughly 3x on the median with the ~82 KB per-decrypt allocation gone. +- XEdDSA memoization for sender signing and verification ([#838](https://github.com/oxidezap/whatsapp-rust/pull/838), [#839](https://github.com/oxidezap/whatsapp-rust/pull/839), [#1213](https://github.com/oxidezap/whatsapp-rust/pull/1213)), pre-keyed zero-salt HKDF extracts ([#847](https://github.com/oxidezap/whatsapp-rust/pull/847), [#863](https://github.com/oxidezap/whatsapp-rust/pull/863), [#864](https://github.com/oxidezap/whatsapp-rust/pull/864)), and shift-free eviction of skipped message keys ([#946](https://github.com/oxidezap/whatsapp-rust/pull/946)). + +### Binary protocol and JID + +- Token lookup moved to a length-bucketed `tiny_map` on encode ([#873](https://github.com/oxidezap/whatsapp-rust/pull/873)), taking `marshal_auto_small` from 168.7 ns to 111.8 ns (-34%), and then to a build-time static hash table ([#1056](https://github.com/oxidezap/whatsapp-rust/pull/1056)) that is 29 KiB smaller, faster on hits, and drops the `hashify` dependency. `marshal_exact` got 3x faster by replaying plan-pass hints instead of reclassifying every string ([#1057](https://github.com/oxidezap/whatsapp-rust/pull/1057)). +- Small attribute lists are stored inline ([#819](https://github.com/oxidezap/whatsapp-rust/pull/819)): a message-shaped stanza went from 15 to 11 allocations, an 800-participant group stanza from 4012 to 2412 (-40%). Packed values are unpacked through a byte-pair table ([#1214](https://github.com/oxidezap/whatsapp-rust/pull/1214)), and the per-node box around `NodeRef` content is gone ([#1216](https://github.com/oxidezap/whatsapp-rust/pull/1216)), worth -9.5% instructions on the fan-out bench and -40% on the allocation count. +- `Jid` display emits one `write_str` ([#848](https://github.com/oxidezap/whatsapp-rust/pull/848)), 176.7 ns to 69.7 ns, followed by a parsing sweep that stops the scan at the `@`, reads the device and agent fields with a decimal-only reader, and stops re-validating UTF-8 the writer just assembled ([#1171](https://github.com/oxidezap/whatsapp-rust/pull/1171) to [#1176](https://github.com/oxidezap/whatsapp-rust/pull/1176), [#1183](https://github.com/oxidezap/whatsapp-rust/pull/1183), [#1187](https://github.com/oxidezap/whatsapp-rust/pull/1187)). +- Wire strings are validated through `smoothutf8` ([#932](https://github.com/oxidezap/whatsapp-rust/pull/932)), and inflate writes into an uninitialized buffer, dropping a `memset` that was 6% self time ([#933](https://github.com/oxidezap/whatsapp-rust/pull/933)). + +### App-state and history sync + +- ltHash index-MAC dedup: `bench_collect_unique_index_macs[1000]` went from 5,678 µs to 550.8 µs ([#868](https://github.com/oxidezap/whatsapp-rust/pull/868)), then another -19% at 1000 and -72% at 10 with inline index-MAC keys ([#955](https://github.com/oxidezap/whatsapp-rust/pull/955)). The N+1 previous-MAC lookups became a single query ([#687](https://github.com/oxidezap/whatsapp-rust/pull/687), [#821](https://github.com/oxidezap/whatsapp-rust/pull/821)), and the O(n²) in-patch overwrite scan became an O(1) map ([#701](https://github.com/oxidezap/whatsapp-rust/pull/701)). +- History sync got streaming decompression ([#672](https://github.com/oxidezap/whatsapp-rust/pull/672)), a pooled inflate reader ([#683](https://github.com/oxidezap/whatsapp-rust/pull/683)), storage of the compressed payload with a streaming reader ([#853](https://github.com/oxidezap/whatsapp-rust/pull/853)) and a decode-gating scan ([#836](https://github.com/oxidezap/whatsapp-rust/pull/836)). Together: allocation churn from 20.20 MB to 14.43 MB (-28.6%), CodSpeed stream-drain memory from 243.8 KB to 115.2 KB, and -16.2% peak heap on a 20k-row secret batch ([#1058](https://github.com/oxidezap/whatsapp-rust/pull/1058), [#1054](https://github.com/oxidezap/whatsapp-rust/pull/1054)). + +### Send path and caching + +- The per-group device fan-out is memoized behind a topology generation ([#824](https://github.com/oxidezap/whatsapp-rust/pull/824)), the group phash is memoized on the same entry ([#840](https://github.com/oxidezap/whatsapp-rust/pull/840)), warm sends stay warm under the own-device SKDM steady state ([#1021](https://github.com/oxidezap/whatsapp-rust/pull/1021)), and cold sender-key distribution is single-flight ([#937](https://github.com/oxidezap/whatsapp-rust/pull/937)). +- One message encode is shared between the reporting token and the DM or group plaintext ([#904](https://github.com/oxidezap/whatsapp-rust/pull/904), [#905](https://github.com/oxidezap/whatsapp-rust/pull/905)), and DM content is encoded once and spliced into the recipient and DSM plaintexts ([#787](https://github.com/oxidezap/whatsapp-rust/pull/787), [#788](https://github.com/oxidezap/whatsapp-rust/pull/788)). +- Device-registry lookups borrow their keys and build them inline ([#681](https://github.com/oxidezap/whatsapp-rust/pull/681), [#682](https://github.com/oxidezap/whatsapp-rust/pull/682)), taking per-member device resolution on an 800-member group send from 50,915 to 12,538 allocations (-75%). +- moka is gone. `PortableCache` is the only in-process cache backend ([#860](https://github.com/oxidezap/whatsapp-rust/pull/860)). + +### Storage + +- Per-session memory and thread use was cut and made configurable ([#926](https://github.com/oxidezap/whatsapp-rust/pull/926)), and the per-checkout `SELECT 1` liveness probe (36.6% of the bare checkout cost) was removed ([#874](https://github.com/oxidezap/whatsapp-rust/pull/874)). +- WAL readers are no longer blocked by search ([#1151](https://github.com/oxidezap/whatsapp-rust/pull/1151)): an FTS5 prefix query over 60k rows went from 13.3 ms to 0.8 ms. + +### Binary size + +Binary size is now a CI gate with a per-PR budget and a historical series ([#859](https://github.com/oxidezap/whatsapp-rust/pull/859)). + +- `waproto::codec` was pinned to a single instantiation ([#842](https://github.com/oxidezap/whatsapp-rust/pull/842)), taking `.text` from 13.03 to 11.85 MiB (-9.1%). Cold entry-point futures were boxed ([#843](https://github.com/oxidezap/whatsapp-rust/pull/843)) and the proto `PartialEq` anchor dropped, taking 148 `eq` impls to 0 ([#846](https://github.com/oxidezap/whatsapp-rust/pull/846)). +- `wa::Message` shrank from 3784 to 952 bytes (-75%) by boxing its inline content variants ([#866](https://github.com/oxidezap/whatsapp-rust/pull/866)). +- A full audit landed -1.47 MiB stripped (-13.3%) and -14.8% `.text` ([#1055](https://github.com/oxidezap/whatsapp-rust/pull/1055)) through `-Zshare-generics`, lld with ICF, SQLite at `-Oz` with a subsystem trim, and the rest of the codec sweep. Every rejected lever is documented with its drawback. + +## WhatsApp Web compliance and reliability + +- **App-state convergence** ([#1158](https://github.com/oxidezap/whatsapp-rust/pull/1158)). A `snapshotMac` mismatch on a patch means the collection's ltHash has diverged, not that the server is tampering. We treated it as fatal and re-requested the same unusable patch forever (the report was 114 identical failures over one evening, surviving restarts). WA Web degrades the collection instead. The same PR stops losing patches rejected with 409. Related: aggregate MAC validation for genesis patches ([#988](https://github.com/oxidezap/whatsapp-rust/pull/988)), snapshot-rollback and duplicate-index guards ([#752](https://github.com/oxidezap/whatsapp-rust/pull/752), [#753](https://github.com/oxidezap/whatsapp-rust/pull/753)), index-mode ltHash for SET plus REMOVE on the same index ([#829](https://github.com/oxidezap/whatsapp-rust/pull/829)), and key recovery across companion devices ([#1052](https://github.com/oxidezap/whatsapp-rust/pull/1052)). +- **Signal durability** ([#1027](https://github.com/oxidezap/whatsapp-rust/pull/1027), [#1041](https://github.com/oxidezap/whatsapp-rust/pull/1041) to [#1045](https://github.com/oxidezap/whatsapp-rust/pull/1045)): sender-key advances gated before the wire, retry advances persisted before the wire, durability gates retained through deletes, sender-key mutations serialized, and cancelled session checkouts recovered. A deterministic chaos suite covers it ([#1049](https://github.com/oxidezap/whatsapp-rust/pull/1049)). +- **A blind WA Web parity sweep** ([#965](https://github.com/oxidezap/whatsapp-rust/pull/965)) raised the offline-drain snapshot from 200 to 400, lowered the Signal forward-jump cap from 25000 to 2000, made the reconnect backoff reset only after a stable connection of 30 s or more (with explicit penalties surviving the reset), and made `` force-close the socket. +- Top-level `` stanzas are handled and acked ([#1101](https://github.com/oxidezap/whatsapp-rust/pull/1101)). An unacked one was recycling the stream. +- **Pair-code lifetime** ([#1163](https://github.com/oxidezap/whatsapp-rust/pull/1163)). A phone-number link no longer dies with the QR rotation. The stage-1 and stage-2 crypto was re-audited and cleared first. The `companion_finish` answer is now read instead of assumed ([#1198](https://github.com/oxidezap/whatsapp-rust/pull/1198)), and `link_code_pairing_nonce` was corrected ([#976](https://github.com/oxidezap/whatsapp-rust/pull/976), [#979](https://github.com/oxidezap/whatsapp-rust/pull/979)). +- **JID correctness** ([#1178](https://github.com/oxidezap/whatsapp-rust/pull/1178), [#1182](https://github.com/oxidezap/whatsapp-rust/pull/1182), [#1184](https://github.com/oxidezap/whatsapp-rust/pull/1184)). The AD-JID domain byte was being kept as an `agent`, so encode followed by decode was not idempotent. Agent identity, the AD form and device dedup now agree, and interop JIDs encode with the token that carries their integrator. +- LID and PN: DM wire addressing is gated on the account's 1:1 migration state ([#943](https://github.com/oxidezap/whatsapp-rust/pull/943)), the write policy is source-aware and matches `createLidPnMappings` ([#1011](https://github.com/oxidezap/whatsapp-rust/pull/1011)), and mappings are learned from `phoneNumberToLidMappings` ([#1010](https://github.com/oxidezap/whatsapp-rust/pull/1010)) and from embedder-supplied sources ([#1013](https://github.com/oxidezap/whatsapp-rust/pull/1013)). +- Retry and SKDM: recoverable group and 1:1 decrypt failures retry instead of NACK ([#986](https://github.com/oxidezap/whatsapp-rust/pull/986), [#987](https://github.com/oxidezap/whatsapp-rust/pull/987)), the per-group resend rate is bounded to avoid `AccountLocked` ([#871](https://github.com/oxidezap/whatsapp-rust/pull/871)), SKDM redistribution is gated on the primary device ([#872](https://github.com/oxidezap/whatsapp-rust/pull/872)), own devices are never memoized in the sender-key map ([#999](https://github.com/oxidezap/whatsapp-rust/pull/999)), per-device sessions are locked across the SKDM fan-out ([#990](https://github.com/oxidezap/whatsapp-rust/pull/990)), and the inbound sender-key chain is serialized ([#992](https://github.com/oxidezap/whatsapp-rust/pull/992)). +- Hardening: node-decode recursion depth is bounded against hostile frames ([#994](https://github.com/oxidezap/whatsapp-rust/pull/994)), nothing is sent after a transport failure so the nonce cannot be reused ([#1117](https://github.com/oxidezap/whatsapp-rust/pull/1117)), the keepalive watchdog is anchored to first send ([#995](https://github.com/oxidezap/whatsapp-rust/pull/995)), noise frame-counter exhaustion is an error rather than a wrap ([#782](https://github.com/oxidezap/whatsapp-rust/pull/782)), the WARP MI tag is authenticated before recv ROC state is folded ([#998](https://github.com/oxidezap/whatsapp-rust/pull/998)), `download_to_writer` leaves only verified media ([#1197](https://github.com/oxidezap/whatsapp-rust/pull/1197)), and self-only protocol messages are honored only from our own account ([#646](https://github.com/oxidezap/whatsapp-rust/pull/646)). +- Connection lifecycle: the failure stanza is no longer dropped on logout, ban or outdated ([#1165](https://github.com/oxidezap/whatsapp-rust/pull/1165)), the reconnect backoff races the terminal shutdown ([#1179](https://github.com/oxidezap/whatsapp-rust/pull/1179)), the `Origin` header is sent on the WebSocket upgrade ([#1166](https://github.com/oxidezap/whatsapp-rust/pull/1166)), and SIGTERM shuts the client down cleanly ([#960](https://github.com/oxidezap/whatsapp-rust/pull/960)). + +## Breaking changes + +### Protobuf codegen: prost to buffa + +[#557](https://github.com/oxidezap/whatsapp-rust/pull/557) migrates codegen to [buffa](https://crates.io/crates/buffa) and puts its zero-copy views on the real decode paths. This changes the shape of every generated type: + +| Pattern | Before (prost) | After (buffa) | +|---|---|---| +| Sub-message fields | `Option>` | `MessageField` (`.as_option()` / `.is_set()`) | +| Enum fields | `Option` | `Option` (typed) | +| Enum variants | `CamelCase` | `SCREAMING_SNAKE_CASE` | +| Type names | `AdvSignedDeviceIdentity` | `ADVSignedDeviceIdentity` | +| Decode (owned) | `Type::decode(bytes)` | `Type::decode_from_slice(bytes)` | +| Decode (borrowed) | not available | `TypeView::decode_view(bytes)` | +| Encode | `msg.encode(&mut buf)?` | `msg.encode_to_vec()` | + +`whatsapp.rs` is no longer tracked: `build.rs` generates it into `OUT_DIR` from the committed descriptor, so consumers never need `protoc`. Proto field names are snake_case, which is wire-compatible. Encoding a waproto type from outside `waproto` should go through the pinned `waproto::codec` helpers ([#1091](https://github.com/oxidezap/whatsapp-rust/pull/1091)); a `clippy.toml` guard enforces that in-repo. + +### Errors + +- `anyhow` no longer appears in any public signature ([#893](https://github.com/oxidezap/whatsapp-rust/pull/893)). There is one typed error per feature domain (`SendError`, `GroupError`, `ContactError`, `BlockingError`, `ChatStateError`, `PresenceError`, `CommunityError`, `NewsletterError`, `ProfileError`, `PollError`, `SignalError`, `TcTokenError`, `MediaReuploadError`, `AppStateError`), all built on a shared `ClientError` base with `#[from]` chaining. Every listed method's `Err` variant changed, though `?` into an `anyhow` context still works. +- `ConnectError` and `ConnectStage` ([#1090](https://github.com/oxidezap/whatsapp-rust/pull/1090)): `connect()`, `wait_for_socket()` and `wait_for_connected()` return `Result<(), ConnectError>`, `ClientError::AlreadyConnected` is gone, and `logout()` is now infallible. +- All 46 occurrences of `#[error(transparent)]` became `#[error("{0}")]` ([#1100](https://github.com/oxidezap/whatsapp-rust/pull/1100)). The `Display` output is byte-for-byte identical, but the wrapped typed error stays reachable through `source()`, so a 403 or a 409 can be classified without matching on strings. `ErrorChainExt` writes that walk once. A refused HTTP status is recoverable by type ([#1195](https://github.com/oxidezap/whatsapp-rust/pull/1195)). +- The read loop exits with a typed reason ([#956](https://github.com/oxidezap/whatsapp-rust/pull/956)). Routine server recycles are not errors, and `Disconnected` carries the reason. + +### Public API freeze ahead of 1.0 + +- Event payloads are sealed ([#1000](https://github.com/oxidezap/whatsapp-rust/pull/1000), [#1002](https://github.com/oxidezap/whatsapp-rust/pull/1002) to [#1004](https://github.com/oxidezap/whatsapp-rust/pull/1004)): 25 field-bearing payloads plus the notification and sync families are `#[non_exhaustive]` with a `bon` builder, three inline `Event` variants became sealed newtypes, and four unit markers became empty sealed structs. `EventInterest` moved from `u64` to `u128`. Payloads are now built with `Type::builder().build()`. +- `#[non_exhaustive]` was added to public error enums, public value enums and lib-constructed response structs ([#735](https://github.com/oxidezap/whatsapp-rust/pull/735), [#736](https://github.com/oxidezap/whatsapp-rust/pull/736), [#794](https://github.com/oxidezap/whatsapp-rust/pull/794)), and dead privacy enums were dropped. +- The bot API was overhauled ([#852](https://github.com/oxidezap/whatsapp-rust/pull/852)). `bot.run().await?.await?` becomes `bot.run().await` in the foreground or `bot.spawn()` in the background, returning a `BotHandle` with `client()`, a graceful `shutdown()` and `abort()`. `with_backend(arc)` becomes `with_backend_arc`, `whatsapp_rust::transport::UreqHttpClient` moved to `whatsapp_rust::http`, `bot::Missing` was replaced by named typestate markers, and `BotBuilderError::Other` is gone. Registering two handlers now runs both, where the second used to silently replace the first. There are typed registrars (`on_message`, `on_qr_code`, `on_pair_code`, `on_connected`, `on_logged_out`) and a `prelude`. +- One dependency line is enough ([#852](https://github.com/oxidezap/whatsapp-rust/pull/852), [#954](https://github.com/oxidezap/whatsapp-rust/pull/954)): `wacore`, `wacore_binary` and `waproto` are re-exported wholesale. +- `impl Into` across the public surface ([#834](https://github.com/oxidezap/whatsapp-rust/pull/834)), alloc-aware message-id parameter types ([#775](https://github.com/oxidezap/whatsapp-rust/pull/775)), a `DownloadParams` struct instead of six positional arguments ([#768](https://github.com/oxidezap/whatsapp-rust/pull/768)), `get_participating` returning a `HashMap` ([#767](https://github.com/oxidezap/whatsapp-rust/pull/767)), and tagged `WireEnum` variants serialized as structs ([#1096](https://github.com/oxidezap/whatsapp-rust/pull/1096)). + +### Storage and state + +- `sqlite-storage` persists blobs with prost instead of bincode ([#911](https://github.com/oxidezap/whatsapp-rust/pull/911)). +- messageSecret retention is bounded by policy and by an event-time horizon ([#668](https://github.com/oxidezap/whatsapp-rust/pull/668)). +- The Signal cache flushes on disconnect, which stops SKDM re-fanout ([#670](https://github.com/oxidezap/whatsapp-rust/pull/670)). +- App-state `Mutation` no longer stores duplicate index and value MACs ([#689](https://github.com/oxidezap/whatsapp-rust/pull/689)), and previous-MAC lookups are batched ([#687](https://github.com/oxidezap/whatsapp-rust/pull/687)). +- `FDownload` is a `&BlobDownloadFn` trait object instead of a generic parameter ([#1055](https://github.com/oxidezap/whatsapp-rust/pull/1055)), which affects `wacore` consumers calling the app-state sync entry points directly. +- `wa::Message` boxes its inline content variants ([#866](https://github.com/oxidezap/whatsapp-rust/pull/866)), so pattern matches that bound those fields by value need a deref. +- Message edits are decrypted on receive ([#665](https://github.com/oxidezap/whatsapp-rust/pull/665)), and the inbound commit pipeline batches during the offline drain ([#961](https://github.com/oxidezap/whatsapp-rust/pull/961)), which changes when handlers observe drained messages. + +### Cargo features + +- Removed: `moka-cache` (moka is gone, `PortableCache` is the only backend) and `debug-diagnostics`. +- Added: `voip`, `voip-runtime`, `voip-mlow`, `voip-libopus`, `voip-encoded`, `plugins`, `client-lifecycle`, `tracing`, `tracing-pii`, `metrics`, `legacy-session-interop`, `danger-skip-cert-chain-verify`. +- New workspace members: `storages/chat-store`, `plugins/metrics`, `examples/voip-cli`. +- MSRV is declared and enforced at 1.94 ([#1093](https://github.com/oxidezap/whatsapp-rust/pull/1093)). + +## Tooling and CI + +- CodSpeed benchmarks run on every PR, with deterministic RNG and hashers, a fixed 2-worker runtime and sharding ([#828](https://github.com/oxidezap/whatsapp-rust/pull/828), [#835](https://github.com/oxidezap/whatsapp-rust/pull/835), [#898](https://github.com/oxidezap/whatsapp-rust/pull/898), [#902](https://github.com/oxidezap/whatsapp-rust/pull/902), [#950](https://github.com/oxidezap/whatsapp-rust/pull/950)), plus benchmarks built from the stanza shapes production actually sends ([#1186](https://github.com/oxidezap/whatsapp-rust/pull/1186)). +- Binary size has a per-PR budget gate and a historical series ([#859](https://github.com/oxidezap/whatsapp-rust/pull/859)). +- Supply chain, docs, MSRV and feature-matrix gates were added ([#1093](https://github.com/oxidezap/whatsapp-rust/pull/1093)): workspace lints (`dbg_macro` and `todo` denied), `cargo-deny`, a rustdoc gate and a per-feature build check. +- Miri covers the unsafe decode path ([#1162](https://github.com/oxidezap/whatsapp-rust/pull/1162)), the decoder has roundtrip property tests and an unmarshal fuzz target ([#895](https://github.com/oxidezap/whatsapp-rust/pull/895)), and fixed sleeps in tests were replaced with bounded polling ([#1094](https://github.com/oxidezap/whatsapp-rust/pull/1094)). +- The test suites run through cargo-nextest ([#1209](https://github.com/oxidezap/whatsapp-rust/pull/1209)), and the Docker image is multi-arch, runs unprivileged and is published to GHCR ([#927](https://github.com/oxidezap/whatsapp-rust/pull/927)). + +## Contributors + +Thanks to everyone who shipped code into this release: + +- [@jlucaso1](https://github.com/jlucaso1): VoIP (voice, video, group calls, the MLow codec), the buffa migration, the error and event API freeze, the plugin host, chat-store, tracing and metrics, and most of the performance, WA Web parity and durability work. +- [@Salientekill](https://github.com/Salientekill): admin-revoke fan-out fix, prekey re-upload after re-pair, `groupStatusV2` unwrapping, participant phone-number backfill, `edit_message_with_stanza_id`. +- [@blaueeiner](https://github.com/blaueeiner): PDO placeholder-resend skips for view-once, PN-LID mappings from history sync, public `add_lid_pn_mapping`, pushname matching, app-state collection reservation. +- [@Bot-Dev-RPA](https://github.com/Bot-Dev-RPA): removal of the obsolete device-registry cleanup task, VoIP relay port racing, sibling-device call handling. +- [@zdanysfa](https://github.com/zdanysfa): `` recipient preservation and softened unknown `stream:error`, incoming peer message-edit decryption. +- [@arsa0x](https://github.com/arsa0x): `ContextInfo` in media options. +- [@JeanCapixaba](https://github.com/JeanCapixaba): the observe-only `ServerAck` event. +- [@oonid](https://github.com/oonid): own-device LID namespace alignment for LID-addressed DMs. +- [@alexandme](https://github.com/alexandme): Rust 1.93 build fix for the pushname arm. +- [@dependabot](https://github.com/apps/dependabot): kept dependencies current across the release window. + +## New contributors + +- @alexandme made their first contribution in [#632](https://github.com/oxidezap/whatsapp-rust/pull/632) +- @zdanysfa made their first contribution in [#633](https://github.com/oxidezap/whatsapp-rust/pull/633) +- @oonid made their first contribution in [#636](https://github.com/oxidezap/whatsapp-rust/pull/636) +- @arsa0x made their first contribution in [#931](https://github.com/oxidezap/whatsapp-rust/pull/931) +- @blaueeiner made their first contribution in [#934](https://github.com/oxidezap/whatsapp-rust/pull/934) +- @JeanCapixaba made their first contribution in [#989](https://github.com/oxidezap/whatsapp-rust/pull/989) +- @Bot-Dev-RPA made their first contribution in [#1070](https://github.com/oxidezap/whatsapp-rust/pull/1070) + +--- + +**Full changelog**: [v0.6.0...v0.7.0](https://github.com/oxidezap/whatsapp-rust/compare/v0.6.0...v0.7.0), 488 PRs across VoIP, the protobuf migration, the pre-1.0 API work, performance, WA Web parity and reliability. + +
+All PRs in this release (auto-generated, full attribution) + +* feat!: migrate from prost to buffa for protobuf codegen by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/557 +* fix(send): admin revoke not applied on recipient devices by @Salientekill in https://github.com/oxidezap/whatsapp-rust/pull/621 +* audit: WA Web protocol compliance fixes by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/623 +* audit (round 2): more WA Web protocol compliance fixes by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/624 +* fix(groups): correct change_number stanza shape by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/625 +* feat(groups): capture display_name on participant info by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/626 +* fix: align inbound parsing with WA Web for receipts and messages by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/627 +* fix(send): emit correct / stanza for native-flow buttons by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/628 +* fix(offline): drive WA Web pull-batch loop for offline backlog by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/629 +* fix: zombie connection after stream-error 500 (ClientPayload + receipt compliance) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/630 +* fix(message): nack unrecoverable decrypt errors instead of silent drop by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/631 +* fix(wacore): drop if-let guard so the pushname arm builds on Rust 1.93 by @alexandme in https://github.com/oxidezap/whatsapp-rust/pull/632 +* fix(client): preserve recipient in , soften unknown stream:error by @zdanysfa in https://github.com/oxidezap/whatsapp-rust/pull/633 +* fix: self-DM stops working, WA Web compliance for fanout and BadMac by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/634 +* fix: self-DM/sibling decryption deadlock (retry shape + session recovery + peer pkmsg identity) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/635 +* fix(send): align own devices to LID namespace for LID-addressed DMs by @oonid in https://github.com/oxidezap/whatsapp-rust/pull/636 +* fix(prekeys): re-upload after re-pair to heal stale server bundle by @Salientekill in https://github.com/oxidezap/whatsapp-rust/pull/641 +* fix(polls): align poll vote encryption/decryption to conversation addressing by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/642 +* feat(history-sync): server-error receipt to request blob re-upload by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/643 +* feat(stickers): fetch first-party sticker pack data from the CDN by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/644 +* feat(secret-encrypted): decrypt poll-edit / poll-add-option / event-edit envelopes by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/645 +* fix(security): only honor self-only protocol messages from our own account by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/646 +* fix(offline): drain offline queue by acking duplicate and undecryptable messages by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/647 +* fix(offline): transport-ack stanzas with only unrecognized enc types by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/648 +* fix(receipt): preserve sender device in delivery receipt `to` by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/649 +* feat(msmsg): decrypt Meta AI / fbid bot replies (``) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/650 +* fix(runtime): introduce Spawnable trait for WASM compatibility by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/651 +* fix(send): fail DM send when every per-device encrypt fails by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/652 +* fix(messages): pad to uniform 1..=16 bytes matching WA Web by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/653 +* fix(send): classify poll-add-option as poll and album as text by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/654 +* fix(send): hide decrypt-fail for conditional-reveal and poll-add-option by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/655 +* fix(send): don't hide decrypt-fail on SenderRevoke by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/656 +* fix(send): serialize the group sender-key chain per (group, sender) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/657 +* perf(retry): replace session-recreate Mutex with a TTL cache, atomic per-peer check+stamp by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/658 +* fix(offline): clear self-fanout with sender receipt, not a bare ack by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/659 +* perf(appstate): drop per-operand allocations in LTHash fold and update_hash by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/660 +* perf(jid): add consuming into_non_ad for owned send-path JIDs by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/661 +* perf(send): build encrypt-task Signal address from a borrow, not a Jid clone by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/662 +* feat(transport): surface the disconnect reason in logs by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/663 +* fix(message): ack SKDM-only session decrypts by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/664 +* fix(message)!: decrypt secret encrypted edits on receive by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/665 +* fix(send): set peer PDO push priority attrs by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/666 +* fix(message): decrypt incoming peer message edits by @zdanysfa in https://github.com/oxidezap/whatsapp-rust/pull/667 +* feat(msg-secret)!: bound messageSecret retention by policy and event-time horizon by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/668 +* perf(history-sync): free LazyHistorySync raw bytes after a successful decode by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/669 +* fix(connection)!: flush Signal cache on disconnect to stop SKDM re-fanout by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/670 +* ci(wasm): guard whatsapp-rust wasm32 build and fix two #668 regressions by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/671 +* perf: implement streaming decompression for history sync processing by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/672 +* perf(message): cut per-message allocation churn on the hot path by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/673 +* perf(send): Arc immutable device fields + recent-message bytes by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/674 +* perf(send): box the phash-mismatch cold path out of the spawned future by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/675 +* perf(events): typed event subscription to skip boxing unwanted events by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/676 +* perf(lid-pn): skip PN->LID session migration for peers with no PN state by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/677 +* fix(send): correct group phash and mark full SKDM target set (WA Web parity) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/678 +* feat: WA Web phash parity — usync device_hash (#3), group-metadata phash (#7), bcl hash validation (#6) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/679 +* perf(usync): batch LID-PN re-learn from device response off the cold group-send path by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/680 +* perf(device-registry): borrow lookup keys in get_devices_from_registry (hot group-send path) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/681 +* perf(device-registry): build lookup keys with CompactString (inline, no heap) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/682 +* perf(zlib): pool streaming InflateReader state across history-sync blobs by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/683 +* feat(upload): constant-memory streaming upload + media streaming sidecar by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/684 +* feat(upload): add encrypted_len() and UploadSource for Bytes by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/685 +* perf(history-sync): single-byte fast-path for read_varint by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/686 +* perf(appstate)!: batch previous-MAC lookups (N+1 to 1 query) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/687 +* perf(appstate): parse sync response once (+ dedup external-blob download) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/688 +* perf(appstate)!: drop duplicate index/value MAC storage from Mutation by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/689 +* perf!: cut clones/allocs in LID resolution, single-device send, history-sync by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/690 +* perf: cut copies in PBKDF2, noise large-frame send, history-sync secret records by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/691 +* fix(send): unwrap groupStatusV2 + align unwrap_message with WA Web (not text) by @Salientekill in https://github.com/oxidezap/whatsapp-rust/pull/692 +* fix(send): classify payment-family stanzas as text + add stanza-type override by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/693 +* fix(client): use portable_atomic::AtomicU64 in offline_resume by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/694 +* feat(prekeys): make pre-key upload batch size configurable via the builder by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/695 +* fix(send): hoist messageContextInfo to outer in DeviceSentMessage (WA Web parity) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/696 +* perf(send): cut throwaway allocations in DM phash and LID conversion by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/697 +* fix(message): emit quote remoteJid only for cross-chat quotes by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/698 +* chore(deps): bump yoke from 0.8.2 to 0.8.3 by @dependabot[bot] in https://github.com/oxidezap/whatsapp-rust/pull/699 +* chore(deps): bump log from 0.4.30 to 0.4.31 by @dependabot[bot] in https://github.com/oxidezap/whatsapp-rust/pull/700 +* perf(appstate): replace O(n²) in-patch overwrite scan with an O(1) map by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/701 +* fix(retry): send raw 32-byte prekey/signed-prekey values in retry receipts by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/702 +* perf(device-registry): cache Arc to avoid deep clone on warm hits by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/703 +* fix(time): stop wall-clock default from panicking on wasm32 by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/704 +* fix(retry): match WA Web's bot gate so bot DM retry receipts aren't dropped by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/705 +* fix(prekeys): force the upload on prekey-low instead of re-querying the server count by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/706 +* fix(blocking): resolve LID/PN before is_blocked compares, fixing PN-query false negatives by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/707 +* fix(receipt): downgrade delivery ack to "sent" on lid feature-incapable error by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/708 +* fix(retry): allocate retry-receipt prekey from the monotonic counter, not random by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/709 +* perf(groups): cache Arc to avoid deep-cloning group metadata on warm sends by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/710 +* fix(send): emit mediatype for interactive/list/order/product/native-flow sends by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/711 +* perf(decrypt): trim per-SKDM allocations in the group fan-in path by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/712 +* perf(signal): cache Arc to avoid deep-cloning the message-key backlog by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/713 +* perf(store): batch session/identity/sender-key flush into one transaction per category by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/714 +* feat(labels): add chat label create/delete/associate API with inbound sync by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/715 +* refactor(appstate): drive syncd actions from a generated schema registry + public generic action API by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/716 +* feat(signal): react to locally-detected peer identity changes by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/717 +* perf(identity): gate identity-change reset behind had-prior-identity, like WA Web by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/718 +* perf(events): snapshot handlers behind an Arc to drop the per-event Vec clone by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/719 +* feat(reaction): add Client::send_reaction and MessageContext::react for DM/group by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/720 +* perf(signal-cache): amortize eviction and stop scanning on reads by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/721 +* fix(prekey): remove the consumed one-time prekey atomically with the session flush by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/722 +* fix(edit): send message edits as a top-level protocolMessage, matching WA Web by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/723 +* fix: @call JID decode, receipt participant_pn, profile-pic empty→remove, has_device churn by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/724 +* feat(newsletter): plaintext channel edit/revoke + reject newsletter on the E2E send path by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/725 +* chore(proto): sync to WhatsApp Web 2.3000.1040878135 by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/726 +* feat(mex): typed mex operations from the whatspec IR, drop hand-maintained mex_ids by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/728 +* feat(abprops): vendor typed A/B-props registry, drop hand-maintained config_codes by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/729 +* fix(send): address LID-mapped DM by LID end to end so the server stops 400-rejecting it by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/731 +* refactor: split message/client/send monoliths into per-theme modules by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/732 +* feat(observability): optional tracing instrumentation (off by default, OTel-ready) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/733 +* feat(metrics): optional metrics layer (off by default, Prometheus/OTLP-ready) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/734 +* feat(api): mark public error enums #[non_exhaustive] for 1.0 forward-compat by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/735 +* feat(api): #[non_exhaustive] on public value enums + drop dead privacy enums by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/736 +* feat(receipt): add mark_as_played for voice/video notes by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/737 +* feat(send): add forward_message + MessageExt::prepare_for_forward by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/738 +* feat(groups): high-level set/remove group profile picture by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/739 +* feat(send): add keep_message (keep-in-chat for everyone) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/740 +* feat(usync): surface verified business name (verified_name) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/741 +* feat(chat-actions): add save_contact (outgoing contact-name sync) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/742 +* feat(send): emit for view-once sends by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/743 +* feat(receipt): read-receipt status parity (read-self, context, peer_participant_pn) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/744 +* feat(client): set_chat_disappearing_timer for 1:1 disappearing messages by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/745 +* fix(binary): coerce "1"/"0" wire booleans in attr parsers by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/746 +* feat(iq): keep server error_type + backoff on IQ error responses by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/747 +* fix(appstate): require snapshot MAC when validating (no silent skip) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/748 +* fix(polls): match WA Web poll wire shape (pollContentType=TEXT, no vote metadata) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/749 +* perf(appstate): return Arc from key-lookup callback (no per-mutation 160B clone) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/750 +* fix(appstate): repair main build broken by #748/#750 merge by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/751 +* feat(appstate): reject duplicate index within a patch (anti-tampering parity) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/752 +* feat(appstate): guard snapshot apply against version rollback by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/753 +* feat(polls): support quiz polls on send (create_quiz) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/754 +* feat(chat): support clearChat app-state action (incoming + outgoing) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/755 +* fix(wasm): make cache backend target-aware so moka-cache defaults don't break wasm32 by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/756 +* feat(newsletter): mute/unmute channel notifications by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/757 +* feat(events): create and respond (RSVP) API by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/758 +* fix(appstate): don't swallow external-blob download failures by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/759 +* feat(chat): support userStatusMute app-state action (incoming + outgoing) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/760 +* fix(groups): keep persisted group metadata in sync on membership change by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/761 +* feat(edit): support message-secret encrypted edits (secret_encrypted_message) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/762 +* perf(portable-cache): O(log n) remove_key instead of O(n) insertion scan by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/763 +* feat(media): high-level media message builders from UploadResponse by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/764 +* perf(cache): raise device_registry_cache default capacity 1000 -> 5000 by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/765 +* fix(appstate): clear stale mutation MACs on snapshot re-sync by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/766 +* refactor(groups): get_participating returns HashMap by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/767 +* refactor(download): take DownloadParams struct instead of 6 positional args by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/768 +* fix(appstate): validate index MAC even when the decrypted index field is absent by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/769 +* refactor(download): remove dead, full-buffering download_to_file by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/770 +* refactor(polls): group enc_payload + enc_iv into PollVoteCiphertext by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/771 +* feat(prekeys): validate companion device-identity (ADV) on fetched bundles by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/772 +* fix(appstate): re-sync unsynced collection that gets patches without a snapshot by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/773 +* perf(upload): slice ciphertext zero-copy instead of copying per attempt by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/774 +* refactor(api): consistent, alloc-aware message-id param types by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/775 +* feat(usync): surface device list from get_user_info by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/776 +* fix(receipt): chunk read/played receipts into 256 ids per stanza by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/777 +* refactor(groups): set_description prev takes Option<&str> by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/778 +* perf(retry): peek the cached message on resend instead of take + re-add by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/779 +* feat(receipt): expose the 'offline' attr on the Receipt event by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/780 +* feat(send): emit member_label meta attrs (appdata + tag_reason) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/781 +* fix(noise): error on frame counter exhaustion instead of wrapping by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/782 +* fix(iq): pong server pings with an absent type, not only type=get by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/783 +* refactor(framing): drop dead, would-desync oversize check in decode_frame by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/784 +* fix(conn): log benign server recycles quietly without hiding real errors by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/785 +* perf(appstate): move snapshot mutations into the accumulator instead of extend by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/786 +* perf(send): encode DM content once, splice into recipient + DSM plaintexts [PoC] by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/787 +* perf(send): splice reporting context onto plaintexts, drop per-send Message clone by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/788 +* fix(adv): fall back to stored account identity for ADV account_signature_key by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/790 +* chore(session): remove dead SessionManager (zero production callers) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/791 +* perf(receive): make custom enc handlers an immutable set-once snapshot by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/792 +* fix(wasm): relax EncHandler Send+Sync via MaybeSendSync, gate async_trait by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/793 +* api: mark lib-constructed response/result structs #[non_exhaustive] for 1.0 by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/794 +* fix(wasm): relax networking traits Send+Sync via MaybeSendSync by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/795 +* refactor(handlers): split notification.rs god-file by domain by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/796 +* fix(device-list): always keep the primary device after a raw_id mismatch patch by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/797 +* fix(retry): recover from unknown-device retries that carry a key bundle by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/798 +* fix(usync): refetch an empty device record instead of trusting it by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/799 +* chore(send): log benign prekey-fetch skips at debug instead of warn by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/800 +* fix(device-list): never drop the primary on a device-remove patch by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/801 +* fix(retry): resync the device list on a retry from an unknown device by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/802 +* perf(receive): adopt inbound payloads zero-copy in FrameDecoder by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/803 +* perf(receive): resolve the noise socket once per read loop, drop ack re-encode copy by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/804 +* perf(send): establish sessions before taking the sender-key chain lock by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/807 +* perf(store): cache the device snapshot as Arc<Device> by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/808 +* perf(signal-cache): share cached sessions via Arc, peek without deep clone by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/809 +* perf(group): derive the PN-to-LID reverse index, stop persisting it by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/810 +* perf(lid-pn): share identifier strings between cache keys and entries by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/811 +* chore(deps): bump metrics-exporter-prometheus from 0.16.2 to 0.18.3 by @dependabot[bot] in https://github.com/oxidezap/whatsapp-rust/pull/812 +* chore(deps): bump chrono from 0.4.44 to 0.4.45 by @dependabot[bot] in https://github.com/oxidezap/whatsapp-rust/pull/813 +* chore(deps): bump http from 1.4.1 to 1.4.2 by @dependabot[bot] in https://github.com/oxidezap/whatsapp-rust/pull/814 +* chore(deps): bump prost from 0.14.3 to 0.14.4 by @dependabot[bot] in https://github.com/oxidezap/whatsapp-rust/pull/815 +* chore(deps): bump diesel from 2.3.9 to 2.3.10 by @dependabot[bot] in https://github.com/oxidezap/whatsapp-rust/pull/816 +* fix(protocol): tighten error handling gaps by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/817 +* perf(appstate): move snapshot and patches into the blocking handoff instead of deep-cloning by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/818 +* perf(binary): store small attribute lists inline, dropping the per-node heap allocation by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/819 +* perf(receipt): aggregate offline delivery receipts per chat like WA Web by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/820 +* perf(appstate): batch the previous-MAC prefetch in build_patch like the inbound path by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/821 +* perf(send): hash the participant list from one arena instead of a String per device by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/822 +* perf(send): probe the LID-PN map in one direction on warm device lookups by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/823 +* perf(send): memoize the per-group device list behind a topology generation by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/824 +* fix(contacts): use fn items for LID mapping extractors so boxed futures compile by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/826 +* perf(client): borrow the ack id instead of allocating a String per stanza by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/827 +* Add CodSpeed performance measurement setup by @codspeed-hq[bot] in https://github.com/oxidezap/whatsapp-rust/pull/828 +* fix(appstate): match WA Web index-mode ltHash for SET+REMOVE on the same index by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/829 +* feat(messages): encrypted CAG reactions and channel comments, both directions by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/830 +* perf(messages): write-behind buffer for messageSecret persistence by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/831 +* fix(storage): serialize msg_secret reads through the db semaphore by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/832 +* feat(prekeys): track the first un-uploaded prekey and reuse the window like WA Web by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/833 +* feat(api): adopt the impl Into convention across the public surface by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/834 +* ci(codspeed): run simulation and memory instruments in a single job by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/835 +* perf(history-sync): gate prost decode behind a secret-presence scan, share chat ids, schema-pinned wire tags by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/836 +* bench: measure the operation, not the harness by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/837 +* perf(libsignal): memoize the sender signing key with a pre-warmed XEdDSA cache by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/838 +* perf(libsignal): cache the verify-side Edwards derivations per sender key by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/839 +* perf(send): memoize the group phash on the device-list memo entry by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/840 +* fix(pdo): request a placeholder resend at most once per message by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/841 +* perf(waproto): pin the Message codec to one instantiation via non-generic helpers by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/842 +* perf(api): box the cold entry-point futures so consumers stop re-codegening the graphs by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/843 +* perf(retry): fuse the retry count and reason into one cache entry by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/844 +* perf(docker): enable -Zshare-generics in the image build by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/845 +* perf(wacore): drop the proto PartialEq anchor from the skdm-only check by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/846 +* perf(appstate): pre-key the ltHash HKDF extract once by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/847 +* perf(binary): emit the Jid display as one write_str by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/848 +* bench: borrow inputs in the benches that only read them by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/849 +* perf(noise): pre-key the transport AES-GCM once per connection by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/850 +* deps: upgrade curve25519-dalek and x25519-dalek to the 5.0/3.0 release candidates by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/851 +* feat!: overhaul the public bot API ahead of 1.0 by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/852 +* perf(history-sync)!: store the compressed payload and expose a streaming reader by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/853 +* chore(reporting-token): draw the message secret from the thread RNG by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/855 +* bench: cover the receive path — plaintext decode and appstate index-MAC dedup by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/856 +* bench: cover four inbound/group hot paths for CodSpeed baselines by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/858 +* ci: track binary size with a per-PR budget gate and historical series by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/859 +* perf: drop moka, use PortableCache as the sole in-process cache backend by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/860 +* perf(send): de-monomorphize Signal encrypt fan-out to dyn dispatch by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/861 +* perf(iq): de-monomorphize send_and_wait_iq via boxed send future by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/862 +* perf(signal): pre-key the zero-salt HKDF extract for message-key derivation by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/863 +* perf(reporting-token): pre-key the zero-salt HKDF extract for token-key derivation by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/864 +* refactor(appstate): scan instead of HashSet for index-mac dedup in the patch path by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/865 +* perf(proto)!: shrink wa::Message ~75% by boxing inline content variants by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/866 +* perf(appstate): index-sort dedup for large patches, O(n²) scan stays for small by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/868 +* perf: drop a ~67 KiB duplicate prost decode tree + hoist a per-message traversal by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/869 +* fix(retry): bound outbound resend rate per group to prevent AccountLocked by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/871 +* fix(send): gate SKDM redistribution on the primary device (WA Web parity) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/872 +* perf(binary): length-bucketed token lookup (tiny_map) on the encode hot path by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/873 +* perf(sqlite): skip the per-checkout SELECT 1 liveness probe by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/874 +* feat(example): add 🦀send chat command by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/875 +* perf(conn): reuse the shutdown listener across the read loop by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/876 +* perf(signal): skip the rollback clone for in-order decrypts by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/877 +* test(bench): add an in-order DM decrypt benchmark by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/878 +* perf(signal): reuse the encrypt buffer instead of take + realloc by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/879 +* perf(signal): share the sender-key message backlog behind an Arc by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/881 +* feat(retry): WA Web log-level parity and retry-flow observability counters by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/887 +* perf(signal): flush the signal cache without holding the device read-lock by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/888 +* fix(retry): dedup registration-id parsing and reject oversized payloads by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/889 +* chore(deps): update Cargo.lock to latest compatible versions by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/890 +* chore: bump nightly toolchain to nightly-2026-06-16 by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/891 +* refactor: move the demo binary to examples/ and make env_logger a dev-dependency by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/892 +* refactor(error)!: replace anyhow in public APIs with per-domain typed errors by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/893 +* ci: add an all-features build job and fix the --nocapture flag by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/894 +* test(binary): add decoder roundtrip property tests and unmarshal fuzz target by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/895 +* refactor(send): extract tc-token lifecycle and pin/revoke actions into submodules by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/896 +* perf(send): resolve group SKDM warm gate with one inner-map lookup per device by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/897 +* bench(integration): port integration benchmarks to CodSpeed (simulation + memory) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/898 +* chore(deps): prune the aes-gcm dev-dep and tidy dependency declarations by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/899 +* perf(prekeys): avoid full record decode on the pre-key upload path by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/900 +* perf(prekeys): stream prekey generation to cut the connect-time peak by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/901 +* bench(integration): cut CodSpeed variance with a fixed 2-worker runtime + deterministic allocator by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/902 +* perf(send): skip the unused DeviceSentMessage plaintext on companion-less DMs by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/903 +* perf(send): share one message encode between the reporting token and DM plaintext by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/904 +* perf(send): share one message encode between the group reporting token and skmsg plaintext by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/905 +* ci(codspeed): drop the memory instrument from the integration benches by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/906 +* feat(groups): backfill participant phone_number from LID-PN mapping by @Salientekill in https://github.com/oxidezap/whatsapp-rust/pull/909 +* chore(deps): cargo update by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/910 +* refactor(sqlite-storage)!: replace bincode with prost for persisted blobs by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/911 +* perf(size): size-optimize off-hot-path crates via per-package opt-level by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/912 +* fix(atomics): use portable_atomic for 64-bit atomics + lint against std by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/913 +* VoIP 1:1 calling by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/918 +* feat: opt-in inbound durability hook (at-least-once delivery) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/920 +* chore(deps): update workspace dependencies to latest by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/921 +* fix(build): isolate voip example so the demo build drops cpal/alsa-sys by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/922 +* fix(status): omit addressing_mode on status@broadcast send by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/925 +* perf(sqlite-storage): cut per-session memory & threads, with configurable tuning by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/926 +* ci(docker): portable multi-arch image, unprivileged runtime, GHCR publish by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/927 +* feat(passkey): SHORTCAKE_PASSKEY companion linking by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/928 +* chore(deps): bump aes-gcm from 0.11.0-rc.4 to 0.11.0 by @dependabot[bot] in https://github.com/oxidezap/whatsapp-rust/pull/929 +* feat(media): support ContextInfo in media options by @arsa0x in https://github.com/oxidezap/whatsapp-rust/pull/931 +* perf(binary): validate wire strings with smoothutf8 by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/932 +* perf(binary): inflate into uninitialized buffer, drop the zero-init memset by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/933 +* fix(receive): skip PDO placeholder-resend for view-once, ack instead by @blaueeiner in https://github.com/oxidezap/whatsapp-rust/pull/934 +* fix(receive): skip PDO placeholder-resend for bot and hosted unavailable too by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/935 +* perf(send): trim group-send warm-path CPU and cold fan-out allocations by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/936 +* perf(send): single-flight cold group sender-key distribution by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/937 +* perf(send): keep send_message's future pointer-sized by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/938 +* fix(send): gate DM LID wire addressing on the account's 1:1 migration state by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/943 +* perf(history-sync): size the secret-record accumulator by sampled density by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/945 +* perf(libsignal): evict skipped message keys without shifting the buffer by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/946 +* perf(history-sync): keep heap-error types off the scanner happy path by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/947 +* perf(send): keep public send futures pointer-scale by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/948 +* perf(binary): keep BinaryError construction off the decoder happy path by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/949 +* bench: deterministic rng and hashers; pin and shard the CodSpeed CI by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/950 +* tracing: tag wa.iq / wa.send.message / wa.conn.run spans with account identity by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/951 +* perf: keep droppy error/default construction off per-message happy paths by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/952 +* perf(libsignal): store the sender-key backlog as Copy (iteration, seed) pairs by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/953 +* api: re-export the crates whose types appear in the public API by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/954 +* perf: inline index-MAC keys, reusable conversation decode, single-encode send by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/955 +* fix!: typed read-loop exit — routine server recycles are not errors; Disconnected carries the reason by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/956 +* perf(voip): zerocopy the RTP/STUN fixed-layout parse; borrow STUN attr values by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/957 +* perf(connect): move own-device sync off the pre-active path; drop keepalive loop span by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/958 +* perf(tracing): drop client-lifetime wa.conn.run span by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/959 +* fix: shut down on SIGTERM, not just SIGINT (docker stop timeout) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/960 +* feat(recv)!: batch the inbound commit pipeline during the offline drain by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/961 +* Per-session metrics: wire I/O counters, memory report in bytes, runtime-agnostic task instrumentation by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/962 +* Meter Bot::run's main future through the task instrument by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/963 +* fix(stats): make Client::memory_report() Send by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/964 +* feat: WA Web parity fixes (offline drain, crypto caps, reconnect, groups) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/965 +* fix(tctoken): persist issuance timestamp on IQ success and gate cstoken independently by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/966 +* feat(stats): attribute per-session resources beyond the Client by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/967 +* feat: rotate signed pre-key on a cadence (WA Web RotateKeyJob) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/968 +* feat(tctoken): attach tctoken in usync status/about and spam-report IQs by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/969 +* feat(tctoken): attach and issue tctoken on outgoing VoIP call offers by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/970 +* feat: gate DM read/played receipts on readreceipts privacy by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/971 +* fix(waproto): stop the build script recompiling on every run by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/973 +* fix(appstate): bound pairing key-share wait by the 180s critical deadline by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/974 +* perf: parallelize remaining serial/blocking hot paths (startup, media, send/recv) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/975 +* fix(pair-code): correct link_code_pairing_nonce byte and close WA Web stage-2 gaps by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/976 +* fix(iq): cancellation-safe IQ response waiters (unblocks try_join!) + named fan-out consts by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/978 +* fix(pair-code): canonicalize companion_platform_display OS to a server-safe set by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/979 +* fix(tc-token): atomic newer-wins store; drop tc_token_lock and close the cross-source race by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/980 +* feat(events): opt-in ordered + bounded inbound event delivery by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/981 +* perf(recv): hold the per-sender session lock only around Signal decrypt by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/983 +* feat(retry): opt-in RetryAdmission hook (compliant supersede of #982) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/985 +* fix(recv): retry (not NACK) recoverable group skmsg decrypt failures by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/986 +* fix(recv): retry (not NACK) InvalidSignedPreKeyId on the 1:1 decrypt path by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/987 +* fix(appstate): validate aggregate snapshot/patch MACs for genesis patches by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/988 +* feat(events): observe-only ServerAck event for server stanzas by @JeanCapixaba in https://github.com/oxidezap/whatsapp-rust/pull/989 +* fix(send): lock per-device sessions across the group SKDM fan-out by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/990 +* fix(cache): don't capacity-evict a session lock a task still holds by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/991 +* fix(recv): serialize the group inbound sender-key chain with a lock by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/992 +* fix(binary): bound node-decode recursion depth to reject hostile frames by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/994 +* fix(keepalive): anchor dead-socket watchdog to first send, not last by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/995 +* fix(send): isolate a group device's session-setup failure from the cohort by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/996 +* fix(passkey): don't skip the verification-code UX on a fresh link by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/997 +* fix(voip): authenticate WARP MI tag before folding recv ROC state by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/998 +* fix(send): never memoize own devices in the sender-key map (WA Web parity) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/999 +* refactor(events): model ServerAck.class as Option, document payload stability by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1000 +* chore(proto): bump WhatsApp protocol surface to 2.3000.1042742319 by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1001 +* refactor(events): seal ServerAck with non_exhaustive + bon builder by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1002 +* refactor(events): seal notification, sync event payloads (non_exhaustive + bon builder) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1003 +* refactor(events): complete the event-payload API freeze by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1004 +* chore(deps): update dependencies to latest versions by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1009 +* feat(history-sync): learn PN-LID mappings from phoneNumberToLidMappings by @blaueeiner in https://github.com/oxidezap/whatsapp-rust/pull/1010 +* feat(lid-pn): source-aware write policy matching WA Web createLidPnMappings by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1011 +* feat(lid-pn): make add_lid_pn_mapping pub for embedder-learned sources by @blaueeiner in https://github.com/oxidezap/whatsapp-rust/pull/1013 +* feat(chat-store): SQLite-backed chat/message history store by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1014 +* perf(message): reduce hot-path allocations by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1015 +* perf(receipt): persistent worker for live delivery receipts by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1016 +* perf(signal): sync fast paths for hot store adapter methods by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1017 +* perf(node): cut ack re-encode, exact stanza sizing, warm session pre-filter by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1018 +* perf(send): dispatch send_message_impl to per-branch boxed futures by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1019 +* perf(binary): inline the exact-marshal string hint cache by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1020 +* perf(group): keep warm sends warm under the own-device SKDM steady state by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1021 +* perf(signal): coalesce receive flushes and persist outbound state pre-wire by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1022 +* VoIP 1:1 video calls by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1024 +* perf(client): trim per-message allocations on the send/receive hot path by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1025 +* perf(signal): lease outbound counters in batches instead of flushing every send by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1026 +* fix(signal): gate the group sender-key advance before the wire by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1027 +* ci: cache target dir and drop incremental to stop full recompiles by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1033 +* chore(deps): update workspace dependencies to latest compatible versions by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1034 +* fix(signal): persist retry advances before wire by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1041 +* fix(signal): retain durability gates through deletes by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1042 +* fix(signal): serialize sender-key mutations by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1043 +* fix(signal): recover cancelled session checkouts by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1044 +* fix(message): preserve active chat lanes by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1045 +* ci: reclaim runner disk before tests by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1047 +* perf(signal): reuse leases across ciphertext APIs by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1048 +* test(signal): add deterministic durability chaos coverage by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1049 +* feat(voip): add encoded audio pipeline and native Opus negotiation by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1050 +* fix(voip): prevent stale audio-to-video upgrades by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1051 +* fix(appstate): recover missing keys across companion devices by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1052 +* feat(proto): upgrade buffa to 0.9 and open SyncdOperation by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1053 +* perf(core): reduce allocation churn across sync and crypto paths by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1054 +* perf(size): -1.47 MiB (-13.3%) stripped binary — build-config levers + control-plane demonomorphization by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1055 +* perf(size): token lookup via static hash table — −29 KiB, faster than hashify on hits and marshal, dep dropped by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1056 +* perf: marshal_exact 3× faster — replay plan-pass hints instead of reclassifying every string by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1057 +* perf(core): reduce history sync and decrypt allocation churn by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1058 +* perf(send): reduce DM allocation and serialization churn by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1059 +* feat(core): expand protocol metadata and shared primitives by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1060 +* feat(plugins): add extensible client architecture by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1061 +* feat(core): expose signal record components and dirty events by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1062 +* feat(core): add typed USync query engine by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1063 +* feat(core): expose typed stanza responses and retries by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1069 +* fix(client): remove obsolete device-registry cleanup task by @Bot-Dev-RPA in https://github.com/oxidezap/whatsapp-rust/pull/1070 +* feat(send): add targeted retransmission controls by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1071 +* feat(signal): add typed legacy session interop by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1072 +* feat(sqlite-storage): add connection-init hook before pragmas and migrations by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1074 +* chore(deps): update all workspace dependencies to latest by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1075 +* fix(contacts): short-circuit user-directed IQs for the system JID by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1080 +* feat(messages): surface business verified name from the message envelope by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1081 +* feat(chat-store): classify business template/buttons/list/interactive messages by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1082 +* fix(chat-store): resolve PN/LID aliases so receipts reach their rows by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1083 +* feat(messaging): add edit_message_with_stanza_id to override the outer stanza id by @Salientekill in https://github.com/oxidezap/whatsapp-rust/pull/1084 +* chore(deps): update workspace dependencies to latest by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1085 +* perf(cache): non-generic single-flight registry + boxed-on-miss init futures by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1086 +* chore: remove dead public API surface from wacore and libsignal by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1087 +* fix(concurrency): cancellation-safe app-state sync dedup and message-secret backpressure by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1088 +* refactor(api): rustdoc for entry types, must_use, root re-exports, and constructor consistency by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1089 +* refactor(errors): typed ConnectError and friends for the public API by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1090 +* feat(waproto): pinned HistorySync encode wrappers in codec by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1091 +* feat(wacore): stable ReceiptType variant-name accessor backing Serialize by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1092 +* ci: workspace lints, cargo-deny, rustdoc gate, MSRV, and feature-matrix checks by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1093 +* test: replace fixed sleeps with bounded polling and add negative coverage for server-controlled parsers by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1094 +* refactor(derive): serialize tagged WireEnum variants as structs by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1096 +* fix(groups): send the previous description id when updating a group description by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1097 +* fix(chat-store): key receipts and contacts by the peer's bare identity by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1099 +* refactor(errors): make the public error surface recoverable without string matching by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1100 +* fix(recv): handle and acknowledge top-level status stanzas by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1101 +* fix(voip): dial the relay endpoint on the web client port by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1102 +* perf(stats): stop dating every wire frame for a field nothing reads by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1103 +* fix(voip): race the relay's advertised and web-client ports for media by @Bot-Dev-RPA in https://github.com/oxidezap/whatsapp-rust/pull/1104 +* perf(send): stamp one message from one clock read by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1106 +* fix(voip): a busy device must not end an outgoing call for its siblings by @Bot-Dev-RPA in https://github.com/oxidezap/whatsapp-rust/pull/1107 +* docs(agents): correct stale agent docs and add a WA Web verification path by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1109 +* ci: skip the mock-server jobs on fork PRs instead of failing their template by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1110 +* chore(voip/mlow): make the decoder cross-check vectors regenerable by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1112 +* perf: cut two per-message allocations on the pingpong hot path by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1114 +* perf: remove four per-message tasks and buffers from the send/receive round trip by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1116 +* fix(socket): stop sending after a transport failure instead of reusing the nonce by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1117 +* perf(send): memoize the DM device fan-out per recipient by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1118 +* perf(socket): coalesce queued noise frames into one transport write by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1119 +* perf(client): send queued acks and receipts as one burst by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1120 +* perf(socket): seal each noise frame where it lands in the batch buffer by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1121 +* perf: cut a dozen per-message allocations from the DM round trip by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1122 +* perf: answer three store calls without boxing a ready future by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1124 +* chat-store: record locally-originated amendments by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1126 +* fix(chat-store): reconcile outgoing timestamps from server acks by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1127 +* fix(voip): send callee answer signaling by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1128 +* ci: centralize and pin bartender image by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1129 +* feat(voip): add group calls and call links by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1130 +* perf: five cuts to the per-message allocation count by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1131 +* perf(noise): keep the frame decoder's buffer instead of giving it away by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1136 +* perf: four more cuts to the per-message allocation count by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1137 +* Two follow-ups: the burst's other half, and a DM 406 that went nowhere by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1139 +* fix: six chat-store and send-path issues by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1144 +* fix(signal): stop a DH ratchet stranding the counter lease by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1149 +* perf(storage): let WAL readers run, and stop search from monopolizing them by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1151 +* fix(signal): store pre-key public keys in one encoding by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1152 +* fix(prekeys): act on the device a rejected fetch names by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1153 +* fix(chat-store): keep receipt times for 1:1 chats, per state by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1155 +* fix(appstate): let a diverged collection converge, and stop losing 409'd patches by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1158 +* ci(miri): check the unsafe decode path under the interpreter by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1162 +* fix(pair-code): keep a phone-number link alive past the QR rotation by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1163 +* fix(device-props): stop asking every pairing for a full history sync by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1164 +* fix(conn): stop dropping the failure stanza on logout, ban and outdated by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1165 +* fix(transport): send the Origin header on the WebSocket upgrade by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1166 +* perf(jid): stop the scan at the `@` and resolve the server once by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1171 +* perf(jid): parse the device and agent fields with a decimal-only reader by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1172 +* perf(jid): stop re-validating UTF-8 the writer just assembled by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1173 +* perf(node): iterate children by tag without going through Flatten by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1174 +* perf(attrs): read required attributes with a single lookup by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1175 +* perf(jid): compare structured JID attributes without rendering them by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1176 +* fix(jid): stop keeping the AD-JID domain byte as an agent by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1178 +* fix(lifecycle): race the reconnect backoff against the terminal shutdown by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1179 +* fix(jid): make agent identity, the AD form, and device dedup agree by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1182 +* perf(jid): skip the agent normalisation when the raw agents already match by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1183 +* fix(jid): encode interop JIDs with the token that carries their integrator by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1184 +* bench(send): measure the shapes production actually sends by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1186 +* perf(libsignal): stop revalidating UTF-8 the address buffer just wrote by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1187 +* fix(media): let a CDN's non-2xx status reach the download classifier by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1188 +* perf(messages): draw the pad length from the thread RNG by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1189 +* feat(pair-code): report a refused pair-code request to the consumer by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1191 +* perf(secret-enc): draw the addon IV from the thread RNG by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1192 +* feat(download): allow media download without a connected session by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1194 +* feat(errors): make a refused HTTP status recoverable by type by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1195 +* fix(download): guarantee download_to_writer leaves only verified media by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1197 +* fix(pair-code): read the companion_finish answer instead of assuming it by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1198 +* chore(deps): update workspace dependencies to latest by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1199 +* refactor(deps): drop redundant crates and unused dependency features by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1200 +* build(deps): drop three dependency features nothing activates by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1201 +* fix(iq): report detail the IQ parser drops, once per process by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1202 +* fix(history-sync): match our own pushname entry by JID user by @blaueeiner in https://github.com/oxidezap/whatsapp-rust/pull/1203 +* fix(appstate): reserve the collection for an AppStateSync task by @blaueeiner in https://github.com/oxidezap/whatsapp-rust/pull/1205 +* docs(appstate): correct the batched-sync iteration cap comment by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1206 +* fix(appstate): report what a batched sync actually achieved by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1207 +* fix(appstate): unbreak the bootstrap gate and read the right lifecycle signals by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1208 +* ci(test): run the suites through cargo-nextest by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1209 +* fix(libsignal): keep skipped message-key seeds projectable by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1210 +* feat(libsignal): let a consumer opt out of counter leasing by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1211 +* bench(libsignal): measure what the sender-key XEdDSA memo is worth by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1212 +* feat(libsignal): let a caller prewarm the sender-key derivation memo by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1213 +* perf(binary): unpack packed values through a byte-pair table by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1214 +* perf(binary): validate packed output with the wire path's validator by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1215 +* perf(binary): drop the per-node box around NodeRef content by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1216 +* feat(libsignal): let a consumer provide the X25519 agreement by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1218 + +
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 25cb22333..9ea3731d4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -85,13 +85,18 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - if gh release view "${{ steps.version.outputs.tag }}" > /dev/null 2>&1; then - echo "Release ${{ steps.version.outputs.tag }} already exists, skipping." + TAG="${{ steps.version.outputs.tag }}" + # Hand-written notes win when the release has them: --generate-notes + # emits one bullet per PR, which for a release this size is thousands + # of lines with no structure. Falls back to the generated list so a + # release without a notes file still publishes. + NOTES=".github/release-notes/$TAG.md" + if gh release view "$TAG" > /dev/null 2>&1; then + echo "Release $TAG already exists, skipping." + elif [ -f "$NOTES" ]; then + gh release create "$TAG" --title "$TAG" --notes-file "$NOTES" --latest else - gh release create "${{ steps.version.outputs.tag }}" \ - --title "${{ steps.version.outputs.tag }}" \ - --generate-notes \ - --latest + gh release create "$TAG" --title "$TAG" --generate-notes --latest fi # The tag pushed above with GITHUB_TOKEN can't trigger docker.yml's tag event, diff --git a/Cargo.lock b/Cargo.lock index 8943d7156..02fe54b7e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3829,7 +3829,7 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wacore" -version = "0.6.0" +version = "0.7.0" dependencies = [ "aes 0.9.2", "aes-gcm 0.11.0", @@ -3884,7 +3884,7 @@ dependencies = [ [[package]] name = "wacore-appstate" -version = "0.6.0" +version = "0.7.0" dependencies = [ "anyhow", "buffa", @@ -3905,7 +3905,7 @@ dependencies = [ [[package]] name = "wacore-binary" -version = "0.6.0" +version = "0.7.0" dependencies = [ "bytes", "codspeed-divan-compat", @@ -3924,7 +3924,7 @@ dependencies = [ [[package]] name = "wacore-derive" -version = "0.6.0" +version = "0.7.0" dependencies = [ "proc-macro2", "quote", @@ -3933,7 +3933,7 @@ dependencies = [ [[package]] name = "wacore-libsignal" -version = "0.6.0" +version = "0.7.0" dependencies = [ "aes 0.9.2", "async-lock", @@ -3965,7 +3965,7 @@ dependencies = [ [[package]] name = "wacore-noise" -version = "0.6.0" +version = "0.7.0" dependencies = [ "anyhow", "buffa", @@ -4011,7 +4011,7 @@ dependencies = [ [[package]] name = "waproto" -version = "0.6.0" +version = "0.7.0" dependencies = [ "buffa", "buffa-build", @@ -4216,7 +4216,7 @@ dependencies = [ [[package]] name = "whatsapp-rust" -version = "0.6.0" +version = "0.7.0" dependencies = [ "aes 0.9.2", "anyhow", @@ -4307,7 +4307,7 @@ dependencies = [ [[package]] name = "whatsapp-rust-sqlite-storage" -version = "0.6.0" +version = "0.7.0" dependencies = [ "async-trait", "buffa", @@ -4327,7 +4327,7 @@ dependencies = [ [[package]] name = "whatsapp-rust-tokio-transport" -version = "0.6.0" +version = "0.7.0" dependencies = [ "anyhow", "async-channel", @@ -4346,7 +4346,7 @@ dependencies = [ [[package]] name = "whatsapp-rust-ureq-http-client" -version = "0.6.0" +version = "0.7.0" dependencies = [ "anyhow", "async-trait", @@ -4357,7 +4357,7 @@ dependencies = [ [[package]] name = "whatsapp-rust-voip-cli" -version = "0.6.0" +version = "0.7.0" dependencies = [ "anyhow", "async-channel", diff --git a/Cargo.toml b/Cargo.toml index 1faf10726..92cb2e75e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "whatsapp-rust" -version = "0.6.0" +version = "0.7.0" edition = "2024" rust-version.workspace = true authors = ["João Lucas "] @@ -124,13 +124,13 @@ uuid = { version = "1", default-features = false } zeroize = { version = "1.9", default-features = false, features = ["alloc"] } # Internal workspace crates -wacore = { path = "./wacore", default-features = false, version = "0.6.0" } -wacore-appstate = { path = "./wacore/appstate", default-features = false, version = "0.6.0" } -wacore-binary = { path = "./wacore/binary", default-features = false, version = "0.6.0" } -wacore-derive = { path = "./wacore/derive", version = "0.6.0" } -wacore-libsignal = { path = "./wacore/libsignal", version = "0.6.0" } -wacore-noise = { path = "./wacore/noise", version = "0.6.0" } -waproto = { path = "./waproto", version = "0.6.0" } +wacore = { path = "./wacore", default-features = false, version = "0.7.0" } +wacore-appstate = { path = "./wacore/appstate", default-features = false, version = "0.7.0" } +wacore-binary = { path = "./wacore/binary", default-features = false, version = "0.7.0" } +wacore-derive = { path = "./wacore/derive", version = "0.7.0" } +wacore-libsignal = { path = "./wacore/libsignal", version = "0.7.0" } +wacore-noise = { path = "./wacore/noise", version = "0.7.0" } +waproto = { path = "./waproto", version = "0.7.0" } yoke = { version = "0.8", features = ["derive"] } zerocopy = { version = "0.8.55", default-features = false, features = ["derive"] } zlib-rs = { version = "0.6.6", default-features = false, features = ["std", "rust-allocator"] } @@ -238,9 +238,9 @@ webrtc-dtls = { version = "0.12", optional = true } webrtc-sctp = { version = "0.17", optional = true } webrtc-util = { version = "0.17", optional = true } webrtc-util-011 = { package = "webrtc-util", version = "0.11", optional = true } -whatsapp-rust-sqlite-storage = { path = "./storages/sqlite-storage", version = "0.6.0", optional = true } -whatsapp-rust-tokio-transport = { path = "./transports/tokio-transport", version = "0.6.0", optional = true } -whatsapp-rust-ureq-http-client = { path = "./http_clients/ureq-client", version = "0.6.0", optional = true } +whatsapp-rust-sqlite-storage = { path = "./storages/sqlite-storage", version = "0.7.0", optional = true } +whatsapp-rust-tokio-transport = { path = "./transports/tokio-transport", version = "0.7.0", optional = true } +whatsapp-rust-ureq-http-client = { path = "./http_clients/ureq-client", version = "0.7.0", optional = true } # wasm32-unknown-unknown has no default getrandom backend, so make the browser # one available (the only valid choice there) to build the crate standalone: diff --git a/examples/voip-cli/Cargo.toml b/examples/voip-cli/Cargo.toml index 655e1a6c5..f564a30cb 100644 --- a/examples/voip-cli/Cargo.toml +++ b/examples/voip-cli/Cargo.toml @@ -5,7 +5,7 @@ # it explicitly with `cargo run -p whatsapp-rust-voip-cli`. [package] name = "whatsapp-rust-voip-cli" -version = "0.6.0" +version = "0.7.0" edition = "2024" rust-version.workspace = true license = "MIT" diff --git a/http_clients/ureq-client/Cargo.toml b/http_clients/ureq-client/Cargo.toml index 2ca7a272a..6daa83556 100644 --- a/http_clients/ureq-client/Cargo.toml +++ b/http_clients/ureq-client/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "whatsapp-rust-ureq-http-client" -version = "0.6.0" +version = "0.7.0" edition = "2024" rust-version.workspace = true authors = ["João Lucas "] diff --git a/storages/chat-store/Cargo.toml b/storages/chat-store/Cargo.toml index aa70df4a2..83d008a3b 100644 --- a/storages/chat-store/Cargo.toml +++ b/storages/chat-store/Cargo.toml @@ -7,6 +7,9 @@ authors = ["João Lucas "] license = "MIT" repository = "https://github.com/jlucaso1/whatsapp-rust" description = "SQLite-backed chat/message history store for whatsapp-rust" +# Held back from crates.io: the schema and the query surface are still moving, +# and publishing pins both. Drop this line to release it alongside the rest. +publish = false [features] default = [] @@ -28,7 +31,7 @@ tokio = { workspace = true, features = ["sync", "rt", "macros"] } wacore = { workspace = true } wacore-binary = { workspace = true } waproto = { workspace = true } -whatsapp-rust-sqlite-storage = { path = "../sqlite-storage", version = "0.6.0" } +whatsapp-rust-sqlite-storage = { path = "../sqlite-storage", version = "0.7.0" } [dev-dependencies] flate2 = { workspace = true } diff --git a/storages/sqlite-storage/Cargo.toml b/storages/sqlite-storage/Cargo.toml index 4d5128776..a432dba25 100644 --- a/storages/sqlite-storage/Cargo.toml +++ b/storages/sqlite-storage/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "whatsapp-rust-sqlite-storage" -version = "0.6.0" +version = "0.7.0" edition = "2024" rust-version.workspace = true authors = ["João Lucas "] diff --git a/transports/tokio-transport/Cargo.toml b/transports/tokio-transport/Cargo.toml index 529edfbca..d68ba27c0 100644 --- a/transports/tokio-transport/Cargo.toml +++ b/transports/tokio-transport/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "whatsapp-rust-tokio-transport" -version = "0.6.0" +version = "0.7.0" edition = "2024" rust-version.workspace = true authors = ["João Lucas "] diff --git a/wacore/Cargo.toml b/wacore/Cargo.toml index f161e9043..b9b2b263c 100644 --- a/wacore/Cargo.toml +++ b/wacore/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wacore" -version = "0.6.0" +version = "0.7.0" edition = "2024" rust-version.workspace = true authors = ["João Lucas "] diff --git a/wacore/appstate/Cargo.toml b/wacore/appstate/Cargo.toml index e06618236..fccbb0b5b 100644 --- a/wacore/appstate/Cargo.toml +++ b/wacore/appstate/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wacore-appstate" -version = "0.6.0" +version = "0.7.0" edition = "2024" rust-version.workspace = true authors = ["João Lucas "] diff --git a/wacore/binary/Cargo.toml b/wacore/binary/Cargo.toml index 2e20ff587..0531adc77 100644 --- a/wacore/binary/Cargo.toml +++ b/wacore/binary/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wacore-binary" -version = "0.6.0" +version = "0.7.0" edition = "2024" rust-version.workspace = true authors = ["João Lucas "] diff --git a/wacore/derive/Cargo.toml b/wacore/derive/Cargo.toml index a1bd3d90c..2a1ccc640 100644 --- a/wacore/derive/Cargo.toml +++ b/wacore/derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wacore-derive" -version = "0.6.0" +version = "0.7.0" edition = "2024" rust-version.workspace = true authors = ["João Lucas "] diff --git a/wacore/libsignal/Cargo.toml b/wacore/libsignal/Cargo.toml index 77d2c7afd..5747943fd 100644 --- a/wacore/libsignal/Cargo.toml +++ b/wacore/libsignal/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wacore-libsignal" -version = "0.6.0" +version = "0.7.0" edition = "2024" rust-version.workspace = true authors = ["João Lucas "] diff --git a/wacore/noise/Cargo.toml b/wacore/noise/Cargo.toml index 54ead65e9..f7410c01d 100644 --- a/wacore/noise/Cargo.toml +++ b/wacore/noise/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wacore-noise" -version = "0.6.0" +version = "0.7.0" edition = "2024" rust-version.workspace = true authors = ["João Lucas "] diff --git a/waproto/Cargo.toml b/waproto/Cargo.toml index b59e7000b..6ffda4251 100644 --- a/waproto/Cargo.toml +++ b/waproto/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "waproto" -version = "0.6.0" +version = "0.7.0" edition = "2024" rust-version.workspace = true authors = ["João Lucas "] From c764acbabd57bb9b71afaa7343970d1857399974 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Lucas?= <55464917+jlucaso1@users.noreply.github.com> Date: Thu, 6 Aug 2026 22:42:28 -0300 Subject: [PATCH 2/5] chore(release): drop the notes file, harden the tag handoff The 0.7.0 notes stay out of the repo; the workflow keeps preferring .github/release-notes/v.md and falls back to --generate-notes, so adding one later needs no workflow change. Review follow-up: the version string becomes a git tag, a crates.io release and an image tag, and it came from a grep with no validation. It is now checked against semver at the point it is read, and reaches both steps through env instead of being expanded into the shell source. --- .github/release-notes/v0.7.0.md | 703 -------------------------------- .github/workflows/release.yml | 14 +- 2 files changed, 12 insertions(+), 705 deletions(-) delete mode 100644 .github/release-notes/v0.7.0.md diff --git a/.github/release-notes/v0.7.0.md b/.github/release-notes/v0.7.0.md deleted file mode 100644 index 5e7360261..000000000 --- a/.github/release-notes/v0.7.0.md +++ /dev/null @@ -1,703 +0,0 @@ -488 pull requests since 0.6.0, over roughly three months. The big items are VoIP (1:1 voice, 1:1 video, group calls and call links, with the media plane and not only the signaling), the switch of protobuf codegen from prost to buffa, a pre-1.0 pass over the public API (typed errors, sealed event payloads, `#[non_exhaustive]`), a plugin host, an optional SQLite chat store, opt-in tracing and metrics, and a per-message round trip about a third cheaper than 0.6.0's. - -A lot of APIs changed. See [Breaking changes](#breaking-changes) below. The two invasive ones are buffa's generated proto surface and the anyhow to typed-error migration, and both touch nearly every call site in a downstream crate. They are taken now because breaking changes are still cheap before 1.0. - -## Highlights - -### VoIP - -- **1:1 voice calls** ([#918](https://github.com/oxidezap/whatsapp-rust/pull/918)). Incoming and outgoing, against the official WhatsApp app, with the full media path: mic capture, encode, encrypt, relay, decrypt, decode, playout. Includes a pure-Rust MLow codec (WhatsApp's proprietary voice codec, ported from the reverse-engineered reference and pinned by a byte-exact golden roundtrip test), E2E-SRTP with the WARP integrity tag and the SFrame layer, and a sans-IO `CallEngine` that owns no socket, clock or thread. No codec FFI, so it still builds for wasm and embedded targets. -- **1:1 video calls** ([#1024](https://github.com/oxidezap/whatsapp-rust/pull/1024)). Start with video, upgrade from audio, downgrade back, and accept the same transitions from an official client. Codec-neutral: consumers pass complete H.264 Annex-B access units through `VideoSource` / `VideoSink`. Authenticated PLI/FIR is treated as a decoder resynchronization boundary, which is what made real webcam video render on the Android peer. -- **Group calls and call links** ([#1130](https://github.com/oxidezap/whatsapp-rust/pull/1130)). Group-bound calls, ad-hoc promotion of an active direct call, reusable call links, 128-user waiting rooms with admission and administrator controls, roster-driven subscriptions, atomic epoch rotation and key fan-out, participant-attributed audio, video, RTCP and data, plus reactions, hand raising and screen sharing. -- **Encoded audio and native Opus** ([#1050](https://github.com/oxidezap/whatsapp-rust/pull/1050)). Applications can supply raw MLOW or Opus packets without linking a codec. Full-duplex native Opus negotiation (PT120/16 kHz as the production profile, PT111/48 kHz as an explicit variant). MLOW stays the compatibility default. -- tctoken on outgoing call offers ([#970](https://github.com/oxidezap/whatsapp-rust/pull/970)), zero-copy RTP/STUN parsing ([#957](https://github.com/oxidezap/whatsapp-rust/pull/957)), and a tail of live-tested signaling fixes ([#1102](https://github.com/oxidezap/whatsapp-rust/pull/1102), [#1104](https://github.com/oxidezap/whatsapp-rust/pull/1104), [#1107](https://github.com/oxidezap/whatsapp-rust/pull/1107), [#1128](https://github.com/oxidezap/whatsapp-rust/pull/1128)). - -```rust -let handle = client.voip() - .call(peer) - .audio(mic_source, speaker_sink) - .video(h264_source, h264_sink) // optional - .start() - .await?; - -handle.wait_ended().await; -``` - -All of it sits behind optional features (`voip`, `voip-runtime`, `voip-mlow`, `voip-libopus`, `voip-encoded`). A default build pulls none of it. - -### Extensibility - -- **Plugin architecture** ([#1061](https://github.com/oxidezap/whatsapp-rust/pull/1061)). A build-time native plugin host behind the `plugins` feature, with a generation-scoped extension lifecycle (`client-lifecycle`), typed plugin APIs, capabilities, dependency ordering, scoped tasks with completion barriers, and a bounded event router keyed by `(plugin_id, topic)`. -- **`whatsapp-rust-chat-store`** ([#1014](https://github.com/oxidezap/whatsapp-rust/pull/1014)). An opt-in crate that materializes the client's event stream into queryable SQLite tables, in the same database file as the device store, so a UI or a stateful bot survives a restart without re-syncing. Event-sourced write-behind writer, encoded `wa::Message` as the source of truth, keyset pagination, a `StoreChange` broadcast for invalidation, and FTS5 search. -- **Optional tracing** ([#733](https://github.com/oxidezap/whatsapp-rust/pull/733)): around 172 spans under a `wa.{conn,recv,send,iq,appstate,pair,media,receipt,retry,pdo,notif,session,bot}.*` taxonomy, OTel-ready, with phone numbers redacted by default. **Optional metrics** ([#734](https://github.com/oxidezap/whatsapp-rust/pull/734)) through the `metrics` facade, plus a `whatsapp-rust-plugin-metrics` plugin. Both are off by default, with no dependency and no overhead when disabled. -- Per-session resource attribution in `stats()` ([#967](https://github.com/oxidezap/whatsapp-rust/pull/967)). -- Typed legacy session interop ([#1072](https://github.com/oxidezap/whatsapp-rust/pull/1072)) behind `legacy-session-interop`, for importing externally produced auth state. -- A SQLite connection-init hook that runs before pragmas and migrations ([#1074](https://github.com/oxidezap/whatsapp-rust/pull/1074)). - -### Delivery guarantees - -- **Opt-in inbound durability hook** ([#920](https://github.com/oxidezap/whatsapp-rust/pull/920)) turns the inbound consumer from at-most-once into at-least-once. The transport ack is deferred until the hook commits, and the decrypted message is buffered durably before the Signal ratchet flushes. With no hook registered the behaviour is unchanged. -- **Ordered and bounded event delivery** ([#981](https://github.com/oxidezap/whatsapp-rust/pull/981)). `EventDelivery::Ordered { capacity }` delivers callbacks in arrival order through one bounded mailbox, matching WA Web's `preserveOrder: true`. A full mailbox drops the event and counts it in `StatsSnapshot::events_dropped` instead of growing without limit. -- **Batched inbound commit during the offline drain** ([#961](https://github.com/oxidezap/whatsapp-rust/pull/961)). WA Web commits durability, signal-store writes and aggregate receipts once per snapshot; we were doing all of it per message. A 421-message backlog was spending about 1.2 s in per-message round-trips. -- An opt-in `RetryAdmission` hook ([#985](https://github.com/oxidezap/whatsapp-rust/pull/985)) and an observe-only `ServerAck` event ([#989](https://github.com/oxidezap/whatsapp-rust/pull/989)). - -### Messaging surface - -- **Passkey companion linking** ([#928](https://github.com/oxidezap/whatsapp-rust/pull/928)): WhatsApp's SHORTCAKE_PASSKEY WebAuthn device-link gate, end to end, with a `PasskeyAuthenticator` seam for the one step that cannot be reproduced in-tree. -- Reactions ([#720](https://github.com/oxidezap/whatsapp-rust/pull/720)), message forwarding ([#738](https://github.com/oxidezap/whatsapp-rust/pull/738)), keep-in-chat ([#740](https://github.com/oxidezap/whatsapp-rust/pull/740)), quiz polls ([#754](https://github.com/oxidezap/whatsapp-rust/pull/754)), events with RSVP ([#758](https://github.com/oxidezap/whatsapp-rust/pull/758)), chat labels ([#715](https://github.com/oxidezap/whatsapp-rust/pull/715)), a 1:1 disappearing-message timer ([#745](https://github.com/oxidezap/whatsapp-rust/pull/745)), `mark_as_played` for voice and video notes ([#737](https://github.com/oxidezap/whatsapp-rust/pull/737)), group profile pictures ([#739](https://github.com/oxidezap/whatsapp-rust/pull/739)), `save_contact` ([#742](https://github.com/oxidezap/whatsapp-rust/pull/742)), the `clearChat` ([#755](https://github.com/oxidezap/whatsapp-rust/pull/755)) and `userStatusMute` ([#760](https://github.com/oxidezap/whatsapp-rust/pull/760)) app-state actions, and newsletter mute/unmute ([#757](https://github.com/oxidezap/whatsapp-rust/pull/757)). -- Encrypted channel (CAG) reactions and comments, both directions ([#830](https://github.com/oxidezap/whatsapp-rust/pull/830)). -- Meta AI and fbid bot replies via `` ([#650](https://github.com/oxidezap/whatsapp-rust/pull/650)). -- Secret-encrypted envelopes: poll-edit, poll-add-option and event-edit ([#645](https://github.com/oxidezap/whatsapp-rust/pull/645)), and message edits on receive ([#665](https://github.com/oxidezap/whatsapp-rust/pull/665), [#667](https://github.com/oxidezap/whatsapp-rust/pull/667), [#762](https://github.com/oxidezap/whatsapp-rust/pull/762)). -- Constant-memory streaming upload with a media sidecar ([#684](https://github.com/oxidezap/whatsapp-rust/pull/684)), high-level media builders from `UploadResponse` ([#764](https://github.com/oxidezap/whatsapp-rust/pull/764)), `ContextInfo` in media options ([#931](https://github.com/oxidezap/whatsapp-rust/pull/931)), and media download without a connected session ([#1194](https://github.com/oxidezap/whatsapp-rust/pull/1194)). -- First-party sticker packs from the CDN ([#644](https://github.com/oxidezap/whatsapp-rust/pull/644)), and the verified business name on messages and usync ([#741](https://github.com/oxidezap/whatsapp-rust/pull/741), [#1081](https://github.com/oxidezap/whatsapp-rust/pull/1081)). - -### Generated protocol registries - -Hand-maintained protocol tables were replaced with registries generated from the [whatspec](https://github.com/oxidezap/whatspec) IR, so refreshing them is a file copy instead of an audit. - -- mex operations ([#728](https://github.com/oxidezap/whatsapp-rust/pull/728)), with typed `Variables` and `Response` per op. This also fixed two doc-ids the current WA Web bundle no longer ships, which had been failing silently. -- A/B props ([#729](https://github.com/oxidezap/whatsapp-rust/pull/729)): 2100 typed flags replacing 11 magic `u32`s, at no binary cost for the ones nothing references. -- App-state (syncd) schemas ([#716](https://github.com/oxidezap/whatsapp-rust/pull/716)). Every action now carries its own `version`, `scope` and index shape; we had been stamping `version = 1` on all of them. -- Proto bumped to WhatsApp Web 2.3000.1040878135 ([#726](https://github.com/oxidezap/whatsapp-rust/pull/726)), then 2.3000.1042742319 ([#1001](https://github.com/oxidezap/whatsapp-rust/pull/1001)). - -## Performance - -The allocation work from 0.6.0 continued, with one change in method: CodSpeed now runs deterministic instruction-count and memory instruments on every PR ([#828](https://github.com/oxidezap/whatsapp-rust/pull/828), [#898](https://github.com/oxidezap/whatsapp-rust/pull/898), [#950](https://github.com/oxidezap/whatsapp-rust/pull/950)), so a regression is caught before merge and every number below was measured rather than estimated. - -### Per-message round trip - -The ping-pong allocation series ([#1015](https://github.com/oxidezap/whatsapp-rust/pull/1015) to [#1022](https://github.com/oxidezap/whatsapp-rust/pull/1022)) took 32.3% off the bytes allocated per cycle, from a 46,088 B baseline, across five PRs: hot-path allocations, a persistent receipt worker, sync fast paths for the store adapter, exact stanza sizing, per-branch boxed send futures and an inlined marshal hint cache. - -A second sweep removed another dozen-plus allocations from the DM round trip ([#1114](https://github.com/oxidezap/whatsapp-rust/pull/1114), [#1116](https://github.com/oxidezap/whatsapp-rust/pull/1116), [#1122](https://github.com/oxidezap/whatsapp-rust/pull/1122), [#1131](https://github.com/oxidezap/whatsapp-rust/pull/1131), [#1137](https://github.com/oxidezap/whatsapp-rust/pull/1137)), coalesced queued noise frames into one transport write ([#1119](https://github.com/oxidezap/whatsapp-rust/pull/1119), [#1120](https://github.com/oxidezap/whatsapp-rust/pull/1120), [#1121](https://github.com/oxidezap/whatsapp-rust/pull/1121)) and memoized the DM device fan-out per recipient ([#1118](https://github.com/oxidezap/whatsapp-rust/pull/1118)). - -### Signal and crypto - -- Counter leasing ([#1026](https://github.com/oxidezap/whatsapp-rust/pull/1026), [#1048](https://github.com/oxidezap/whatsapp-rust/pull/1048)): warm group encryption went from 84.69 ms to 23.98 ms (-71.7%), warm participant fan-out from 49.16 ms to 8.69 ms (-82.3%). -- The sender-key backlog is stored as `Copy` pairs ([#953](https://github.com/oxidezap/whatsapp-rust/pull/953)), taking worst-case out-of-order group decrypt from 163.7 µs to 113.3 µs (-31%), and is shared behind an `Arc` ([#881](https://github.com/oxidezap/whatsapp-rust/pull/881)), roughly 3x on the median with the ~82 KB per-decrypt allocation gone. -- XEdDSA memoization for sender signing and verification ([#838](https://github.com/oxidezap/whatsapp-rust/pull/838), [#839](https://github.com/oxidezap/whatsapp-rust/pull/839), [#1213](https://github.com/oxidezap/whatsapp-rust/pull/1213)), pre-keyed zero-salt HKDF extracts ([#847](https://github.com/oxidezap/whatsapp-rust/pull/847), [#863](https://github.com/oxidezap/whatsapp-rust/pull/863), [#864](https://github.com/oxidezap/whatsapp-rust/pull/864)), and shift-free eviction of skipped message keys ([#946](https://github.com/oxidezap/whatsapp-rust/pull/946)). - -### Binary protocol and JID - -- Token lookup moved to a length-bucketed `tiny_map` on encode ([#873](https://github.com/oxidezap/whatsapp-rust/pull/873)), taking `marshal_auto_small` from 168.7 ns to 111.8 ns (-34%), and then to a build-time static hash table ([#1056](https://github.com/oxidezap/whatsapp-rust/pull/1056)) that is 29 KiB smaller, faster on hits, and drops the `hashify` dependency. `marshal_exact` got 3x faster by replaying plan-pass hints instead of reclassifying every string ([#1057](https://github.com/oxidezap/whatsapp-rust/pull/1057)). -- Small attribute lists are stored inline ([#819](https://github.com/oxidezap/whatsapp-rust/pull/819)): a message-shaped stanza went from 15 to 11 allocations, an 800-participant group stanza from 4012 to 2412 (-40%). Packed values are unpacked through a byte-pair table ([#1214](https://github.com/oxidezap/whatsapp-rust/pull/1214)), and the per-node box around `NodeRef` content is gone ([#1216](https://github.com/oxidezap/whatsapp-rust/pull/1216)), worth -9.5% instructions on the fan-out bench and -40% on the allocation count. -- `Jid` display emits one `write_str` ([#848](https://github.com/oxidezap/whatsapp-rust/pull/848)), 176.7 ns to 69.7 ns, followed by a parsing sweep that stops the scan at the `@`, reads the device and agent fields with a decimal-only reader, and stops re-validating UTF-8 the writer just assembled ([#1171](https://github.com/oxidezap/whatsapp-rust/pull/1171) to [#1176](https://github.com/oxidezap/whatsapp-rust/pull/1176), [#1183](https://github.com/oxidezap/whatsapp-rust/pull/1183), [#1187](https://github.com/oxidezap/whatsapp-rust/pull/1187)). -- Wire strings are validated through `smoothutf8` ([#932](https://github.com/oxidezap/whatsapp-rust/pull/932)), and inflate writes into an uninitialized buffer, dropping a `memset` that was 6% self time ([#933](https://github.com/oxidezap/whatsapp-rust/pull/933)). - -### App-state and history sync - -- ltHash index-MAC dedup: `bench_collect_unique_index_macs[1000]` went from 5,678 µs to 550.8 µs ([#868](https://github.com/oxidezap/whatsapp-rust/pull/868)), then another -19% at 1000 and -72% at 10 with inline index-MAC keys ([#955](https://github.com/oxidezap/whatsapp-rust/pull/955)). The N+1 previous-MAC lookups became a single query ([#687](https://github.com/oxidezap/whatsapp-rust/pull/687), [#821](https://github.com/oxidezap/whatsapp-rust/pull/821)), and the O(n²) in-patch overwrite scan became an O(1) map ([#701](https://github.com/oxidezap/whatsapp-rust/pull/701)). -- History sync got streaming decompression ([#672](https://github.com/oxidezap/whatsapp-rust/pull/672)), a pooled inflate reader ([#683](https://github.com/oxidezap/whatsapp-rust/pull/683)), storage of the compressed payload with a streaming reader ([#853](https://github.com/oxidezap/whatsapp-rust/pull/853)) and a decode-gating scan ([#836](https://github.com/oxidezap/whatsapp-rust/pull/836)). Together: allocation churn from 20.20 MB to 14.43 MB (-28.6%), CodSpeed stream-drain memory from 243.8 KB to 115.2 KB, and -16.2% peak heap on a 20k-row secret batch ([#1058](https://github.com/oxidezap/whatsapp-rust/pull/1058), [#1054](https://github.com/oxidezap/whatsapp-rust/pull/1054)). - -### Send path and caching - -- The per-group device fan-out is memoized behind a topology generation ([#824](https://github.com/oxidezap/whatsapp-rust/pull/824)), the group phash is memoized on the same entry ([#840](https://github.com/oxidezap/whatsapp-rust/pull/840)), warm sends stay warm under the own-device SKDM steady state ([#1021](https://github.com/oxidezap/whatsapp-rust/pull/1021)), and cold sender-key distribution is single-flight ([#937](https://github.com/oxidezap/whatsapp-rust/pull/937)). -- One message encode is shared between the reporting token and the DM or group plaintext ([#904](https://github.com/oxidezap/whatsapp-rust/pull/904), [#905](https://github.com/oxidezap/whatsapp-rust/pull/905)), and DM content is encoded once and spliced into the recipient and DSM plaintexts ([#787](https://github.com/oxidezap/whatsapp-rust/pull/787), [#788](https://github.com/oxidezap/whatsapp-rust/pull/788)). -- Device-registry lookups borrow their keys and build them inline ([#681](https://github.com/oxidezap/whatsapp-rust/pull/681), [#682](https://github.com/oxidezap/whatsapp-rust/pull/682)), taking per-member device resolution on an 800-member group send from 50,915 to 12,538 allocations (-75%). -- moka is gone. `PortableCache` is the only in-process cache backend ([#860](https://github.com/oxidezap/whatsapp-rust/pull/860)). - -### Storage - -- Per-session memory and thread use was cut and made configurable ([#926](https://github.com/oxidezap/whatsapp-rust/pull/926)), and the per-checkout `SELECT 1` liveness probe (36.6% of the bare checkout cost) was removed ([#874](https://github.com/oxidezap/whatsapp-rust/pull/874)). -- WAL readers are no longer blocked by search ([#1151](https://github.com/oxidezap/whatsapp-rust/pull/1151)): an FTS5 prefix query over 60k rows went from 13.3 ms to 0.8 ms. - -### Binary size - -Binary size is now a CI gate with a per-PR budget and a historical series ([#859](https://github.com/oxidezap/whatsapp-rust/pull/859)). - -- `waproto::codec` was pinned to a single instantiation ([#842](https://github.com/oxidezap/whatsapp-rust/pull/842)), taking `.text` from 13.03 to 11.85 MiB (-9.1%). Cold entry-point futures were boxed ([#843](https://github.com/oxidezap/whatsapp-rust/pull/843)) and the proto `PartialEq` anchor dropped, taking 148 `eq` impls to 0 ([#846](https://github.com/oxidezap/whatsapp-rust/pull/846)). -- `wa::Message` shrank from 3784 to 952 bytes (-75%) by boxing its inline content variants ([#866](https://github.com/oxidezap/whatsapp-rust/pull/866)). -- A full audit landed -1.47 MiB stripped (-13.3%) and -14.8% `.text` ([#1055](https://github.com/oxidezap/whatsapp-rust/pull/1055)) through `-Zshare-generics`, lld with ICF, SQLite at `-Oz` with a subsystem trim, and the rest of the codec sweep. Every rejected lever is documented with its drawback. - -## WhatsApp Web compliance and reliability - -- **App-state convergence** ([#1158](https://github.com/oxidezap/whatsapp-rust/pull/1158)). A `snapshotMac` mismatch on a patch means the collection's ltHash has diverged, not that the server is tampering. We treated it as fatal and re-requested the same unusable patch forever (the report was 114 identical failures over one evening, surviving restarts). WA Web degrades the collection instead. The same PR stops losing patches rejected with 409. Related: aggregate MAC validation for genesis patches ([#988](https://github.com/oxidezap/whatsapp-rust/pull/988)), snapshot-rollback and duplicate-index guards ([#752](https://github.com/oxidezap/whatsapp-rust/pull/752), [#753](https://github.com/oxidezap/whatsapp-rust/pull/753)), index-mode ltHash for SET plus REMOVE on the same index ([#829](https://github.com/oxidezap/whatsapp-rust/pull/829)), and key recovery across companion devices ([#1052](https://github.com/oxidezap/whatsapp-rust/pull/1052)). -- **Signal durability** ([#1027](https://github.com/oxidezap/whatsapp-rust/pull/1027), [#1041](https://github.com/oxidezap/whatsapp-rust/pull/1041) to [#1045](https://github.com/oxidezap/whatsapp-rust/pull/1045)): sender-key advances gated before the wire, retry advances persisted before the wire, durability gates retained through deletes, sender-key mutations serialized, and cancelled session checkouts recovered. A deterministic chaos suite covers it ([#1049](https://github.com/oxidezap/whatsapp-rust/pull/1049)). -- **A blind WA Web parity sweep** ([#965](https://github.com/oxidezap/whatsapp-rust/pull/965)) raised the offline-drain snapshot from 200 to 400, lowered the Signal forward-jump cap from 25000 to 2000, made the reconnect backoff reset only after a stable connection of 30 s or more (with explicit penalties surviving the reset), and made `` force-close the socket. -- Top-level `` stanzas are handled and acked ([#1101](https://github.com/oxidezap/whatsapp-rust/pull/1101)). An unacked one was recycling the stream. -- **Pair-code lifetime** ([#1163](https://github.com/oxidezap/whatsapp-rust/pull/1163)). A phone-number link no longer dies with the QR rotation. The stage-1 and stage-2 crypto was re-audited and cleared first. The `companion_finish` answer is now read instead of assumed ([#1198](https://github.com/oxidezap/whatsapp-rust/pull/1198)), and `link_code_pairing_nonce` was corrected ([#976](https://github.com/oxidezap/whatsapp-rust/pull/976), [#979](https://github.com/oxidezap/whatsapp-rust/pull/979)). -- **JID correctness** ([#1178](https://github.com/oxidezap/whatsapp-rust/pull/1178), [#1182](https://github.com/oxidezap/whatsapp-rust/pull/1182), [#1184](https://github.com/oxidezap/whatsapp-rust/pull/1184)). The AD-JID domain byte was being kept as an `agent`, so encode followed by decode was not idempotent. Agent identity, the AD form and device dedup now agree, and interop JIDs encode with the token that carries their integrator. -- LID and PN: DM wire addressing is gated on the account's 1:1 migration state ([#943](https://github.com/oxidezap/whatsapp-rust/pull/943)), the write policy is source-aware and matches `createLidPnMappings` ([#1011](https://github.com/oxidezap/whatsapp-rust/pull/1011)), and mappings are learned from `phoneNumberToLidMappings` ([#1010](https://github.com/oxidezap/whatsapp-rust/pull/1010)) and from embedder-supplied sources ([#1013](https://github.com/oxidezap/whatsapp-rust/pull/1013)). -- Retry and SKDM: recoverable group and 1:1 decrypt failures retry instead of NACK ([#986](https://github.com/oxidezap/whatsapp-rust/pull/986), [#987](https://github.com/oxidezap/whatsapp-rust/pull/987)), the per-group resend rate is bounded to avoid `AccountLocked` ([#871](https://github.com/oxidezap/whatsapp-rust/pull/871)), SKDM redistribution is gated on the primary device ([#872](https://github.com/oxidezap/whatsapp-rust/pull/872)), own devices are never memoized in the sender-key map ([#999](https://github.com/oxidezap/whatsapp-rust/pull/999)), per-device sessions are locked across the SKDM fan-out ([#990](https://github.com/oxidezap/whatsapp-rust/pull/990)), and the inbound sender-key chain is serialized ([#992](https://github.com/oxidezap/whatsapp-rust/pull/992)). -- Hardening: node-decode recursion depth is bounded against hostile frames ([#994](https://github.com/oxidezap/whatsapp-rust/pull/994)), nothing is sent after a transport failure so the nonce cannot be reused ([#1117](https://github.com/oxidezap/whatsapp-rust/pull/1117)), the keepalive watchdog is anchored to first send ([#995](https://github.com/oxidezap/whatsapp-rust/pull/995)), noise frame-counter exhaustion is an error rather than a wrap ([#782](https://github.com/oxidezap/whatsapp-rust/pull/782)), the WARP MI tag is authenticated before recv ROC state is folded ([#998](https://github.com/oxidezap/whatsapp-rust/pull/998)), `download_to_writer` leaves only verified media ([#1197](https://github.com/oxidezap/whatsapp-rust/pull/1197)), and self-only protocol messages are honored only from our own account ([#646](https://github.com/oxidezap/whatsapp-rust/pull/646)). -- Connection lifecycle: the failure stanza is no longer dropped on logout, ban or outdated ([#1165](https://github.com/oxidezap/whatsapp-rust/pull/1165)), the reconnect backoff races the terminal shutdown ([#1179](https://github.com/oxidezap/whatsapp-rust/pull/1179)), the `Origin` header is sent on the WebSocket upgrade ([#1166](https://github.com/oxidezap/whatsapp-rust/pull/1166)), and SIGTERM shuts the client down cleanly ([#960](https://github.com/oxidezap/whatsapp-rust/pull/960)). - -## Breaking changes - -### Protobuf codegen: prost to buffa - -[#557](https://github.com/oxidezap/whatsapp-rust/pull/557) migrates codegen to [buffa](https://crates.io/crates/buffa) and puts its zero-copy views on the real decode paths. This changes the shape of every generated type: - -| Pattern | Before (prost) | After (buffa) | -|---|---|---| -| Sub-message fields | `Option>` | `MessageField` (`.as_option()` / `.is_set()`) | -| Enum fields | `Option` | `Option` (typed) | -| Enum variants | `CamelCase` | `SCREAMING_SNAKE_CASE` | -| Type names | `AdvSignedDeviceIdentity` | `ADVSignedDeviceIdentity` | -| Decode (owned) | `Type::decode(bytes)` | `Type::decode_from_slice(bytes)` | -| Decode (borrowed) | not available | `TypeView::decode_view(bytes)` | -| Encode | `msg.encode(&mut buf)?` | `msg.encode_to_vec()` | - -`whatsapp.rs` is no longer tracked: `build.rs` generates it into `OUT_DIR` from the committed descriptor, so consumers never need `protoc`. Proto field names are snake_case, which is wire-compatible. Encoding a waproto type from outside `waproto` should go through the pinned `waproto::codec` helpers ([#1091](https://github.com/oxidezap/whatsapp-rust/pull/1091)); a `clippy.toml` guard enforces that in-repo. - -### Errors - -- `anyhow` no longer appears in any public signature ([#893](https://github.com/oxidezap/whatsapp-rust/pull/893)). There is one typed error per feature domain (`SendError`, `GroupError`, `ContactError`, `BlockingError`, `ChatStateError`, `PresenceError`, `CommunityError`, `NewsletterError`, `ProfileError`, `PollError`, `SignalError`, `TcTokenError`, `MediaReuploadError`, `AppStateError`), all built on a shared `ClientError` base with `#[from]` chaining. Every listed method's `Err` variant changed, though `?` into an `anyhow` context still works. -- `ConnectError` and `ConnectStage` ([#1090](https://github.com/oxidezap/whatsapp-rust/pull/1090)): `connect()`, `wait_for_socket()` and `wait_for_connected()` return `Result<(), ConnectError>`, `ClientError::AlreadyConnected` is gone, and `logout()` is now infallible. -- All 46 occurrences of `#[error(transparent)]` became `#[error("{0}")]` ([#1100](https://github.com/oxidezap/whatsapp-rust/pull/1100)). The `Display` output is byte-for-byte identical, but the wrapped typed error stays reachable through `source()`, so a 403 or a 409 can be classified without matching on strings. `ErrorChainExt` writes that walk once. A refused HTTP status is recoverable by type ([#1195](https://github.com/oxidezap/whatsapp-rust/pull/1195)). -- The read loop exits with a typed reason ([#956](https://github.com/oxidezap/whatsapp-rust/pull/956)). Routine server recycles are not errors, and `Disconnected` carries the reason. - -### Public API freeze ahead of 1.0 - -- Event payloads are sealed ([#1000](https://github.com/oxidezap/whatsapp-rust/pull/1000), [#1002](https://github.com/oxidezap/whatsapp-rust/pull/1002) to [#1004](https://github.com/oxidezap/whatsapp-rust/pull/1004)): 25 field-bearing payloads plus the notification and sync families are `#[non_exhaustive]` with a `bon` builder, three inline `Event` variants became sealed newtypes, and four unit markers became empty sealed structs. `EventInterest` moved from `u64` to `u128`. Payloads are now built with `Type::builder().build()`. -- `#[non_exhaustive]` was added to public error enums, public value enums and lib-constructed response structs ([#735](https://github.com/oxidezap/whatsapp-rust/pull/735), [#736](https://github.com/oxidezap/whatsapp-rust/pull/736), [#794](https://github.com/oxidezap/whatsapp-rust/pull/794)), and dead privacy enums were dropped. -- The bot API was overhauled ([#852](https://github.com/oxidezap/whatsapp-rust/pull/852)). `bot.run().await?.await?` becomes `bot.run().await` in the foreground or `bot.spawn()` in the background, returning a `BotHandle` with `client()`, a graceful `shutdown()` and `abort()`. `with_backend(arc)` becomes `with_backend_arc`, `whatsapp_rust::transport::UreqHttpClient` moved to `whatsapp_rust::http`, `bot::Missing` was replaced by named typestate markers, and `BotBuilderError::Other` is gone. Registering two handlers now runs both, where the second used to silently replace the first. There are typed registrars (`on_message`, `on_qr_code`, `on_pair_code`, `on_connected`, `on_logged_out`) and a `prelude`. -- One dependency line is enough ([#852](https://github.com/oxidezap/whatsapp-rust/pull/852), [#954](https://github.com/oxidezap/whatsapp-rust/pull/954)): `wacore`, `wacore_binary` and `waproto` are re-exported wholesale. -- `impl Into` across the public surface ([#834](https://github.com/oxidezap/whatsapp-rust/pull/834)), alloc-aware message-id parameter types ([#775](https://github.com/oxidezap/whatsapp-rust/pull/775)), a `DownloadParams` struct instead of six positional arguments ([#768](https://github.com/oxidezap/whatsapp-rust/pull/768)), `get_participating` returning a `HashMap` ([#767](https://github.com/oxidezap/whatsapp-rust/pull/767)), and tagged `WireEnum` variants serialized as structs ([#1096](https://github.com/oxidezap/whatsapp-rust/pull/1096)). - -### Storage and state - -- `sqlite-storage` persists blobs with prost instead of bincode ([#911](https://github.com/oxidezap/whatsapp-rust/pull/911)). -- messageSecret retention is bounded by policy and by an event-time horizon ([#668](https://github.com/oxidezap/whatsapp-rust/pull/668)). -- The Signal cache flushes on disconnect, which stops SKDM re-fanout ([#670](https://github.com/oxidezap/whatsapp-rust/pull/670)). -- App-state `Mutation` no longer stores duplicate index and value MACs ([#689](https://github.com/oxidezap/whatsapp-rust/pull/689)), and previous-MAC lookups are batched ([#687](https://github.com/oxidezap/whatsapp-rust/pull/687)). -- `FDownload` is a `&BlobDownloadFn` trait object instead of a generic parameter ([#1055](https://github.com/oxidezap/whatsapp-rust/pull/1055)), which affects `wacore` consumers calling the app-state sync entry points directly. -- `wa::Message` boxes its inline content variants ([#866](https://github.com/oxidezap/whatsapp-rust/pull/866)), so pattern matches that bound those fields by value need a deref. -- Message edits are decrypted on receive ([#665](https://github.com/oxidezap/whatsapp-rust/pull/665)), and the inbound commit pipeline batches during the offline drain ([#961](https://github.com/oxidezap/whatsapp-rust/pull/961)), which changes when handlers observe drained messages. - -### Cargo features - -- Removed: `moka-cache` (moka is gone, `PortableCache` is the only backend) and `debug-diagnostics`. -- Added: `voip`, `voip-runtime`, `voip-mlow`, `voip-libopus`, `voip-encoded`, `plugins`, `client-lifecycle`, `tracing`, `tracing-pii`, `metrics`, `legacy-session-interop`, `danger-skip-cert-chain-verify`. -- New workspace members: `storages/chat-store`, `plugins/metrics`, `examples/voip-cli`. -- MSRV is declared and enforced at 1.94 ([#1093](https://github.com/oxidezap/whatsapp-rust/pull/1093)). - -## Tooling and CI - -- CodSpeed benchmarks run on every PR, with deterministic RNG and hashers, a fixed 2-worker runtime and sharding ([#828](https://github.com/oxidezap/whatsapp-rust/pull/828), [#835](https://github.com/oxidezap/whatsapp-rust/pull/835), [#898](https://github.com/oxidezap/whatsapp-rust/pull/898), [#902](https://github.com/oxidezap/whatsapp-rust/pull/902), [#950](https://github.com/oxidezap/whatsapp-rust/pull/950)), plus benchmarks built from the stanza shapes production actually sends ([#1186](https://github.com/oxidezap/whatsapp-rust/pull/1186)). -- Binary size has a per-PR budget gate and a historical series ([#859](https://github.com/oxidezap/whatsapp-rust/pull/859)). -- Supply chain, docs, MSRV and feature-matrix gates were added ([#1093](https://github.com/oxidezap/whatsapp-rust/pull/1093)): workspace lints (`dbg_macro` and `todo` denied), `cargo-deny`, a rustdoc gate and a per-feature build check. -- Miri covers the unsafe decode path ([#1162](https://github.com/oxidezap/whatsapp-rust/pull/1162)), the decoder has roundtrip property tests and an unmarshal fuzz target ([#895](https://github.com/oxidezap/whatsapp-rust/pull/895)), and fixed sleeps in tests were replaced with bounded polling ([#1094](https://github.com/oxidezap/whatsapp-rust/pull/1094)). -- The test suites run through cargo-nextest ([#1209](https://github.com/oxidezap/whatsapp-rust/pull/1209)), and the Docker image is multi-arch, runs unprivileged and is published to GHCR ([#927](https://github.com/oxidezap/whatsapp-rust/pull/927)). - -## Contributors - -Thanks to everyone who shipped code into this release: - -- [@jlucaso1](https://github.com/jlucaso1): VoIP (voice, video, group calls, the MLow codec), the buffa migration, the error and event API freeze, the plugin host, chat-store, tracing and metrics, and most of the performance, WA Web parity and durability work. -- [@Salientekill](https://github.com/Salientekill): admin-revoke fan-out fix, prekey re-upload after re-pair, `groupStatusV2` unwrapping, participant phone-number backfill, `edit_message_with_stanza_id`. -- [@blaueeiner](https://github.com/blaueeiner): PDO placeholder-resend skips for view-once, PN-LID mappings from history sync, public `add_lid_pn_mapping`, pushname matching, app-state collection reservation. -- [@Bot-Dev-RPA](https://github.com/Bot-Dev-RPA): removal of the obsolete device-registry cleanup task, VoIP relay port racing, sibling-device call handling. -- [@zdanysfa](https://github.com/zdanysfa): `` recipient preservation and softened unknown `stream:error`, incoming peer message-edit decryption. -- [@arsa0x](https://github.com/arsa0x): `ContextInfo` in media options. -- [@JeanCapixaba](https://github.com/JeanCapixaba): the observe-only `ServerAck` event. -- [@oonid](https://github.com/oonid): own-device LID namespace alignment for LID-addressed DMs. -- [@alexandme](https://github.com/alexandme): Rust 1.93 build fix for the pushname arm. -- [@dependabot](https://github.com/apps/dependabot): kept dependencies current across the release window. - -## New contributors - -- @alexandme made their first contribution in [#632](https://github.com/oxidezap/whatsapp-rust/pull/632) -- @zdanysfa made their first contribution in [#633](https://github.com/oxidezap/whatsapp-rust/pull/633) -- @oonid made their first contribution in [#636](https://github.com/oxidezap/whatsapp-rust/pull/636) -- @arsa0x made their first contribution in [#931](https://github.com/oxidezap/whatsapp-rust/pull/931) -- @blaueeiner made their first contribution in [#934](https://github.com/oxidezap/whatsapp-rust/pull/934) -- @JeanCapixaba made their first contribution in [#989](https://github.com/oxidezap/whatsapp-rust/pull/989) -- @Bot-Dev-RPA made their first contribution in [#1070](https://github.com/oxidezap/whatsapp-rust/pull/1070) - ---- - -**Full changelog**: [v0.6.0...v0.7.0](https://github.com/oxidezap/whatsapp-rust/compare/v0.6.0...v0.7.0), 488 PRs across VoIP, the protobuf migration, the pre-1.0 API work, performance, WA Web parity and reliability. - -
-All PRs in this release (auto-generated, full attribution) - -* feat!: migrate from prost to buffa for protobuf codegen by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/557 -* fix(send): admin revoke not applied on recipient devices by @Salientekill in https://github.com/oxidezap/whatsapp-rust/pull/621 -* audit: WA Web protocol compliance fixes by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/623 -* audit (round 2): more WA Web protocol compliance fixes by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/624 -* fix(groups): correct change_number stanza shape by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/625 -* feat(groups): capture display_name on participant info by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/626 -* fix: align inbound parsing with WA Web for receipts and messages by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/627 -* fix(send): emit correct / stanza for native-flow buttons by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/628 -* fix(offline): drive WA Web pull-batch loop for offline backlog by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/629 -* fix: zombie connection after stream-error 500 (ClientPayload + receipt compliance) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/630 -* fix(message): nack unrecoverable decrypt errors instead of silent drop by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/631 -* fix(wacore): drop if-let guard so the pushname arm builds on Rust 1.93 by @alexandme in https://github.com/oxidezap/whatsapp-rust/pull/632 -* fix(client): preserve recipient in , soften unknown stream:error by @zdanysfa in https://github.com/oxidezap/whatsapp-rust/pull/633 -* fix: self-DM stops working, WA Web compliance for fanout and BadMac by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/634 -* fix: self-DM/sibling decryption deadlock (retry shape + session recovery + peer pkmsg identity) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/635 -* fix(send): align own devices to LID namespace for LID-addressed DMs by @oonid in https://github.com/oxidezap/whatsapp-rust/pull/636 -* fix(prekeys): re-upload after re-pair to heal stale server bundle by @Salientekill in https://github.com/oxidezap/whatsapp-rust/pull/641 -* fix(polls): align poll vote encryption/decryption to conversation addressing by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/642 -* feat(history-sync): server-error receipt to request blob re-upload by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/643 -* feat(stickers): fetch first-party sticker pack data from the CDN by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/644 -* feat(secret-encrypted): decrypt poll-edit / poll-add-option / event-edit envelopes by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/645 -* fix(security): only honor self-only protocol messages from our own account by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/646 -* fix(offline): drain offline queue by acking duplicate and undecryptable messages by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/647 -* fix(offline): transport-ack stanzas with only unrecognized enc types by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/648 -* fix(receipt): preserve sender device in delivery receipt `to` by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/649 -* feat(msmsg): decrypt Meta AI / fbid bot replies (``) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/650 -* fix(runtime): introduce Spawnable trait for WASM compatibility by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/651 -* fix(send): fail DM send when every per-device encrypt fails by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/652 -* fix(messages): pad to uniform 1..=16 bytes matching WA Web by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/653 -* fix(send): classify poll-add-option as poll and album as text by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/654 -* fix(send): hide decrypt-fail for conditional-reveal and poll-add-option by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/655 -* fix(send): don't hide decrypt-fail on SenderRevoke by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/656 -* fix(send): serialize the group sender-key chain per (group, sender) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/657 -* perf(retry): replace session-recreate Mutex with a TTL cache, atomic per-peer check+stamp by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/658 -* fix(offline): clear self-fanout with sender receipt, not a bare ack by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/659 -* perf(appstate): drop per-operand allocations in LTHash fold and update_hash by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/660 -* perf(jid): add consuming into_non_ad for owned send-path JIDs by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/661 -* perf(send): build encrypt-task Signal address from a borrow, not a Jid clone by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/662 -* feat(transport): surface the disconnect reason in logs by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/663 -* fix(message): ack SKDM-only session decrypts by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/664 -* fix(message)!: decrypt secret encrypted edits on receive by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/665 -* fix(send): set peer PDO push priority attrs by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/666 -* fix(message): decrypt incoming peer message edits by @zdanysfa in https://github.com/oxidezap/whatsapp-rust/pull/667 -* feat(msg-secret)!: bound messageSecret retention by policy and event-time horizon by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/668 -* perf(history-sync): free LazyHistorySync raw bytes after a successful decode by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/669 -* fix(connection)!: flush Signal cache on disconnect to stop SKDM re-fanout by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/670 -* ci(wasm): guard whatsapp-rust wasm32 build and fix two #668 regressions by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/671 -* perf: implement streaming decompression for history sync processing by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/672 -* perf(message): cut per-message allocation churn on the hot path by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/673 -* perf(send): Arc immutable device fields + recent-message bytes by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/674 -* perf(send): box the phash-mismatch cold path out of the spawned future by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/675 -* perf(events): typed event subscription to skip boxing unwanted events by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/676 -* perf(lid-pn): skip PN->LID session migration for peers with no PN state by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/677 -* fix(send): correct group phash and mark full SKDM target set (WA Web parity) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/678 -* feat: WA Web phash parity — usync device_hash (#3), group-metadata phash (#7), bcl hash validation (#6) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/679 -* perf(usync): batch LID-PN re-learn from device response off the cold group-send path by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/680 -* perf(device-registry): borrow lookup keys in get_devices_from_registry (hot group-send path) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/681 -* perf(device-registry): build lookup keys with CompactString (inline, no heap) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/682 -* perf(zlib): pool streaming InflateReader state across history-sync blobs by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/683 -* feat(upload): constant-memory streaming upload + media streaming sidecar by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/684 -* feat(upload): add encrypted_len() and UploadSource for Bytes by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/685 -* perf(history-sync): single-byte fast-path for read_varint by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/686 -* perf(appstate)!: batch previous-MAC lookups (N+1 to 1 query) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/687 -* perf(appstate): parse sync response once (+ dedup external-blob download) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/688 -* perf(appstate)!: drop duplicate index/value MAC storage from Mutation by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/689 -* perf!: cut clones/allocs in LID resolution, single-device send, history-sync by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/690 -* perf: cut copies in PBKDF2, noise large-frame send, history-sync secret records by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/691 -* fix(send): unwrap groupStatusV2 + align unwrap_message with WA Web (not text) by @Salientekill in https://github.com/oxidezap/whatsapp-rust/pull/692 -* fix(send): classify payment-family stanzas as text + add stanza-type override by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/693 -* fix(client): use portable_atomic::AtomicU64 in offline_resume by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/694 -* feat(prekeys): make pre-key upload batch size configurable via the builder by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/695 -* fix(send): hoist messageContextInfo to outer in DeviceSentMessage (WA Web parity) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/696 -* perf(send): cut throwaway allocations in DM phash and LID conversion by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/697 -* fix(message): emit quote remoteJid only for cross-chat quotes by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/698 -* chore(deps): bump yoke from 0.8.2 to 0.8.3 by @dependabot[bot] in https://github.com/oxidezap/whatsapp-rust/pull/699 -* chore(deps): bump log from 0.4.30 to 0.4.31 by @dependabot[bot] in https://github.com/oxidezap/whatsapp-rust/pull/700 -* perf(appstate): replace O(n²) in-patch overwrite scan with an O(1) map by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/701 -* fix(retry): send raw 32-byte prekey/signed-prekey values in retry receipts by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/702 -* perf(device-registry): cache Arc to avoid deep clone on warm hits by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/703 -* fix(time): stop wall-clock default from panicking on wasm32 by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/704 -* fix(retry): match WA Web's bot gate so bot DM retry receipts aren't dropped by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/705 -* fix(prekeys): force the upload on prekey-low instead of re-querying the server count by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/706 -* fix(blocking): resolve LID/PN before is_blocked compares, fixing PN-query false negatives by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/707 -* fix(receipt): downgrade delivery ack to "sent" on lid feature-incapable error by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/708 -* fix(retry): allocate retry-receipt prekey from the monotonic counter, not random by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/709 -* perf(groups): cache Arc to avoid deep-cloning group metadata on warm sends by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/710 -* fix(send): emit mediatype for interactive/list/order/product/native-flow sends by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/711 -* perf(decrypt): trim per-SKDM allocations in the group fan-in path by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/712 -* perf(signal): cache Arc to avoid deep-cloning the message-key backlog by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/713 -* perf(store): batch session/identity/sender-key flush into one transaction per category by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/714 -* feat(labels): add chat label create/delete/associate API with inbound sync by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/715 -* refactor(appstate): drive syncd actions from a generated schema registry + public generic action API by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/716 -* feat(signal): react to locally-detected peer identity changes by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/717 -* perf(identity): gate identity-change reset behind had-prior-identity, like WA Web by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/718 -* perf(events): snapshot handlers behind an Arc to drop the per-event Vec clone by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/719 -* feat(reaction): add Client::send_reaction and MessageContext::react for DM/group by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/720 -* perf(signal-cache): amortize eviction and stop scanning on reads by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/721 -* fix(prekey): remove the consumed one-time prekey atomically with the session flush by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/722 -* fix(edit): send message edits as a top-level protocolMessage, matching WA Web by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/723 -* fix: @call JID decode, receipt participant_pn, profile-pic empty→remove, has_device churn by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/724 -* feat(newsletter): plaintext channel edit/revoke + reject newsletter on the E2E send path by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/725 -* chore(proto): sync to WhatsApp Web 2.3000.1040878135 by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/726 -* feat(mex): typed mex operations from the whatspec IR, drop hand-maintained mex_ids by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/728 -* feat(abprops): vendor typed A/B-props registry, drop hand-maintained config_codes by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/729 -* fix(send): address LID-mapped DM by LID end to end so the server stops 400-rejecting it by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/731 -* refactor: split message/client/send monoliths into per-theme modules by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/732 -* feat(observability): optional tracing instrumentation (off by default, OTel-ready) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/733 -* feat(metrics): optional metrics layer (off by default, Prometheus/OTLP-ready) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/734 -* feat(api): mark public error enums #[non_exhaustive] for 1.0 forward-compat by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/735 -* feat(api): #[non_exhaustive] on public value enums + drop dead privacy enums by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/736 -* feat(receipt): add mark_as_played for voice/video notes by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/737 -* feat(send): add forward_message + MessageExt::prepare_for_forward by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/738 -* feat(groups): high-level set/remove group profile picture by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/739 -* feat(send): add keep_message (keep-in-chat for everyone) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/740 -* feat(usync): surface verified business name (verified_name) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/741 -* feat(chat-actions): add save_contact (outgoing contact-name sync) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/742 -* feat(send): emit for view-once sends by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/743 -* feat(receipt): read-receipt status parity (read-self, context, peer_participant_pn) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/744 -* feat(client): set_chat_disappearing_timer for 1:1 disappearing messages by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/745 -* fix(binary): coerce "1"/"0" wire booleans in attr parsers by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/746 -* feat(iq): keep server error_type + backoff on IQ error responses by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/747 -* fix(appstate): require snapshot MAC when validating (no silent skip) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/748 -* fix(polls): match WA Web poll wire shape (pollContentType=TEXT, no vote metadata) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/749 -* perf(appstate): return Arc from key-lookup callback (no per-mutation 160B clone) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/750 -* fix(appstate): repair main build broken by #748/#750 merge by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/751 -* feat(appstate): reject duplicate index within a patch (anti-tampering parity) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/752 -* feat(appstate): guard snapshot apply against version rollback by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/753 -* feat(polls): support quiz polls on send (create_quiz) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/754 -* feat(chat): support clearChat app-state action (incoming + outgoing) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/755 -* fix(wasm): make cache backend target-aware so moka-cache defaults don't break wasm32 by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/756 -* feat(newsletter): mute/unmute channel notifications by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/757 -* feat(events): create and respond (RSVP) API by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/758 -* fix(appstate): don't swallow external-blob download failures by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/759 -* feat(chat): support userStatusMute app-state action (incoming + outgoing) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/760 -* fix(groups): keep persisted group metadata in sync on membership change by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/761 -* feat(edit): support message-secret encrypted edits (secret_encrypted_message) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/762 -* perf(portable-cache): O(log n) remove_key instead of O(n) insertion scan by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/763 -* feat(media): high-level media message builders from UploadResponse by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/764 -* perf(cache): raise device_registry_cache default capacity 1000 -> 5000 by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/765 -* fix(appstate): clear stale mutation MACs on snapshot re-sync by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/766 -* refactor(groups): get_participating returns HashMap by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/767 -* refactor(download): take DownloadParams struct instead of 6 positional args by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/768 -* fix(appstate): validate index MAC even when the decrypted index field is absent by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/769 -* refactor(download): remove dead, full-buffering download_to_file by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/770 -* refactor(polls): group enc_payload + enc_iv into PollVoteCiphertext by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/771 -* feat(prekeys): validate companion device-identity (ADV) on fetched bundles by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/772 -* fix(appstate): re-sync unsynced collection that gets patches without a snapshot by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/773 -* perf(upload): slice ciphertext zero-copy instead of copying per attempt by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/774 -* refactor(api): consistent, alloc-aware message-id param types by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/775 -* feat(usync): surface device list from get_user_info by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/776 -* fix(receipt): chunk read/played receipts into 256 ids per stanza by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/777 -* refactor(groups): set_description prev takes Option<&str> by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/778 -* perf(retry): peek the cached message on resend instead of take + re-add by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/779 -* feat(receipt): expose the 'offline' attr on the Receipt event by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/780 -* feat(send): emit member_label meta attrs (appdata + tag_reason) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/781 -* fix(noise): error on frame counter exhaustion instead of wrapping by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/782 -* fix(iq): pong server pings with an absent type, not only type=get by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/783 -* refactor(framing): drop dead, would-desync oversize check in decode_frame by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/784 -* fix(conn): log benign server recycles quietly without hiding real errors by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/785 -* perf(appstate): move snapshot mutations into the accumulator instead of extend by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/786 -* perf(send): encode DM content once, splice into recipient + DSM plaintexts [PoC] by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/787 -* perf(send): splice reporting context onto plaintexts, drop per-send Message clone by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/788 -* fix(adv): fall back to stored account identity for ADV account_signature_key by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/790 -* chore(session): remove dead SessionManager (zero production callers) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/791 -* perf(receive): make custom enc handlers an immutable set-once snapshot by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/792 -* fix(wasm): relax EncHandler Send+Sync via MaybeSendSync, gate async_trait by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/793 -* api: mark lib-constructed response/result structs #[non_exhaustive] for 1.0 by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/794 -* fix(wasm): relax networking traits Send+Sync via MaybeSendSync by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/795 -* refactor(handlers): split notification.rs god-file by domain by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/796 -* fix(device-list): always keep the primary device after a raw_id mismatch patch by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/797 -* fix(retry): recover from unknown-device retries that carry a key bundle by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/798 -* fix(usync): refetch an empty device record instead of trusting it by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/799 -* chore(send): log benign prekey-fetch skips at debug instead of warn by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/800 -* fix(device-list): never drop the primary on a device-remove patch by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/801 -* fix(retry): resync the device list on a retry from an unknown device by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/802 -* perf(receive): adopt inbound payloads zero-copy in FrameDecoder by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/803 -* perf(receive): resolve the noise socket once per read loop, drop ack re-encode copy by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/804 -* perf(send): establish sessions before taking the sender-key chain lock by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/807 -* perf(store): cache the device snapshot as Arc<Device> by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/808 -* perf(signal-cache): share cached sessions via Arc, peek without deep clone by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/809 -* perf(group): derive the PN-to-LID reverse index, stop persisting it by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/810 -* perf(lid-pn): share identifier strings between cache keys and entries by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/811 -* chore(deps): bump metrics-exporter-prometheus from 0.16.2 to 0.18.3 by @dependabot[bot] in https://github.com/oxidezap/whatsapp-rust/pull/812 -* chore(deps): bump chrono from 0.4.44 to 0.4.45 by @dependabot[bot] in https://github.com/oxidezap/whatsapp-rust/pull/813 -* chore(deps): bump http from 1.4.1 to 1.4.2 by @dependabot[bot] in https://github.com/oxidezap/whatsapp-rust/pull/814 -* chore(deps): bump prost from 0.14.3 to 0.14.4 by @dependabot[bot] in https://github.com/oxidezap/whatsapp-rust/pull/815 -* chore(deps): bump diesel from 2.3.9 to 2.3.10 by @dependabot[bot] in https://github.com/oxidezap/whatsapp-rust/pull/816 -* fix(protocol): tighten error handling gaps by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/817 -* perf(appstate): move snapshot and patches into the blocking handoff instead of deep-cloning by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/818 -* perf(binary): store small attribute lists inline, dropping the per-node heap allocation by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/819 -* perf(receipt): aggregate offline delivery receipts per chat like WA Web by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/820 -* perf(appstate): batch the previous-MAC prefetch in build_patch like the inbound path by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/821 -* perf(send): hash the participant list from one arena instead of a String per device by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/822 -* perf(send): probe the LID-PN map in one direction on warm device lookups by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/823 -* perf(send): memoize the per-group device list behind a topology generation by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/824 -* fix(contacts): use fn items for LID mapping extractors so boxed futures compile by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/826 -* perf(client): borrow the ack id instead of allocating a String per stanza by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/827 -* Add CodSpeed performance measurement setup by @codspeed-hq[bot] in https://github.com/oxidezap/whatsapp-rust/pull/828 -* fix(appstate): match WA Web index-mode ltHash for SET+REMOVE on the same index by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/829 -* feat(messages): encrypted CAG reactions and channel comments, both directions by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/830 -* perf(messages): write-behind buffer for messageSecret persistence by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/831 -* fix(storage): serialize msg_secret reads through the db semaphore by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/832 -* feat(prekeys): track the first un-uploaded prekey and reuse the window like WA Web by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/833 -* feat(api): adopt the impl Into convention across the public surface by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/834 -* ci(codspeed): run simulation and memory instruments in a single job by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/835 -* perf(history-sync): gate prost decode behind a secret-presence scan, share chat ids, schema-pinned wire tags by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/836 -* bench: measure the operation, not the harness by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/837 -* perf(libsignal): memoize the sender signing key with a pre-warmed XEdDSA cache by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/838 -* perf(libsignal): cache the verify-side Edwards derivations per sender key by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/839 -* perf(send): memoize the group phash on the device-list memo entry by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/840 -* fix(pdo): request a placeholder resend at most once per message by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/841 -* perf(waproto): pin the Message codec to one instantiation via non-generic helpers by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/842 -* perf(api): box the cold entry-point futures so consumers stop re-codegening the graphs by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/843 -* perf(retry): fuse the retry count and reason into one cache entry by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/844 -* perf(docker): enable -Zshare-generics in the image build by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/845 -* perf(wacore): drop the proto PartialEq anchor from the skdm-only check by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/846 -* perf(appstate): pre-key the ltHash HKDF extract once by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/847 -* perf(binary): emit the Jid display as one write_str by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/848 -* bench: borrow inputs in the benches that only read them by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/849 -* perf(noise): pre-key the transport AES-GCM once per connection by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/850 -* deps: upgrade curve25519-dalek and x25519-dalek to the 5.0/3.0 release candidates by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/851 -* feat!: overhaul the public bot API ahead of 1.0 by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/852 -* perf(history-sync)!: store the compressed payload and expose a streaming reader by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/853 -* chore(reporting-token): draw the message secret from the thread RNG by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/855 -* bench: cover the receive path — plaintext decode and appstate index-MAC dedup by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/856 -* bench: cover four inbound/group hot paths for CodSpeed baselines by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/858 -* ci: track binary size with a per-PR budget gate and historical series by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/859 -* perf: drop moka, use PortableCache as the sole in-process cache backend by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/860 -* perf(send): de-monomorphize Signal encrypt fan-out to dyn dispatch by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/861 -* perf(iq): de-monomorphize send_and_wait_iq via boxed send future by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/862 -* perf(signal): pre-key the zero-salt HKDF extract for message-key derivation by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/863 -* perf(reporting-token): pre-key the zero-salt HKDF extract for token-key derivation by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/864 -* refactor(appstate): scan instead of HashSet for index-mac dedup in the patch path by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/865 -* perf(proto)!: shrink wa::Message ~75% by boxing inline content variants by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/866 -* perf(appstate): index-sort dedup for large patches, O(n²) scan stays for small by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/868 -* perf: drop a ~67 KiB duplicate prost decode tree + hoist a per-message traversal by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/869 -* fix(retry): bound outbound resend rate per group to prevent AccountLocked by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/871 -* fix(send): gate SKDM redistribution on the primary device (WA Web parity) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/872 -* perf(binary): length-bucketed token lookup (tiny_map) on the encode hot path by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/873 -* perf(sqlite): skip the per-checkout SELECT 1 liveness probe by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/874 -* feat(example): add 🦀send chat command by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/875 -* perf(conn): reuse the shutdown listener across the read loop by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/876 -* perf(signal): skip the rollback clone for in-order decrypts by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/877 -* test(bench): add an in-order DM decrypt benchmark by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/878 -* perf(signal): reuse the encrypt buffer instead of take + realloc by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/879 -* perf(signal): share the sender-key message backlog behind an Arc by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/881 -* feat(retry): WA Web log-level parity and retry-flow observability counters by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/887 -* perf(signal): flush the signal cache without holding the device read-lock by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/888 -* fix(retry): dedup registration-id parsing and reject oversized payloads by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/889 -* chore(deps): update Cargo.lock to latest compatible versions by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/890 -* chore: bump nightly toolchain to nightly-2026-06-16 by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/891 -* refactor: move the demo binary to examples/ and make env_logger a dev-dependency by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/892 -* refactor(error)!: replace anyhow in public APIs with per-domain typed errors by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/893 -* ci: add an all-features build job and fix the --nocapture flag by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/894 -* test(binary): add decoder roundtrip property tests and unmarshal fuzz target by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/895 -* refactor(send): extract tc-token lifecycle and pin/revoke actions into submodules by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/896 -* perf(send): resolve group SKDM warm gate with one inner-map lookup per device by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/897 -* bench(integration): port integration benchmarks to CodSpeed (simulation + memory) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/898 -* chore(deps): prune the aes-gcm dev-dep and tidy dependency declarations by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/899 -* perf(prekeys): avoid full record decode on the pre-key upload path by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/900 -* perf(prekeys): stream prekey generation to cut the connect-time peak by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/901 -* bench(integration): cut CodSpeed variance with a fixed 2-worker runtime + deterministic allocator by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/902 -* perf(send): skip the unused DeviceSentMessage plaintext on companion-less DMs by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/903 -* perf(send): share one message encode between the reporting token and DM plaintext by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/904 -* perf(send): share one message encode between the group reporting token and skmsg plaintext by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/905 -* ci(codspeed): drop the memory instrument from the integration benches by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/906 -* feat(groups): backfill participant phone_number from LID-PN mapping by @Salientekill in https://github.com/oxidezap/whatsapp-rust/pull/909 -* chore(deps): cargo update by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/910 -* refactor(sqlite-storage)!: replace bincode with prost for persisted blobs by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/911 -* perf(size): size-optimize off-hot-path crates via per-package opt-level by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/912 -* fix(atomics): use portable_atomic for 64-bit atomics + lint against std by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/913 -* VoIP 1:1 calling by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/918 -* feat: opt-in inbound durability hook (at-least-once delivery) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/920 -* chore(deps): update workspace dependencies to latest by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/921 -* fix(build): isolate voip example so the demo build drops cpal/alsa-sys by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/922 -* fix(status): omit addressing_mode on status@broadcast send by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/925 -* perf(sqlite-storage): cut per-session memory & threads, with configurable tuning by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/926 -* ci(docker): portable multi-arch image, unprivileged runtime, GHCR publish by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/927 -* feat(passkey): SHORTCAKE_PASSKEY companion linking by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/928 -* chore(deps): bump aes-gcm from 0.11.0-rc.4 to 0.11.0 by @dependabot[bot] in https://github.com/oxidezap/whatsapp-rust/pull/929 -* feat(media): support ContextInfo in media options by @arsa0x in https://github.com/oxidezap/whatsapp-rust/pull/931 -* perf(binary): validate wire strings with smoothutf8 by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/932 -* perf(binary): inflate into uninitialized buffer, drop the zero-init memset by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/933 -* fix(receive): skip PDO placeholder-resend for view-once, ack instead by @blaueeiner in https://github.com/oxidezap/whatsapp-rust/pull/934 -* fix(receive): skip PDO placeholder-resend for bot and hosted unavailable too by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/935 -* perf(send): trim group-send warm-path CPU and cold fan-out allocations by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/936 -* perf(send): single-flight cold group sender-key distribution by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/937 -* perf(send): keep send_message's future pointer-sized by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/938 -* fix(send): gate DM LID wire addressing on the account's 1:1 migration state by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/943 -* perf(history-sync): size the secret-record accumulator by sampled density by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/945 -* perf(libsignal): evict skipped message keys without shifting the buffer by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/946 -* perf(history-sync): keep heap-error types off the scanner happy path by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/947 -* perf(send): keep public send futures pointer-scale by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/948 -* perf(binary): keep BinaryError construction off the decoder happy path by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/949 -* bench: deterministic rng and hashers; pin and shard the CodSpeed CI by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/950 -* tracing: tag wa.iq / wa.send.message / wa.conn.run spans with account identity by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/951 -* perf: keep droppy error/default construction off per-message happy paths by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/952 -* perf(libsignal): store the sender-key backlog as Copy (iteration, seed) pairs by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/953 -* api: re-export the crates whose types appear in the public API by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/954 -* perf: inline index-MAC keys, reusable conversation decode, single-encode send by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/955 -* fix!: typed read-loop exit — routine server recycles are not errors; Disconnected carries the reason by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/956 -* perf(voip): zerocopy the RTP/STUN fixed-layout parse; borrow STUN attr values by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/957 -* perf(connect): move own-device sync off the pre-active path; drop keepalive loop span by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/958 -* perf(tracing): drop client-lifetime wa.conn.run span by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/959 -* fix: shut down on SIGTERM, not just SIGINT (docker stop timeout) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/960 -* feat(recv)!: batch the inbound commit pipeline during the offline drain by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/961 -* Per-session metrics: wire I/O counters, memory report in bytes, runtime-agnostic task instrumentation by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/962 -* Meter Bot::run's main future through the task instrument by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/963 -* fix(stats): make Client::memory_report() Send by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/964 -* feat: WA Web parity fixes (offline drain, crypto caps, reconnect, groups) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/965 -* fix(tctoken): persist issuance timestamp on IQ success and gate cstoken independently by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/966 -* feat(stats): attribute per-session resources beyond the Client by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/967 -* feat: rotate signed pre-key on a cadence (WA Web RotateKeyJob) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/968 -* feat(tctoken): attach tctoken in usync status/about and spam-report IQs by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/969 -* feat(tctoken): attach and issue tctoken on outgoing VoIP call offers by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/970 -* feat: gate DM read/played receipts on readreceipts privacy by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/971 -* fix(waproto): stop the build script recompiling on every run by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/973 -* fix(appstate): bound pairing key-share wait by the 180s critical deadline by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/974 -* perf: parallelize remaining serial/blocking hot paths (startup, media, send/recv) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/975 -* fix(pair-code): correct link_code_pairing_nonce byte and close WA Web stage-2 gaps by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/976 -* fix(iq): cancellation-safe IQ response waiters (unblocks try_join!) + named fan-out consts by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/978 -* fix(pair-code): canonicalize companion_platform_display OS to a server-safe set by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/979 -* fix(tc-token): atomic newer-wins store; drop tc_token_lock and close the cross-source race by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/980 -* feat(events): opt-in ordered + bounded inbound event delivery by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/981 -* perf(recv): hold the per-sender session lock only around Signal decrypt by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/983 -* feat(retry): opt-in RetryAdmission hook (compliant supersede of #982) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/985 -* fix(recv): retry (not NACK) recoverable group skmsg decrypt failures by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/986 -* fix(recv): retry (not NACK) InvalidSignedPreKeyId on the 1:1 decrypt path by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/987 -* fix(appstate): validate aggregate snapshot/patch MACs for genesis patches by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/988 -* feat(events): observe-only ServerAck event for server stanzas by @JeanCapixaba in https://github.com/oxidezap/whatsapp-rust/pull/989 -* fix(send): lock per-device sessions across the group SKDM fan-out by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/990 -* fix(cache): don't capacity-evict a session lock a task still holds by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/991 -* fix(recv): serialize the group inbound sender-key chain with a lock by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/992 -* fix(binary): bound node-decode recursion depth to reject hostile frames by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/994 -* fix(keepalive): anchor dead-socket watchdog to first send, not last by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/995 -* fix(send): isolate a group device's session-setup failure from the cohort by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/996 -* fix(passkey): don't skip the verification-code UX on a fresh link by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/997 -* fix(voip): authenticate WARP MI tag before folding recv ROC state by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/998 -* fix(send): never memoize own devices in the sender-key map (WA Web parity) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/999 -* refactor(events): model ServerAck.class as Option, document payload stability by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1000 -* chore(proto): bump WhatsApp protocol surface to 2.3000.1042742319 by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1001 -* refactor(events): seal ServerAck with non_exhaustive + bon builder by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1002 -* refactor(events): seal notification, sync event payloads (non_exhaustive + bon builder) by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1003 -* refactor(events): complete the event-payload API freeze by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1004 -* chore(deps): update dependencies to latest versions by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1009 -* feat(history-sync): learn PN-LID mappings from phoneNumberToLidMappings by @blaueeiner in https://github.com/oxidezap/whatsapp-rust/pull/1010 -* feat(lid-pn): source-aware write policy matching WA Web createLidPnMappings by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1011 -* feat(lid-pn): make add_lid_pn_mapping pub for embedder-learned sources by @blaueeiner in https://github.com/oxidezap/whatsapp-rust/pull/1013 -* feat(chat-store): SQLite-backed chat/message history store by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1014 -* perf(message): reduce hot-path allocations by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1015 -* perf(receipt): persistent worker for live delivery receipts by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1016 -* perf(signal): sync fast paths for hot store adapter methods by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1017 -* perf(node): cut ack re-encode, exact stanza sizing, warm session pre-filter by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1018 -* perf(send): dispatch send_message_impl to per-branch boxed futures by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1019 -* perf(binary): inline the exact-marshal string hint cache by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1020 -* perf(group): keep warm sends warm under the own-device SKDM steady state by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1021 -* perf(signal): coalesce receive flushes and persist outbound state pre-wire by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1022 -* VoIP 1:1 video calls by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1024 -* perf(client): trim per-message allocations on the send/receive hot path by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1025 -* perf(signal): lease outbound counters in batches instead of flushing every send by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1026 -* fix(signal): gate the group sender-key advance before the wire by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1027 -* ci: cache target dir and drop incremental to stop full recompiles by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1033 -* chore(deps): update workspace dependencies to latest compatible versions by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1034 -* fix(signal): persist retry advances before wire by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1041 -* fix(signal): retain durability gates through deletes by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1042 -* fix(signal): serialize sender-key mutations by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1043 -* fix(signal): recover cancelled session checkouts by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1044 -* fix(message): preserve active chat lanes by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1045 -* ci: reclaim runner disk before tests by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1047 -* perf(signal): reuse leases across ciphertext APIs by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1048 -* test(signal): add deterministic durability chaos coverage by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1049 -* feat(voip): add encoded audio pipeline and native Opus negotiation by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1050 -* fix(voip): prevent stale audio-to-video upgrades by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1051 -* fix(appstate): recover missing keys across companion devices by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1052 -* feat(proto): upgrade buffa to 0.9 and open SyncdOperation by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1053 -* perf(core): reduce allocation churn across sync and crypto paths by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1054 -* perf(size): -1.47 MiB (-13.3%) stripped binary — build-config levers + control-plane demonomorphization by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1055 -* perf(size): token lookup via static hash table — −29 KiB, faster than hashify on hits and marshal, dep dropped by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1056 -* perf: marshal_exact 3× faster — replay plan-pass hints instead of reclassifying every string by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1057 -* perf(core): reduce history sync and decrypt allocation churn by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1058 -* perf(send): reduce DM allocation and serialization churn by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1059 -* feat(core): expand protocol metadata and shared primitives by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1060 -* feat(plugins): add extensible client architecture by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1061 -* feat(core): expose signal record components and dirty events by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1062 -* feat(core): add typed USync query engine by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1063 -* feat(core): expose typed stanza responses and retries by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1069 -* fix(client): remove obsolete device-registry cleanup task by @Bot-Dev-RPA in https://github.com/oxidezap/whatsapp-rust/pull/1070 -* feat(send): add targeted retransmission controls by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1071 -* feat(signal): add typed legacy session interop by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1072 -* feat(sqlite-storage): add connection-init hook before pragmas and migrations by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1074 -* chore(deps): update all workspace dependencies to latest by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1075 -* fix(contacts): short-circuit user-directed IQs for the system JID by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1080 -* feat(messages): surface business verified name from the message envelope by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1081 -* feat(chat-store): classify business template/buttons/list/interactive messages by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1082 -* fix(chat-store): resolve PN/LID aliases so receipts reach their rows by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1083 -* feat(messaging): add edit_message_with_stanza_id to override the outer stanza id by @Salientekill in https://github.com/oxidezap/whatsapp-rust/pull/1084 -* chore(deps): update workspace dependencies to latest by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1085 -* perf(cache): non-generic single-flight registry + boxed-on-miss init futures by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1086 -* chore: remove dead public API surface from wacore and libsignal by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1087 -* fix(concurrency): cancellation-safe app-state sync dedup and message-secret backpressure by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1088 -* refactor(api): rustdoc for entry types, must_use, root re-exports, and constructor consistency by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1089 -* refactor(errors): typed ConnectError and friends for the public API by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1090 -* feat(waproto): pinned HistorySync encode wrappers in codec by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1091 -* feat(wacore): stable ReceiptType variant-name accessor backing Serialize by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1092 -* ci: workspace lints, cargo-deny, rustdoc gate, MSRV, and feature-matrix checks by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1093 -* test: replace fixed sleeps with bounded polling and add negative coverage for server-controlled parsers by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1094 -* refactor(derive): serialize tagged WireEnum variants as structs by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1096 -* fix(groups): send the previous description id when updating a group description by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1097 -* fix(chat-store): key receipts and contacts by the peer's bare identity by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1099 -* refactor(errors): make the public error surface recoverable without string matching by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1100 -* fix(recv): handle and acknowledge top-level status stanzas by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1101 -* fix(voip): dial the relay endpoint on the web client port by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1102 -* perf(stats): stop dating every wire frame for a field nothing reads by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1103 -* fix(voip): race the relay's advertised and web-client ports for media by @Bot-Dev-RPA in https://github.com/oxidezap/whatsapp-rust/pull/1104 -* perf(send): stamp one message from one clock read by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1106 -* fix(voip): a busy device must not end an outgoing call for its siblings by @Bot-Dev-RPA in https://github.com/oxidezap/whatsapp-rust/pull/1107 -* docs(agents): correct stale agent docs and add a WA Web verification path by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1109 -* ci: skip the mock-server jobs on fork PRs instead of failing their template by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1110 -* chore(voip/mlow): make the decoder cross-check vectors regenerable by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1112 -* perf: cut two per-message allocations on the pingpong hot path by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1114 -* perf: remove four per-message tasks and buffers from the send/receive round trip by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1116 -* fix(socket): stop sending after a transport failure instead of reusing the nonce by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1117 -* perf(send): memoize the DM device fan-out per recipient by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1118 -* perf(socket): coalesce queued noise frames into one transport write by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1119 -* perf(client): send queued acks and receipts as one burst by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1120 -* perf(socket): seal each noise frame where it lands in the batch buffer by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1121 -* perf: cut a dozen per-message allocations from the DM round trip by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1122 -* perf: answer three store calls without boxing a ready future by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1124 -* chat-store: record locally-originated amendments by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1126 -* fix(chat-store): reconcile outgoing timestamps from server acks by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1127 -* fix(voip): send callee answer signaling by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1128 -* ci: centralize and pin bartender image by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1129 -* feat(voip): add group calls and call links by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1130 -* perf: five cuts to the per-message allocation count by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1131 -* perf(noise): keep the frame decoder's buffer instead of giving it away by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1136 -* perf: four more cuts to the per-message allocation count by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1137 -* Two follow-ups: the burst's other half, and a DM 406 that went nowhere by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1139 -* fix: six chat-store and send-path issues by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1144 -* fix(signal): stop a DH ratchet stranding the counter lease by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1149 -* perf(storage): let WAL readers run, and stop search from monopolizing them by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1151 -* fix(signal): store pre-key public keys in one encoding by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1152 -* fix(prekeys): act on the device a rejected fetch names by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1153 -* fix(chat-store): keep receipt times for 1:1 chats, per state by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1155 -* fix(appstate): let a diverged collection converge, and stop losing 409'd patches by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1158 -* ci(miri): check the unsafe decode path under the interpreter by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1162 -* fix(pair-code): keep a phone-number link alive past the QR rotation by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1163 -* fix(device-props): stop asking every pairing for a full history sync by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1164 -* fix(conn): stop dropping the failure stanza on logout, ban and outdated by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1165 -* fix(transport): send the Origin header on the WebSocket upgrade by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1166 -* perf(jid): stop the scan at the `@` and resolve the server once by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1171 -* perf(jid): parse the device and agent fields with a decimal-only reader by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1172 -* perf(jid): stop re-validating UTF-8 the writer just assembled by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1173 -* perf(node): iterate children by tag without going through Flatten by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1174 -* perf(attrs): read required attributes with a single lookup by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1175 -* perf(jid): compare structured JID attributes without rendering them by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1176 -* fix(jid): stop keeping the AD-JID domain byte as an agent by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1178 -* fix(lifecycle): race the reconnect backoff against the terminal shutdown by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1179 -* fix(jid): make agent identity, the AD form, and device dedup agree by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1182 -* perf(jid): skip the agent normalisation when the raw agents already match by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1183 -* fix(jid): encode interop JIDs with the token that carries their integrator by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1184 -* bench(send): measure the shapes production actually sends by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1186 -* perf(libsignal): stop revalidating UTF-8 the address buffer just wrote by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1187 -* fix(media): let a CDN's non-2xx status reach the download classifier by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1188 -* perf(messages): draw the pad length from the thread RNG by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1189 -* feat(pair-code): report a refused pair-code request to the consumer by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1191 -* perf(secret-enc): draw the addon IV from the thread RNG by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1192 -* feat(download): allow media download without a connected session by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1194 -* feat(errors): make a refused HTTP status recoverable by type by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1195 -* fix(download): guarantee download_to_writer leaves only verified media by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1197 -* fix(pair-code): read the companion_finish answer instead of assuming it by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1198 -* chore(deps): update workspace dependencies to latest by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1199 -* refactor(deps): drop redundant crates and unused dependency features by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1200 -* build(deps): drop three dependency features nothing activates by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1201 -* fix(iq): report detail the IQ parser drops, once per process by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1202 -* fix(history-sync): match our own pushname entry by JID user by @blaueeiner in https://github.com/oxidezap/whatsapp-rust/pull/1203 -* fix(appstate): reserve the collection for an AppStateSync task by @blaueeiner in https://github.com/oxidezap/whatsapp-rust/pull/1205 -* docs(appstate): correct the batched-sync iteration cap comment by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1206 -* fix(appstate): report what a batched sync actually achieved by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1207 -* fix(appstate): unbreak the bootstrap gate and read the right lifecycle signals by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1208 -* ci(test): run the suites through cargo-nextest by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1209 -* fix(libsignal): keep skipped message-key seeds projectable by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1210 -* feat(libsignal): let a consumer opt out of counter leasing by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1211 -* bench(libsignal): measure what the sender-key XEdDSA memo is worth by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1212 -* feat(libsignal): let a caller prewarm the sender-key derivation memo by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1213 -* perf(binary): unpack packed values through a byte-pair table by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1214 -* perf(binary): validate packed output with the wire path's validator by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1215 -* perf(binary): drop the per-node box around NodeRef content by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1216 -* feat(libsignal): let a consumer provide the X25519 agreement by @jlucaso1 in https://github.com/oxidezap/whatsapp-rust/pull/1218 - -
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9ea3731d4..5f24b3128 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -68,12 +68,21 @@ jobs: id: version run: | VERSION=$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)".*/\1/') + # This string becomes a git tag, a crates.io release and an image tag. + # Reject anything that is not a plain semver here, where the failure is + # one line to read, instead of downstream where it is a git error. + if ! printf '%s' "$VERSION" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$'; then + echo "Refusing to release a malformed version: '$VERSION'" >&2 + exit 1 + fi echo "version=$VERSION" >> "$GITHUB_OUTPUT" echo "tag=v$VERSION" >> "$GITHUB_OUTPUT" - name: Create tag + env: + RELEASE_TAG: ${{ steps.version.outputs.tag }} run: | - TAG="${{ steps.version.outputs.tag }}" + TAG="$RELEASE_TAG" if git ls-remote --exit-code --tags origin "refs/tags/$TAG" > /dev/null 2>&1; then echo "Tag $TAG already exists on origin, skipping." else @@ -83,9 +92,10 @@ jobs: - name: Create GitHub Release env: + RELEASE_TAG: ${{ steps.version.outputs.tag }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - TAG="${{ steps.version.outputs.tag }}" + TAG="$RELEASE_TAG" # Hand-written notes win when the release has them: --generate-notes # emits one bullet per PR, which for a release this size is thousands # of lines with no structure. Falls back to the generated list so a From 0063dbb290e95280a7b5cfc573e5b55a0c4a7f2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Lucas?= <55464917+jlucaso1@users.noreply.github.com> Date: Thu, 6 Aug 2026 22:55:06 -0300 Subject: [PATCH 3/5] ci(release): validate the version before anything is published The version guard sat in github-release, which runs after publish. A version this workflow rejects but Cargo accepts would have published the crates first and then failed, leaving crates.io ahead of the repo with no tag, no release and no image, and crates.io publishes cannot be undone. It now runs in a preflight job that publish depends on, and github-release and publish-docker read the version from there instead of re-deriving it, so one job owns the string every later step keys on. The pattern also accepts build metadata. Being stricter than Cargo is the failure mode that matters here; being looser is not, since a version Cargo rejects never gets past publish. --- .github/workflows/release.yml | 57 +++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5f24b3128..8b7223649 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,6 +16,35 @@ concurrency: cancel-in-progress: false jobs: + # Everything downstream is keyed on this version, and publishing to crates.io + # cannot be undone. Read and check it before that point, not after, so a bad + # version costs a failed job instead of published crates with no tag, no + # release and no image. + preflight: + name: Preflight + runs-on: ubuntu-latest + outputs: + version: ${{ steps.version.outputs.version }} + tag: ${{ steps.version.outputs.tag }} + steps: + - uses: actions/checkout@v6 + + - name: Extract and validate version + id: version + run: | + VERSION=$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)".*/\1/') + # Deliberately no stricter than Cargo: a version Cargo accepts must + # not fail here, or the release stops between publish and tag. Forms + # Cargo itself rejects (leading zeros, empty prerelease identifiers) + # never reach the publish step, so they need no guard of their own. + if ! printf '%s' "$VERSION" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$'; then + echo "Refusing to release a malformed version: '$VERSION'" >&2 + exit 1 + fi + echo "Releasing $VERSION" + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + echo "tag=v$VERSION" >> "$GITHUB_OUTPUT" + validate: name: Validate uses: ./.github/workflows/main.yml @@ -27,7 +56,7 @@ jobs: publish: name: Publish to crates.io - needs: [validate, validate-e2e] + needs: [preflight, validate, validate-e2e] runs-on: ubuntu-latest environment: release steps: @@ -55,32 +84,16 @@ jobs: github-release: name: Create GitHub Release - needs: publish + needs: [preflight, publish] runs-on: ubuntu-latest - outputs: - version: ${{ steps.version.outputs.version }} steps: - uses: actions/checkout@v6 with: fetch-depth: 0 - - name: Extract version - id: version - run: | - VERSION=$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)".*/\1/') - # This string becomes a git tag, a crates.io release and an image tag. - # Reject anything that is not a plain semver here, where the failure is - # one line to read, instead of downstream where it is a git error. - if ! printf '%s' "$VERSION" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$'; then - echo "Refusing to release a malformed version: '$VERSION'" >&2 - exit 1 - fi - echo "version=$VERSION" >> "$GITHUB_OUTPUT" - echo "tag=v$VERSION" >> "$GITHUB_OUTPUT" - - name: Create tag env: - RELEASE_TAG: ${{ steps.version.outputs.tag }} + RELEASE_TAG: ${{ needs.preflight.outputs.tag }} run: | TAG="$RELEASE_TAG" if git ls-remote --exit-code --tags origin "refs/tags/$TAG" > /dev/null 2>&1; then @@ -92,7 +105,7 @@ jobs: - name: Create GitHub Release env: - RELEASE_TAG: ${{ steps.version.outputs.tag }} + RELEASE_TAG: ${{ needs.preflight.outputs.tag }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | TAG="$RELEASE_TAG" @@ -113,11 +126,11 @@ jobs: # so invoke it directly and pass the version for the :X.Y.Z image tag. publish-docker: name: Publish Docker image - needs: github-release + needs: [preflight, github-release] # docker.yml logs into GHCR and pushes the multi-arch image manifest. permissions: contents: read packages: write uses: ./.github/workflows/docker.yml with: - version: ${{ needs.github-release.outputs.version }} + version: ${{ needs.preflight.outputs.version }} From 5dc20af8a0b0d4f0b3edfd052251a77224db34c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Lucas?= <55464917+jlucaso1@users.noreply.github.com> Date: Thu, 6 Aug 2026 23:07:06 -0300 Subject: [PATCH 4/5] ci(release): reject build metadata in the release version `+` is not a legal Docker tag character, so docker/metadata-action rewrites it to `-`. A 0.7.0+build.5 release would publish under that version, tag v0.7.0+build.5, and then ship an image tagged 0.7.0-build.5, which is the tag a genuine 0.7.0-build.5 prerelease would claim. Metadata was allowed in the previous commit to keep the check from being stricter than Cargo, since a stricter check that ran after publish would strand the release. That check now runs in preflight, ahead of publish, so rejecting the form costs a failed job and nothing else. --- .github/workflows/release.yml | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8b7223649..0007670e6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -33,12 +33,15 @@ jobs: id: version run: | VERSION=$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)".*/\1/') - # Deliberately no stricter than Cargo: a version Cargo accepts must - # not fail here, or the release stops between publish and tag. Forms - # Cargo itself rejects (leading zeros, empty prerelease identifiers) - # never reach the publish step, so they need no guard of their own. - if ! printf '%s' "$VERSION" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$'; then - echo "Refusing to release a malformed version: '$VERSION'" >&2 + # Build metadata is rejected even though Cargo takes it. `+` is not a + # legal Docker tag character, so docker/metadata-action rewrites it to + # `-`: 0.7.0+build.5 would ship as the image 0.7.0-build.5, which is + # the tag a real prerelease of that name would claim. Being stricter + # than Cargo is only safe because this runs ahead of publish; as a + # post-publish check it would strand the release between crates.io + # and the tag. + if ! printf '%s' "$VERSION" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$'; then + echo "Refusing to release '$VERSION': expected MAJOR.MINOR.PATCH[-prerelease], no build metadata" >&2 exit 1 fi echo "Releasing $VERSION" From 9d9aed30e13bc7c3a73c9b2260b833a2bc1084e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Lucas?= <55464917+jlucaso1@users.noreply.github.com> Date: Thu, 6 Aug 2026 23:14:20 -0300 Subject: [PATCH 5/5] docs(readme): point the quick start at 0.7 `whatsapp-rust = "0.6"` resolves to `<0.7.0`, so anyone following the quick start after this release lands would keep getting 0.6.0. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9c9361dc1..aee714f6a 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ For the full API reference and guides, see the **[documentation](https://whatsap ```toml [dependencies] -whatsapp-rust = "0.6" +whatsapp-rust = "0.7" tokio = { version = "1", features = ["macros", "rt-multi-thread", "signal"] } ```