Production-grade WhatsApp orchestration daemon for OpenClaw agent workflows.
Quick Start · Features · Architecture · Configuration · Docs · Issues
- Why Concierge
- Quick Start
- Features
- Architecture
- Prerequisites
- Installation
- Configuration
- Running
- CLI Reference
- Deployment
- OpenClaw Sandbox Config
- Security Model
- wacli Integration
- Contributing
- License
If you want to:
- Deliver AI support or sales on WhatsApp
- Run advanced agents per user or per group
- Enforce isolation between sessions
- Keep the system resilient under load
Concierge is the missing layer. It sits on top of wacli (WhatsApp DB sync by steipete) and OpenClaw (formerly Clawdbot/Moltbot) to create isolated, per-user sessions with strong sandboxing, smart queueing, and reliable delivery.
- Install via Homebrew:
brew tap arvorco/tap brew install concierge --HEAD - Copy config:
cp config.example.json config.json - Edit
config.jsonwith your admin number and preferences. - Create prompt (optional):
cp CONCIERGE_TEMPLATE.md CONCIERGE.md - Run:
bin/concierged
| Feature | Description |
|---|---|
| Per-JID isolated workspaces | Each WhatsApp contact gets its own IDENTITY, SOUL, TOOLS, and config |
| Sandboxed sessions | OpenClaw Docker-backed sandboxes per session |
| Per-workspace tool allowlists | Default: no tools; configurable per JID |
| Human takeover detection | Auto-pause after a manual reply (configurable hours) |
| Message coalescing | Multiple quick texts merged into a single response |
| Backlog catch-up | Responds to missed messages on startup or schedule |
| Single-writer sync controller | Prevents wacli DB lock issues |
| Auto sandbox cleanup | Hourly Docker container pruning (configurable) |
| CLI commands | Reprocess, reset, pause, resume per JID or all |
| Hot-reload config | Change config.json without restarting |
| Business hours | Optional schedule with timezone support |
| Rate limiting | Per-chat-per-hour rate caps |
| Safety keywords | Forbidden words, escalation triggers, approval gates |
| Admin notifications | Alerts via OpenClaw for errors, takeovers, new messages |
WhatsApp
|
wacli sync --> ~/.wacli/wacli.db
|
Concierge Monitor (poll every 5s + backlog)
|
ChatHandler (one per JID, isolated process)
|
OpenClaw Agent (sandboxed Docker session)
|
wacli send --> WhatsApp
Notifications to the operator are sent via OpenClaw.
Concierge runs a flat one_for_one supervisor with 9 children:
Concierge.Supervisor
├── ChatRegistry (Registry — name→pid mapping)
├── ConfigServer (GenServer — hot-reload config.json)
├── WacliDB (GenServer — read-only wacli.db)
├── Store (GenServer — tracking.db, single-writer SQLite)
├── AgentManager (GenServer — workspace provisioning)
├── SyncController (GenServer — serializes wacli send ops)
├── SandboxCleaner (GenServer — Docker cleanup)
├── ChatSupervisor (DynamicSupervisor — one ChatHandler per JID)
└── Monitor (GenServer — polls wacli DB, dispatches messages)
- macOS or Linux
- Elixir 1.14+
- SQLite
- Docker (required for OpenClaw sandboxes)
- wacli by steipete
- OpenClaw (gateway + CLI, formerly Clawdbot/Moltbot) — Concierge auto-detects
openclaworclawdbot(compat alias)
brew tap arvorco/tap
brew install concierge --HEAD
git clone https://github.com/arvorco/concierge
cd concierge
mix deps.getThen copy the config and optionally the prompt template:
cp config.example.json config.json
cp CONCIERGE_TEMPLATE.md CONCIERGE.md # optionalEdit CONCIERGE.md with your organization's messaging and business details. Workspaces will symlink their SOUL.md to CONCIERGE.md.
Note:
CONCIERGE.mdis gitignored by default so you can keep private business content local.
By default, Concierge reads config.json from the current working directory.
# Override config path
export CONCIERGE_CONFIG="/path/to/config.json"By default, Concierge uses the process working directory as its home.
# Override home directory
export CONCIERGE_HOME="/path/to/your/concierge-home"You can also set paths.home_dir inside config.json.
| Key | Purpose |
|---|---|
enabled |
Global on/off switch |
hours.* |
Business hours, timezone, weekdays-only |
filters.only_dms |
Only respond to direct messages |
filters.ignore_jids |
List of JIDs to ignore (admin, test users) |
filters.max_response_length |
Max characters per response |
monitor.backlog_* |
Catch-up rules and window |
safety.rate_limit_per_chat_per_hour |
Rate cap per conversation |
safety.forbidden_words |
Block messages containing these |
safety.escalate_to_human_keywords |
Trigger human escalation |
human_takeover.auto_pause_hours |
Pause duration after manual reply |
sandbox_cleanup.* |
Docker cleanup cadence and max age |
model.primary |
AI model identifier |
notifications.notify_admin_number |
Admin phone for alerts |
Full config.json reference
{
"enabled": true,
"name": "Concierge",
"version": "1.0.0",
"hours": {
"enabled": false,
"start": 8,
"end": 20,
"timezone": "America/Sao_Paulo",
"weekdays_only": false
},
"filters": {
"only_dms": true,
"ignore_groups": true,
"ignore_broadcast": true,
"ignore_jids": [],
"max_response_length": 500,
"min_wait_seconds": 3,
"max_wait_seconds": 3
},
"monitor": {
"backlog_enabled": true,
"backlog_interval_seconds": 60,
"backlog_window_seconds": 0,
"backlog_retry_seconds": 300,
"backlog_catch_all_on_start": true,
"backlog_ignore_hours": true
},
"safety": {
"rate_limit_per_chat_per_hour": 10,
"processing_timeout_seconds": 180,
"forbidden_words": ["delete", "exec", "rm -rf", "sudo", "eval", "system"],
"require_human_approval_keywords": [],
"escalate_to_human_keywords": []
},
"human_takeover": {
"auto_pause_hours": 3,
"cooldown_after_human_message": true
},
"sandbox_cleanup": {
"enabled": true,
"interval_seconds": 3600,
"max_age_seconds": 3600,
"name_prefix": "clawdbot-sbx-",
"remove_running": true,
"dry_run": false
},
"whitelist": {
"mode": "all",
"numbers": []
},
"blocklist": {
"enabled": true,
"numbers": []
},
"model": {
"primary": "anthropic/claude-sonnet-4-5",
"max_tokens": 800,
"temperature": 0.9,
"thinking": "off"
},
"sender": {
"provider": "wacli",
"clawdbot_target": "jid",
"clawdbot_timeout_ms": 20000
},
"notifications": {
"notify_admin_number": "+15550000000",
"notify_on_new_message": true,
"notify_on_bot_response": true,
"notify_on_human_takeover": true,
"notify_on_escalation": true,
"notify_on_error": true,
"provider": "clawdbot"
},
"logging": {
"enabled": true,
"log_dir": "logs",
"retention_days": 30,
"log_full_json_on_error": true
},
"paths": {
"home_dir": null
}
}bin/concierged
mix run --no-halt
iex -S mix
| Command | Description |
|---|---|
bin/concierge reprocess <jid> |
Re-queue and process missed messages for a JID |
bin/concierge reprocess-all |
Re-queue all pending messages |
bin/concierge reset <jid> |
Reset chat state for a JID |
bin/concierge reset-all |
Reset all chat states |
bin/concierge install-launchd |
Install macOS LaunchAgent |
bin/concierge uninstall-launchd |
Uninstall macOS LaunchAgent |
Install via the CLI (recommended):
bin/concierge install-launchd
This renders the launchd/co.arvor.concierged.plist template with your local path and loads it.
To uninstall:
bin/concierge uninstall-launchd
Concierge runs well on common cloud images like Amazon Linux 2023, Oracle Linux, or Ubuntu LTS.
wacli install:
brew install steipete/tap/wacli
Or build from source (Go required). The wacli docs recommend building with the sqlite_fts5 tag for fast search.
systemd setup:
sudo cp systemd/concierged.service /etc/systemd/system/concierged.service
sudo systemctl daemon-reload
sudo systemctl enable --now conciergedBefore enabling the service:
- Install Docker, wacli, OpenClaw, Elixir, and SQLite
- Update
User,Group,WorkingDirectory,ExecStart, andCONCIERGE_HOMEin the service file - Optionally build a release:
MIX_ENV=prod mix release
Concierge assumes OpenClaw runs sessions in a restricted sandbox. Configure your OpenClaw agent sandbox like this:
{
"sandbox": {
"mode": "non-main",
"workspaceAccess": "none",
"scope": "session"
}
}Concierge also reads messages.responsePrefix from the OpenClaw/Clawdbot config (via openclaw config get messages.responsePrefix or clawdbot config get messages.responsePrefix). If you don't want a prefix, set it to an empty string in OpenClaw.
For background on sandboxing and why session isolation matters, see the OpenClaw docs.
Each WhatsApp JID gets:
- Its own workspace (
agents/<jid>/) with a dedicated prompt, identity, and config - Sandboxed execution in OpenClaw (Docker containers per session)
- Tool restrictions enforced at the agent level (default: no tools)
Concierge also auto-prunes stale sandboxes (default: every hour) to keep the host clean.
Concierge improves reliability around wacli by:
- Serializing writes (single-writer sync controller)
- Deduping by
msg_id - Safe backlog reconciliation
Long-term, we'd like to integrate deeper with wacli for:
- Native event streaming (no polling)
- Direct send API (no sync stop/start)
- Better message metadata (sender identity + group hints)
- Fork the repository
- Create a feature branch:
git checkout -b feature/my-feature - Ensure code passes lint:
mix format mix credo --strict
- Commit and push
- Open a Pull Request
Please follow the project's 0-lint policy — mix format + mix credo --strict must pass with zero warnings.
MIT — see LICENSE.
Concierge is the orchestration layer that makes WhatsApp agent experiences reliable, secure, and scalable.