I read through a walkthrough of the new Cloudflare `cf` CLI OAuth flow the other day, the one where you let an agent operate your Cloudflare account. The thing that stuck with me wasn't Cloudflare at all.
The flow goes like this: the agent runs `cf auth login`, hands you a URL and a short verification code, you open the URL in your own browser, type the code, confirm, log into your account if it asks. Sometimes the agent has a background process polling for success and messages you "authorization detected." If it doesn't react in a few seconds, you just tell it "I'm done." After that, the agent can go query your domains, workers, KV, D1, R2 in plain language.
Strip away the Cloudflare-specific part and what's left is a pattern you can own: a device-code handshake plus a grant that is scoped, time-boxed, and revocable. The ability to control a worker or an R2 bucket isn't portable. The handshake and the grant ledger are.
Why this beats pasting a key
The alternative most people reach for is a long-lived key pasted straight into the agent. That works until it doesn't. The community signal around long-lived credentials is not encouraging. There's a report of a Claude account getting banned after years of use, with replies claiming new accounts tied to the same phone number, payment method, IP, or language get killed fast. There are muse users asking whether they have to keep a US IP running for every API call, and whether "bare" usage risks a ban. All of that is unverified anecdote, but the shape is familiar: a single durable credential, broadly scoped, with no clean way to see what it can do or to pull it back in a hurry.
For your own hosted tools you don't have to live that way. You can hand an agent a short-lived, inspectable grant instead of a key that outlives the task.
The three pieces
The broker is small. It's a Python backend, an approval page, and a table. Nothing exotic.
The backend is where the handshake lives. You can run Python online and expose a handful of endpoints: `POST /device/start` mints a `device_code` and a human-friendly `user_code` and returns a verification URL; the agent then polls `POST /device/token` with its `device_code`; and your actual tool endpoints each check that the presented grant is still valid before doing anything. The agent never sees your approval step. It just waits on the poll.
The approval page is what you open in your browser. You can run HTML online to serve a plain page with a field for the `user_code`, a readout of exactly which scopes are being requested, and approve or deny buttons. Keep it boring and legible. The whole point is that a human reads what's about to be granted before clicking yes.
The ledger is a SQLite table, and this is the part worth getting right. Point the SQLite editor at a `grants` table with columns like `grant_id`, `user_code`, `device_code`, `scopes`, `status` (pending / approved / denied / revoked / expired), `issued_at`, `expires_at`, and `approved_at`. The short-lived token the agent eventually carries is just a reference back into this row. Don't store the raw token here — store a hash, so reading the ledger doesn't leak live credentials.
How a request actually flows
The agent calls `/device/start`. The broker writes a `pending` row, returns the `user_code` and the verification URL, and the agent starts polling and sits tight. You open the page, type the code, see that it's asking for, say, `reports:read` and `invoices:write`, and approve. The broker flips the row to `approved`, stamps `approved_at`, sets `expires_at` a short way out, and the next poll returns a token. From there the agent uses your in-platform tool calls and hosted endpoints with that token, and each endpoint rejects anything whose grant is expired, revoked, or missing the scope it needs.
Revocation is a one-line change: set `status` to `revoked`. The next tool call fails. Because the ledger is right there and readable, you can always answer "what does this agent currently have, and when does it lapse" without guessing.
The honest boundary
This does not give an agent access to Cloudflare, or any external cloud, no matter how closely the handshake mirrors the `cf` one. A grant from your broker is only meaningful for your own tools and endpoints hosted on the platform. VicroCode can issue and check these grants against things you run in-platform; it can't broker access to someone else's infrastructure. If you genuinely need the agent to touch Cloudflare, you still run Cloudflare's own flow. The broker doesn't bridge that gap, and pretending it could would just be a worse version of pasting a key.
So the clean fit is this: you've built some Python tools and endpoints you'd like an agent to drive, and you want that access to be short-lived, scoped, and killable rather than a static secret sitting in a config file.
Friction worth knowing up front
Polling is simple but chatty; pick a sane interval and a hard timeout so stale `pending` rows don't pile up. Expiry depends on clocks agreeing, so compare against a server timestamp, not whatever the agent thinks the time is. You still have a secret in play — the token — it's just short-lived, which shifts the blast radius rather than removing it. The approval page needs to be hard to spoof, since it's the one human checkpoint in the chain; show the real scopes and don't let the agent prefill anything it shouldn't. And SQLite is a fine fit for a single operator or a small team, but it's not where you'd land a fleet of agents hammering concurrent grants.
None of this is heavy. It's a backend, a page, and a table. The value is that you've turned "here's my key, good luck" into a grant you can read, scope, and pull back the moment you want to.