Deployment

Stand up a protected Worker without losing local mode

Hosted Fleet uses a custom-domain Worker, Cloudflare Access, D1, and encrypted Worker secrets. Generated deployment and operator files stay ignored; the committed defaults are safe, read-only, and unscheduled.

Prerequisites

Begin read-only

The configuration generator defaults to FLEET_READ_ONLY=true. Inspect the deployed inventory and Access boundary before regenerating with --write.

Hosted path

Provision the Worker

1

Install the pinned toolchain

The lockfile pins Wrangler, the Access JWT library, and the browser test stack. Keep the source checkout for deployment, but install the tagged GitHub package for a durable operator command that does not link back to the checkout.

npm install --global "github:j-256/cloudflare-fleet#v0.1.0"
npm ci
cloudflare-fleet --help
2

Create the D1 database

Record the database identifier returned by Wrangler. The generated configuration binds it as FLEET_DB.

npx wrangler d1 create cloudflare-fleet
export CLOUDFLARE_FLEET_D1_DATABASE_ID="the-returned-database-id"
3

Create the Access application

In Cloudflare Zero Trust, create a self-hosted application for the exact Fleet hostname and attach an Allow policy for the intended operators. Copy the Application Audience tag from the application settings and use the full HTTPS team domain as the issuer. The Worker validates both values on every non-loopback request.

Cloudflare documents the flow in Publish a self-hosted application and explains the audience and issuer in Validate JWTs.

4

Generate the ignored Wrangler configuration

Use an exact custom-domain hostname with no existing CNAME. Cloudflare creates the DNS record and certificate when Wrangler deploys the custom domain.

export CLOUDFLARE_ACCOUNT_ID="your-account-id"
export CLOUDFLARE_ACCESS_AUD="your-access-application-audience"
export CLOUDFLARE_ACCESS_TEAM_DOMAIN="https://your-team.cloudflareaccess.com"

cloudflare-fleet hosted configure \
  --account-id "$CLOUDFLARE_ACCOUNT_ID" \
  --database-id "$CLOUDFLARE_FLEET_D1_DATABASE_ID" \
  --hostname fleet.example.com \
  --access-aud "$CLOUDFLARE_ACCESS_AUD" \
  --access-team-domain "$CLOUDFLARE_ACCESS_TEAM_DOMAIN"

The command writes mode-restricted wrangler.jsonc. That file contains account and deployment identifiers, so Git ignores it. wrangler.example.jsonc documents the portable shape without live values.

5

Apply storage and deploy the secret with the code

Wrangler accepts an ignored dotenv file through --secrets-file, allowing the first code version and encrypted secret to deploy together. Delete the local secret file after deployment if the shell environment is your preferred source of truth.

npm run db:migrate:remote
umask 077
printf 'CLOUDFLARE_API_TOKEN="%s"\n' "$CLOUDFLARE_API_TOKEN" > .dev.vars.production
npm run deploy -- --secrets-file .dev.vars.production

The custom domain is the Worker origin, so Cloudflare manages its DNS record and certificate. The Worker serves no workers.dev endpoint and disables preview URLs.

6

Verify Access and inventory

Open the configured hostname in a normal browser. Access should authenticate first, then Fleet should report a read-only session and begin loading the account. Test an unauthenticated private window as well: the Worker itself must reject a request that arrives without a valid Access assertion.

7

Opt into reviewed writes

After reviewing the account boundary, Access policy, proxy allowlist, and read-only inventory, regenerate the same ignored file with --write and deploy again.

cloudflare-fleet hosted configure \
  --account-id "$CLOUDFLARE_ACCOUNT_ID" \
  --database-id "$CLOUDFLARE_FLEET_D1_DATABASE_ID" \
  --hostname fleet.example.com \
  --access-aud "$CLOUDFLARE_ACCESS_AUD" \
  --access-team-domain "$CLOUDFLARE_ACCESS_TEAM_DOMAIN" \
  --write

npm run deploy

Operator policy exceptions

Fleet ships with no policy exceptions. If one zone intentionally uses an exact Email DNS record that differs from the derived fleet baseline, copy fleet-policy.example.json to the per-user Fleet configuration directory and edit the relevant fields. Local launches load it directly; cloudflare-fleet hosted configure validates and embeds the same document into the ignored Wrangler variables.

umask 077
fleet_config_dir="${XDG_CONFIG_HOME:-$HOME/.config}/cloudflare-fleet"
mkdir -p "$fleet_config_dir"
cp fleet-policy.example.json "$fleet_config_dir/fleet-policy.json"
cloudflare-fleet hosted configure ...

An Email DNS exception does not hide the matrix difference. It removes only the exact allowed variant from actionable policy drift.

Import existing local intent and activity

When moving an established local fleet to hosted mode, apply the migration first and import the ignored account-scoped state file once.

cloudflare-fleet hosted import-state

The importer reads the D1 identifier from the ignored Wrangler configuration and refuses to replace populated hosted state. Use --force only after reviewing both local and remote data.

Keep the local interfaces

Hosted deployment does not replace local mode. On macOS, provide the account token and identifier in the shell and launch the ordinary browser workflow. It shares the same UI, policy, intent schema, write planner, activity model, and verification code.

export CLOUDFLARE_API_TOKEN="your-account-token"
export CLOUDFLARE_ACCOUNT_ID="your-account-id"
cloudflare-fleet dashboard

Use cloudflare-fleet dashboard --write for the full local workflow. State defaults to $XDG_STATE_HOME/cloudflare-fleet/state.json, falling back to ~/.local/state/cloudflare-fleet/state.json; policy uses the corresponding per-user configuration directory. Select another profile with --state-file or --policy-file. The checkout-local ./launch.sh wrapper accepts the same dashboard options.

The local fleet CLI and stdio MCP server inherit the same credentials and use the same durable state profile:

cloudflare-fleet alignment list --format json
cloudflare-fleet mcp

An MCP client should use cloudflare-fleet as its command and ["mcp"] as its arguments. Keep the token in the inherited process environment, not a tracked or shared client configuration. The server opens no listener and exposes only named diagnostic, audit, intent, alignment, bounded change, activity, and guarded undo tools. The MCP setup guide includes copyable Codex and Claude Code configuration.

Keep the repository safe to publish

Operator state, deployment configuration, live screenshots, and browser traces stay outside version control. Validate the public source tree before proposing a change.

npm run check:publication

The publication check rejects private operator filenames, machine-local paths, unexpected symbolic links, malformed screenshots, and broken documentation links.

Before writes

Review the security model.

Understand the Access, Worker, proxy, persistence, and browser boundaries before enabling mutations.

Open security