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)
- Keep Edge open with the existing signed-in Vercel, Cloudflare, AWS, Sentry, Meta, Supabase, Railway, GitHub, and Truehost tabs.
- 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.
- 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.
- 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.
Sign in to Vercel. Open your team
franklinechisom96-9712s-projects, then Domains, and selectdebelu.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.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, put10in the priority field.Type Name Value Priority CNAME el26mkey4vxsb4ylwfgo6frvgdb47yde._domainkeyel26mkey4vxsb4ylwfgo6frvgdb47yde.dkim.amazonses.com— CNAME qqq7k2v4nm54tdhikj7csskxc4htnzam._domainkeyqqq7k2v4nm54tdhikj7csskxc4htnzam.dkim.amazonses.com— CNAME ut4mgysdrkx4pr2i6g6c7swz7rypdlxm._domainkeyut4mgysdrkx4pr2i6g6c7swz7rypdlxm.dkim.amazonses.com— MX bouncefeedback-smtp.eu-west-2.amazonses.com.(absolute hostname, including final dot)10TXT bouncev=spf1 include:amazonses.com ~all— TXT _dmarcv=DMARC1; p=none;— Do not change the
bounceMX record now. An earlier DNS lookup returnedfeedback-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 targetfeedback-smtp.eu-west-2.amazonses.comat priority10. 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.Do not remove
mx1.improvmx.com,mx2.improvmx.com, or the rootv=spf1 include:spf.improvmx.com ~allrecord. ThebounceMX/TXT are on a separate subdomain and do not replace inbound support mail.Reopen each saved Vercel record and compare its full name, type, and value character-for-character with the table. Vercel should show exactly one
_dmarcTXT and onebounceSPF TXT; duplicate SPF records at the same name are invalid.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.comand-type=TXT bounce.debelu.comand_dmarc.debelu.com.
2. Finish Amazon SES in London
- 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. - Verified in AWS on 2026-09-28:
debelu.comhas identity status Verified, 2048-bit Easy DKIM Successful with signatures Enabled, and custom MAIL FROMbounce.debelu.comSuccessful. MX failure behavior is Reject message. No further SES DNS correction is needed. - Production access remains pending. The London account dashboard still showed Sandbox (200 emails/day, 1 email/second). AWS support case
179049774500257requested 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. - Set up bounce and complaint visibility before customer sends. In SES, create a configuration set (or equivalent event destination) that records
BOUNCE,COMPLAINT, andDELIVERYevents to an alertable AWS destination such as SNS/EventBridge/CloudWatch. Test alert delivery with a controlled address. Keep the account-level suppression list enabled. - Two different credentials are needed:
- A least-privilege IAM/API credential limited to SES
SendEmailfor the verifieddebelu.comidentity; it belongs only in Supabase Edge Function secrets asSES_ACCESS_KEY_IDandSES_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.
- A least-privilege IAM/API credential limited to SES
- In Supabase > Project Settings > Edge Functions/secrets, set
SES_REGION=eu-west-2, the two SES API secrets, andEMAIL_FROMusing an address underdebelu.comsuch asDebelu <notifications@debelu.com>. SetEMAIL_REPLY_TOto a working inbound support address. SetSES_CONFIGURATION_SETto the configuration set created in step 4 so delivery, bounce, and complaint events are emitted. Deploy thedeliver-notificationfunction only after DNS verification, SES production access, and bounce monitoring. - 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:
| App | Sentry platform | Destination setting |
|---|---|---|
Marketing (debelu.com) | Next.js | Vercel NEXT_PUBLIC_SENTRY_DSN (and optionally server-only SENTRY_DSN) |
Storefront (app.debelu.com) | React/browser | Current Vercel project VITE_SENTRY_DSN; future Pages release: GitHub Actions variable VITE_STOREFRONT_SENTRY_DSN |
Admin (admin.debelu.com) | React/browser | Current host's VITE_SENTRY_DSN; future Pages release: GitHub Actions variable VITE_ADMIN_SENTRY_DSN |
| Railway API | Node.js | Railway 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.
- 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.
- 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.
- In Vercel, find the project currently serving
app.debelu.comunder Settings > Domains, open its Environment Variables, addVITE_SENTRY_DSNwith 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. - In Vercel, open
debelu-marketingproject Settings > Environment Variables, addNEXT_PUBLIC_SENTRY_DSNto Preview and Production, then redeploy. If addingSENTRY_DSNseparately, keep it server-only. - In Railway, add
SENTRY_DSNwith 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 browserVITE_*variable. - 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)
- The Direct Upload Pages projects
debelu-storefrontanddebelu-adminhave their firstrelease-previewdeployments as of 2026-09-28. Both preview hosts serve deep links andnoindexheaders. 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 asCLOUDFLARE_API_TOKENand the account ID asCLOUDFLARE_ACCOUNT_ID; do not use a Global API Key. - Supabase Authentication > URL Configuration now includes
https://release-preview.debelu-storefront.pages.dev/**,https://release-preview.debelu-admin.pages.dev/**, andhttps://admin.debelu.com/**. The malformedadmin.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 theVITE_*settings in GitHub Actions and run Cloudflare Pages release withdeploy_production=falsebefore selecting production. The previews temporarily use the Railway fallback API hostname becauseapi.debelu.comhas 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/healthnow reportsstatus: ok, Redisok, 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. - R2 bucket
debelu-product-imagesalready exists but is private. Create bucket-scoped Object Read & Write credentials for the Railway API; enter them only as Railway VariablesR2_ACCOUNT_ID,R2_ACCESS_KEY_ID,R2_SECRET_ACCESS_KEY,R2_PUBLIC_BUCKET=debelu-product-images, andR2_PUBLIC_URL=https://cdn.debelu.com. - Only after Cloudflare becomes authoritative and the API upload test passes, attach
cdn.debelu.comto R2. The user approved this public domain for displaying product photos; uploads remain restricted to the API. Do not make unrelated buckets public. EnableVITE_R2_PRODUCT_IMAGES=trueonly after a new upload loads publicly and an older Supabase-hosted image still works.
5. WhatsApp Business sender
- 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.
- Confirmed 2026-09-28: the Debelu WhatsApp Business Account (
1771958154014032) contains connected number+234 916 153 1288with Phone Number ID1375907198930745. Do not disconnect the number. - Four English utility templates were submitted on 2026-09-28 and showed In review:
debelu_order_update,debelu_payment_update,debelu_dispute_update, anddebelu_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. - 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; setWHATSAPP_PHONE_ID=1375907198930745there too. In the Meta developer dashboard, check the current supported Graph API version for the app and setWHATSAPP_GRAPH_API_VERSIONin the formvNN.0; the delivery function deliberately skips WhatsApp if that value is absent or malformed. Do not useVITE_*variables. - 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/webhookthat verifies Meta's GET challenge and POST HMAC, but it is local only until deployed withWHATSAPP_WEBHOOK_VERIFY_TOKENandMETA_APP_SECRET. After switching to the correct Railway workspace on 2026-09-28, thedebelu-backendservice was online atapi.debelu.comand 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 themessagesfield until those workflows are implemented. Complete billing yourself; do not share card details in chat. - Apply
23_whatsapp_consent.sqlafter the earlier launch-hardening scripts. It resets the old opt-out default and clears legacyonvalues 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 innotification_deliveries; do not send a broad campaign.
6. DNS cutover is last
- 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. - 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.
- 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.comandkolton.ns.cloudflare.com. This is an explicit production switch: do not do it merely to make a pending zone turn green. - 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.