Architecture
Ruby app
│
├── HTTP ──► commands (send, status, session...)
└── WS ◄── real-time events (messages, QR, connection...)
│
▼
Node.js bridge (Fastify + Baileys)
│
▼
WhatsApp Web (WebSocket)
The gem spawns a local Node.js process running a Fastify HTTP + WebSocket server wrapping Baileys. All communication stays on 127.0.0.1 with a dynamic port. Sessions are persisted to disk so you only need to scan the QR code once.
In multi-session mode, each session gets its own bridge process, dynamic port, and auth directory (auth_dir/SessionName/).
Key design decisions
| Decision | Why |
|---|---|
| Bridge over FFI | Baileys is JS-only — a bridge is the only clean option |
| Fastify (not Express) | Faster, built-in schema validation, native WebSocket |
| HTTP + WebSocket | HTTP for request/response, WS for real-time events |
| Dynamic port | No conflicts, supports multiple sessions |
| 1 session = 1 bridge | Full isolation per WhatsApp account |
| Baileys pinned to commit | npm releases lag behind, protocol changes break things |
| Double rate limiting | Ruby controls the flow, bridge is the safety net |
| Gaussian jitter | Random delays that look human, not uniform bot patterns |
Class overview
| Class | Role |
|---|---|
Whatsapp::Client | Core client — connect, send, events |
Whatsapp::Session | Named session wrapping a Client (status, QR, heartbeat) |
Whatsapp::SessionManager | Thread-safe session registry |
Whatsapp::AccountProxy | Proxy for account: true mode |
Whatsapp::RecordProxy | Proxy for destination mode (has_whatsapp :phone) |
Whatsapp::Bridge | HTTP + WebSocket communication with Node bridge |
Whatsapp::Connection | Spawns/kills the Node bridge process |
Whatsapp::Heartbeat | Periodic health check thread |
Whatsapp::RateLimiter | Ruby-side rate limiting with gaussian jitter |
Whatsapp::MessageHandler | Base class for incoming message handlers |
Whatsapp::MessageDispatcher | Dispatches messages to configured handler |
Whatsapp::Notifier | Notification service with templates and bulk |
Whatsapp::Events | Event emitter |
Whatsapp::Message | Parsed incoming message |