Skip to main content

CHANGELOG.md

# Changelog

## [Unreleased]

## [0.1.0-alpha.9] - 2026-06-02

WhatsApp/Baileys v7 compatibility update. This release moves BaileysEx from the
Baileys v7 rc9 behavior set to `v7.0.0-rc13` and brings over the rc10-rc13
protocol fixes that affect pairing, message resend, app-state sync, trusted
contact tokens, device lists, newsletters, groups, and account-limit handling.

### Upgrade Impact

- No public BaileysEx function was intentionally removed or renamed.
- QR pairing payloads now use Baileys rc13's linked-device format. Regenerate
  any in-flight QR codes after upgrading.
- Direct 1:1 message sends may now attach trusted-contact tokens or issue fresh
  tokens after sending. Apps that inspect raw stanzas or count outbound
  protocol traffic may see additional token-related traffic.
- Missing/unavailable messages can now emit Baileys-shaped placeholder stubs and
  request a resend from the phone device. Message consumers should tolerate
  unavailable-message placeholders and later replacement messages.
- App-state sync can now retry with snapshots, park collections while waiting
  for missing app-state keys, and resume when a key-share arrives. This improves
  recovery after reconnects and key rotation, but sync completion may now
  proceed with partial state when WhatsApp sends an individually corrupted sync
  record.
- Device-list removals now clear stale sessions. This can cause a session to be
  rebuilt on the next send instead of reusing an outdated device session.
- New account restriction and message-cap notifications can be emitted when
  WhatsApp reports reachout timelocks or new-chat limits.
- Parsed events may include new fields such as usernames, group participant
  usernames, and group online counts.

### Added

- `fetch_account_reachout_timelock/2` and
  `fetch_new_chat_message_cap/2` for WhatsApp account-limit checks.
- Reachout timelock and new-chat-limit events from MEX notifications.
- Trusted-contact token persistence, post-send issuance, identity-change
  reissue, expiry cleanup, and sender timestamp preservation.
- Username lookup support in USync results.
- Album message sending.
- Group `@all` mentions.
- Group participant usernames and group online counts in parsed events.
- Device-list notification handling for add, remove, and update changes.
- Newsletter v2 join/leave support and multi-child newsletter notification
  handling.

### Changed

- Updated the compatibility target from Baileys v7 rc9 behavior to
  `v7.0.0-rc13`.
- Default WhatsApp Web version and linked-device QR pairing output now match
  Baileys rc13.
- Direct 1:1 sends now follow Baileys' trusted-contact-token rules: existing
  tokens are attached only for eligible user messages, peer/AppStateSync
  messages are excluded, and fresh tokens are issued after eligible sends.
- App-state sync is more resilient after reconnects and key rotation: missing
  keys retry with a snapshot before parking, parked collections retry when a new
  app-state key arrives, and corrupted individual sync records no longer abort
  the whole sync pass.
- Incoming unavailable-message placeholders now use the Baileys rc10 resend
  behavior, including phone-device requests for missing messages and safety
  skips for bot, hosted, view-once, and old unavailable stanzas.
- Retry and bad-ack handling now preserves Baileys error semantics, including
  unknown retry codes and 463 account-restriction updates.
- Pre-key uploads no longer use a default throttle, matching the Baileys rc10
  send path.
- Media downloads now fall back across direct-path hosts when WhatsApp returns
  a CDN URL that cannot be fetched directly.
- Offline notifications are processed in Baileys-compatible FIFO batches with
  buffered event flushing, reducing event churn during reconnect catch-up.

### Fixed

- Linked-device sync messages routed by WhatsApp as outgoing self stanzas are
  now treated as `from_me`, even when the stanza omits an explicit `recipient`.
  This prevents valid history sync, app-state key-share, LID mapping, and
  peer-data operation responses from being skipped after reconnects or
  companion sync flows.
- Self-only protocol sync messages are ignored when they arrive from another
  sender, preventing peer-originated stanzas from mutating local sync state.
- Privacy-token notifications no longer drop the stored sender timestamp used to
  avoid duplicate trusted-contact token issuance.
- Direct peer messages no longer include `tctoken` nodes that WhatsApp rejects.
- Protocol message parsing now handles WhatsApp's newer peer-routed self-stanza
  shape without dropping legitimate self sync messages.

## [0.1.0-alpha.8] - 2026-05-22

Library-guideline cleanup.

### Changed

- Removed the unused `BaileysEx.Application` callback and global singleton supervisors; connection runtimes are now only caller-owned per-connection supervisors
- File persistence convenience defaults now use literal cwd-based paths instead of `Application.get_env/2`

## [0.1.0-alpha.7] - 2026-03-21

DRY audit and bug-fix pass. Duplicated transport wrappers across feature
and media modules had diverged silently, producing a runtime crash path.

### Added

- `Connection.TransportAdapter` — shared transport dispatch replacing diverged copies across 12 modules
- `Signal.Store.wrap_running/1` — centralized signal store resolution

### Fixed

- `Media.Upload` crash when given `{module, pid}` queryable tuples from runtime APIs
- `Store.put/3` with `nil` silently preserving old values instead of deleting
- Stale LTHash state on Syncd retry-from-scratch due to nil writes being dropped
- EventEmitter silently losing events after dispatcher process death

### Changed

- EventEmitter dispatch hardened with queue-based delivery, monitor/restart, and `throw`/`exit`/`raise` isolation

## [0.1.0-alpha.6] - 2026-03-20

Eliminates the last JS-shaped design pattern in the codebase. Signal store
transactions previously used a zero-argument closure with hidden state in
the process dictionary — a literal port of Baileys' `keys.transaction(async
() => ...)`. Transactions now pass an explicit handle through the closure,
matching the Ecto `Repo.transaction(fn repo -> ... end)` convention. This
makes transaction state visible, testable, and composable without hidden
coupling to the calling process.

### Added

- `Signal.Store.transaction/3` now passes explicit transaction handles
- `TransactionBuffer` module extracts reusable ETS-based transaction caching

### Changed

- Signal store internals redesigned: all consumers receive `tx_store` explicitly through closures

### Fixed

- Weak test assertions (`assert %{} =`) replaced with strict equality checks

## [0.1.0-alpha.5] - 2026-03-20

Post-merge hardening of the persistence architecture from alpha.4. Shared
write primitives extracted, exception-driven JSON parsing replaced with
explicit error handling, and migration edge cases closed.

### Changed

- Extracted shared crash-safe file IO into `PersistenceIO` and key-index merge into `PersistenceHelpers`
- Replaced exception-driven `JSON.decode!` with explicit `JSON.decode` in compatibility backend

### Fixed

- Migration publish now uses backup-and-swap for both empty and existing targets
- Nil propagation in compatibility JSON decoders for malformed input
- Credo nesting depth violation in `read_data`

## [0.1.0-alpha.4] - 2026-03-19

Major persistence architecture overhaul. The library previously used a
generic Elixir term serializer on top of JSON — encoding atoms, structs,
and tuples with custom tags — which caused fresh-VM crashes, atom table
exhaustion risks, and ongoing allowlist maintenance. This release separates
persistence into two backends: a durable ETF-based native backend
(recommended) and a Baileys-compatible JSON backend rewritten with explicit
codecs. Also fixes several Elixir antipatterns across the codebase.

### Added

- `NativeFilePersistence` — durable ETF-based file backend with crash-safe writes, recommended for Elixir-first deployments
- `PersistenceMigration` — staged atomic migration from compatibility JSON to native format
- `PersistenceIO` — shared crash-safe file write primitives (temp + fsync + rename + dir fsync)
- Format versioning and manifest support for both persistence backends
- Cross-backend contract tests and fresh-VM regression tests for persistence decoding
- `Auth.Persistence` behaviour extended with context-aware optional callbacks

### Changed

- `FilePersistence` rewritten with explicit Baileys-shaped JSON codecs — no more generic tagged Elixir term serialization
- Docs and README recommend `NativeFilePersistence` as default for Elixir apps

### Fixed

- Atom table exhaustion risk from `String.to_atom` on untrusted disk data
- Flaky CI test from PBKDF2 timing — iteration count now injectable at socket startup
- Quadratic list appending in protobuf decoder, history sync, message sender, and other hot paths
- Nil-punning (`value && value.field`) replaced with pattern matching across feature modules
- Dialyzer and Credo strict warnings resolved

## [0.1.0-alpha.3] - 2026-03-19

### Changed

- Event emission no longer blocks callers on slow subscribers
- Protocol and connection logging demoted from warning to debug level
- Improved binary node encoding and decoding performance

### Fixed

- Programmer errors in Noise protocol functions are no longer silently swallowed
- Rust NIF error handling hardened to eliminate panic paths

## [0.1.0-alpha.2] - 2026-03-19

Initial alpha release.

### Added

- Connect to WhatsApp Web via the multi-device protocol with QR code or phone number pairing
- End-to-end encrypt all messages via a pure Elixir Signal Protocol implementation
- Send and receive 27+ message types: text, images, video, audio, documents, stickers, contacts, location, polls, reactions, forwards, edits, deletes, and more
- Upload and download encrypted media with AES-256-CBC and HKDF-derived keys
- Manage groups and communities: create, update, leave, add/remove/promote/demote participants, invite flows
- Subscribe to and manage newsletters
- Send presence updates and subscribe to contact presence
- Sync app state (archive, mute, pin, star, read) across linked devices via the Syncd protocol with LTHash integrity verification
- Fetch and manage business profiles, catalogs, collections, and orders
- Reject calls and create call links
- Manage privacy settings and blocklists
- Persist credentials to disk via `FilePersistence` with automatic reconnection
- Encode and send WAM analytics buffers for Baileys wire parity
- Emit telemetry events under the `[:baileys_ex]` prefix for connection, messaging, media, and NIF operations
- Noise Protocol transport encryption via `snow` Rust NIF
- XEdDSA signing via `curve25519-dalek` Rust NIF
- `:gen_statem` connection state machine with automatic reconnection
- ETS-backed concurrent signal key store
- Supervised process tree with `:rest_for_one` strategy