- cf CLI (open beta) is the only Cloudflare CLI this skill uses. Every account call runs the one version CfEnv pins as
CF_PACKAGE in LIFEOS/TOOLS/CfEnv.ts (cf@1.0.0-beta.5 when this was written; read the constant for the current pin), not whatever cf is installed globally. The pin costs container Workers: a Durable Object container needs cf@1.0.0-beta.10 or later (References/RuntimeGotchas.md § Durable Object containers), so a container Worker cannot deploy through CfEnv until the pin moves. Moving it is a deliberate change: beta.14 dropped observability telemetry query, which scheduled log readers rely on (LifeOS's optional Vigil monitor is one), so check every command your callers run against the new version first. Its command map, discovery, output, local mode and extra capabilities are in References/CfCli.md.
LIFEOS/TOOLS/CfEnv.ts runs one cf command with CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID from ~/.claude/.env injected into that child process only, behind a verb allowlist. secret put is translated so the value travels on stdin, never as an argument. Every non-dry-run deploy first runs the independent code review (LIFEOS/TOOLS/Review.ts), which needs a reviewer model from the other vendor and fails closed without one (Setup.md § Deploy review); a blocker stops the deploy. After a deploy it checks each declared domain's X and LinkedIn link card, a public fetch that needs no setup.
- Code Mode MCP is a fallback for API reads only when it is connected and CfEnv is unavailable (
References/CfCli.md § Code Mode MCP fallback).
Wrangler is retired here. Projects carry cloudflare.config.ts + vite.config.ts (the @cloudflare/vite-plugin bundles the Worker, so no project needs Wrangler installed). A repo that still has a wrangler.toml|json|jsonc is unconverted: convert it (Intents/Deploy.md § Converting a Wrangler project) before deploying.
Workers-first doctrine
Default to Workers for everything, static sites included; use Pages only when the user explicitly asks for Pages. A static site (HTML/CSS/JS/JSON, with or without a build step) deploys as a Worker with Static Assets, configured by cloudflare.config.ts and vite.config.ts as in References/StaticSiteConfig.md. Then deploy through Intents/Deploy.md. Add an entrypoint Worker script only if the site needs server logic. Reach for cf pages deploy only when the user explicitly requests Pages.
Dev-URLs-off doctrine
Every worker serves only its authoritative URL. A worker with a custom domain or route ships workersDev: false + previewUrls: false, set explicitly as in References/StaticSiteConfig.md, so there is no *.workers.dev second origin. The one exception is a worker whose workers.dev URL is the authoritative URL because no custom domain exists (for example an internal worker, or one not yet launched on its domain). To turn a stray dev URL off: POST /accounts/{id}/workers/scripts/{name}/subdomain with {"enabled":false,"previews_enabled":false}. A scheduled check that GETs each routed worker's *.workers.dev origin and flags anything other than 404/410 is the backstop; if Config.md names one under dev_url_check:, read its latest result before treating the account as clean.
cf CLI
Every account-touching command runs as bun ~/.claude/LIFEOS/TOOLS/CfEnv.ts <cf args>, on the pinned version. A global cf serves only the plain calls (discovery, --help, --local, a project's dry run); updating it changes nothing CfEnv runs. Re-probe (--version, --help) on every pin move, because command names still move between betas.
bun add -g cf@latest # plain cf for discovery and local work; LifeOS rule: bun only
cf cli telemetry status # match PREFERENCES.md § Telemetry when the install sets one
- Discovery. Keep
cf cli search queries anonymous: describe the action and resource type, never a name, domain, ID or token.
- Auth.
CfEnv.ts auth whoami is the auth probe.
- Safety. Delete and update commands hit the live API. Most accept
--dry-run; use it first on anything destructive.
Credentials
A single master CLOUDFLARE_API_TOKEN lives in ~/.claude/.env: one Account API Token granted only the scopes your deploys use. A site that ships Workers with a D1 database on its own domain, for example, needs Workers Scripts edit, Workers Routes edit, D1 edit and Zone DNS edit, and nothing account-wide beyond that; add a product's scope when you start deploying it. The token's name and exact permission set are yours to record in Config.md, never here. Every Cloudflare operation — deploys, reads, secrets, Pages, D1, R2, KV, DNS — uses it through CfEnv. Code that needs the token at runtime resolves process.env.CLOUDFLARE_API_TOKEN only, never a per-product variant; per-product names (_WORKERS_EDIT, _WORKERS_READ, _D1) split one credential into several that drift apart. Never unset the token to coast on an OAuth login; that false fix hides the real fault and restarts the rotation loop. A Worker that needs the token at runtime gets it with CfEnv.ts secret put CLOUDFLARE_API_TOKEN --name <worker> --from-env, and every such Worker is re-threaded after a rotation; keep the list of them in Config.md.