Overview

cf-cli

A fast, agent-first Cloudflare operations CLI in Go that fills the gaps Wrangler leaves: DNS edits, recent Workers logs, R2 helpers, and multi-account switching by profile, with secrets in the macOS keychain, never a config file. Its sharpest idea is a bootstrap token that lets an agent mint a scoped token for any Cloudflare API call the CLI doesn't cover yet, so I'm never the bottleneck and never have to open the dashboard.

GoCLI
DevOpsCoding AgentsCloudflare
cf-cli

My web stack is a split: Vercel for hosting most projects, Cloudflare for what it’s genuinely best at, including domain management, traffic and bot protection, Workers, and R2. The friction is that these are two different dashboards, and I’m forever bouncing between them. Vercel has a great CLI, so the agent side of a deploy is easy. Cloudflare doesn’t. Wrangler is built for accessing Cloudflare services, not for driving the broad Cloudflare API, which is where domain and DNS work actually lives. So I’d end up back in a browser, clicking through zones. cf is the tool that ended that.

The real goal was to get my agents out of the Cloudflare docs and out of the dashboard entirely. Now a new project’s whole domain setup is one sentence: “assign this domain to the Vercel project and point DNS at it with cf-cli, profile personal.” The agent runs the Vercel CLI and cf back to back and the entire DNS-and-domain dance is done in a couple of minutes. I never touch a thing.

Built to complement Wrangler, not duplicate it

I was deliberate about scope: cf doesn’t re-implement anything Wrangler already does well. It covers the things the Cloudflare API exposes but Wrangler doesn’t surface conveniently. For example, it can pull a Worker’s logs from the last five or ten minutes, which turned out to be exactly what I needed for debugging production Workers and which Wrangler just doesn’t give you cleanly. Alongside that: ergonomic DNS editing (A/AAAA/CNAME/TXT/MX with upsert and proxy controls), scoped token minting, R2 bucket and credential helpers, Logpush wiring, and a doctor command that explains why a credential isn’t resolving instead of failing cryptically.

It even fixes one of Wrangler’s own rough edges: switching accounts. Officially, the only way to change accounts in Wrangler is to fully log out and log back in. Since I run several, that’s painful, so cf wrangler switch just swaps the Wrangler credentials file in place on my machine, flipping between accounts instantly with no re-auth dance.

The bootstrap token: an escape hatch so I’m never the bottleneck

This is the part I’m proudest of, and it comes from a philosophy I keep coming back to: for real speed, I cannot be the bottleneck. I can’t predict what corner of the Cloudflare API I’ll need tomorrow, and I didn’t want every future need to require me stopping to add a subcommand.

So I leaned on a Cloudflare feature where one token can mint other tokens, and built that in as a bootstrap token. When an agent needs something cf doesn’t cover yet, it reads the required permissions from Cloudflare’s own docs, mints a fresh token scoped to exactly those permissions, and hits the Cloudflare API directly: no dashboard, no me. The CLI becomes a self-extending surface: if I notice the agents reaching for a particular raw API call often enough, I promote it into a first-class cf command. It’s a living testing ground that guarantees I never have to open Cloudflare docs or the console again.

The CLI is the documentation

A quieter benefit, but a real one: because this is a CLI, I don’t maintain a single page of agent instructions for it. I keep improving the tool, and the agents just run cf --help, read the menu, and figure out the right command on their own. The interface is the documentation, always current, with nothing for me to keep in sync. I usually don’t even specify commands; I name the intent and the profile, and the agent discovers the rest.

Identity and secrets, done carefully

With agents running infra commands, being explicit about which account matters enormously. Profiles are mandatory: every operational command takes --profile <name> (or CF_PROFILE), so there’s zero ambiguity about which of my several Cloudflare accounts, each holding different domains, is about to change. And because switching accounts in the Cloudflare dashboard was slow and clumsy, I moved the whole problem local: credentials live on my machine and the agent switches between them at runtime with a flag. I deliberately kept keys out of plaintext and out of my shell startup file. On macOS the CLI stores and reads secrets through the login keychain, with per-profile service names (env vars still work everywhere for CI and headless boxes).

It ships as a single Go binary via a one-line install script. I reach for it almost every day, on essentially every web project. It’s faster than clicking, more token-efficient than Wrangler for an agent, and it’s become a permanent part of how I ship.

Use and to navigate