Deployment & Environment

Required Worker bindings

Name Purpose
DB D1 database
MAIL_BUCKET R2 bucket for raw email storage

Required Worker secrets

Name Purpose
SESSION_SECRET session signing

Optional bootstrap-only Worker secrets

Name Purpose
BOOTSTRAP_ADMIN_API_KEY first admin bootstrap key; only needed when BOOTSTRAP_ADMIN_EMAIL is also set

Worker runtime variables

Name Purpose
APP_ENV environment marker
EMAIL_WORKER_NAME inbound Worker target for Email Routing
DEFAULT_MAILBOX_TTL_MINUTES default mailbox TTL
CLEANUP_BATCH_SIZE cleanup batch size
EMAIL_ROUTING_MANAGEMENT_ENABLED whether the app may mutate live Email Routing
CLOUDFLARE_ACCOUNT_ID Cloudflare account ID exposed to the API Worker runtime; required for direct zone binding from /domains
BOOTSTRAP_ADMIN_EMAIL first admin email
BOOTSTRAP_ADMIN_NAME first admin display name
CF_ROUTE_RULESET_TAG Worker route management tag
WEB_APP_ORIGIN primary browser origin used for direct-API compatibility and passkey trust; when only one origin is configured its host becomes the RP ID, and passkeys require localhost or a domain name instead of an IP literal
WEB_APP_ORIGINS comma-separated browser origins to trust when multiple production aliases stay live; passkeys accept every configured origin and derive one shared non-public RP ID suffix from the full set

Legacy single-domain upgrade variables

Name Purpose
MAIL_DOMAIN historical single-domain bootstrap
CLOUDFLARE_ZONE_ID historical single-domain bootstrap

Do not treat these as long-term configuration for new instances.

Web environment variables

Name Purpose
VITE_API_BASE_URL local dev / preview target for the same-origin /api proxy, plus the deploy workflow's canonical direct-API smoke URL
VITE_DEMO_MODE local demo mode
VITE_DOCS_SITE_ORIGIN public docs and Storybook origin used by the control plane

If VITE_DOCS_SITE_ORIGIN is empty, the in-app quick reference still works, but public docs links are hidden. VITE_API_BASE_URL is no longer the production browser API locator. First-party browser traffic defaults to same-origin /api. The deploy workflow renders a generated API Worker config and injects the GitHub repository secret CLOUDFLARE_ACCOUNT_ID into the Worker runtime variables before deploy. Exporting CLOUDFLARE_ACCOUNT_ID only to the GitHub Actions job environment is not sufficient for /api/meta or the /domains direct-binding UI gate.

Pages same-origin /api proxy

The control plane now keeps API access same-origin on every Pages alias:

  • apps/web/public/_routes.json sends only /api/* traffic into Pages Functions
  • apps/web/functions/api/[[path]].ts forwards the incoming Request to env.API.fetch(...)
  • apps/web/wrangler.jsonc declares the Pages build output, keeps the Pages project name aligned with the live kaisoumail target, and binds the proxy to the existing kaisoumail-api service
  • .pages.dev preview hostnames fail closed inside the proxy so preview traffic cannot accidentally reach the live control plane
  • static HTML, JS, CSS, and assets still bypass Workers billing because they do not enter the Function path
  • the deploy workflow uses CF_PAGES_SMOKE_ORIGINS to rerun same-origin /api/version smoke against every declared Pages alias after the Pages deploy completes, and malformed entries fail the workflow instead of being skipped silently

Keep direct API custom domains such as https://api.cfm.707979.xyz and https://api.km.707979.xyz for compatibility or direct API consumers. WEB_APP_ORIGINS remains the CORS allowlist for those direct API aliases, but the first-party browser UI should use same-origin /api instead.

Production alias example

  • Web: https://cfm.707979.xyz, https://km.707979.xyz
  • API: https://api.cfm.707979.xyz, https://api.km.707979.xyz

Deploy workflow safety rails

  • The production deploy workflow always captures a D1 Time Travel restore anchor and, for schema-stable releases, also captures the current 100%-stable API Worker version before publishing a new API release
  • It applies remote D1 migrations automatically, uploads a non-live API Worker version, and only promotes that version to 100% production traffic after shadow /health + /api/version smoke passes through the canonical direct API custom domain
  • After promotion it runs /health + /api/version smoke against the production API origin before any trigger changes; only then does it apply Worker route/domain/cron trigger changes explicitly, rerun smoke after trigger application across VITE_API_BASE_URL plus every routable API URL declared in apps/api-worker/wrangler.jsonc, deploy Pages, and finally rerun same-origin /api/version smoke across every Pages alias declared in CF_PAGES_SMOKE_ORIGINS, all without automatic rollback. Trigger application errors, post-trigger smoke failures, or same-origin Pages smoke failures stop for manual inspection, and automatic Worker rollback is disabled whenever the release is migration-bearing or remote D1 schema changes were involved in the deploy
  • D1 restore remains an explicit workflow_dispatch recovery path, not an automatic failure hook, because automatic DB restore could discard real writes
  • CI Main and CI PR block obviously destructive SQL patterns on the default path, and Deploy re-validates the remote pending migration set before apply; keep migrations expand-only / forward-compatible, keep compatibility code for at most one release, and move destructive cleanup to a later cleanup release

Public GitHub Pages site

docs-pages publishes three entrypoints:

  • /: Rspress docs home
  • /storybook/: static Storybook bundle
  • /storybook.html: Storybook redirect entry