https://github.com/jdtoon/wachat
Lean native desktop WhatsApp client in Go (Gio + whatsmeow). No Electron, no webview.
https://github.com/jdtoon/wachat
desktop gio go low-memory sqlite whatsapp whatsmeow
Last synced: about 1 month ago
JSON representation
Lean native desktop WhatsApp client in Go (Gio + whatsmeow). No Electron, no webview.
- Host: GitHub
- URL: https://github.com/jdtoon/wachat
- Owner: jdtoon
- License: mit
- Created: 2026-05-23T05:02:15.000Z (about 1 month ago)
- Default Branch: main
- Last Pushed: 2026-05-23T07:11:44.000Z (about 1 month ago)
- Last Synced: 2026-05-23T07:12:52.720Z (about 1 month ago)
- Topics: desktop, gio, go, low-memory, sqlite, whatsapp, whatsmeow
- Language: Go
- Size: 313 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
- Codeowners: .github/CODEOWNERS
- Security: SECURITY.md
- Roadmap: docs/roadmap.md
Awesome Lists containing this project
README
# wachat
[](./LICENSE)
[](./go.mod)
[](https://github.com/jdtoon/wachat/releases)
[](https://github.com/jdtoon/wachat)
[](https://github.com/jdtoon/wachat/commits/main)
A lean, native desktop WhatsApp client written in Go. No Electron, no webview,
no bundled browser. One process, low RAM, fast cold start.
> The official WhatsApp desktop client is Electron and eats hundreds of MB
> of RAM at rest. `wachat` aims to be the opposite: tens of MB at idle, sub-
> second cold start, and smooth scrolling over 100k-message histories.
**Status:** early bootstrap. See [Status](#status).
## North star
Performance first. Memory usage must be independent of history size, achieved
by **virtualized rendering** (only visible rows laid out) and **keyset
pagination** (never `OFFSET`). If a harder approach is meaningfully faster or
lighter, we take the harder approach.
## Stack
| Layer | Choice | Why |
|------|--------|-----|
| Language | Go | One lean runtime, protocol + UI in one process |
| Protocol | [`whatsmeow`](https://pkg.go.dev/go.mau.fi/whatsmeow) | Mature multidevice library, compiled into the binary |
| GUI | [`Gio`](https://gioui.org) | Immediate-mode, pure-Go, GPU-rendered, no webview |
| Storage | [`modernc.org/sqlite`](https://pkg.go.dev/modernc.org/sqlite) | Pure-Go SQLite driver — no cgo, clean cross-compilation |
| Media | Files on disk | Never blobs in the DB |
See [`CLAUDE.md`](./CLAUDE.md) for the full architecture and the non-negotiable
constraints (no Electron/webview, memory must be independent of history size,
no DB blobs for media, no `OFFSET` pagination, etc.).
## Build & run
Requires **Go 1.25+** (pinned by `modernc.org/sqlite`). No CI pipeline — every
gate runs locally.
```bash
make hooks # one-time: installs the local pre-commit hook
make check # gofmt + go vet + go test ./...
make run # go run .
make build # produces stripped `wachat` binary
```
First run pairs with your phone via QR code displayed in the terminal
(companion-device flow, same as WhatsApp Web). The session is persisted to
SQLite — subsequent launches skip pairing.
## Project layout
```
wachat/
main.go # Gio window + frame loop
internal/
wa/ client.go # whatsmeow connect, pairing, events
store/ store.go # SQLite: schema, dedup insert, keyset paging
ui/ app.go # view-model state, two-pane layout
chatlist.go # virtualized chat list
messages.go # virtualized message view
scripts/ # pre-commit hook + installer
Makefile
```
## Status
Tracks [`CLAUDE.md §12`](./CLAUDE.md#12-status--next-steps).
- [x] `go.mod` + initial dependencies pinned (`modernc.org/sqlite`, `whatsmeow`, `gioui.org`)
- [x] whatsmeow client wrapper (connect, QR pairing, session container)
- [x] SQLite store: schema, insert-with-dedup, keyset page query
- [x] Event handler → channel → notify pipeline (persist-first, non-blocking send)
- [x] Gio frame loop + two-pane layout (chat list | messages)
- [x] Virtualized chat list — bounded by viewport regardless of total
- [x] Virtualized message view + bubble rendering (newest-at-bottom, grouping)
- [x] whatsmeow connect + in-window QR pairing
- [x] Auto-page older messages on scroll-near-end
- [x] Send text end-to-end (composer + optimistic bubble)
- [x] Full-text search across all history (SQLite FTS5)
- [x] Jump-to-message from search hits (PageAround)
- [x] Pairing state machine + connection banner + auto-reconnect
- [x] Dark mode + density toggle + narrow-window collapse + persisted settings
- [x] Lazy media cache + visibility tracker (see `internal/media`)
- [x] Perf measurement harness — `make bench` (see Performance budget below)
## Performance budget (validated as we go)
Targets:
- Idle RSS in the tens of MB (vs. hundreds for the official client)
- Sub-second cold start on a warm OS file cache
- No frame hitches scrolling a 100k-message chat
Measured via `make bench` at v0.1.0 (100k seeded messages, Intel Core
Ultra 7 258V, Windows 11, pure-Go build):
| Metric | Result |
|-------------------------|-------------------------------|
| `store.Open` | ~6 ms |
| Go heap after 100k msgs | ~0.5 MB (**flat with N**) |
| Go runtime `Sys` (RSS≈) | ~16 MB |
| First keyset page (50) | ~1 ms |
| Deep keyset page (90%) | ~20 ms (cold index pages) |
| Bulk insert | ~2.5k msgs/s (incl. FTS5) |
The flat heap line is the load-bearing measurement — it proves the
"memory must be independent of history size" constraint from
[`CLAUDE.md §2`](./CLAUDE.md#2-non-negotiable-constraints). FTS5
indexing on insert is the headline cost between v0.0.1 and v0.0.5
(seed throughput dropped from ~10k msgs/s to ~2.5k); both numbers are
far above real-world inbound message rates.
## Contributing
See [`CONTRIBUTING.md`](./CONTRIBUTING.md). The TL;DR: run `make hooks` once,
keep `make check` green, never violate the guardrails in `CLAUDE.md §11`.
## Risks & scope
`wachat` uses a reverse-engineered library and violates WhatsApp's Terms of
Service; there is real account-ban risk. It is built for **personal use**.
Bulk-messaging or automation features are explicitly out of scope.
## License
[MIT](./LICENSE).