Help us shape the future of Go! Take the 2026 Go Developer Survey

wawarden

module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 4, 2026 License: MIT

README

WaWarden

WaWarden is a self-hosted WhatsApp gateway whose link to a WhatsApp account and REST and MCP interfaces are planned and not built yet: the current milestone, M0, is a scaffold (see Status). When complete, it will link to one personal WhatsApp account as a companion device and expose that account to your own AI agents and applications, over REST and MCP, with access scoped per client and per chat: each client will get a token that may read, or read and write, only the chats on its allowlist.

It is not:

  • an official WhatsApp API, and it is not affiliated with WhatsApp or Meta. It is built on an unofficial implementation of WhatsApp's multi-device protocol, and WhatsApp can restrict, log out or ban an account that links an unofficial client;
  • a hosted or multi-account service: one process serves one account for its owner;
  • a network security layer: it terminates no TLS, keeps no IP allowlist and has no user accounts;
  • a bulk messaging tool: sending will be limited to the chats a client may write to, paced and budgeted, and with no first contact unless a client is explicitly allowed it (M3).

Security posture

  • Tokens are bearer credentials. Whoever holds a token and can reach the service has that token's access. Tokens are not bound to a host or a network address.
  • The service listens on loopback and speaks plain HTTP. By default the client listener is on 127.0.0.1:8080, the admin listener on 127.0.0.1:8082 and the health listener on 127.0.0.1:8081.
  • Network access control and TLS are your responsibility as the deployer. Put the service behind something that decides who may reach it and encrypts the path: a VPN or mesh network that forwards to loopback, a private subnet, or a reverse proxy that terminates TLS. Never expose WaWarden to the public internet. A listener bound to a non-loopback address makes the service log a warning at every start, whatever WAWARDEN_LOG_LEVEL is set to.
  • The admin listener is guarded by the admin token alone. It is opened only when the token's SHA-256 is configured; the service never accepts the token itself in its configuration. Restrict who can reach it more tightly than the client listener.
  • The service refuses to start rather than run misconfigured: on any WAWARDEN_ variable it does not implement, a plaintext admin token, a non-loopback health address, running as root, or a data directory that is not private to the service's user.
  • Browser-originated requests are refused. Any request to the client or admin listener carrying an Origin or Sec-Fetch-Site header gets 403.
  • Your WhatsApp account is at risk. See the residual risks in the threat model.

Status

WaWarden is at milestone M0, a scaffold. It does not connect to WhatsApp yet.

What works today:

  • wawarden serve validates its configuration, refuses to start on anything it does not implement, and opens the client and health listeners, and the admin listener when an admin token hash is configured.
  • The client listener has no routes and no client tokens yet: it refuses every request, with 401 for anything that reaches authentication.
  • The admin listener, opened only when an admin token hash is configured, serves Prometheus metrics at GET /metrics to the admin token.
  • The health listener answers GET /healthz.
  • wawarden admin init generates the admin token; wawarden healthcheck is the container health check; wawarden version prints the build.
  • Internally: the policy core that mints read, write and admin grants, route registration that requires a policy class, and architecture tests and lint rules that enforce both.

Planned:

Milestone Scope
M1 The WhatsApp engine: pairing guarded by an account check, history sync, the session and the message archive in SQLite, admin status, pair and reconnect, notification events, metrics on standard output, encrypted backups.
M2 Clients with per-chat read scopes and expiring tokens; the read API over REST (chats, messages, search, change feed) and MCP; the audit trail.
M3 Sending over REST and MCP, with idempotency, pacing, per-client budgets and a first-contact rule; nightly encrypted backups with retention; the v1.0 documentation.

Quick start (M0)

The commands below use the default ports 8080, 8081 and 8082 on 127.0.0.1. If one is taken, choose other addresses with WAWARDEN_LISTEN, WAWARDEN_HEALTH_LISTEN or WAWARDEN_ADMIN_LISTEN (see the configuration reference) and apply them to every command, not only serve: export the variables in the shell, so that healthcheck reads the same WAWARDEN_HEALTH_LISTEN, and change the ports in the curl URLs to match.

From source

You need git, curl and Go. The build uses the Go version on the toolchain line of go.mod; with its default settings, the go command (1.21 or later) downloads that version when the installed one is older.

git clone https://github.com/dortort/wawarden
cd wawarden
go build -trimpath -o wawarden ./cmd/wawarden
./wawarden version

Generate an admin token. It is printed once, together with its SHA-256; the service is configured with the hash only. mktemp -d creates a directory that only you can read:

demo=$(mktemp -d)
./wawarden admin init > "$demo/admin.txt"
sed -n 's/^sha256: //p' "$demo/admin.txt" > "$demo/admin.sha256"
sed -n 's/^token: /Authorization: Bearer /p' "$demo/admin.txt" > "$demo/admin.header"

Run the service with a data directory. It creates the directory with mode 0700 and refuses to start as root:

WAWARDEN_DATA_DIR="$demo/data" \
WAWARDEN_ADMIN_TOKEN_SHA256_FILE="$demo/admin.sha256" \
  ./wawarden serve &

It writes JSON log lines to standard output; wait for the line with "event":"ready". Then check its health, send a client request, and read the metrics with the admin token:

./wawarden healthcheck && echo healthy
curl -i http://127.0.0.1:8080/v1/me
curl -H @"$demo/admin.header" http://127.0.0.1:8082/metrics

M0 has no client routes and no client tokens, so every client request is refused:

HTTP/1.1 401 Unauthorized
Cache-Control: no-store
Content-Length: 24
Content-Type: application/json; charset=utf-8
Www-Authenticate: Bearer
X-Content-Type-Options: nosniff
Date: <date>

{"error":"unauthorized"}

The metrics are the failed-authentication counters and the build information:

# HELP wawarden_admin_auth_failures_total Failed admin authentications.
# TYPE wawarden_admin_auth_failures_total counter
wawarden_admin_auth_failures_total 0
# HELP wawarden_auth_failures_total Failed client authentications.
# TYPE wawarden_auth_failures_total counter
wawarden_auth_failures_total 1
# HELP wawarden_build_info Build metadata of the running binary.
# TYPE wawarden_build_info gauge
wawarden_build_info{version="dev",revision="<commit>",dev="false"} 1

Stop the service; it shuts down gracefully and exits 0. Then delete the temporary directory, which holds the admin token and the data directory:

kill %1
wait
rm -rf "$demo"
From the container image

Images are published to ghcr.io/dortort/wawarden by the release workflow only. Take the image index digest from a release's notes, and verify the image as RELEASING.md describes. If no release is listed yet, use the steps from source.

image=ghcr.io/dortort/wawarden@sha256:<digest>
demo=$(mktemp -d)
docker run --rm "$image" admin init > "$demo/admin.txt"
sed -n 's/^sha256: //p' "$demo/admin.txt" > "$demo/admin.sha256"
sed -n 's/^token: /Authorization: Bearer /p' "$demo/admin.txt" > "$demo/admin.header"

docker volume create wawarden-data
docker run -d --name wawarden --read-only --stop-timeout 15 \
  --mount type=volume,src=wawarden-data,dst=/data \
  -e WAWARDEN_ADMIN_TOKEN_SHA256="$(cat "$demo/admin.sha256")" \
  -e WAWARDEN_LISTEN=0.0.0.0:8080 \
  -e WAWARDEN_ADMIN_LISTEN=0.0.0.0:8082 \
  -p 127.0.0.1:8080:8080 -p 127.0.0.1:8082:8082 \
  "$image"

docker exec wawarden /wawarden healthcheck && echo healthy
curl -i http://127.0.0.1:8080/v1/me
curl -H @"$demo/admin.header" http://127.0.0.1:8082/metrics
docker stop wawarden

docker rm wawarden
docker volume rm wawarden-data
rm -rf "$demo"

The last three commands remove the container, its data volume and the temporary directory that holds the admin token.

The image runs as 65532:65532 with serve as its default command. /data is the only path the service writes and the only one that must be writable; a new named volume mounted there gets the right owner and mode. Run the container with a read-only root filesystem, as the example does with --read-only, so that /data is also the only writable path.

Inside the container, a listener on 127.0.0.1 cannot be reached through a published port, so the example binds the client and admin listeners to 0.0.0.0 and publishes them on the host's loopback only. The service logs a listener_not_loopback warning for each of them, and other containers on the same Docker network can reach them. The image has no shell, so docker run --health-cmd cannot run its health check; use the exec form ["/wawarden","healthcheck"] in Compose or Kubernetes. The configuration reference describes the full container contract.

Documentation

Document Content
docs/configuration.md Every environment variable, flag, subcommand, exit code, startup refusal, listener, log event and metric of the current build, and the container contract.
docs/threat-model.md Assets, actors, trust boundaries, invariants, the mechanisms that enforce them with their milestones, and residual risks.
SECURITY.md How to report a vulnerability, supported versions and scope.
RELEASING.md How releases are built, what they contain, and how to verify and reproduce one.

Verifying a release

Every release is built from main by a workflow that needs the maintainer's approval, rebuilt independently and compared bit for bit, attested and signed. RELEASING.md gives the commands to verify the image, the binaries and their SBOMs, and to reproduce a release from source.

Licence

MIT. See LICENSE.

Directories

Path Synopsis
cmd
wawarden command
Command wawarden runs the WaWarden gateway and its operator subcommands.
Command wawarden runs the WaWarden gateway and its operator subcommands.
internal
api
Package api serves the client and admin surfaces through policy-classed registrations, plus a fixed loopback health endpoint.
Package api serves the client and admin surfaces through policy-classed registrations, plus a fixed loopback health endpoint.
api/dto
Package dto declares every response body the API can send, so nothing else can reach a caller.
Package dto declares every response body the API can send, so nothing else can reach a caller.
app
Package app wires configuration, logging, metrics, handlers and listeners into the running service.
Package app wires configuration, logging, metrics, handlers and listeners into the running service.
buildinfo
Package buildinfo reports the version, VCS state and flavour of the running binary.
Package buildinfo reports the version, VCS state and flavour of the running binary.
config
Package config turns an injected environment into a validated configuration or a startup refusal.
Package config turns an injected environment into a validated configuration or a startup refusal.
listeners
Package listeners is the only package that opens sockets: named plain-HTTP listeners served by hardened servers.
Package listeners is the only package that opens sockets: named plain-HTTP listeners served by hardened servers.
listeners/listenertest
Package listenertest reports the TCP sockets the calling process holds in the listening state.
Package listenertest reports the TCP sockets the calling process holds in the listening state.
metrics
Package metrics is a small registry of counters and gauges with Prometheus text exposition.
Package metrics is a small registry of counters and gauges with Prometheus text exposition.
policy
Package policy decides what an authenticated caller may do and mints the grants that prove it.
Package policy decides what an authenticated caller may do and mints the grants that prove it.
safego
Package safego starts goroutines and recovers panics without exposing panic values.
Package safego starts goroutines and recovers panics without exposing panic values.
token
Package token generates and hashes admin bearer tokens.
Package token generates and hashes admin bearer tokens.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL