---
title: "cf CLI and cloudflare.config.ts"
description: "Org standard for Cloudflare cf CLI, Vite, and typed cloudflare.config.ts. Wrangler is transitional."
url: "https://architecture.mnppi.org/delivery/cf-cli-and-cloudflare-config/"
status: "Accepted for pilots"
last_updated: "2026-09-29"
---

# cf CLI and cloudflare.config.ts

**Standard details**

- Status: Accepted for pilots
- Reviewed: September 29, 2026
- Cadence: Review after each cf CLI or Workers Builds change
- Source: org-standards

Vite-ready Workers that have a Worker entry use the Cloudflare
<code>cf</code> CLI, Vite, the official Vite plugin, and typed
<code>cloudflare.config.ts</code>. Wrangler stays where the platform still
delegates, including assets-only Astro until those gaps close.

Mark GO 2026-09-28 CT accepted this standard for **pilots**. First
`MNPPI/airport-time-calc` (PR #78), then `MNPPI/tesseract` (PR #36). Both
keep labeled transitional `wrangler.jsonc`. No mass fleet migrate.
BotMail (`automations.uihq.com`) is out of scope.

The internal ADR is `docs/adr/ADR-cf-cli-migration.md` in `MNPPI/org-standards`.
Deploy ownership stays in [Cloudflare deployment](https://architecture.mnppi.org/delivery/cloudflare-deployment/index.md).

## Canonical toolchain

| Piece      | Standard                                                                                |
| ---------- | --------------------------------------------------------------------------------------- |
| Runtime    | Node.js **≥ 22**. `cf` refuses Node 20.                                                 |
| CLI        | Project dependency `cf`. Global install is optional for operators.                      |
| Config     | `cloudflare.config.ts` with `defineConfig`, `bindings`, and `triggers` from `cf/config` |
| Bundler    | Vite + `@cloudflare/vite-plugin` for Worker-entry apps. Not for assets-only Astro.      |
| Day-to-day | `cf init` / `cf migrate` / `cf dev` / `cf build` / `cf deploy` when BOS exists          |

Branch environments with `defineConfig(({ mode }) => …)` and CLI `--mode`.
Do not invent Wrangler-style nested `env.production` blocks in the new format.

Verified on open-beta day against `cf@1.0.0-beta.5`. Re-check the installed
CLI before you assume a newer flag exists.

## Agent discovery

Prefer:

```sh
cf cli search "migrate wrangler project"
cf cli search "deploy worker"
```

Then run `<command> --help`. Do not invent flags. Do not chain nested
`--help`. Keep search queries free of account IDs, domains, emails, and
tokens.

Launch post + installed CLI/types/codemods are source of truth until
Cloudflare’s public docs index catches up.

## Official migrate path

```sh
cf migrate --dry-run --bundler vite
cf migrate --bundler vite
```

Use `--bundler vite` only when the repo has a Worker entry. Assets-only
Astro is out of scope for that path. See [pilot-proven fences](#pilot-proven-fences).

`cf migrate` always creates `cloudflare.config.ts`. It does not overwrite an
existing target file. It returns `followUps`. Status may be
`needs-intervention` (exit 1) when blocking follow-ups remain.

Replace `wrangler deploy` with `cf deploy` **only when BOS**
(`.cloudflare/output/v0/config.json`) exists after the project build.
Keep the repository script name `cf:check`. Point it at `cf deploy --dry-run`
when BOS exists, otherwise `wrangler deploy --dry-run`.

**No shims.** Do not wrap Wrangler in a binary or script named `cf`.
Do not delete `wrangler.jsonc` or flip Workers Builds to
`wrangler --experimental-new-config`.

## Transitional Wrangler

Wrangler is allowed only when Cloudflare still delegates. Label it
**transitional** in `AGENTS.md` and on any retained `wrangler.*` file.

| Case                                            | Label                                     |
| ----------------------------------------------- | ----------------------------------------- |
| JS Workers still on Wrangler/esbuild            | `transitional: wrangler-esbuild-delegate` |
| Rust Workers                                    | `transitional: rust-wrangler`             |
| Python Workers                                  | `transitional: python-pywrangler`         |
| Optional `wrangler.config.ts` tooling           | `transitional: wrangler-tooling-config`   |
| Pages → Workers Assets before or beside migrate | `transitional: pages-to-workers-assets`   |
| Assets-only Astro, no Worker entry              | `transitional: astro-assets-no-entry`     |
| No BOS after the project build                  | `transitional: wrangler-until-bos`        |
| Keep `wrangler.jsonc` (no experimental flip)    | `transitional: wrangler-jsonc-retained`   |

`cf migrate` does not convert Durable Object `migrations`, `containers`,
`pages_build_output_dir`, `site`, `access`, `keep_vars`, or other unsupported
fields. Escalate those. Do not invent a workaround.

`cf` has no `keep_vars`. Move every non-secret plain var into
`cloudflare.config.ts` or Wrangler `vars` instead of depending on
`keep_vars`. Under transitional Wrangler, `keep_vars` is short-lived
migration only. Secrets survive deploy without it. See
[Worker secrets and vars](https://architecture.mnppi.org/operations/worker-secrets-and-vars/index.md).
Do not invent a `cf deploy` keep-vars flag.

This architecture site (`mnppi-architecture`) stays on Wrangler until a later
pilot. That is intentional. Docs land first. It is the same assets-only Astro
shape as the first pilots.

## Pilot-proven fences

Recorded 2026-09-28 CT from CF Migrate → Grok on the two Mark GO pilots.

1. **Assets-only Astro → Workers Static Assets.**
   `cf migrate --bundler vite` fails on `assets.directory` when there is no
   Worker entry. Vite is not the honest path. Keep Wrangler transitional.

2. **BOS required for `cf build` / `cf deploy`.**
   Those commands fail without `.cloudflare/output/v0/config.json` after the
   project build. Without BOS, Wrangler stays the deploy and `cf:check`
   dry-run delegate.

3. **Do not fleet-flip experimental new config.**
   `wrangler --experimental-new-config` (observed on 4.112.0) rejects
   `defineConfig({ worker })`. Do not delete `wrangler.jsonc` or point
   Workers Builds at that flag. Pilots keep labeled transitional
   `wrangler.jsonc` beside any `cloudflare.config.ts` work.

## Fences

Do not do these during a `cf` migrate or deploy change:

- Rewrite Proton MX on the `uihq.com` apex.
- Invent a public unauthenticated webhook.
- Drop or “clean up” FedRAMP D1 UUID pins. `cf migrate` must not drop pins.
- Force-push a protected main.
- Commit secrets. Keep them in Cloudflare or 1Password.
- Touch BotMail / `automations.uihq.com`.
- Change a named production-critical Worker that this standard excludes from
  first pilots without an explicit waiver.

## Pilots and exceptions

A feature this new stays on the [platform adoption](https://architecture.mnppi.org/architecture/platform-adoption/index.md)
pilot path. Do not flip the fleet because the CLI launched.

Need a Wrangler hold past the whitelist? Use the
[exception process](https://architecture.mnppi.org/governance/exceptions/index.md). Name the whitelist case, the
owner, and the removal criterion.
