Packages

An independent, OTP-native WhatsApp Web client for Elixir — Noise handshake, Signal end-to-end encryption, multi-device.

Retired package: Security issue - CVE-2026-48063: spoofed self-only protocolMessages. Upgrade to >= 0.4.1

Current section

Files

Jump to
amarula lib amarula config.ex
Raw

lib/amarula/config.ex

defmodule Amarula.Config do
@wa_version [2, 3000, 1_035_194_821]
@moduledoc """
Connection config + the single source of truth for protocol/connection defaults.
There are two kinds of configuration:
## 1. Per-connection config — passed to `Amarula.new/1`
A map; you supply only what differs (at minimum `:profile`), `merge/1` fills the
rest from the defaults below.
| Key | Default | Meaning |
|-----|---------|---------|
| `:profile` | — (**required**) | names + scopes this account's stored state |
| `:storage` | `{Amarula.Storage.File, root: AMARULA_DATA_DIR || "./amarula_data"}` | storage backend `{module, opts}` |
| `:retry_cache` | ETS (see `Amarula.RetryCache`) | sent-message cache backend |
| `:registry` | `Amarula.ProfileRegistry` (local) | `{module, name}` or bare `name` for the profile→connection registry; swap for `Horde.Registry` to enforce one-conn-per-profile cluster-wide (default: per node). See `Amarula.ProfileRegistry` |
| `:auth` | loaded from storage | explicit creds (advanced; normally Amarula loads/persists these itself) |
| `:version` | `#{inspect(@wa_version)}` | WhatsApp Web *protocol* version — MUST track `src/Defaults/index.ts` or the handshake is rejected. (Distinct from Baileys *source* parity — see `Amarula.Baileys` / `docs/PARITY.md`.) |
| `:browser` | `["Mac OS", "Chrome", "14.4.1"]` | browser triple shown as the linked device |
| `:max_retries` | `5` | reconnect attempts |
| `:retry_delay` | `1000` | base reconnect backoff (ms) |
| `:connect_timeout_ms` | `30_000` | WebSocket connect timeout |
| `:keep_alive_interval_ms` | `30_000` | WA-level keep-alive ping interval |
| `:sync_full_history` | `true` | request full history on link |
| `:mark_online_on_connect` | `true` | send presence available on connect |
| `:fire_init_queries` | `true` | run the post-login init IQ queries |
| `:country_code` | `"US"` | |
| `:headers` / `:origin` / `:agent` | see defaults | HTTP/WS handshake |
Amarula.new(%{profile: :me, sync_full_history: false}) |> Amarula.connect()
## 2. App-global config — `config :amarula, ...`
Only the pluggable seams (apply to every connection that doesn't override them):
config :amarula, :default_storage_adapter, Amarula.Storage.File
config :amarula, :retry_cache_adapter, Amarula.RetryCache.ETS
## Logging
Amarula logs through Elixir's `Logger`. Almost everything is `:debug`; only
connection lifecycle, pairing, and errors are `:info`/`:warning`/`:error`. To
keep your dev console clean, set the global level — or silence Amarula
specifically without affecting your own logs:
# your app's config
config :logger, level: :info
# or, mute just Amarula (Elixir 1.13+):
Logger.put_module_level(Amarula.Connection, :warning)
Telemetry (`Amarula.Telemetry`) is the structured, log-independent way to observe
Amarula in production.
"""
@defaults %{
wa_websocket_url: "wss://web.whatsapp.com/ws/chat",
version: @wa_version,
browser: ["Mac OS", "Chrome", "14.4.1"],
# connection tunables
max_retries: 5,
retry_delay: 1000,
connect_timeout_ms: 30_000,
keep_alive_interval_ms: 30_000,
fire_init_queries: true,
mark_online_on_connect: true,
sync_full_history: true,
country_code: "US",
# http/ws handshake
headers: [],
origin: "https://web.whatsapp.com",
agent: nil
}
@doc "The default config map (without `:profile`/`:auth`/`:storage`, which are caller-supplied)."
@spec defaults() :: map()
def defaults, do: @defaults
@doc "Merge `config` over the defaults (caller values win)."
@spec merge(map()) :: map()
def merge(config) when is_map(config), do: Map.merge(@defaults, config)
end