Skip to main content

Decision

The target toolchain for active Worker-backed apps and services is cf + Void. Adopt it per verified operation. Preserve existing commands and configuration until authentication, target identity, bindings and migrations, emitted artifacts and rollback parity are established. Wrangler remains for recorded gaps with an owner and removal condition. The shrink-only usage guard requires structured admission for expansion, changed Wrangler lines, or changed gap scope. This page refines the cf adoption gates in Oxc and Cloudflare adoption. A merged tooling switch is CI-verified, not production-delivered: every deploy target change still needs the gated production workflow, exact-commit evidence and the cutover checklist below.

Toolchain shape

The Void SDK is not applied to native Workers. Void’s supported app types are Void apps, meta-frameworks and static sites, so a native fetch/scheduled/Durable Object Worker would have to be rewritten into Void’s routes and crons model. For publication, Void’s own Cloudflare deploy converts the emitted config into a cf Build Output and calls cf deploy --prebuilt. Native Workers emit that format directly, so they share Void’s publication path without its runtime layer. The stable Vite plugin (1.64) already supports cloudflare.config.ts and Build Output emission; the 2.0 beta is not required. Loading cloudflare.config.ts requires Node.js (not Bun). The cf package requires Node ^22.23 || ^24.22 || ^26.12. The cf/Void Wrangler-exit lane owns the cf@1.0.0-beta.20 pin. Upgrade through a scoped dependency PR after checking upstream release notes and rerunning config/output parity, credential-free artifact checks, secret handling, settings readback fixtures and real Worker CI tests on the candidate version. DO publication remains Wrangler until a disposable migration rehearsal and a separate reviewed publisher change. Renovate holds cf prerelease pins via the cf package rule in renovate.json; update this hold only with the qualified upgrade PR. Do not float this beta pin automatically.

Operation parity matrix

Verified against cf 1.0.0-beta.20 help, the cf documentation and @cloudflare/vite-plugin 1.64.0 on 2026-10-23.

Native Worker template

Applied first to Cron Control. Each migrated Worker has:
  • cloudflare.config.ts, generated by cf migrate --no-install and reviewed. Durable Object migrations become exports: { Class: exports.durableObject({ storage: 'sqlite' }) } for every live class.
  • vite.config.ts with cloudflare({ experimental: { newConfig: { cfBuildOutput: true } } }), no configPath, explicit preview URL and observability/log settings, loopback dev server settings, persistState under a disposable directory for runtime tests (normal dev uses .cloudflare/state), and the Worker environment’s build.sourcemap: false (Wrangler did not upload source maps).
  • Scripts: dev: vp dev (host/port in vite.config.ts), build: vp build, type-check: cf workers types && tsc. cf is an exact devDependency; the app-level wrangler devDependency is removed.
  • A parity baseline in scripts/ci/fixtures/cf-worker-parity/<app>.json, normalized from the final wrangler.jsonc. scripts/ci/cf-worker-parity.test.mjs compares cloudflare.config.ts with it, and CI compares the emitted Build Output after the build with node scripts/ci/cf-worker-parity.mjs apps/<app> scripts/ci/fixtures/cf-worker-parity/<app>.json.
  • The workflow validates, runs the runtime checks on source and on the emitted Build Output, dry-runs cf deploy --prebuilt, then uploads .cloudflare/output as the exact-commit artifact. The gated deploy job downloads that artifact and publishes it without rebuilding.
@cloudflare/vite-plugin 1.x still depends on the Wrangler library internally. That is a library dependency, not CLI usage; it disappears with plugin 2.x.

Rollback

Rollback cannot cross a Durable Object lifecycle change. Secret changes can block rollback; inspect the error before using --bypass-deployment-checks. Retained CI artifacts (<app>-worker-<sha>) allow re-publishing an exact earlier build. The local harness rejects .dev.vars* and .env* files in both the app root and working directory (template .example files are allowed). Every declared secret needs a nonempty disposable fixture. Undeclared/control-key fixtures are rejected, and declared text vars are masked from the shell before process-env loading. It restores the first prior environment value after startup failure or close. cf-vite-worker.test.mjs covers both source and artifact modes using a disposable Vite fixture; real Worker runtime acceptance remains a CI gate.

Wrangler usage guard

node scripts/ci/wrangler-usage.js prints the inventory. The guard scans tracked files, including executables under plugins/, .agents/ and tmp/; Markdown, text, lockfiles and binary assets are excluded. Ignored/untracked tmp/ files are not an inventory: executable files there enter the guard only when tracked. .md, .mdx and .txt are documentation exclusions in every directory; commands embedded in those files need normal review, since the guard does not parse executable documentation examples. It counts mention lines and all Wrangler token occurrences conservatively, including comments and strings. A SHA-256 digest also covers each file’s Wrangler lines (all content for config files), so same-count substitutions require admission. Lower or remove counts when usage falls. An admitted record must be { "decision": "https://github.com/tutur3u/platform/pull/<id>", "reason": "<specific reviewed change>" }. New entries, growth, changed digests and changed gap scope need a fresh record; reverting a migration follows the same rule. CI compares the allowlist with its base commit (HEAD^1 of the pull request merge ref) and fails if that commit is unavailable. Current gaps: paused-runtime (retained paused runtimes), opennext-adapter, void-wave1-publication, pending-migration (services awaiting their parity PR) shared-toolchain (root CLI provider, trusted-dependency and policy files) and parity-tooling (the migration parity checker, which reads former Wrangler configs).

Service inventory

Settings preflight token permissions

The deploy token must cover the pinned account and every zone in that account. The settings snapshots require these Cloudflare API token permissions in addition to the publication permissions: Grant zone visibility for all zones in the target account. Limiting visibility to the expected hostname’s zone can hide unexpected routes and cannot establish route parity. Do not grant unrelated accounts. These requirements apply to COLAB_CLOUDFLARE_API_TOKEN and to COORDINATION_CLOUDFLARE_API_TOKEN when supplied instead of the fallback. Keep values in GitHub secrets; never attach token values to receipts. Actual token scope remains unverified until the bounded read-only preflight succeeds. Missing access, truncated lists and unsupported account size fail before publication; do not bypass that gate to deploy. Cloudflare documents the accepted permissions for Worker secrets, deployments, custom domains, zones and routes.

Deployment account variable

Set the non-secret Actions repository variable CLOUDFLARE_ACCOUNT_ID in tutur3u/platform (Settings → Secrets and variables → Actions → Variables). Use the account ID recorded in the service’s cloudflare.config.ts and parity baseline; the deploy job checks it against the emitted artifact. One repository variable supplies cron-control-production, devbox-control-production and coordination-production. If any of those environments defines the same variable, it must have the same value because the environment value takes precedence. Missing or mismatched configuration blocks deployment. Source publication does not provision this variable; the release owner must set it before promotion.

Cached Build Output

The root Turbo build.outputs includes .cloudflare/output/**, including hidden Vite metadata. Cache hits must restore the complete emitted tree before parity, runtime and publication checks. .cloudflare/state remains outside the output scope. cf-build-output-cache.test.mjs runs a disposable emit-only task through an actual cache miss and hit, verifies restored bytes and execution count, and checks the old output list as a negative control. It never compiles an app or uses shared/remote cache credentials.

Production cutover checklist

For each service, the orchestrator verifies before promotion:
  1. Exact-commit CI on the promoted SHA is green for the service workflow, including: source runtime checks, emitted Build Output parity (cf-worker-parity.mjs prints matches <worker> deployment identity), emitted runtime checks, the cf deploy --prebuilt --dry-run --no-provision step, and the uploaded <app>-worker-<sha> artifact.
  2. The production job publishes that artifact with cf deploy --prebuilt (no rebuild) and the same --message SHA.
  3. The deploy job runs cf-worker-settings.mjs before before any publication or secret mutation, then after before recording success. It checks the pinned account, compatibility, preview URLs, observability/logs, cron schedules and declared secret names; snapshots routes/custom domains and hashes remaining settings without persisting text bindings. It captures the active deployment and version IDs (cf workers deployments list) as the rollback target. Also capture cf workers get <name>, cf workers secrets list and, with the Cloudflare API, the script’s cron schedules (/workers/scripts/<name>/schedules), routes or custom domains, and workers.dev/preview URL state. The job captures them again after deployment and compares; unexplained changes fail the job before its success marker. Evidence is retained even on failure. This route-free pilot supports at most 20 account zones and one API list page, uses at most 28 GET requests per snapshot, and fails closed on missing read permissions, truncated results or unsupported scope. It never retries writes. The expected preview setting is explicitly true; this is unverified against current production until the preflight runs. A mismatch stops before mutation.
  4. Production publication for Devbox Control and Coordination remains Wrangler with their applied v1 migration metadata. A separate reviewed change must carry the rehearsal receipt before enabling cf publication. For these Workers, rehearse the first cf deploy on a disposable Worker created with the same Wrangler migration history and data, and confirm object data survives the switch to exports. Do not promote until that rehearsal passes.
  5. Health checks pass on the canonical host, and the deployment marker records the SHA.
  6. If a check fails, roll back with cf workers deployments create to the captured version, then reconcile before retrying.