Headless WhatsApp backend for Nexus. Owns WhatsApp connectivity via whatsmeow, persists sessions, API keys, webhooks, and jobs in Postgres, and exposes an auth-unaware REST API over Redis streams. See docs/prd/prd-nexus-core.md in the parent workspace for product requirements.
Hexagonal (entity <- ports <- service <- adapters), cmd/ is the composition root:
cmd/
http/ REST API composition root (Fiber + oapi-codegen contracts)
worker/ Redis two-queue worker — router + executor (selected via WORKER_ROLE)
internal/
core/entity/ domain types: Session, APIKey, WebhookConfig, Job, QRCode
core/ports/ repository + service interfaces owned by the core
service/ business logic (use cases), depends only on ports
adapters/
postgresql/ pgx repositories with own dto/ + mappers
redis/ Streams producer + ingress/provider consumers
whatsmeow/ DeviceManager, QR handling, send/receive
webhook/ outbound webhook forwarder
crypto/ AES-256-GCM cipher + API key secret generator
handlers/http/ Fiber handlers, thin with own dto/ + mappers
config/ env + ~/.nexus/config.yaml loader (YAML takes precedence)
contracts/ generated Fiber server from OpenAPI (api.gen.go)
migrations/ golang-migrate SQL files
Dependencies point inward; every dependency crosses the boundary through a port interface. No authentication code exists by design.
| Binary | Role | Notes |
|---|---|---|
http |
REST API | All PRD endpoints under /api/v1 (Fiber), no auth |
worker |
Router + Executor | Redis Streams two-queue pipeline, role selected via WORKER_ROLE |
WORKER_ROLE controls which queues the worker drains:
| Value | Behavior |
|---|---|
all |
Runs both router and executor (default, suitable for local dev) |
router |
Drains nexus:queue:ingress (router group) and fans out to nexus:queue:provider:* |
executor |
Drains nexus:queue:provider:* (executor group) and executes via whatsmeow |
Executor is a singleton. Leadership is acquired via SETNX nexus:leader:executor with a 30s TTL and 10s renewal; a second executor replica fails fast. Within the leader pod, WORKER_EXECUTOR_WORKERS is kept at 1 to preserve per-number ordering for WhatsApp. The executor also runs a heartbeat loop (QUEUE_HEARTBEAT_INTERVAL_MS) that touches sessions.last_seen_at for connected clients and an inbound_listener that forwards incoming WhatsApp messages to the configured webhooks.
cp .env.example .env
# Generate an encryption key for API key secrets
openssl rand -base64 32 # -> API_KEY_ENCRYPTION_KEY
docker compose up -d postgres redis
make migrate-up
make run-http # or: make build && ./bin/http
make run-worker # or: ./bin/worker (WORKER_ROLE=all by default)Health check:
curl http://localhost:8080/healthConfiguration is loaded from ~/.nexus/config.yaml (or $NEXUS_CONFIG_PATH) with env vars as fallback. YAML takes precedence when both are present. A single cp .env.example .env covers both binaries — http reads PORT and worker reads WORKER_ROLE from the same file. nexus-core/.env.worker is supported for per-process overrides (e.g., docker run --env-file .env.worker) but is gitignored and optional; config.example.yaml at the workspace root shows the canonical YAML shape.
Key variables:
| Variable | Description | Default |
|---|---|---|
DB_DSN |
Postgres DSN (source of truth) | postgres://nexus:nexus@localhost:5432/nexus_core?sslmode=disable |
REDIS_ADDR |
Redis address for Streams | localhost:6379 |
API_KEY_ENCRYPTION_KEY |
32-byte base64 key for AES-256-GCM (required) | — |
API_KEY_ENCRYPTION_KEY_PREVIOUS |
Previous key for rotation | — |
WHATSMEOW_STORE_DSN |
WhatsApp device store DSN (separate DB, falls back to DB_DSN) |
postgres://nexus:nexus@localhost:5432/nexus_whatsmeow?sslmode=disable |
WORKER_ROLE |
Worker role: all | router | executor |
all |
Additional tuning: REDIS_INGRESS_STREAM, REDIS_PROVIDER_PREFIX, REDIS_BLOCK_MS, QUEUE_MAX_ATTEMPTS, QUEUE_SEND_TIMEOUT_MS, QUEUE_HEARTBEAT_TTL_MS, QUEUE_HEARTBEAT_INTERVAL_MS, WORKER_ROUTER_WORKERS, WORKER_EXECUTOR_WORKERS, WORKER_INGRESS_COUNT, WORKER_PROVIDER_COUNT. See .env.example and internal/config/config.go for the full list.
make build # CGO_ENABLED=0 binaries -> bin/http, bin/worker
make test # go test ./...
make vet # go vet ./...
make fmt # gofmt -w .
make migrate-up # migrate -path migrations -database $DB_DSN up
make migrate-down # migrate down 1
make docker-build # docker build -t nexus-core .Contracts are defined in ../nexus-contract/nexus-core.yaml (workspace root nexus-contract/). Generated Fiber server types live in internal/contracts/api.gen.go — do not edit by hand. Regenerate from the contract repo:
# from nexus-contract/
npm run contracts:gen
# or
go run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@latest -config oapi-codegen.yaml nexus-core.yamlMIT — see LICENSE.
Copyright (c) 2026 afikrim.