¶ One connector instance only
The Baileys connector holds every live WhatsApp socket. Never scale it past instance_count: 1, and do not rebuild it for unrelated changes.
Prepared 1 September 2026
A practical field guide for an engineer taking over ReplyBox: what it is, what can break production, how releases work, and where to look when something goes wrong.
Start here
The Baileys connector holds every live WhatsApp socket. Never scale it past instance_count: 1, and do not rebuild it for unrelated changes.
api.replybox.utopiagroup.com.my is production with live paying customers. Every production test is a real production test.
Pull requests have no CI checks. The meaningful gates only run after code lands on main.
Check alembic heads before every release. Split heads are the most common deploy failure.
Nobody pushes, merges, deploys, or releases without owner approval for that specific change.
Product context
ReplyBox is a multi-tenant shared inbox SaaS for WhatsApp. Teams work one WhatsApp number together with assignment, tagging, automation, broadcasts, and a public API.
It is both a product and an internal shared service. Roughly 105 organisations use it. Utopia BD Lead runs WhatsApp outreach through it, and the payroll disciplinary bridge sends Show Cause and Warning Letters through it.
Feature surface
Threads, assignment, status, pins, stars, notes, activity feed, search, contact rail, command palette, and keyboard shortcuts.
Text, media, voice, documents, stickers, locations, reactions, replies, edits, revokes, contact cards, groups, mentions, and receipts.
QR pairing, lifecycle state, health tracking, migration, reparenting, and multiple channels per organisation.
Baileys WhatsApp Web and Meta WhatsApp Cloud API, including Coexistence on one number.
Message Flows, conditions, HTTP steps, AI nodes, keyword replies, scheduled messages, and paced broadcasts.
Organisations, RBAC, invitations, UtopiaSpace SSO, REST API, webhooks, API tokens, GitHub bug reports, Sentry, and notifications.
Repository
apps/api/FastAPI backend. REST, Socket.IO, queue producer. The source of truth.apps/web/Next.js 14 App Router frontend.apps/baileys-connector/Node.js Baileys WhatsApp Web adapter. Disposable and restart-safe.infra/docker/Local Postgres, Redis, MinIO, and optional pgweb tooling.docs/Canonical design and runbook documents.SHIPPED.mdRelease log with re-runnable verification evidence.Architecture
REST over httpOnly cookie JWT via apps/web/lib/api.ts.
BullMQ jobs on Redis through the Python producer.
HMAC-SHA256 signed HTTP POST to /internal/v1/....
Socket.IO events in one room per organisation: org:{id}.
Do not add a fifth path. Do not add a second implementation of any existing path. Extend the existing transport instead.
Infrastructure
Vercel production alias: replybox-dev.vercel.app.
DigitalOcean App Platform public service, port 8000, health at /healthz.
DigitalOcean internal service, private port 8787, exactly one instance.
PRE_DEPLOY job running sh scripts/release.sh.
Managed PostgreSQL 16, managed Valkey/Redis, and private Spaces storage.
SGP1 Singapore. Estimated cost: USD 45-80/month depending on sizing.
CI/CD
A hand-rolled git diff decides whether API and connector builds run.
Postgres 16 replays every Alembic revision and asserts exactly one head.
Push :sha, copy that digest to :latest, and verify both tags resolve to the same digest.
There is no doctl apps create-deployment. Moving :latest is the deployment trigger.
Parallel latest pushes can cancel each other. New job kinds require connector support before API production.
Release workflow
development via PR.cd apps/api && alembic heads. It must print exactly one line.development -> main. Checks 0 is expected and proves nothing./healthz returns 200.SHIPPED.md: run id, deployment id, image digests, and probes.Local dev and testing
./dev.shBring up infra, API, web, and connector.ruff check .Python linting.mypy appBackend type checking.pytestBackend tests; check skip count.pnpm lintFrontend lint.pnpm typecheckFrontend type checks.pnpm buildFrontend production build.vitestConnector test suite.Nothing except Alembic replay gates a deploy. Run lint, type checks, tests, and builds deliberately before promoting.
Operations
Run python -m app.cli diagnose-stuck-sends. It separates send failure from browser cache mismatch.
Run python -m app.cli reconcile-tags to report and repair drift between tag tables.
Use doctl apps list-deployments, doctl apps get-deployment, and component logs. Do not stack another deploy on top.
CPU tracks media volume. Drain the media/avatar backlog through BullMQ before blaming app code.
Do
SHIPPED.md with evidence.Do not
whatsapp queue.Security
Production secrets live in DO App Platform encrypted env vars and GitHub Actions secrets.
INTERNAL_HMAC_SECRET, CHANNEL_SESSION_KEY, and MEDIA_OBJECT_KEY must match between API and connector.
Do not commit secrets, paste them into docs, tickets, wiki pages, or chat.
Production Valkey/Redis has no Trusted Sources configured. Authentication protects it, but queue write access would be serious.
Some shipped features are silently disabled by unset or misplaced DO env vars. Audit against app/config.py.
Do not merge CHANNEL_SESSION_KEY and MEDIA_OBJECT_KEY.
Known gaps
Lookup map
First week
replybox_ro.doctl; confirm you can list deployments and read logs.python -m app.cli diagnose-stuck-sends against production once.app/config.py.