WhatsApp in pure Rust. One 5 MB binary. 15 MB RAM. No Node.js required.
A single Rust binary that handles the full WhatsApp Web protocol: text, images, audio, video, stickers, reactions, edits, polls, status stories, groups, and chat management. No npm. No Python. No Docker. Just cargo build and scan the QR code.
Install | How It Works | Features | API | MCP Tools | Contributing
Every WhatsApp automation library follows the same playbook: Node.js, Baileys, and a node_modules folder the size of a small country. It works until 3 AM when the process crashes, the memory leak finally wins, and you're reading someone else's JavaScript trying to figure out what went wrong.
We needed WhatsApp inside agent software and got tired of babysitting a Node sidecar. So we replaced it with one Rust binary.
| Node.js + Baileys | WhatsRust | |
|---|---|---|
| Language | JavaScript / Node.js | Rust |
| Install size | ~200 MB with node_modules | 5 MB single binary |
| Memory usage | ~50-120 MB idle | ~15 MB |
| Dependencies | npm install + hope |
cargo build |
| Crash recovery | Roll your own | SQLite-backed queue + per-message backoff |
| API | None built-in | 54 REST endpoints + CLI + MCP server |
| Status/stories | DIY | Text, image, video, revoke -- all built in |
| Chat management | Manual API calls | 12 operations (pin, mute, archive, star...) |
| Group management | Manual | 12 methods, full CRUD |
| Anti-ban protection | None | Read receipt batching, typing sim, jitter |
| AI agent support | None | 30 MCP tools for Claude Code, etc. |
git clone https://github.com/199-biotechnologies/whatsrust
cd whatsrust && cargo runScan the QR code with your phone. That's it.
# Phone number pairing instead of QR
WHATSAPP_PAIR_PHONE="+1234567890" cargo run
# With sender allowlist + custom port
WHATSAPP_ALLOWED="1234567890" WHATSRUST_PORT=8080 cargo runWhatsRust runs as a daemon with a built-in REPL, REST API, and MCP server. Every outbound message goes through a durable SQLite queue -- messages survive crashes and restarts. Inbound messages are deduplicated with a generation-tracked concurrent map.
src/
bridge.rs Core: events, messaging, queue, groups, chat management
outbound.rs 21 typed outbound ops, durable SQLite queue
bridge_events.rs Broadcast event bus (tokio::sync::broadcast)
api.rs REST API (54 endpoints) + SSE + CLI HTTP client
mcp.rs MCP server (30 tools, JSON-RPC over stdio)
storage.rs rusqlite Signal Protocol store + schema migrations
polls.rs Poll crypto (HKDF-SHA256 + AES-256-GCM)
dedup.rs Generation-tracked DashMap (concurrent-safe)
read_receipts.rs Batched receipt scheduler
qr.rs QR rendering (terminal/PNG/HTML/SVG)
instance_lock.rs flock-based single-instance guard
main.rs Daemon (REPL) + CLI (54 commands) + MCP mode
lib.rs Library crate exports
~11,000 lines across 13 files. Built on whatsapp-rust (v0.5.0, MIT) for the protocol layer.
| Mode | How |
|---|---|
| Daemon | cargo run -- REPL with 54 commands |
| CLI | whatsrust send 15551234567 "Hello" -- JSON to stdout |
| Library | WhatsAppBridge::start(config, tx, cancel) -- embed in your Rust app |
| MCP server | whatsrust mcp -- 30 tools for AI agents (Claude Code, etc.) |
Text, image, audio/voice, video, document, sticker, location, contact card, reaction (add/remove), edit, revoke, reply/quote, forward, view-once (image/video), poll (create + encrypted vote decryption), status/story (text/image/video/revoke).
Pin/unpin, mute/unmute, archive/unarchive, mark read/unread, delete chat, delete message for me, star/unstar. Direct app-state mutations that sync across all linked devices.
List groups, get info, create, rename, set description, add/remove/promote/demote participants, invite links. Group metadata cached with TTL and mutation invalidation.
Text stories with custom background colors and fonts. Image and video stories. Revoke posted stories. Privacy controls (contacts, allowlist, denylist). All go through the durable outbound queue.
- Crash-safe outbound queue in SQLite. Messages survive restarts.
- Atomic dedup with generation-tracked DashMap. No double-processing under concurrent handlers.
- Per-message exponential backoff. One stuck message doesn't block the rest.
- Graduated reconnect backoff with jitter. Not the naive kind.
- Graceful shutdown. Drains in-flight messages on SIGINT/SIGTERM.
- Single-instance flock. Two bridges can't fight over one session.
- SQLite backup on startup and shutdown. WAL mode, no corruption.
- Read receipt batching. Groups message IDs per chat into single stanzas on a 200 ms coalesce.
- Flush-before-reply. Marks messages as read before responding.
- Recording indicator before voice notes.
- Configurable send pacing with randomized jitter intervals.
- Auto presence management. Available on connect, unavailable on shutdown.
With the daemon running, use whatsrust as a one-shot CLI. Every command returns JSON to stdout:
# Messaging
whatsrust send 15551234567 "Hello from the CLI"
whatsrust image 15551234567 /tmp/photo.jpg "Check this out"
whatsrust video 15551234567 /tmp/clip.mp4
whatsrust react 15551234567 MSG_ID "thumbsup"
whatsrust poll 15551234567 1 "Lunch?" -- "Pizza" "Sushi" "Tacos"
# Status/stories
whatsrust status-text 15551234567 "Hello world"
whatsrust status-image 15551234567 /tmp/photo.jpg
# Chat management
whatsrust pin-chat 15551234567
whatsrust mute-chat 15551234567
whatsrust archive-chat 15551234567
whatsrust mark-read 15551234567
whatsrust delete-chat 15551234567
# Groups
whatsrust groups
whatsrust group-info 120363012345678901@g.us
whatsrust group-create "My Group" 15551234567 15559876543Output:
{"ok": true, "id": "3EB0A1B2C3D4E5F6"}54 REST endpoints on localhost:7270. JSON in, JSON out. No framework -- raw TCP for minimal overhead.
| Endpoint | Body |
|---|---|
POST /api/send |
{jid, text, mentions?, schedule_at?} |
POST /api/reply |
{jid, id, sender, text} |
POST /api/edit |
{jid, id, text} |
POST /api/react |
{jid, id, emoji} |
POST /api/revoke |
{jid, id} |
POST /api/image |
{jid, data, mime?, caption?} |
POST /api/video |
{jid, data, mime?, caption?} |
POST /api/audio |
{jid, data} |
POST /api/poll |
{jid, question, options, selectable_count} |
POST /api/forward |
{jid, msg_id} |
| Endpoint | Body |
|---|---|
POST /api/status-text |
{recipients, text, background_argb?, font?} |
POST /api/status-image |
{recipients, data, caption?} |
POST /api/status-video |
{recipients, data, caption?} |
POST /api/status-revoke |
{recipients, message_id} |
| Endpoint | Body |
|---|---|
POST /api/pin-chat |
{jid} |
POST /api/mute-chat |
{jid} |
POST /api/archive-chat |
{jid} |
POST /api/mark-read |
{jid} |
POST /api/delete-chat |
{jid} |
POST /api/delete-for-me |
{jid, id} |
POST /api/star |
{jid, id} |
Plus: SSE streaming (GET /api/events), groups, presence, health check, QR.
Async sends return {ok, job_id}. Add ?sync=true to wait for the WhatsApp message ID.
Run whatsrust mcp to start a Model Context Protocol server with 30 tools over JSON-RPC stdio. Connect it to Claude Code, Cursor, or any MCP-compatible AI agent and your agent can send messages, manage groups, post stories, and handle chat operations through WhatsApp.
Works out of the box with Claude Code -- just add it to your MCP config and your AI agent gets full WhatsApp access.
Designed to be embedded. About 10 lines to wire up:
use whatsrust::bridge::{BridgeConfig, WhatsAppBridge, WhatsAppInbound};
use tokio::sync::mpsc;
use tokio_util::sync::CancellationToken;
let (inbound_tx, mut inbound_rx) = mpsc::channel::<WhatsAppInbound>(256);
let cancel = CancellationToken::new();
let bridge = WhatsAppBridge::start(
BridgeConfig {
db_path: "whatsapp.db".into(),
health_port: 8080,
..Default::default()
},
inbound_tx,
cancel.clone(),
);
// Send
bridge.send_message_with_id("1234567890", "Hello from Rust").await?;
// Receive
while let Some(msg) = inbound_rx.recv().await {
println!("{}: {}", msg.sender, msg.content.display_text());
}Inbound messages arrive as WhatsAppInbound with sender, JID, message ID, reply context, MessageFlags (forwarded, view-once), and typed content. Media arrives as raw bytes -- no second download step.
| Variable | Default | What it does |
|---|---|---|
WHATSAPP_PAIR_PHONE |
(QR mode) | Phone number for pair-code linking |
WHATSAPP_ALLOWED |
(everyone) | Comma-separated sender allowlist |
WHATSRUST_PORT |
7270 |
API server port (0 = disabled) |
WHATSRUST_BIND |
127.0.0.1 |
API bind address |
WHATSRUST_API_TOKEN |
(none) | Bearer token for API auth |
BACKUP_DIR |
whatsapp.db.backups |
SQLite backup directory |
RUST_LOG |
info |
Log level (debug for protocol) |
PRs welcome. Run cargo test and cargo clippy before submitting. See CONTRIBUTING.md.