Phase 0 — Prereqs & Meta Setup
Effort: S (mostly waiting on Meta verification)
Goal
Have all external accounts, secrets, DNS, and host baseline ready before any code is written.
Deliverables
Meta side
- Meta Business Manager account verified (business name, address, tax id).
- Meta App created at developers.facebook.com (App type: Business).
- WhatsApp product added to the App.
- WhatsApp Business Account (WABA) created or linked.
- v1 phone number added to the WABA and verified (SMS / voice OTP). Note: number verification can take hours; start this first.
- System User at the Business level with:
whatsapp_business_messagingpermission on the WABAwhatsapp_business_managementpermission on the WABA- Permanent access token generated and copied (do not use temporary 24h tokens).
- App Secret captured from the App Dashboard → Settings → Basic (for
WA_APP_SECRET). - Webhook verify token chosen (32 random chars; this becomes
WA_WEBHOOK_VERIFY_TOKEN). - IDs noted:
WA_DEFAULT_PHONE_NUMBER_ID(Meta’s numeric id, not the display number)WA_DEFAULT_WABA_ID
DNS
- A (and AAAA if applicable) record for
wa.<yourdomain>→ Ubuntu host public IP. - TTL ≤ 300 so cert issuance + future moves are quick.
dig wa.<yourdomain>from a third location returns the expected IP.
Ubuntu host baseline (22.04 or 24.04 LTS)
- Non-root sudo user
wamcp. - UFW enabled:
22/tcpallowed only from admin source IPs (the owner’s static IPs / VPN).80/tcpand443/tcpopen to the world (needed for certbot + Meta webhooks + MCP HTTPS).- All other inbound denied by default.
- Docker (Engine + Compose v2) installed; the
wamcpuser is in thedockergroup. - Base tools:
git,curl,make,htop,unattended-upgrades,fail2ban(for sshd). - Directories owned by the service user:
/var/lib/whatsapp-mcp/media/var/lib/whatsapp-mcp/backups/var/lib/whatsapp-mcp/audit-archive
unattended-upgradesconfigured for security updates.- Time sync:
chronyorsystemd-timesyncd.
Inngest Cloud
- Inngest Cloud account.
- New Environment named
prod. - Event Key and Signing Key copied for the env.
- (Optional) a second
devEnvironment for Phase 3 development.
Secret vault decision
- v1 (Phases 1–6):
.envfile at/opt/whatsapp-mcp/.env, mode0600, owned by service user, never committed. - Phase 7: migrate to SOPS + age. Decision documented; not implemented yet.
Runbooks (skeletons)
docs/operations/phase-0-meta-setup.md— step-by-step with screenshots of the Meta Dashboard click paths.docs/operations/host-bootstrap.md— ansible-style checklist (manual for v1; can be automated later).
Tests
Phase 0 has no automated tests (no code yet). Manual verification only — see Acceptance.
Code documentation
None — no code in this phase. Runbook docs above are the deliverable.
Acceptance
- From the Ubuntu host as
wamcp:returns 200 with the expected WABA name.curl -s -H "Authorization: Bearer $WA_DEFAULT_ACCESS_TOKEN" \ "https://graph.facebook.com/v23.0/$WA_DEFAULT_WABA_ID?fields=id,name" dig +short wa.<yourdomain>from outside the host resolves to the host’s public IP.ssh wamcp@<host>works from an allowed admin IP;sshfrom a non-allowed IP times out.docker run --rm hello-worldworks as thewamcpuser.- The Inngest Cloud dashboard shows the
prodEnvironment with Event Key + Signing Key visible (still hidden behind reveal, but present). - The three
/var/lib/whatsapp-mcp/*dirs exist with correct ownership and mode0750.
Risks
- Meta number verification can take hours or fail. Start Phase 0 first; do not block Phase 1 on it (Phase 1 can be built dry against a test number Meta provides automatically in the dashboard).
- Permanent token is bound to the System User. Losing the System User breaks the token. Document the recovery path in
docs/operations/incident-runbook.md(Phase 7). - Graph API version pinned at
v23.0. Do not uselatest. Plan to bump quarterly via the ops/upgrade.md procedure.
Definition of Done
Tick each box as the item is verified. Phase is done when every box including Phase signoff is checked.
Meta side
- Meta Business Manager account verified.
- Meta App (Business type) created with WhatsApp product added.
- WABA created or linked.
- v1 phone number added to the WABA and verified (OTP).
- System User created with
whatsapp_business_messaging+whatsapp_business_managementon the WABA. - Permanent access token generated and stored securely.
- App Secret captured.
- Webhook verify token chosen (32+ random chars).
-
WA_DEFAULT_PHONE_NUMBER_IDandWA_DEFAULT_WABA_IDrecorded.
DNS
- A (and AAAA if applicable) record for
wa.<yourdomain>→ host IP. - TTL ≤ 300.
-
dig wa.<yourdomain>from a third location returns the expected IP.
Ubuntu host baseline
- Non-root sudo user
wamcp. - UFW: 22/tcp restricted to admin IPs, 80/tcp + 443/tcp open, default deny.
- Docker Engine + Compose v2 installed;
wamcpindockergroup. - Base tools installed (
git,curl,make,htop,unattended-upgrades,fail2ban). -
/var/lib/whatsapp-mcp/{media,backups,audit-archive}exist with correct ownership and mode0750. -
unattended-upgradesconfigured. - Time sync (
chronyorsystemd-timesyncd) running.
Inngest Cloud
- Account created.
-
prodEnvironment created. - Event Key + Signing Key copied to secure storage.
Runbooks
-
docs/operations/phase-0-meta-setup.mdwritten (with screenshots of Meta dashboard click paths). -
docs/operations/host-bootstrap.mdwritten.
Acceptance verified
-
curl … /v23.0/$WA_DEFAULT_WABA_IDreturns 200 with expected WABA name. -
dig +short wa.<yourdomain>from outside returns the host IP. - SSH from allowed IP works; SSH from non-allowed IP times out.
-
docker run --rm hello-worldworks aswamcp. - Inngest dashboard shows
prodenv with both keys present.
Phase signoff
- Phase 0 complete. README.md status table updated to ✅.