Skip to content

Debelu browser migration runbook ​

Prepared 2026-09-27. Follow this in order. Do not paste passwords, API secrets, one-time codes, private keys, or payment details into a chat or a screenshot. Record each completed step and its result. If a screen differs, stop at that step and inspect its current labels rather than guessing.

0. Restore the browser connection (optional) ​

  1. Keep Edge open with the existing signed-in Vercel, Cloudflare, AWS, Sentry, Meta, Supabase, Railway, GitHub, and Truehost tabs.
  2. In Edge's toolbar, open the Codex browser extension and check whether it says connected. If it asks to reconnect this tab/session, do so. Do not install an extension from a link in email or an unfamiliar website.
  3. If Edge offers a site-access permission, grant it only for the accounts you intend Codex to work with. Do not disable browser security warnings.
  4. Tell Codex that the browser is ready. If the connection still fails, use the manual steps below; no live DNS or payment change is needed just to restore the connection.

1. Publish SES records in Vercel DNS (current live provider) ​

Cloudflare already contains all six SES records, but its zone is pending and does not currently answer public DNS. Vercel is authoritative because the Truehost nameservers are ns1.vercel-dns.com and ns2.vercel-dns.com.

  1. Sign in to Vercel. Open your team franklinechisom96-9712s-projects, then Domains, and select debelu.com. Open DNS Records. Check that the existing Improvmx MX records and root SPF TXT remain present. All six SES records now resolve correctly from Vercel's authoritative nameserver; do not edit or duplicate them merely because an older DNS answer was wrong.

  2. Check the records below. Vercel generally expects the Name relative to debelu.com; enter the part shown in the Name column, not an extra .debelu.com. Use the full Value. For CNAME records, choose DNS-only if a proxy option appears. For MX, put 10 in the priority field.

    TypeNameValuePriority
    CNAMEel26mkey4vxsb4ylwfgo6frvgdb47yde._domainkeyel26mkey4vxsb4ylwfgo6frvgdb47yde.dkim.amazonses.com—
    CNAMEqqq7k2v4nm54tdhikj7csskxc4htnzam._domainkeyqqq7k2v4nm54tdhikj7csskxc4htnzam.dkim.amazonses.com—
    CNAMEut4mgysdrkx4pr2i6g6c7swz7rypdlxm._domainkeyut4mgysdrkx4pr2i6g6c7swz7rypdlxm.dkim.amazonses.com—
    MXbouncefeedback-smtp.eu-west-2.amazonses.com. (absolute hostname, including final dot)10
    TXTbouncev=spf1 include:amazonses.com ~all—
    TXT_dmarcv=DMARC1; p=none;—
  3. Do not change the bounce MX record now. An earlier DNS lookup returned feedback-smtp.eu-west-2.amazonses.com.debelu.com, but Vercel's live record list and fresh queries to both Vercel nameservers, Cloudflare's resolver, and Google's resolver all return the correct target feedback-smtp.eu-west-2.amazonses.com at priority 10. The live record is stored with an absolute trailing dot. The cause of the earlier answer is not established; recheck if AWS still reports MAIL FROM as pending.

  4. Do not remove mx1.improvmx.com, mx2.improvmx.com, or the root v=spf1 include:spf.improvmx.com ~all record. The bounce MX/TXT are on a separate subdomain and do not replace inbound support mail.

  5. Reopen each saved Vercel record and compare its full name, type, and value character-for-character with the table. Vercel should show exactly one _dmarc TXT and one bounce SPF TXT; duplicate SPF records at the same name are invalid.

  6. Optional independent check from a terminal: query the authoritative server directly, for example nslookup -type=CNAME el26mkey4vxsb4ylwfgo6frvgdb47yde._domainkey.debelu.com ns1.vercel-dns.com. Repeat for the other DKIM records and query -type=MX bounce.debelu.com and -type=TXT bounce.debelu.com and _dmarc.debelu.com.

2. Finish Amazon SES in London ​

  1. Open AWS Console > Amazon SES. Confirm the region selector says Europe (London) (eu-west-2). SES identities and sandbox approval are region-specific; do not accidentally do this in Stockholm.
  2. Verified in AWS on 2026-09-28: debelu.com has identity status Verified, 2048-bit Easy DKIM Successful with signatures Enabled, and custom MAIL FROM bounce.debelu.com Successful. MX failure behavior is Reject message. No further SES DNS correction is needed.
  3. Production access remains pending. The London account dashboard still showed Sandbox (200 emails/day, 1 email/second). AWS support case 179049774500257 requested more use-case information. A response describing Debelu's transactional traffic, registered recipients, preferences, and planned bounce/complaint controls was submitted on 2026-09-28. The case changed to Customer action completed; wait for AWS review and verify the dashboard says production access granted before live customer sends.
  4. Set up bounce and complaint visibility before customer sends. In SES, create a configuration set (or equivalent event destination) that records BOUNCE, COMPLAINT, and DELIVERY events to an alertable AWS destination such as SNS/EventBridge/CloudWatch. Test alert delivery with a controlled address. Keep the account-level suppression list enabled.
  5. Two different credentials are needed:
    • A least-privilege IAM/API credential limited to SES SendEmail for the verified debelu.com identity; it belongs only in Supabase Edge Function secrets as SES_ACCESS_KEY_ID and SES_SECRET_ACCESS_KEY.
    • SES SMTP credentials from SMTP settings for Supabase Auth's Custom SMTP settings. These are not the API credentials. Create or enter credentials yourself if the site prompts for keys or a secret. Never paste them into the repository or chat.
  6. In Supabase > Project Settings > Edge Functions/secrets, set SES_REGION=eu-west-2, the two SES API secrets, and EMAIL_FROM using an address under debelu.com such as Debelu <[email protected]>. Set EMAIL_REPLY_TO to a working inbound support address. Set SES_CONFIGURATION_SET to the configuration set created in step 4 so delivery, bounce, and complaint events are emitted. Deploy the deliver-notification function only after DNS verification, SES production access, and bounce monitoring.
  7. In Supabase > Authentication > SMTP Settings, enable custom SMTP using the AWS SES London SMTP hostname shown in AWS, port/TLS mode shown by AWS, SES SMTP username/password, verified sender address, and a working reply-to. Preserve the existing Auth redirect allowlist and test signup confirmation and password reset on a controlled account.

3. Sentry projects and deployment variables ​

The Sentry screenshot shows a Next.js project for the marketing site. That DSN should not be reused for the Vite storefront or admin, or the Railway API. Create separate projects in the same Sentry organization:

AppSentry platformDestination setting
Marketing (debelu.com)Next.jsVercel NEXT_PUBLIC_SENTRY_DSN (and optionally server-only SENTRY_DSN)
Storefront (app.debelu.com)React/browserCurrent Vercel project VITE_SENTRY_DSN; future Pages release: GitHub Actions variable VITE_STOREFRONT_SENTRY_DSN
Admin (admin.debelu.com)React/browserCurrent host's VITE_SENTRY_DSN; future Pages release: GitHub Actions variable VITE_ADMIN_SENTRY_DSN
Railway APINode.jsRailway server-only SENTRY_DSN

The storefront and admin React DSNs were supplied by the user and saved as the GitHub Actions variables VITE_STOREFRONT_SENTRY_DSN and VITE_ADMIN_SENTRY_DSN on 2026-09-27. Do not paste the browser-loader script into the React app: its installed SDK already initializes Sentry. A separate backend DSN was also supplied and a labelled local test event flushed through the Sentry transport, but Railway is not yet configured with that DSN. Marketing still needs its own DSN. The snippet's suggested tracing and replay rates are intentionally not enabled. The published storefront workflow still builds on Vercel, so the GitHub variable alone does not configure the current live deployment.

  1. In Sentry, create the missing projects, select the matching platforms, and open each project's Settings > Client Keys (DSN). Copy the DSN for that project. A DSN is a public project identifier, not a credential, but keep the API project's environment settings on Railway.
  2. In GitHub repository Settings > Secrets and variables > Actions > Variables, check both saved React DSNs under the exact names above. Do not put a Sentry auth token in Variables; any source-map upload token belongs in Secrets with restricted scope.
  3. In Vercel, find the project currently serving app.debelu.com under Settings > Domains, open its Environment Variables, add VITE_SENTRY_DSN with the storefront DSN supplied in chat for Preview and Production, then redeploy both targets after the storefront Sentry code changes are released. Vite embeds this value at build time; changing it without a new build will not activate Sentry. For the admin's current Vercel project, use the same variable name but its own project DSN supplied in chat. Do not copy the storefront DSN into admin.
  4. In Vercel, open debelu-marketing project Settings > Environment Variables, add NEXT_PUBLIC_SENTRY_DSN to Preview and Production, then redeploy. If adding SENTRY_DSN separately, keep it server-only.
  5. In Railway, add SENTRY_DSN with the backend DSN supplied in chat to the backend service Variables, then deploy the version of the backend containing the Sentry integration. Do not place it in a browser VITE_* variable.
  6. Trigger one deliberate non-customer error in each preview/test app and check it appears only in the correct Sentry project. Do not enable session replay or performance tracing without a privacy review.

4. Cloudflare Pages and R2 (after previews pass) ​

  1. The Direct Upload Pages projects debelu-storefront and debelu-admin have their first release-preview deployments as of 2026-09-28. Both preview hosts serve deep links and noindex headers. Their Railway CORS origins were deployed and verified with HTTP 204 preflight responses. For automated releases, add a narrowly scoped Cloudflare Pages token to GitHub Secrets as CLOUDFLARE_API_TOKEN and the account ID as CLOUDFLARE_ACCOUNT_ID; do not use a Global API Key.
  2. Supabase Authentication > URL Configuration now includes https://release-preview.debelu-storefront.pages.dev/**, https://release-preview.debelu-admin.pages.dev/**, and https://admin.debelu.com/**. The malformed admin.debelu.com/** entry was removed and the eight-entry allowlist was verified on 2026-09-28. Test login, checkout, admin access, and mobile links. Populate the VITE_* settings in GitHub Actions and run Cloudflare Pages release with deploy_production=false before selecting production. The previews temporarily use the Railway fallback API hostname because api.debelu.com has a TLS certificate mismatch: Railway expects a newer CNAME target and verification TXT than the former live Vercel DNS records. The user approved both updates on 2026-09-28; Vercel and public DNS now show the new values. Railway initially still showed Waiting for DNS update, and HTTPS still had a certificate mismatch. Confirm Railway's domain and certificate before production cutover; do not bypass certificate validation. The user deployed the corrected Redis reference on 2026-09-28; the Railway fallback /health now reports status: ok, Redis ok, and a queue with no failed jobs. The custom hostname still fails TLS validation and Railway still shows Waiting for DNS update despite public DNS returning the required records.
  3. R2 bucket debelu-product-images already exists but is private. Create bucket-scoped Object Read & Write credentials for the Railway API; enter them only as Railway Variables R2_ACCOUNT_ID, R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY, R2_PUBLIC_BUCKET=debelu-product-images, and R2_PUBLIC_URL=https://cdn.debelu.com.
  4. Only after Cloudflare becomes authoritative and the API upload test passes, attach cdn.debelu.com to R2. The user approved this public domain for displaying product photos; uploads remain restricted to the API. Do not make unrelated buckets public. Enable VITE_R2_PRODUCT_IMAGES=true only after a new upload loads publicly and an older Supabase-hosted image still works.

5. WhatsApp Business sender ​

  1. In Meta for Developers, open the Debelu business app with the connected WhatsApp number. The user reports that the number is now linked to Meta. Complete any business verification and WhatsApp Business Account setup Meta still requires.
  2. Confirmed 2026-09-28: the Debelu WhatsApp Business Account (1771958154014032) contains connected number +234 916 153 1288 with Phone Number ID 1375907198930745. Do not disconnect the number.
  3. Four English utility templates were submitted on 2026-09-28 and showed In review: debelu_order_update, debelu_payment_update, debelu_dispute_update, and debelu_security_alert. Each has three body variables in this order: notification title, message, and Debelu URL. The repository now selects the matching template by notification category and does not send unrelated categories on WhatsApp. Wait for all statuses to become Approved before production sends; revise any rejected template.
  4. In the Meta Business portfolio, create a production system-user access token with only the WhatsApp messaging permissions needed for this app. Keep it server-only in Supabase Edge Function secrets as WHATSAPP_ACCESS_TOKEN; set WHATSAPP_PHONE_ID=1375907198930745 there too. In the Meta developer dashboard, check the current supported Graph API version for the app and set WHATSAPP_GRAPH_API_VERSION in the form vNN.0; the delivery function deliberately skips WhatsApp if that value is absent or malformed. Do not use VITE_* variables.
  5. Meta's production setup also shows Configure Webhooks incomplete and an Add payment to send business-initiated messages step. The app must be published for production webhooks. The backend now has a callback at /api/whatsapp/webhook that verifies Meta's GET challenge and POST HMAC, but it is local only until deployed with WHATSAPP_WEBHOOK_VERIFY_TOKEN and META_APP_SECRET. After switching to the correct Railway workspace on 2026-09-28, the debelu-backend service was online at api.debelu.com and deploys via GitHub. The new callback has not been deployed or verified. Do not publish the Meta app yet. Once this code is deployed, set those secrets server-side, enter the public callback URL and the matching verify token in Meta, and confirm Meta's verification succeeds. The callback does not yet persist delivery receipts or route inbound replies; do not subscribe the messages field until those workflows are implemented. Complete billing yourself; do not share card details in chat.
  6. Apply 23_whatsapp_consent.sql after the earlier launch-hardening scripts. It resets the old opt-out default and clears legacy on values because their consent cannot be proven. Members must opt in again using the storefront's WhatsApp switch. Verify opt-in capture and withdrawal before customer messages. Test only with a controlled, opted-in recipient. Confirm one delivery and failure record in notification_deliveries; do not send a broad campaign.

6. DNS cutover is last ​

  1. Review every record in docs/dns-migration-plan.md, especially the Vercel-managed apex/www/app/admin targets, Railway API and verification, Improvmx mail, SES, and certificate CAA records. Staged A snapshots are not a reliable substitute for Vercel's current external-DNS targets.
  2. In Truehost, check domain renewal/invoice status, registrar lock, and DNSSEC. The page showed an unpaid-invoice warning and an unlocked domain. Do not pay an invoice or change security settings unless you intend to.
  3. Only after preview checks, SES verification, inbound-mail checks, and record-by-record review, schedule a cutover. At Truehost Nameservers, replace the two Vercel nameservers with exactly janet.ns.cloudflare.com and kolton.ns.cloudflare.com. This is an explicit production switch: do not do it merely to make a pending zone turn green.
  4. After delegation propagates, verify the marketing site, storefront, admin, Railway API, Supabase login, Paystack checkout/webhook, inbound support mail, SES DKIM/MX/SPF/DMARC, R2 CDN, and mobile deep links. Keep the Vercel deployments available until the new setup is stable.

Released under Proprietary Enterprise License.