WhatsApp Logger — Headless Message Archiver
A headless Node.js service that connects to WhatsApp Web and logs message metadata, text, and media attachments to a local SQLite database, with a browser-based dashboard to search and browse everything.
March 5, 2026
Overview
I needed a reliable way to archive WhatsApp messages and media from specific chats — searchable, self-hosted, and running without a GUI. WhatsApp Logger connects to WhatsApp Web via a headless Chromium session, monitors the chats you select, and writes everything to a local SQLite database. Media attachments are downloaded automatically and served back through the web UI.
No raw protocol buffers are ever stored — only the fields that matter.
How It Works
On first run, a QR code appears in the terminal (or on a browser page if you're running it remotely). Scan it once with your phone and the session persists via LocalAuth. From there the service runs silently, logging every new message in the monitored chats.
You pick which chats to monitor through a two-column browser UI or via CLI commands — no need to touch the terminal once it's running.
What Gets Logged
For each message in a monitored chat:
- Sender name and JID (including individual members in group chats)
- Message body text
- Timestamp, direction (inbound/outbound), delivery/read receipt status
- Media attachments — images, audio, video, and documents are downloaded and stored locally; the dashboard shows thumbnails, audio players, and download links inline
Web UI
Three pages, all token-authenticated:
| Page | Purpose |
|---|---|
/ | Dashboard — browse, filter, and search all logged messages with inline media previews |
/monitor.html | Add or remove monitored chats from a two-column UI |
/qr-scan.html | Pair or re-pair your phone remotely without terminal access |
The dashboard is fully mobile-responsive — slide-in chat drawer, collapsible filter panel, card layout on small screens.
Filtering and Search
Messages are filterable by chat, direction, message type, date range, and full-text search across body and sender name. Pagination is built in (default 50 per page, max 500).
Reliability
- Reconnect guard — exponential backoff, only one reconnect sequence at a time
- Crash recovery — Chromium is properly destroyed on startup failure to prevent lock-file loops
- Fire-and-forget media downloads — never block message processing; failures are logged at WARN and the row stays queryable
- Auto-migrations — schema changes apply on startup, tracked in a
_migrationstable
Stack
| Component | Technology |
|---|---|
| Runtime | Node.js 18+, TypeScript |
| whatsapp-web.js (LocalAuth, Puppeteer/Chromium) | |
| Database | SQLite via better-sqlite3 |
| Web UI | Express + vanilla HTML/JS |
| Logging | pino (pretty in dev, JSON in production) |
| Packaging | Docker Compose, PM2 |
Deployment
Runs anywhere Node.js does — bare metal, Docker, or PM2 on a VPS. The Docker image uses a system Chromium and mounts a single volume for the auth session, database, and all media files. Under PM2, --restart-delay=3000 gives Chromium enough time to exit cleanly between restarts.