adbertram

cloudflare-cli

Use this skill for service operations only. DO NOT use this skill for CLI implementation lifecycle work such as creating, testing, updating, troubleshooting, validating, removing, or documenting the CLI tool itself; delegate those tasks to cli-tool-expert. Execute cloudflare operations using the `cloudflare` CLI tool. CLI interface for Cloudflare API — manage zones, DNS records, cache, IP access rules, zone traffic analytics, and Workers scripts. Triggers: cloudflare, cloudflare cli, cloudflare dns, cloudflare zones, cloudflare cache, cloudflare access rules, manage dns records, purge cloudflare cache, cloudflare ip rules, block ip cloudflare, cloudflare analytics, zone traffic, page views, top paths, cloudflare workers, worker scripts, upload worker

adbertram 6 Updated 1mo ago

Resources

1
GitHub

Install

npx skillscat add adbertram/cli-tools/cloudflare-cli

Install via the SkillsCat registry.

SKILL.md
Execute cloudflare operations using the `cloudflare` CLI. All cloudflare interactions should use this CLI. The `cloudflare` CLI follows this pattern: ```bash cloudflare [arguments] [options] ```
Task Command
List zones cloudflare zones list --table
Get zone details cloudflare zones get ZONE_ID
List DNS records cloudflare dns records list ZONE_ID --table
Create DNS record cloudflare dns records create ZONE_ID --type A --name sub.example.com --content 1.2.3.4
Purge cache cloudflare cache purge ZONE_ID
List access rules cloudflare access-rules list ZONE_ID --table
Block an IP cloudflare access-rules create ZONE_ID --target ip --value 1.2.3.4 --mode block
Set Under Attack mode cloudflare zones update ZONE_ID --security-level under_attack
Traffic totals for a date range cloudflare analytics summary example.com --start 2026-06-01 --end 2026-06-30
Top pages by HTML page views cloudflare analytics top-paths example.com --limit 5 --table
List Workers scripts cloudflare workers list
Download a Worker script cloudflare workers get my-worker > worker.js
Upload a Worker script cloudflare workers upload my-worker --file ./worker.js
Delete a Worker script cloudflare workers delete my-worker --force
List all queues cloudflare queues list --limit 0
Get queue details cloudflare queues get QUEUE_ID ACCOUNT_ID
Create a queue cloudflare queues create issue-manager ACCOUNT_ID
</quick_start>
**MANDATORY: Consult the adjacent `usage.json` at `/_repo/skills/-cli/usage.json` before executing ANY `cloudflare` command.** This file contains complete command syntax, all arguments, all options, and usage instructions for every command. Never guess at command syntax. - **queues consumers** — `list QUEUE_ID [ACCOUNT]`, `get QUEUE_ID CONSUMER_ID [ACCOUNT]`, `create QUEUE_ID [ACCOUNT]`. Create enables HTTP pull only, with optional `--batch-size`, `--max-retries`, `--retry-delay` (seconds), `--visibility-timeout-ms` (milliseconds), and `--dead-letter-queue` (name). Omitted settings use Cloudflare defaults. Consumer create sends one POST without retry; inspect consumers list before retrying any failure. It does not remove existing consumers, run a poller, or acknowledge messages. External consumers require their own Queues read/write token. List/get retain shared output/profile options; list filters before limit and 0 returns the entire consumer collection. - **queues** — `queues list [ACCOUNT]`, `queues get QUEUE_ID [ACCOUNT]`, and `queues create QUEUE_NAME [ACCOUNT] [--jurisdiction eu|us|fedramp]`. List follows page metadata; client-side filters run before limit (100 by default, 0 means all pages). List/get support properties and table output. Create makes exactly one POST attempt; inspect `queues list ACCOUNT --limit 0` for the requested name before retrying any failure. Read requires Queues Read/Write or Workers Scripts Read/Write; create requires Queues Write or Workers Scripts Write. Read success does not prove write permission. Creation does not configure consumers or purchase a plan. - **auth** — Manage authentication (login, logout, status, refresh, test) - **auth** -- Authentication commands and nested `auth profiles` management - **zones** — Manage Cloudflare zones (list, get, update security settings) - **cache** — `cache purge ZONE_ID` purges the Cloudflare CDN edge cache for a zone; `cache clear` removes the local CLI response cache for a profile. These are different caches. - **access-rules** — Manage IP access rules (whitelist, block, challenge IPs/ranges/ASNs/countries) - **dns** — Manage DNS with sub-groups: `dns zones` (list/get zones) and `dns records` (full CRUD on DNS records) - **analytics** — Zone traffic analytics via the GraphQL Analytics API: `analytics summary` (totals for a date range) and `analytics top-paths` (top pages by HTML page views). Zone argument accepts a zone name or zone ID. Requires the `Analytics: Read` zone permission on the API token. - **workers** — Account-level Workers script management: `workers list`, `workers get SCRIPT_NAME` (raw source to stdout or `--output FILE`), `workers upload SCRIPT_NAME --file ./worker.js [--format modules|service-worker] [--compatibility-date YYYY-MM-DD] [--bindings '']`, and `workers delete SCRIPT_NAME --force`. All four take an optional ACCOUNT argument (name or ID); omit it when the token sees exactly one account. Requires `Account > Workers Scripts > Read` (list/get) or `Account > Workers Scripts > Edit` (upload/delete) on the API token. - **workers routes** — Zone-scoped Worker routes: `workers routes list ZONE`, `workers routes get ZONE ROUTE_ID`, `workers routes create ZONE --pattern ... --script ...`, and `workers routes delete ZONE ROUTE_ID --force`. Requires `Zone > Workers Routes > Read` (list/get) or `Zone > Workers Routes > Edit` (create/delete). - **pages** — Account-level Cloudflare Pages management with three sub-groups: - `pages projects` — `list`, `get PROJECT_NAME`, `create`, `update`, `delete`, `purge-build-cache`, `get-upload-token`. Projects are addressed by NAME, not ID. - `pages deployments` — `list PROJECT`, `get PROJECT DEPLOYMENT_ID`, `create` (pass `--directory PATH` for full direct-upload deploys: hashes the tree, uploads missing assets, ships the site; pass `--manifest '{json}'` only for advanced manual creates), `retry`, `rollback`, `delete`. - `pages domains` — `list PROJECT`, `get PROJECT DOMAIN`, `create` (add), `update` (reprovision/retry validation), `delete`. - All Pages commands take an optional ACCOUNT argument (name or ID). Requires `Pages Read` (list/get) or `Pages Write` (mutations) on the API token. `usage.json` is the command contract. If a needed Cloudflare API area is absent from `usage.json` (for example rulesets/header transforms for HSTS discovery), do not run a bare guessed command and let `No such command` fail the workflow. Either report the missing CLI capability and route a deliberate CLI-extension task, or wrap any exploratory `cloudflare --help` probe so expected absence prints an explicit unsupported marker and exits 0. Do not mutate Cloudflare configuration while probing capability. </essential_principles> **Symptom.** Read commands work and return correct data:
cloudflare zones list --table
cloudflare dns records list <zone_id> --limit 200

Every write fails with exit 1 and a 403:

cloudflare dns records delete <zone_id> <record_id> --force
Error: API request failed (403): Authentication error

The failure hits every record and every zone identically.

Cause. The stored credential is a scoped Cloudflare User API Token, not
a Global API Key; the CLI authenticates with Authorization: Bearer <token>
using the API token stored in the CLI-tools secret manager as cloudflare-api-key. That token carries only Read permission groups. Cloudflare returns
HTTP 403 with the generic message Authentication error for an out-of-scope
token, which reads like a broken or expired credential and sends diagnosis down
the wrong path.

This is not a header-shape bug. CloudflareClient._update_headers builds
Authorization: Bearer <token> once in __init__, and _make_request sends
that same header for every HTTP method. Reads and writes are authenticated
identically. Do not go looking for an X-Auth-Key/X-Auth-Email branch or a
per-method code path; there is none, and
cloudflare/tests/test_write_path_auth.py locks that in.

cloudflare auth test cannot detect this. Its handler issues
GET /zones, so a read-only token returns authenticated: true and
api_test: passed while every write still fails. Never treat a green
auth test or auth status as proof that writes will work.

How to confirm the credential is a token, not a Global API Key. A Global API
Key is 37 hex characters. An API token is longer, mixed-case, and starts with
cfut. auth status shows the masked prefix. GET /user/tokens/verify with the
Authorization: Bearer header returns 200 and
"This API Token is valid and active" for a token; a Global API Key fails that
endpoint under Bearer. A valid verify plus a 403 write proves scope, not
validity.

Fix (Adam must mint the token; an agent cannot). Create a replacement API
token in the Cloudflare dashboard at
https://dash.cloudflare.com/profile/api-tokens with Edit permission for each
area the CLI must write, then store it in the secret manager:

CLI command group Permission group required
dns records create/update/delete Zone > DNS > Edit
access-rules create/update/delete Zone > Firewall Services > Edit
cache purge Zone > Cache Purge > Purge
zones update (security level) Zone > Zone Settings > Edit
zones list/get Zone > Zone > Read
analytics summary/top-paths Zone > Analytics > Read
workers list/get Account > Workers Scripts > Read
workers upload/delete Account > Workers Scripts > Edit

Set Zone Resources to include every zone the CLI manages, then rotate:

<cli-tools-root>/_repo/_secret-manager/secrets.sh set cloudflare-api-key

Verify the new scope with a real write against a low-risk zone
(atademos.com, 1bb82acebb2c9cc1e8c334e599db915d) — create a throwaway TXT
record, confirm it, then delete it. Never test writes against
actionblogger.com, adamtheautomator.com, or brickbuddy.io.

CLI behavior since the fix. _make_request now routes any 403 through
build_forbidden_error, so the CLI names the refused method and endpoint, the
exact permission group required, the fact that working reads prove missing Edit
scope rather than an expired token, the auth test false-green warning, and the
secrets.sh set rotation command. A bare
API request failed (403): Authentication error from this CLI now means the
error path itself regressed.

The `cloudflare` credential type is `api_key`, and the active profile stores it as a `secret://cloudflare-api-key` placeholder resolved from the CLI-tools secret manager. Because the API key lives in the secret manager, `cloudflare auth login` and `cloudflare auth login --force` cannot prompt for it interactively — they now print an actionable notice naming the secret and the exact command to set/rotate it, then re-validate the existing credential. This is expected, not an error.

To change or rotate the API key, update the secret directly (never edit the profile
.env or paste the key into a prompt):

<cli-tools-root>/_repo/_secret-manager/secrets.sh set cloudflare-api-key

If auth login reports Missing secret 'cloudflare-api-key', the referenced
secret is absent from the secret manager. Set it with the command above; do not
attempt to re-enter the key through auth login.

This pattern applies to any api_key/PAT/username_password CLI whose sensitive fields
are stored as secret://<name> placeholders: rotate the value with
secrets.sh set <secret-name>, not through interactive auth login.

</known_issues>

**`usage.json`** — Complete command tree with arguments, options, defaults, and usage instructions for every command. </reference_index> - Command executes without error - Output is displayed in requested format - Correct command and flags used (verified against usage.json) </success_criteria>

Categories