wa — safe WhatsApp automation

io.github.yolo-labz由 GitHub 账号 yolo-labz 发布v2.3.0更新于 Oct 25, 2026

Safety-first WhatsApp tools: draft-gated sends, enforced rate limits, append-only audit.

已验证STDIO仅桌面效率与工作流沟通与协作

概览

AI 生成的概览

本地 MCP 服务器,让助手通过带安全管制的守护进程读取和发送 WhatsApp 消息,默认将发送内容转为草稿等待人工批准。

可以做什么
通过强制执行的安全管线发送或起草文本与媒体消息,并可安排稍后发送;草稿队列可列出并批准待审草稿。搜索已同步消息、获取某个聊天的近期消息、列出活跃聊天、解析联系人并读取群组信息。等待匹配的入站回复并转写语音消息。查看守护进程连接与会话状态,支持只读模式和工具集范围限制。
适用场景
当你希望 AI 代理操作个人 WhatsApp 账号——起草或发送消息、检索历史、等待回复——同时保留人工审批和速率限制时使用。不适合群发消息或多租户网关场景。
运行要求
以本地桌面进程(stdio)运行,可通过签名安装包或 wa 命令行工具的 mcp serve 子命令启动。需要运行 wa 守护进程并已配对 WhatsApp 多设备会话(二维码或手机号配对),且需在允许名单中为每个收件人授予发送/读取权限。支持 macOS 和 Linux。
安装前请注意
它通过逆向工程的多设备协议操作真实个人 WhatsApp 账号,而非官方 Cloud API,存在账号风险。发送默认走草稿审批,但可切换为直接发送模式;允许名单、速率限制和预热仍会生效。仅追加的审计日志会记录发送和允许名单决策,且不会自动轮转。入站消息文本不可信,可能包含提示注入。

安装

在 SourceWeft 中

  1. 打开 控制台中的 wa — safe WhatsApp automation,将其添加到工作区。
  2. 为需要使用其工具的对话启用该服务。

Desktop only,通过 STDIO。 STDIO 服务会启动本地进程,因此需要 SourceWeft 桌面宿主。

其他 MCP 客户端

参照 仓库 中的启动说明。

README

[wa: persistent WhatsApp daemon for Claude Code]

wa

Personal WhatsApp automation CLI + daemon, written in Go.

A hexagonal Go daemon that owns a WhatsApp Multi-Device session and a thin JSON-RPC client that talks to it — safe enough to let a language model send messages on your behalf, crash-safe enough to survive a power loss mid-migration, and paranoid enough to refuse every destructive flag you might expect.

[CI] [Release] [Go] [License] [Conventional Commits] [Nix flake] [OpenSSF Scorecard] [SLSA L2] [Verified by Sigstore]

Quickstart · Install · Manual · Architecture · Security · Contributing · Português (BR)


Capability

Pattern. Persistent WhatsApp daemon over a Unix socket — one wad process holds the Multi-Device session, ratchet store, and websocket; a thin wa JSON-RPC client invokes it.

Trade-off. ~30 MB RSS per profile and a 5–8 s cold connect, in exchange for sub-second warm-call latency on every subsequent wa send. Per-call session reattachment is avoided entirely.

Use when. A shell pipeline, cron job, or Claude Code plugin needs to dispatch WhatsApp messages with predictable latency and a non-overridable safety pipeline (default-deny allowlist + rate limiter + warmup ramp + append-only audit log) sitting below every RPC path.

bash
brew install yolo-labz/tap/wawad &                                                # daemon, single instance per profilewa pair                                              # QR-code pairing on first runwa allow add [email protected] --actions sendwa send --to [email protected] --body "hello"

Demo

[wa CLI demo]

Every frame is real output from the binaries in this repo, recorded against a throwaway unpaired daemon in a private temp directory. doctor honestly warns that the device is unpaired (and may flag missing backups in the fresh sandbox). No real account data is used. The revoke command is help only, not a mutation.

The recording is generated, never hand-edited. The source of truth is the plain-text docs/assets/wa-demo.tape; regenerate the GIF, MP4, WebM, static PNG and captured text with one command:

bash
nix develop -c ./scripts/record-demo.sh

Prefer playback controls or reduced motion? Use MP4, WebM, the static frame, or the text equivalent. Recording notes cover isolation, safety controls, byte budgets and checks. Playback is 1× and silent; no live sends are demonstrated.

How wa compares

Closest peers in the reverse-engineered-WhatsApp ecosystem:

Capabilitywa (this repo)whatsmeow directwhatsapp-web-cli
Persistent daemon (sub-second warm-call)yesno (per-call session attach)no (browser-driven)
JSON-RPC over Unix socketyesn/a (library, not a daemon)no (Chrome bridge)
Default-deny allowlist (per-action)yesmanual implementationno
Non-overridable limiter (2/sec + 30/min)yesmanual implementationno
Warmup ramp for fresh sessionsyesmanual implementationno
Append-only JSON-Lines audit logyesmanual implementationno
SLSA L2 + Sigstore signed releasesyesn/ano
Dual SBOM (CycloneDX 1.6 + SPDX 2.3)yesn/ano
CGO_ENABLED=0 static binaryyesdepends on consumerno (browser-driven)
Inbound prompt-injection firewallyesn/ano

For multi-tenant REST gateways see EvolutionAPI or WAHA — different shape of problem, listed in What this is NOT.

What this is

Two binaries, one repo:

  • wad — long-running daemon that owns the WhatsApp session, the SQLite ratchet store, and the websocket to web.whatsapp.com. Runs under systemd (Linux), launchd (macOS), or a NixOS module. Single-instance per profile, never as root.
  • wa — thin JSON-RPC client that speaks to wad over a unix socket. This is what shell scripts, cron jobs, and Claude Code plugins actually invoke.

It is built on go.mau.fi/whatsmeow — the library that powers mautrix-whatsapp at production scale — because it is the only reverse-engineered WhatsApp library actively maintained in 2026. It also ships a safety-first MCP server (wa mcp serve) so an AI agent drives WhatsApp through the same non-bypassable safety pipeline — draft-gated by default. See MCP server.

What this is NOT

  • Not a bulk-messaging tool. The rate limiter is non-overridable and there is no --force flag anywhere.
  • Not a multi-tenant SaaS. Each wa install is scoped to one person, with optional multi-profile isolation for work/personal splits.
  • Not a Matrix bridge. Use mautrix-whatsapp if that's what you want.
  • Not a REST gateway. Use EvolutionAPI or WAHA if that's what you want.
  • Not the official WhatsApp Cloud API. This project uses the reverse-engineered Multi-Device protocol via whatsmeow.

Quickstart

bash
# Install (Homebrew — macOS + Linuxbrew)brew install yolo-labz/tap/wa
# Or via Nix (recommended for NixOS/nix-darwin users)nix profile install github:yolo-labz/wa
# Or the checksum-verified installer (80 lines — inspect first if you like)curl -fsSL https://raw.githubusercontent.com/yolo-labz/wa/main/install.sh | bash
# Or Docker — single distroless container (~12 MB); /data holds the sessiondocker compose up -d   # see docker-compose.yaml; pair via `docker compose exec`
# Start the daemon (default profile)wad &
# Pair your phone — QR code in terminalwa pair
# Allowlist yourself (default-deny policy)wa allow add [email protected] --actions send
# Send a messagewa send --to [email protected] --body "hello from wa"
# Install as a persistent system servicewad install-service --profile default
# Hand it to an AI agent — MCP over stdio, draft-gated sends by default:# the agent PROPOSES messages into a human-review queue; nothing leaves# until you run `wa draft approve`. Add to Claude Desktop/Code, Cursor:#   {"mcpServers": {"wa": {"command": "wa", "args": ["mcp", "serve"]}}}wa mcp serve --help

The recipient flag is spelled --to, --jid, or --group depending on the command; --chat <jid> is accepted as a universal alias on all of them (the original flags still work).

For the full tour including multi-profile setup, shell completion, migration, and the audit log, see docs/manual.md.

MCP server

wa mcp serve exposes the daemon to AI agents over the Model Context Protocol (stdio transport). Every tool call runs through the same non-bypassable safety pipeline as the CLI — default-deny allowlist, enforced short-window limits (2/second with burst 2; 30/minute with burst 30; no daily ordinary-send cap) with warmup, and an append-only audit log. Sends default to draft mode: the model proposes a message into a human-review queue and nothing leaves until you run wa draft approve. That draft-gate is the point — it is what makes handing WhatsApp to an autonomous agent safe.

Live on the official MCP Registry as io.github.yolo-labz/wa; one-click install via the signed .mcpb bundle on each release, or wire it manually into Claude Desktop/Code or Cursor:

json
{ "mcpServers": { "wa": { "command": "wa", "args": ["mcp", "serve"] } } }

Tools (12):

ToolPurpose
wa_send_messageSend (or draft) a message through the safety pipeline
wa_send_mediaSend (or draft) a media file
wa_schedule_messageSchedule a send for later (drafts respected)
wa_draft_reviewList pending drafts awaiting human approval
wa_search_messagesFull-text search across synced messages
wa_get_threadFetch recent messages of one chat
wa_list_chatsList chats with recent activity
wa_resolve_contactResolve a name/phone to a JID
wa_group_infoGroup metadata and participants
wa_wait_for_replyBlock until a matching inbound event arrives
wa_transcribe_voiceTranscribe a voice note
wa_statusDaemon connection and session status

It also serves resources (read-only context surfaces) and prompts (pre-built workflows) per the MCP spec. Scope the surface with --toolsets; the send mode is draft (default), direct, or deny, and a read-only set hides every mutating tool.

See docs/mcp-registry.md for the registry publish flow and bundle internals.

Install

Homebrew (macOS + Linux)

bash
brew install yolo-labz/tap/wa

Nix flake

bash
# One-shotnix run github:yolo-labz/wa -- profile list
# Install to profilenix profile install github:yolo-labz/wa
# Dev shell (go + gopls + golangci-lint + goreleaser + sqlite + jq)nix develop github:yolo-labz/wa

NixOS module — import the system module and enable:

nix
{  inputs.wa.url = "github:yolo-labz/wa";  outputs = { self, nixpkgs, wa, ... }: {    nixosConfigurations.myhost = nixpkgs.lib.nixosSystem {      modules = [        wa.nixosModules.default        { services.wa.enable = true; services.wa.profile = "default"; }      ];    };  };}

home-manager module — import for a per-user installation:

nix
{  imports = [ wa.homeManagerModules.default ];  services.wa = {    enable = true;    profile = "default";    autoStart = true;  };}

GoReleaser tarball + supply-chain verification

bash
VERSION=v2.0.5                                      # pin to a known tagARCH=linux_amd64                                    # or darwin_arm64 / linux_arm64BASE="https://github.com/yolo-labz/wa/releases/download/$VERSION"
# 1. Download the tarball + checksumscurl -LO "$BASE/wa_${VERSION#v}_${ARCH}.tar.gz"curl -LO "$BASE/checksums.txt"
# 2. Verify the SHA256 (defends against accidental corruption)sha256sum -c checksums.txt --ignore-missing
# 3. Verify the SLSA-L2 attestation against THIS tarball (not just the#    manifest) via GitHub's native attestation store. From v2.0.5 onward#    every artifact (.tar.gz / .deb / .rpm / .apk) is registered as its#    own attestation subject — earlier releases (≤ v2.0.4) only attested#    `checksums.txt` and would 404 on per-artifact verify. Requires#    `gh` CLI ≥ 2.50 (Sigstore bundle v0.3.1 support). The verify step#    transparently checks Rekor v2 transparency-log inclusion proof on#    the Sigstore bundle — no separate `rekor-cli` invocation needed.gh attestation verify "wa_${VERSION#v}_${ARCH}.tar.gz" --repo yolo-labz/wa
# 4. Installtar xzf "wa_${VERSION#v}_${ARCH}.tar.gz"install -m 0755 wa wad ~/.local/bin/

The gh attestation verify step proves that this exact tarball was produced by the yolo-labz/wa GoReleaser job on the exact commit SHA the tag points at. Every release ships:

  • wa_<version>_<os>_<arch>.tar.gz — the platform tarball
  • checksums.txt — SHA256 of every artifact
  • checksums.txt.sigstore.json — Cosign-signed Sigstore bundle for checksums.txt (legacy/optional verify path; not needed when using gh attestation verify against the artifact above)
  • sbom.cdx.json — CycloneDX 1.6 SBOM (full repo, syft)
  • sbom.spdx.json — SPDX 2.3 SBOM (full repo, syft)
  • sbom.gomod.{wa,wad}.cdx.json — Go-native CycloneDX SBOMs (cyclonedx-gomod, per binary, with stdlib + license info)
  • openvex.json — OpenVEX 0.2 statement file. VEX-aware scanners (Trivy, Grype) consume this to filter "is this CVE actually exploitable in wa?" — most transitive CVEs are not_affected because the depguard-enforced port boundary keeps them off the runtime path. Statements grow as findings emerge; current baseline is empty pending per-CVE triage.
  • CHANGELOG.md — git-cliff generated changelog

darwin users — unsigned release: releases cut without an Apple Developer account ship the darwin-arm64 binary unsigned and un-notarized. Gatekeeper will quarantine it on first launch. Strip the quarantine flag before running:

bash
xattr -cr ~/.local/bin/wa ~/.local/bin/wad

Or install via the Homebrew tap (brew install yolo-labz/tap/wa) — brew rebuilds from source locally and bypasses the quarantine entirely. Signed + notarized darwin binaries return on the next GA tag cut after APPLE_DEVELOPER_ID_APPLICATION_{CERT,KEY} secrets are populated.

go install

The module path includes the /v2 suffix per Go's semantic import versioning rule for v2+ releases.

bash
# Pin to a tag (recommended — reproducible builds need a known revision):go install github.com/yolo-labz/wa/v2/cmd/[email protected]go install github.com/yolo-labz/wa/v2/cmd/[email protected]
# Or follow main:go install github.com/yolo-labz/wa/v2/cmd/wa@latestgo install github.com/yolo-labz/wa/v2/cmd/wad@latest

go install on a tag does NOT inherit the GoReleaser build flags (-trimpath, -buildvcs=true, ldflag-stamped version). The resulting binary will run, but wa --version reports (devel). Use the GoReleaser tarball or the Homebrew tap if version-stamped, reproducible binaries matter to you.

Multi-profile

Run two WhatsApp accounts side-by-side — personal and work, or one per client — with full process isolation. Each profile has its own session.db, allowlist.toml, audit.log, rate limiter, unix socket, and warmup timestamp.

bash
wa profile create workwad --profile work &wa --profile work pairwa --profile work status
wa profile list# PROFILE   ACTIVE  STATUS      JID                          LAST_SEEN# default   *       connected   [email protected] 2026-04-11T17:00:00Z# work              connected   [email protected] 2026-04-11T17:00:00Z

Profile selection precedence (highest wins):

  1. --profile <name> flag
  2. WA_PROFILE env var (empty = unset)
  3. $XDG_CONFIG_HOME/wa/active-profile pointer
  4. Singleton (if exactly one profile exists)
  5. Literal default

See docs/manual.md §4.

Safety

wa is safe enough to let a language model invoke it on your behalf. The safety pipeline is non-overridable and lives inside the daemon, below every RPC path:

  • Allowlist, default-deny. Per-action (read / send / group.add / group.create). Hot-reloaded on SIGHUP. Mutated via wa allow add/remove.
  • Rate limiter. Ordinary sends are capped at 2/sec (burst 2) and 30/min (burst 30), with no daily ordinary-send cap. No --force flag. Ever.
  • Warmup ramp. Fresh sessions run at 25 % caps while age is <7 days, 50 % from age 7 through <14 days, and 100 % from age 14 days onward. Timestamp sourced from the session store so daemon restarts don't reset the clock.
  • Audit log. Append-only JSON Lines at $XDG_STATE_HOME/wa/<profile>/audit.log. Every send + every allowlist decision + every migration is recorded. Never auto-rotated — back it up yourself.
  • Inbound prompt-injection firewall. Inbound message bodies are wrapped in <channel source="wa" ...>…</channel> tags before reaching Claude Code so the model can structurally distinguish "the user typed this in the terminal" from "an unknown contact sent this".
  • Crash-safe migration. 007→008 migration uses a write-ahead marker + single os.Rename pivot + fsync barriers. Proven by a subprocess SIGKILL injection test that kills the process between every step and asserts zero data loss on recovery.
  • Socket hardening. Unix socket + sibling lockfile opened with O_NOFOLLOW (CVE-2025-68146), parent directory verified mode 0700 + euid-owned, SO_PEERCRED check on every accept, umask-narrowed bind.

Full threat model: SECURITY.md.

Architecture

                  ┌──────────────────────────┐                  │         cmd/wa           │  thin JSON-RPC client                  │  (cobra, no business     │  (stateless, re-invokable,                  │   logic, no state)       │   what scripts actually call)                  └─────────────┬────────────┘                                │  JSON-RPC 2.0 over                                │  $XDG_RUNTIME_DIR/wa/<profile>.sock                                ▼┌──────────────────────────────────────────────────────────────┐│                         cmd/wad                              │  composition root│                                                              │  (one process per profile)│   ┌─────────────────────────┐    ┌────────────────────────┐  ││   │ internal/app/dispatcher │◄──►│ Safety pipeline:       │  ││   │  (use cases, port-only) │    │  allowlist + rate lim  │  ││   │                         │    │  + warmup + audit      │  ││   └───────────┬─────────────┘    └────────────────────────┘  ││               │                                              ││               ▼  9 port interfaces                           ││   ┌───────────────────────────────────────────────────────┐  ││   │             internal/adapters/secondary              │  ││   │  whatsmeow | sqlitestore | sqlitehistory | memory    │  ││   │  slogaudit | …                                       │  ││   └───────────────────────────────────────────────────────┘  │└──────────────────────────────────────────────────────────────┘                                │                                │  Signal Protocol ratchets +                                │  WhatsApp Multi-Device websocket                                ▼                        web.whatsapp.com
  • Hexagonal core (internal/domain/, internal/app/) depends only on the 9 port interfaces in internal/app/ports.go. Not one import of go.mau.fi/whatsmeow outside the adapters, enforced by a golangci-lint depguard rule that is CI-blocking.
  • Port-boundary fakes: every secondary adapter ships an in-memory twin for tests. The contract test suite (internal/app/porttest/) runs against any adapter. No test reaches the real websocket.
  • One daemon per profile. Feature 008 bounds the blast radius: a crash in one profile's daemon never touches another. Trade-off: ~30 MB RSS per profile.
  • No CGO. Ever. modernc.org/sqlite is the only SQLite path. Enforced in the Nix flake (env.CGO_ENABLED = "0"), in GoReleaser, and in the go.mod toolchain flags.

For the full design and reliability principles, see docs/manual.md.

Command reference (at a glance)

Client (wa):

CommandPurpose
wa pairScan a QR or use --phone for phone-code pairing
wa statusNon-blocking connection state
wa send --to <jid> --body <text>Send a text message (allowlist + rate limiter apply)
wa sendMedia --to <jid> --path <file>Send an image/video/audio/document (over --remote, the client-local file is auto-uploaded then sent by sha256)
wa reply --to <jid> --quoted-id <id> --body <text>Send a quoted reply that threads under a message
wa markRead --chat <jid> --messageId <id>Mark a message as read
wa react --chat <jid> --messageId <id> --emoji 👍Add/remove a reaction
wa msg revoke|edit|forward|star|disappearingModerate an already-sent message
wa poll create --chat <jid> --question <text> --option <text> --option <text>Send a poll (2-12 options, --selectable for multi-choice)
wa poll vote --chat <jid> --poll-id <id> --option <n>Vote on a poll (--option repeatable for multi-select)
wa presence composing|recording start|stop --chat <jid>Send typing / recording indicators
wa groupsList joined groups
wa group create|leave|add|remove|promote|demote|edit|inviteAdminister a group (admin actions)
wa chat list / wa chat last-activeList chats, most-recently-active first (read-only)
wa chat archive|pin|mute|mark-unread --chat <jid>Change chat-level state
wa history --chat <jid> [--before <id>]Show one chat's history (paginated)
wa messages list --chat <jid> --media-type audioFilter messages by chat, media kind, direction, time window
wa search --query <fts5>Full-text (FTS5) search across all messages
wa thread get --chat <jid> [--cursor <c>]Fetch a cursor-paginated message window
wa export --chat <jid> [--since <ts>]Export a chat oldest-first as NDJSON
wa contacts list / wa contacts search --query <q>List or trigram-search the local contact directory
wa contact block|unblock|blocklist|lid|pnServer-side blocklist + PN↔LID resolution
wa privacy get [--key <k>]Read the live account privacy settings
wa privacy set --key <k> --value <v>Change one privacy setting (e.g. lastSeen → contacts)
wa media list --chat <jid> [--sender <jid>] [--caption <text>] [--since/--until <rfc3339>]List media with cache status (sha256, size, duration), narrowed before you fetch
wa media resolve|download|fetchResolve / lazy-fetch content-addressed media
wa media gc --dry-runPreview GC candidates as NDJSON + a reclaimable-bytes summary
wa --remote <url> push <file>Upload a client-local file to a remote daemon's media store; prints its sha256 for reuse with sendMedia --sha256
wa schedule send|list|cancel|updateSchedule future sends (pending → fired|cancelled|failed)
wa draft list|get|approve|rejectHuman-review draft queue
wa labels list|create|delete|assign|unassignWhatsApp Business labels (behind labels flag)
wa session logout-allUnlink every device from the account
wa allow add <jid> --actions send,readGrant actions
wa allow remove <jid>Revoke all actions
wa allow listDump the allowlist
wa wait --events message --timeout 30sBlock until an event arrives
wa subscribe --events <types> [--since <seq>]Stream filtered events as NDJSON (cursor-resumable)
wa stream [--chat <jid>]Live-tail incoming messages as NDJSON (wraps wa subscribe --events message)
wa sync force [--chat <jid>] [--count N]Force an immediate history pull when the DB lags the phone
wa sync statusShow the on-demand sync engine state (in-flight pulls, queue depth)
wa embeddings status / wa embeddings purge --yesInspect / drop the vector index (behind embeddings flag)
wa healthNon-blocking liveness probe (paired/connected/last-event)
wa doctorRun 11 self-diagnostic checks against the local install
wa debug pprof [cpu|heap|goroutine|block|mutex]Capture a runtime profile from wad
wa audit verify [--path <log>] [--key <keyfile>]Verify the audit-log HMAC chain
wa config featuresShow resolved feature flags (embeddings, scheduled_sends, labels)
wa profile list/use/create/rm/showMulti-profile lifecycle
wa migrate [--dry-run|--rollback]Explicit 007→008 migration
wa panicUnlink device + wipe local session
wa versionVersion, commit, build date
wa upgradePrint the upgrade command for your install method
wa completion bash|zsh|fish|powershellShell completion script

Daemon (wad):

CommandPurpose
wad [--profile <name>] [--log-level <lvl>]Run the daemon in the foreground
wad install-service --profile <name>Install systemd/launchd unit for a profile
wad uninstall-service --profile <name>Remove only the specified profile's unit
wad migrate [--dry-run|--rollback]Internal target for wa migrate

Full flag reference: docs/manual.md §6.

Inspecting the daemon (when the CLI "lies")

The daemon owns the SQLite truth; the CLI is a thin client over it. When a command's output looks wrong — a chat you expect is missing, a message count seems stale — work down this ladder. The first rung resolves almost every case without touching the database.

1. First-class discovery commands (the supported path)

These read straight from the daemon and need no SQL. They replace the old docker cp messages.db + hand-rolled SELECT workaround:

QuestionCommand
Which chats exist, most-recent first?wa chat list (or wa chat last-active)
What's in one chat, filtered?wa messages list --chat <jid> --media-type audio --since 2026-01-01T00:00:00Z
Who's in the contact directory?wa contacts list / wa contacts search --query <name>
What media is cached on disk?wa media list --chat <jid> (SIZE / CACHED / SHA256 columns)
Which of these attachments are the ones I actually want?wa media list --chat <jid> --sender <jid> --caption <text> — narrow first; fetching a group by timestamp proximity pulls down other people's private files
What would GC reclaim?wa media gc --dry-run (NDJSON candidates + reclaimable bytes on stderr)
Messages on the phone but missing here?wa sync force (global) or wa sync force --chat <jid> (blocks until that chat's pull lands), then re-run the query
Is a sync in flight right now?wa sync status (syncing, in-flight force pulls, worker queue depth)
Watch new messages arrive live?wa stream (or wa stream --chat <jid>)

Add --json to any of them for NDJSON you can pipe to jq. Against a Dokku deploy, run the same commands inside the container — dokku enter <app> -- /usr/local/bin/wa chat list — or over an SSH-forwarded socket with scripts/wa-remote chat list.

2. wa doctor

If the discovery commands themselves look impossible (e.g. zero chats on a paired account), run wa doctor. It runs 11 checks — socket perms, pairing state, on-disk layout version, audit-log size — and prints a hint: line for every WARN/FAIL. A schema_version WARN means the on-disk layout drifted from domain.LayoutSchemaVersion; run wa migrate to forward-migrate.

3. Raw SQLite (last resort)

The production image is gcr.io/distroless/static-debian12:nonroot — no shell, no sqlite3, no ls/find. You therefore cannot dokku enter <app> -- sqlite3 …, and there is intentionally no wa-debug sidecar image (it would widen the attack surface the distroless base exists to shrink). Instead, query a WAL-safe snapshot from the Dokku host, which has its own sqlite3:

bash
# messages.db lives under XDG_DATA_HOME → on the host storage mount:DB=/var/lib/dokku/data/storage/<app>/data/wa/<profile>/messages.db   # <profile> defaults to "default"
# Snapshot via the online-backup API (WAL-safe; never `cp` a live WAL DB):ssh dokku.example.com sudo -u dokku-65532 \    sqlite3 "$DB" ".backup '/tmp/messages-snapshot.db'"
# Inspect the snapshot, not the live file:ssh dokku.example.com sqlite3 /tmp/messages-snapshot.db \    "SELECT chat_jid, push_name, COUNT(*) AS msgs, MAX(ts) AS last       FROM messages      WHERE chat_jid LIKE '%@s.whatsapp.net'      GROUP BY chat_jid ORDER BY last DESC LIMIT 20;"

On-disk layout (container path → Dokku host path under /var/lib/dokku/data/storage/<app>):

FileContainer pathPurpose
messages.db/data/data/wa/<profile>/messages.dbHistory + FTS5
session.db/data/data/wa/<profile>/session.dbSignal session (never copy live)
contacts.db/data/data/wa/<profile>/contacts.dbContact mirror
media blobs/data/cache/wa/media/sha256/Content-addressed cache
.schema-version/data/config/wa/.schema-versionOn-disk layout version

Copying a live WAL-mode DB with cp can produce a corrupt file — always go through .backup. See the Backups section of docs/deploy/dokku.md.

Development

bash
# Clonegit clone [email protected]:yolo-labz/wa.gitcd wa
# Devshell (Nix users)nix develop
# Or use your own Go toolchain (1.26+, matching go.mod)go version
# Buildgo build ./cmd/wa ./cmd/wad
# Test (race detector on by default; ~20s wall clock)go test -race ./...
# Lintgolangci-lint run
# Formatgofumpt -w .
# Nix build + smoke testnix build .#default && ./result/bin/wa version
# Snapshot release (local only, no publish)goreleaser release --snapshot --clean --skip=publish

Workflow: every change lands via PR against main, commit subjects follow Conventional Commits. See CONTRIBUTING.md.

CI/CD runs on a self-hosted GitHub Actions runner pool (label set [self-hosted, dokku], with one runner — wa-sonar-runner — additionally labelled sonar). Required-check jobs: detect, lint (golangci-lint), test (race + shuffle + coverage), sonar (SonarQube scan, consumes the cover.out artifact), nix (nix flake check + nix build .#default + smoke), commitlint (PR title), Reproducibility (two-build byte-identity), OSV-Scanner (vuln DB + Go call-graph reachability via internal govulncheck), gitleaks (secret scan), CodeQL (Go + actions). Hosted runners are reserved for OpenSSF Scorecard (needs fresh image guarantees). Release workflow triggers on v* tags and publishes GoReleaser tarballs + dual SBOMs (CycloneDX 1.6 + SPDX 2.3 + Go-native per-binary) + Cosign Sigstore bundles + GitHub-native attestations to GitHub Releases. Apple notarization and Homebrew tap publication graceful-degrade when their secrets are absent.

Project status

VersionHighlights
v2.0.2Hot-fix the system.hello handshake regression (issue #41) so every CLI command works against a v2 daemon.
v2.0.1Release-pipeline hot-fixes: cyclonedx-gomod path, syft CycloneDX 1.6 cap, gomod proxy off, Apple GA gate post-rc7 amendment. Broken: CLI handshake — superseded by v2.0.2.
v2.0.0Parity hardening — 7 new ports + 20 P0 methods + idempotency + chat-state + blocker + privacy + profile editor + group admin + poll manager. OTel runtime metrics, fuzz, doctor. JSON-RPC frozen at protoVersion: 2.
v1.2.xAgent-experience tier-3 release — embeddings sidecar, scheduled drafts, contact search FTS5 trigram, observability ring buffer.
v1.0.x – v1.1.xFirst production releases — supply-chain attestations, OSV-Scanner V2, OpenSSF Scorecard, Cosign signing, dual SBOMs.
v0.xPre-1.0 — multi-profile, crash-safe migration, hardened systemd + launchd, all benchmarked SCs pass with 7.6×–870× headroom.

Live backlog (deferred until a profile pin or external action exists): homebrew_casks: migration (replaces brews:, but Linuxbrew loses formula path on darwin-only casks — needs apt/rpm via nFPM as a Linux substitute first), LID/BSUID dual-keying in domain.JID (Meta June 2026 username rollout), default.pgo PGO profile (capture pipeline + commit), actions/attest migration off the attest-build-provenance@v4 wrapper.

Who this is for

One person — the maintainer. Multi-tenancy, hosted SaaS, and group-bulk-messaging use cases are explicitly out of scope. The entire safety story assumes a single-user threat model where FileVault / LUKS is the encryption boundary and wa panic is the recovery button. If that's not what you want, use one of the alternatives listed in What this is NOT.

License

Apache-2.0. The go.mau.fi/whatsmeow upstream is MPL-2.0, which is file-level copyleft and does not propagate to consumers (Mozilla MPL FAQ Q9–Q11). The Apache choice matches the precedent set by Anthropic's official Telegram channel plugin and gives an explicit patent grant.

Acknowledgements

See also (yolo-labz ecosystem)


Services

Compliance-grade AI architecture for regulated workloads — async-first, USD-denominated, LATAM-based / EN-fluent. See blog.home301server.com.br/services.

来源:README.md,提交 d2efe6d

工具

0
SourceWeft 尚未在沙箱中启动过该软件包,因此其工具未知。

版本历史

1
  1. v2.3.0最新Sep 16, 2026

评分

已满足适用检查项的 50%

该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。

  • 近 30 天内可连通25 分

    请保持端点能够响应 MCP 握手;SourceWeft 每 30 天探测一次。

  • 已声明认证方式10 分

    请在注册中心条目中声明凭据请求头,或保持端点能够响应探测,以便判定其认证方式。

  • 已列出工具15 分

    请让服务器在无凭据时响应 tools/list,或发布可在沙箱中无凭据启动的软件包。

  • 描述必需15 分
  • 图标10 分
  • 文档15 分
  • 许可证10 分