wacli.me alpha

Self-hosting

The hosted service is a convenience, not a moat. Everything it does, you can run on a spare machine — in one command with Docker, or by hand if you would rather see every part. This page is the honest version of both, including the bits that are annoying.

The short way

One command. The image builds the CLI with the right flags, so the mistake described further down cannot happen to you.

docker run -d --name wacli-me \
  -p 127.0.0.1:8787:8787 \
  -v wacli-stores:/app/stores \
  -v wacli-config:/app/config \
  -e WACLI_DEVICE_LABEL=your-domain.example \
  ghcr.io/wacli-me/wacli-me:latest

Then link an account by opening http://127.0.0.1:8787/connect and scanning the code. The two volumes hold the message stores and the account file; keep them, or every linked account is gone.

Set WACLI_DEVICE_LABEL to something you recognise. Whatever it says is what appears under Linked devices in WhatsApp. Leave it unset and a container id shows up on the phone of whoever linked the account.

Do not publish port 8787 straight to the internet. The example binds it to localhost on purpose — put a tunnel or a TLS terminator in front, as described in step 5.

Prefer to build it yourself? docker build -t wacli-me . from a clone of the repository produces the same image; it takes a few minutes, most of it compiling the CLI. --build-arg WACLI_VERSION=v0.18.2 pins which CLI version goes in.

The long way

Everything below runs the same pieces without Docker — worth reading even if you took the short way, because the operational notes apply either way.

What you need

RequirementWhy
A machine that stays awakeThe sync daemon must hold a connection; a laptop that sleeps loses history
Go 1.24+Builds the wacli binary. Not needed with Docker
Node 20+Runs the gateway. Not needed with Docker
~1 GB disk per busy accountSQLite store plus any media you download
A tunnel or reverse proxyOnly if you want to reach it from outside the machine

1 · Build the CLI

git clone https://github.com/openclaw/wacli
cd wacli
go build -tags sqlite_fts5 -ldflags="-s -w" -o ~/bin/wacli ./cmd/wacli
~/bin/wacli --version

The build tag matters. Without -tags sqlite_fts5 the store migration fails with no such module: fts5, and full-text search is gone. It is the single most common setup mistake.

2 · Link an account

wacli --store ~/wa-store auth

A QR code appears in the terminal; scan it in WhatsApp under Settings → Linked devices. The code expires after about a minute, so have the phone ready. Over SSH the block graphics are often unscannable — link from a real terminal, or render the code to an image first.

The store directory now holds device credentials. Anyone who copies it can read that account: chmod 700 it, and keep it off shared volumes and backups you do not control.

3 · Keep it synced

wacli --store ~/wa-store sync

This is a long-running process, not a cron job. It holds the store lock, streams new messages in, and backfills history. Without it running, your store freezes at whatever it last fetched — the second most common setup mistake.

Run it under whatever your system uses to keep services alive: a launchd agent on macOS, a systemd unit on Linux.

# /etc/systemd/system/wacli-sync.service
[Unit]
Description=wacli sync
After=network-online.target

[Service]
ExecStart=/home/you/bin/wacli --store /home/you/wa-store sync
Restart=always
RestartSec=10
User=you

[Install]
WantedBy=multi-user.target

4 · Run the gateway

# the gateway running this service, MIT
git clone https://github.com/wacli-me/wacli-me
cd wacli-me
npm install --prefix server
node scripts/build.mjs                      # site/ -> web/
PORT=8787 HOST=127.0.0.1 node server/server.mjs

Tenants live in a JSON file that the process re-reads every few seconds, so adding an account needs no restart:

{
  "tenants": [
    { "id": "me",
      "token": "<openssl rand -hex 32>",
      "store": "/home/you/wa-store",
      "allowSend": false }
  ]
}
VariableDefaultMeaning
PORT8787Listen port
HOST127.0.0.1Bind address — keep it on loopback
WACLI_BIN./bin/wacliPath to the CLI
WACLI_ME_TENANTS./config/tenants.jsonTenant file

5 · Expose it — carefully

If the client runs on the same machine, stop here: http://127.0.0.1:8787/mcp is all you need, and nothing is reachable from outside.

To reach it from elsewhere, put a TLS terminator in front and leave the service on loopback. A Cloudflare tunnel does this without opening a port or exposing your home IP:

cloudflared tunnel --url http://127.0.0.1:8787

Never bind the service to 0.0.0.0 on a public network. The token is the only thing between the internet and your message history, and a plaintext HTTP hop hands it to anyone on the path.

Operational reality

Sessions drop. WhatsApp logs linked devices out now and then. Watch account_status; re-linking means another QR scan, in person.

Old CLI versions get refused. A client outdated error in the sync log means rebuilding from a current tag. Pin a version you tested.

One writer per store. The sync daemon holds the lock. Read commands are fine alongside it; a second writer is not.

Back up deliberately. The store is one directory. Copying it copies the credentials too — encrypt the backup or skip it.