Overview
rousseau whatsapp runs the WhatsApp bridge in the foreground. It embeds go.mau.fi/whatsmeow (unofficial WhatsApp Web protocol) and speaks directly to Meta's servers. On first launch a QR code is printed to stdout; scan it from your phone under WhatsApp > Settings > Linked devices. Device credentials are cached to a SQLite store separate from the session database.
Source: internal/cli/whatsapp.go. Transport implementation: internal/transport/whatsapp/.
Synopsis
rousseau whatsapp [--store <path>] [--allow <jid>...] [--config <path>]
Flags
| Flag | Type | Default | Effect |
|---|---|---|---|
--store |
string | $XDG_DATA_HOME/rousseau/whatsapp.db |
Path to the whatsmeow device store (SQLite with WAL, busy_timeout=15000, synchronous=NORMAL). |
--allow |
[]string | empty | Restrict inbound to these JIDs. Repeatable. Empty allows anyone — never do this on a public number. |
--config |
string | inherits from root | Path to the YAML config file. |
Config keys respected
Read from internal/config/config.go WhatsAppConfig:
| Key | Type | Default | Effect |
|---|---|---|---|
whatsapp.reply_header |
string | "💎 *Rousseau Agent*\n\n" |
Prefix prepended to every outbound message. Set to " " to disable. |
whatsapp.voice.enabled |
bool | false |
Enable whisper-based transcription for inbound voice notes. |
whatsapp.voice.binary |
string | whisper |
Whisper CLI to invoke. |
whatsapp.voice.model |
string | empty | Passed to --model (e.g. base.en). |
whatsapp.voice.model_path |
string | empty | Explicit .bin path; takes precedence over model. |
whatsapp.voice.language |
string | empty | Passed to --language; empty auto-detects. |
whatsapp.voice.extra_args |
[]string | empty | Appended to every whisper invocation. |
Allowlist syntax
Every allowlist entry is a WhatsApp JID (Jabber ID). The two forms in use:
447900123456@s.whatsapp.net
E.164 without a leading + followed by @s.whatsapp.net. This is the JID that identifies a single phone number.
447900123456-1567890123@g.us
A group JID: the number of the group creator, a dash, the group creation timestamp, then @g.us. Groups can be added to the allowlist explicitly; individual members inside an allowed group do not need their personal JIDs listed.
The router (internal/transport/router.go) checks the sender's normalised JID against the allowlist. Non-matching senders are dropped with a router.transport.rejected log event.
Environment variables
| Variable | Effect |
|---|---|
ROUSSEAU_WHATSAPP_REPLY_HEADER |
Overrides the reply header. |
XDG_DATA_HOME |
Base directory for the device store. |
ROUSSEAU_LOG_LEVEL |
Also translated into whatsmeow's log level (DEBUG, INFO, WARN, ERROR). |
Plus every provider variable — see rousseau chat.
Startup sequence
setUnattendedPermissionDefault(opts, "whatsapp")— flipsclaudecli.permission_modetobypassPermissionsif empty, because unattended daemons cannot answer permission prompts interactively.assembleDaemon— opens the session store, builds the provider, tool registry, approver, compressor.resolveWhatsAppDSN— computes the SQLite DSN with WAL pragmas.whatsapp.New— creates the whatsmeow client and (optionally) the whisper transcriber.wiring.startCron— starts the cron scheduler with the whatsappDeliveras the delivery callback, sorousseau cron addjobs land on the configured target JIDs.client.Start— blocks until the context is cancelled (Ctrl+C, SIGTERM).
Log events
| Event | Attributes | Emitted by |
|---|---|---|
whatsapp.qr_ready |
none | first-pairing QR code rendered to stdout |
whatsapp.paired |
jid |
phone accepted the QR |
whatsapp.connected |
none | websocket handshake completed |
whatsapp.starting |
store, allowlist |
pre-connect |
whatsapp.voice_enabled |
binary, model |
when voice transcription is enabled |
router.transport.rejected |
sender, reason |
inbound sender not on the allowlist |
Exit codes
| Code | Meaning |
|---|---|
| 0 | Clean context cancellation (SIGTERM). |
| 1 | Provider, store, whatsmeow, or DSN error before or during startup. |
| 130 | SIGINT interrupt. |
Worked examples
# Personal chat with a single allowed number
rousseau whatsapp --allow 447900123456@s.whatsapp.net
# Multiple numbers plus a team group
rousseau whatsapp \
--allow 447900123456@s.whatsapp.net \
--allow 447900222222@s.whatsapp.net \
--allow 447900123456-1567890123@g.us
# Explicit store path (useful in Podman with a bind-mounted volume)
rousseau whatsapp --store /var/lib/rousseau/whatsapp.db
# Voice notes with a local whisper.cpp binary
cat >> ~/.config/rousseau/config.yaml <<'EOF'
whatsapp:
voice:
enabled: true
binary: /usr/local/bin/whisper
model: base.en
EOF
rousseau whatsapp --allow 447900123456@s.whatsapp.net
Common failure modes
- QR displayed but never accepted — clock drift, stale
whatsapp.db, or Meta-invalidated number. Deletewhatsapp.dband re-pair; checktimedatectl status. store: locked— anotherrousseau whatsappprocess is holding the WAL lock. WAL allows readers but only one writer.- Voice notes not transcribed —
whatsapp.voice.enabled: false, or thewhisperbinary is not on$PATH.rousseau doctorsurfaces both. - Silent inbound drops — the sender is not on the allowlist. Check
router.transport.rejectedin the logs.
Related pages
- Transports: WhatsApp — protocol, risk, operational posture.
- Reference: Commands: cron — schedule prompts that deliver to WhatsApp JIDs.
- Best Practices: Session hygiene — when to nuke the store.
- Deployment — running under Podman.
- Reference: Logs — every structured log event.