Skip to main content
Each packet is what the release operator needs to flip one app from Vercel to its native Cloudflare Worker, and to flip it back. Provider writes (DNS, routes, custom domains, aliases, deployments) stay with the release operator; the source only produces the sealed Worker bytes. Secret values never appear here, only names.

Common

  • Build (CI parity): bun turbo:local run build --filter=@tuturuuu/<app> --concurrency=2, then node scripts/ci/wave1-native-artifact.mjs prepare <app> production <sha> <workers-subdomain>, node scripts/ci/wave1-native-runtime.mjs <app> production <sha> (needs CI=true) and node scripts/ci/wave1-native-publish.mjs dry-run <app> production <sha>.
  • The production publication profile (scripts/ci/wave1-native-profiles.json) uploads a route-free Worker (workers_dev: false, routes: []). Binding the canonical host is a separate provider step, so uploading never moves traffic.
  • Production publication is gated by productionPublicationPolicy in tuturuuu.ci.ts and the wave1-native-production.yaml dispatch, which lists calendar, mail and apps. All three are admitted. Each run uses the protected GitHub environment wave1-<app>-production (publish only succeeds when PUBLICATION_ENVIRONMENT equals that exact name) and must be dispatched from the production ref.
  • Environments the release owner creates (restrict each to the production branch and add a required reviewer): wave1-calendar-production, wave1-mail-production, wave1-apps-production. Each holds the same four secrets, by name: PRODUCTION_SUPABASE_URL, PRODUCTION_SUPABASE_PUBLISHABLE_KEY (public build inputs), WAVE1_PRODUCTION_CLOUDFLARE_READ_TOKEN (read-only topology, used by the preflight and verify steps) and WAVE1_PRODUCTION_CLOUDFLARE_API_TOKEN (upload only, used by the upload step). The per-app Worker secrets listed below are set on the Worker, not in GitHub.
  • Legacy vercel-production-mail.yaml and vercel-production-apps.yaml are disabled in tuturuuu.ci.ts, like Calendar and Meet: Vercel can no longer build either app.
  • Tasks and Track are not wave-1 profile apps. Their mail-calendar-tasks-builds.yaml (tasks-cutover-artifact) and track-cloudflare.yaml (track-cutover-artifact) jobs already rewrite the emitted config to the production Worker names tuturuuu-tasks and tuturuuu-track (route-free, workers_dev and preview_urls off) and upload the artifact for an operator wrangler deploy. They need no environment and have no production publisher job.
  • Rollback: Vercel can no longer redeploy calendar, mail or apps from main. Their build command now emits a native Worker and Vercel fails with NEXT_OUTPUT_DIR_MISSING, so the rollback target is the retained READY deployment below, restored by moving the alias. Never delete these deployments until a full release cycle has run on the Worker.
  • Smoke, after the flip, per host <host>:
    A passing smoke shows server: cloudflare and no x-vercel-id header.

Verified at 506c1ea9

The full production-profile sequence (build, seal, original-bytes workerd acceptance, upload-limit dry-run, final seal) passes on an isolated checkout for these apps: Every size is inside the recorded paid-plan limits (64 MiB script). Mail and Apps runtime acceptance returns 200 for GET/HEAD /api/build-info, 405 for POST, and 307 for the unauthenticated workspace route. Meet and Roadmap use their own workflows and are covered above.

Calendar

Mail

Apps

Meet

meet.tuturuuu.com is already served by the tuturuuu-meet Worker (server: cloudflare, anonymous / answers 307 to the Web login). Source carries the custom-domain route in apps/meet/void.config.ts. Bindings: ASSETS, WORKER_SELF_REFERENCE, ABUSE_GUARD (service tuturuuu-abuse-guard), R2 cache bucket tuturuuu-meet-cache, IMAGES, and the MEET_LIVE Durable Object (migration tag meet-live-v1). Required secret names: MEET_REALTIME_TOKEN_SECRET, NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY, SUPABASE_SECRET_KEY, GOOGLE_GENERATIVE_AI_API_KEY. Meet is not part of the wave-1 profile set: meet-cloudflare.yaml validates it on every change (native build, emitted-config receipt, restored-artifact acceptance) and its deploy job publishes from the production ref with a deployment marker. Meet AI stays contained (MEET_AI_DISABLED=true). Migration changes follow the Meet migration serialization and are not part of this packet. Vercel holds no READY production deployment for Meet (all recent ones are canceled), so rollback for a Meet regression is the previous Worker version, not Vercel.

Roadmap

tuturuuu.com/roadmap is served by the tuturuuu-roadmap Worker through Web’s proxy (apps/web/src/lib/roadmap-public-proxy.ts), which fetches the non-redirecting worker origin and never forwards cookies or authorization. The Worker also owns the routes tuturuuu.com/roadmap and tuturuuu.com/roadmap/* in apps/roadmap/void.config.ts.
  • Rollback: remove the isRoadmapPublicPath branch in proxy.ts (Web then serves its own page) or point ROADMAP_UPSTREAM_ORIGIN elsewhere; no Worker deletion is needed.
  • Smoke: curl -sS -L --max-redirs 3 -o /dev/null -w '%{http_code}\n' https://tuturuuu.com/roadmap expects 200; https://tuturuuu.com/vi/roadmap expects one redirect to /roadmap.