wa-go

module
v0.1.10 Latest Latest
Warning

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

Go to latest
Published: Aug 1, 2026 License: MIT

README

wa-go

CI Go Reference Go

A WhatsApp Web (multi-device) protocol stack written from scratch in Gono whatsmeow, no Baileys. The wire framing, the Noise handshake, the Signal E2E layer (X3DH + Double Ratchet), group sender keys, app-state (LTHash), media crypto — all reimplemented to get full control: device fingerprint, human send cadence, lightweight multi-account, and access to raw frames.

Think of it as Baileys, in Go, built from the bytes up. The wa/ package is the public entry point (the equivalent of Baileys' index.ts).

import "github.com/jfelipesjc/wa-go/wa"

Why build it from scratch?

Existing Go options wrap a fixed feature set and a fixed fingerprint. Rebuilding the protocol — validated byte-for-byte against golden traces captured from real Baileys — buys things a wrapper can't:

  • Fingerprint control — own the device props, client payload, and version.
  • Cadence control — a send-pacer models human timing instead of bursting.
  • Raw frame hooks — inspect/modify nodes pre-encrypt and post-decrypt.
  • Lightweight multi-session — many numbers in one process, supervised.
  • Zero-CGO storage — SQLite via modernc.org/sqlite, so static builds.

Status

Decomposed into 9 sub-projects (specs in docs/superpowers/):

# Sub-project Status
0 Capture harness (golden traces from real Baileys)
1 Wire layer (framing · Noise XX · binary-node codec)
2 Pairing/Auth (multi-device, QR + pairing-code) proven live
3 Signal/E2E (X3DH · Double Ratchet) from scratch ✅ proven live (golden vectors + real msgs decrypted)
4 Messaging 1:1 (send + receive) proven live — text + media + reaction
4+ Groups (sender keys), media crypto+transfer, all msg types proven live — create + sender-key send + receive; participant mgmt offline
5 App-state sync (LTHash) — decode/encode/resync archiveChat proven live; pin/mute/resync offline
6 Control layer (fingerprint · SendPacer · frame hooks) ✅ offline
7 Instance manager (multi-session) ✅ offline (-race, 50 instances)
8 Evolution-compat HTTP/WS ✅ separate repo → wa-evolution
Proven live vs offline

This distinction is tracked honestly:

  • Proven live = exercised end-to-end against real WhatsApp: pairing (QR and pairing-code — the latter fully automated via ADB), receiving (1:1 between two real numbers, delivered via webhook) and sending text + image + reaction, group create + sender-key send, archiveChat (app-state), and privacy-settings fetch.
  • Offline = code is complete and passes its tests, but those tests are golden-vector / round-trip, not yet smoke-tested against a live account: group participant management (add/remove/promote), app-state pin/mute/resync, profile/privacy writes, status/stories, newsletters, business, calls, and media receive.

"Offline" does not mean untested — it means tested without the network. See the test suite: 440+ tests, -race green.

Feature coverage

Messages: text, reply, mention, image/video/audio/document/sticker (crypto + HTTP up/download), location, contact, reaction, edit, delete, poll, view-once, buttons/list/template/interactive; receive parses every type into rich events. Groups & communities: sender-key E2E send/receive, create, add/remove/ promote/demote, subject/description, invites, settings, ephemeral, sub-groups. App-state: archive/pin/mute/read/star/clear/delete chat, labels, resync. Profile/privacy: name/status/picture, fetch status/picture, privacy settings, block/unblock/blocklist. Status/Newsletters/Business: text status, channel create/follow/admin, business profile/catalog/orders. Other: presence/typing/receipts, calls (offer/reject/terminate), onWhatsApp, history sync. Infra: multi-session manager, per-instance fingerprint, send pacer, raw frame hooks. → Full API on pkg.go.dev.

Install

go get github.com/jfelipesjc/wa-go@latest

Quick start

package main

import (
	"context"
	"fmt"
	"time"

	"github.com/jfelipesjc/wa-go/wa"
)

func main() {
	store, err := wa.OpenStore("./creds.db") // SQLite; reused on next run
	if err != nil {
		panic(err)
	}
	c := wa.NewClient(store)

	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
	defer cancel()
	go c.Connect(ctx) // pairs via QR (if needed), then logs in

	for ev := range c.Events() {
		switch ev := ev.(type) {
		case wa.QREvent:
			fmt.Println("scan in WhatsApp > Linked devices:\n", ev.Code)
		case wa.LoggedInEvent:
			fmt.Println("logged in")
			c.SendText(ctx, "5512999999999@s.whatsapp.net", "hello from wa-go")
		case wa.MessageEvent:
			if !ev.IsGroup {
				fmt.Printf("← %s: %s\n", ev.From, ev.Text)
			}
		case wa.DisconnectedEvent:
			fmt.Println("disconnected:", ev.Reason)
		}
	}
}

Pairing

Two ready-to-run commands (use an isolated, sacrificial number — see below):

# QR — renders a QR in the terminal to scan
go run ./cmd/wa-pair -db ./wa-pair.creds.db -timeout 120s

# Pairing code — prints an 8-char code to type in WhatsApp > Linked devices
go run ./cmd/wa-paircode -phone 5512999998888 -db ./paircode.creds.db

Both flows (companion_hello → primary_hello → companion_finish → pair-success → login) are validated end-to-end against real WhatsApp.

Multi-session

go run ./cmd/wa-manager -dir ./sessions -concurrency 8

wa.NewManager() supervises many Clients with auto-reconnect and exponential backoff, aggregating every instance's events into one stream.

Architecture

wa/                 public facade (type aliases over internal/)
internal/
  wire/             3-byte framing, token dictionary, node codec, Noise XX
  signal/           X3DH, Double Ratchet, sender keys
  keys/  store/     key material + SQLite persistence
  client/           connection, send/receive, groups, app-state, profile…
  appstate/         LTHash decode/encode/resync
  media/            media crypto + HTTP transfer
  control/          fingerprint, SendPacer, frame hooks
  manager/          multi-session supervisor
  waproto/          protobuf schema subset (regenerate via `go generate`)

Development

go test ./...            # offline suite (unit + golden vectors + round-trips)
go test -race ./...      # what CI runs
go run ./cmd/wiredump    # replay a trace, decode the pair-device frame (no net)

Regenerate the protobuf after editing internal/waproto/waproto.proto:

go generate ./internal/waproto/    # needs protoc + protoc-gen-go v1.36.x

Re-capture golden traces (optional, needs Node): cd harness && npm i && node harness/capture.mjs connects to real WhatsApp up to the QR (no number needed) and rewrites testdata/traces/.

⚠️ Operational note

Connecting from Go talks to real WhatsApp. Use an isolated, sacrificial number, and do not re-pair / remove the same account in a loop — that burns the account's device-management and the server stops relaying your sends.

Relationship to wa-evolution

This is the library. An Evolution-API-style multi-instance HTTP service that imports it lives in wa-evolution.

License

MIT © José Felipe Leal

Directories

Path Synopsis
cmd
wa-features command
Command wa-features relogs in with saved creds and exercises the lib features that aren't surfaced in the wa/ facade or wa-evolution yet — profile, status (stories), newsletters and app-state resync — so they can be validated LIVE.
Command wa-features relogs in with saved creds and exercises the lib features that aren't surfaced in the wa/ facade or wa-evolution yet — profile, status (stories), newsletters and app-state resync — so they can be validated LIVE.
wa-manager command
Command wa-manager runs N WhatsApp sessions concurrently in one process using the instance Manager (#7).
Command wa-manager runs N WhatsApp sessions concurrently in one process using the instance Manager (#7).
wa-pair command
Command wa-pair runs the interactive WhatsApp multi-device pairing flow.
Command wa-pair runs the interactive WhatsApp multi-device pairing flow.
wa-paircode command
Command wa-paircode runs the WhatsApp multi-device pairing-by-code flow ("link with phone number") instead of QR scanning.
Command wa-paircode runs the WhatsApp multi-device pairing-by-code flow ("link with phone number") instead of QR scanning.
wiredump command
wiredump replays the connect_pair golden trace in memory, runs the Noise XX handshake, and prints each decoded binary node.
wiredump replays the connect_pair golden trace in memory, runs the Noise XX handshake, and prints each decoded binary node.
internal
appstate
Package appstate implements WhatsApp Web "app state" sync: decoding the encrypted SyncdPatch blobs the server pushes (contacts, chat names, mute, read, pin, archive, ...) and maintaining the per-collection state with an LTHash integrity check.
Package appstate implements WhatsApp Web "app state" sync: decoding the encrypted SyncdPatch blobs the server pushes (contacts, chat names, mute, read, pin, archive, ...) and maintaining the per-collection state with an LTHash integrity check.
client
Package client: appstate_sync.go implements app-state RESYNC — fetching and applying the server's app-state patches/snapshots for a set of collections.
Package client: appstate_sync.go implements app-state RESYNC — fetching and applying the server's app-state patches/snapshots for a set of collections.
control
Package control is the wa-go anti-ban "Control Layer".
Package control is the wa-go anti-ban "Control Layer".
keys
Package keys generates the cryptographic identity of a WhatsApp multi-device "companion" client.
Package keys generates the cryptographic identity of a WhatsApp multi-device "companion" client.
manager
Package manager runs N WhatsApp sessions (each a client.Client with its own store) concurrently inside one process, with per-instance supervision, exponential-backoff reconnection with jitter, and an aggregated event stream tagged by instance name.
Package manager runs N WhatsApp sessions (each a client.Client with its own store) concurrently inside one process, with per-instance supervision, exponential-backoff reconnection with jitter, and an aggregated event stream tagged by instance name.
media
Package media implements WhatsApp's media payload encryption: the symmetric crypto layer that cifrar/decifrar image/audio/video/document blobs.
Package media implements WhatsApp's media payload encryption: the symmetric crypto layer that cifrar/decifrar image/audio/video/document blobs.
signal
Package signal implements the subset of the Signal protocol (X3DH + Double Ratchet) that WhatsApp multi-device uses for 1:1 encrypted messages.
Package signal implements the subset of the Signal protocol (X3DH + Double Ratchet) that WhatsApp multi-device uses for 1:1 encrypted messages.
store
Package store persists a WhatsApp device's credentials and the signal-protocol state.
Package store persists a WhatsApp device's credentials and the signal-protocol state.
waproto
Package waproto contains the subset of the WhatsApp Web protobuf schema needed for pairing/auth: ClientPayload (and its dependencies) plus the ADV* device-identity messages used by the pair-success flow.
Package waproto contains the subset of the WhatsApp Web protobuf schema needed for pairing/auth: ClientPayload (and its dependencies) plus the ADV* device-identity messages used by the pair-success flow.
wire
Package wire implements the WhatsApp binary wire protocol.
Package wire implements the WhatsApp binary wire protocol.
ws
Package ws provides the WhatsApp WebSocket transport adapter.
Package ws provides the WhatsApp WebSocket transport adapter.
Package wa is the public API facade for the wa-go WhatsApp library.
Package wa is the public API facade for the wa-go WhatsApp library.

Jump to

Keyboard shortcuts

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