wa — WhatsApp daemon with an auditable trust boundary
A persistent agent channel that treats the inbound message body as an untrusted audit perimeter. Go, hexagonal, Sigstore-signed.
The problem
Give an autonomous agent a real WhatsApp channel and every inbound message body becomes an injection surface. A crafted message must never mutate the allowlist or trigger a send. A partner-API library leaking across the codebase makes the trust boundary impossible to audit.
The solution
Hexagonal layering enforced by golangci-lint, so domain and app code physically cannot import the WhatsApp library — it is quarantined to adapters. Inbound messages arrive wrapped in <channel> tags, so the agent can't trust a body to mutate state. Default-deny allowlist + per-second/minute/day rate limits + a 25/50/100% warmup ramp before the first send. SQLite ratchet store on modernc.org/sqlite — CGO_ENABLED=0, no CGO build chain.
- Constraint
- An autonomous agent with a real WhatsApp channel makes every inbound message body an injection surface — and a partner-API library leaking across the codebase makes the trust boundary impossible to audit.
- Decision
- Quarantine the WhatsApp library to adapters and let golangci-lint fail the build if domain/app code imports it; wrap inbound bodies in <channel> tags so they can't be trusted to mutate state; default-deny the allowlist and ramp sends 25/50/100% on a fresh session.
- Outcome
- A daemon whose trust boundary is auditable: the library is contained, the message body can't escalate privilege, and every release is Sigstore-signed at SLSA L2.
Overview
A persistent WhatsApp session wrapped in a daemon (`wad`) with a thin CLI client (`wa`) that issues commands over a JSON-RPC unix socket. SQLite ratchet store, allowlist + rate limiter + warmup ramp before the first send. Inbound messages are wrapped in <channel> tags so a downstream agent cannot trust a message body to mutate the allowlist or trigger sends. Apache-2.0, GoReleaser to GitHub Releases + homebrew-tap + flake.nix.