Public wiki

Mailbox and Email

Canonical source: docs/wiki in the repository (mirrored to GitHub Wiki).

Mailbox and Email

Outbound mail uses branded HTML templates per category. Product mail sends from [email protected]; help and support replies use [email protected]. Transport is via Cloudflare Email Sending (Lorapok Labs domain).

Transport priority

  1. ccm-mail-relay Worker (Pages service binding → send_email) — preferred; no REST API token
  2. Cloudflare EMAIL binding (Workers only)
  3. Cloudflare Email REST API (CLOUDFLARE_EMAIL_API_TOKEN)
  4. Resend fallback (RESEND_API_KEY)

Production setup

  1. Onboard lorapok.tech in Cloudflare Email → Email Sending
  2. One-time sync cred vault → GitHub admin-production environment secrets:
gh secret set CLOUDFLARE_EMAIL_API_TOKEN --env admin-production \
  --body "$(cred get cursor cloudflare_email_api_token)"
# CLOUDFLARE_API_TOKEN + CLOUDFLARE_ACCOUNT_ID should already be set for admin-deploy
  1. One-time inbound routing (forward @lorapok.tech mail to ops inbox):
export CLOUDFLARE_API_TOKEN="$(cred get cursor cloudflare_api_token)"
export CLOUDFLARE_ACCOUNT_ID="$(cred get cursor cloudflare_account_id)"
node website/admin/scripts/setup-email-addresses.mjs
  1. CI/CD on push to mainadmin-deploy deploys Pages only when Mission Control paths change. It skips enable-mail.mjs and stats-cron deploy to avoid slow Cloudflare mail API calls. Full mail repair runs via workflow_dispatch → deploy-infra or locally.
  1. Manual repair when mail breaks or you need an immediate fix:
node website/admin/scripts/repair-mail.mjs   # same steps as CI, locally
  1. Use Mailbox → Send branded test email to verify outbound delivery
  1. Verify inbound routing (external → @lorapok.tech):
node website/admin/scripts/verify-inbound-routing.mjs
# Manual: send from Gmail to [email protected] → should forward to opsForwardTo

For Mission Control Sync routing, sync Pages secret once: node website/admin/scripts/setup-routing-secret.mjs

Full blueprint (inbound + outbound + CI): Cloudflare Email and Routing.

Message categories

CategoryTemplateLogo assetTrigger
subscribeBranded welcomelogo-product.pngWebsite subscribe form
inviteAdmin invitationlogo-product.pngTeam invite
noticeSeverity-styled noticelogo-notice.pngDev notice broadcast
composeMission Control messagelogo-product.pngAdmin compose
testDelivery confirmationlogo-help.pngMailbox test button

Storage (D1 + R2)

Outbound mail metadata is migrating off hot KV paths:

StoreContents
ADMIN_D1 mail_messagesPer-send mailbox rows (replaces mailbox:messages KV blob)
ADMIN_D1 mail_audit_resendMasked Resend audit index
ADMIN_D1 subscriber_indexSubscriber email hash index (Phase 2)
STATS_R2 mail/audit/Raw masked audit JSON
KV scattersubscriber:email:*, legacy subscribers array, mail aliases config

Apply D1 schema after deploy:

cd website/admin
npx wrangler d1 execute ccm-admin-d1 --remote --file=./d1/schema.sql

Replay saved sends (vault redirect default):

node website/admin/scripts/resend-saved-mail.mjs              # dry-run
node website/admin/scripts/resend-saved-mail.mjs --send       # deliver (redirected)
node website/admin/scripts/resend-saved-mail.mjs --source=d1  # reconstruct from D1 logs

Subscribe validation

POST /api/subscribe rejects duplicate emails with 409 and { ok: false, error: "already_subscribed" }. The marketing site shows a friendly “already subscribed” message instead of an error.

All templates use the CCM / Lorapok Labs dark theme with animated gradient header bar, stat pills, and CTA buttons. Template logic lives in website/admin/functions/api/_shared/mail-branding.js and mail.js.

Mission Control mailbox UI

Credential split (required)

VariablePurpose
CLOUDFLARE_API_TOKENPages/Workers deploy only (wrangler)
CLOUDFLARE_EMAIL_API_TOKENEmail Sending REST only (Pages secret)

Never sync the deploy token as CLOUDFLARE_EMAIL_API_TOKEN. Load from secure cred vault:

export CLOUDFLARE_API_TOKEN="$(cred get cursor cloudflare_api_token)"
export CLOUDFLARE_EMAIL_API_TOKEN="$(cred get cursor cloudflare_email_api_token)"
export CLOUDFLARE_ACCOUNT_ID="$(cred get cursor cloudflare_account_id)"

Scripts

node website/admin/scripts/setup-email-addresses.mjs  # inbound routing (one-time)
node website/admin/scripts/verify-mail-setup.mjs      # probe email token
node website/admin/scripts/enable-mail.mjs            # relay worker + Pages secret
node website/admin/scripts/repair-mail.mjs            # full fix: enable + build + deploy + verify
node website/admin/scripts/deploy-pages-fast.mjs        # fast deploy without mail API calls
node website/admin/scripts/probe-mail-token.mjs
node website/admin/scripts/setup-mail-secrets.mjs
node website/admin/scripts/resend-saved-mail.mjs       # replay KV/D1 saved mail (dry-run default)

Troubleshooting

SymptomFix
HTTP 401 on sendCLOUDFLARE_EMAIL_API_TOKEN needs Email Sending → Edit; redeploy Pages after wrangler pages secret put
Code 10203Enable Email Sending on account; onboard lorapok.tech in dashboard
cloudflare-rest in health, 401MAIL_RELAY missing — run repair-mail.mjs
Mail works locally, fails in prodRedeploy Pages after secret sync

← Home · Admin Panel