AgentStack
SKILL verified Apache-2.0 Self-run

Cloudflare Ops

skill-michael-ltm-cloudflare-skill-cloudflare-ops · by michael-ltm

Manage Cloudflare accounts via API — DNS records, SSL/TLS settings, Origin certificates, Workers, Pages, R2 storage, D1 databases, KV namespaces, Cloudflare Tunnel, WAF rules, and multi-account credential management. Maintains workspace credential files for instant session resumption. Use when managing Cloudflare DNS, deploying Workers/Pages, configuring SSL, managing R2/D1/KV, creating Origin ce…

No reviews yet
0 installs
16 views
0.0% view→install

Install

$ agentstack add skill-michael-ltm-cloudflare-skill-cloudflare-ops

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access Used
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Cloudflare Ops? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Cloudflare Operations

AI-driven Cloudflare management — DNS, SSL, Workers, Pages, Storage, and more.


⚠ SENSITIVE DATA — MANDATORY OUTPUT RULES

These rules apply to EVERY response. No exceptions.

Never display these in chat output:

| Sensitive field | Instead say | |---|---| | API Token (api_token) | cf_token=*** | | Account ID (account_id) | account_id=*** | | Any *_TOKEN, *_KEY, *_SECRET | *** | | Contents of .cloudflare/credentials.json | Show key names only, mask values |

When constructing shell commands for explanations:

# ✗ Never write this in chat:
curl -H "Authorization: Bearer eyJhbGc..." ...

# ✓ Write this instead (run the actual command silently via tool):
# [cloudflare] Running: list DNS records for awapi.org

Checklist before every response:

  • [ ] No raw API tokens shown
  • [ ] No Account IDs shown
  • [ ] Credentials file content masked

Session Start — Read Credentials First

Before doing anything, check workspace for saved credentials:

ls .cloudflare/credentials.json 2>/dev/null && echo "FOUND" || echo "NOT_FOUND"

If FOUND → Read file silently, load tokens into memory. Tell user: "Loaded Cloudflare credentials for account [label]." Do NOT display token or account ID values.

If NOT_FOUND → Follow [Credential Setup](#credential-setup) below. Ask the user for credentials interactively.


Credential Setup (First Time)

When no credentials file exists, ask the user:

> "To get started, I need your Cloudflare API credentials. Please provide: > 1. API Token — create at https://dash.cloudflare.com/profile/api-tokens > (Required permissions: Zone:DNS:Edit, Zone:SSL:Edit, Zone:Zone:Read, Account:Workers:Edit, Account:R2:Edit, Account:D1:Edit) > 2. Account ID — found in Cloudflare Dashboard right sidebar > 3. Label — a name for this account (e.g. 'personal', 'work') > > I'll save these locally to .cloudflare/credentials.json and add it to .gitignore automatically."

After user provides credentials, write the file using a tool (never echo secrets in terminal):

# Write credentials file (use Write tool, not Shell echo)
import json, os

os.makedirs(".cloudflare", exist_ok=True)

creds = {
    "_note": "Cloudflare credentials — DO NOT COMMIT — managed by cloudflare-ops skill",
    "default": "ACCOUNT_LABEL",
    "accounts": {
        "ACCOUNT_LABEL": {
            "label": "USER_PROVIDED_LABEL",
            "api_token": "USER_PROVIDED_TOKEN",
            "account_id": "USER_PROVIDED_ACCOUNT_ID",
            "email": "optional"
        }
    }
}
json.dump(creds, open(".cloudflare/credentials.json", "w"), indent=2)

Then protect from git:

# Auto-add to .gitignore
grep -q ".cloudflare/" .gitignore 2>/dev/null || echo ".cloudflare/" >> .gitignore

Confirm to user: "Credentials saved to .cloudflare/credentials.json and added to .gitignore. The file will never be committed."


Load Credentials (Every Session)

import json

def load_cf_creds(account=None):
    d = json.load(open(".cloudflare/credentials.json"))
    acct = d["accounts"][account or d["default"]]
    return acct["api_token"], acct["account_id"]

CF_TOKEN, CF_ACCOUNT = load_cf_creds()
CF_API = "https://api.cloudflare.com/client/v4"

DNS Management

Get Zone ID

ZONE_ID=$(curl -s -H "Authorization: Bearer $CF_TOKEN" \
  "$CF_API/zones?name=DOMAIN.COM" | \
  python3 -c "import json,sys; print(json.load(sys.stdin)['result'][0]['id'])")

List DNS records

curl -s -H "Authorization: Bearer $CF_TOKEN" "$CF_API/zones/$ZONE_ID/dns_records" | \
  python3 -c "
import json,sys
for r in json.load(sys.stdin)['result']:
    print(r['type'], r['name'], r['content'], 'proxied' if r.get('proxied') else 'dns-only')
"

Add record

curl -s -X POST -H "Authorization: Bearer $CF_TOKEN" -H "Content-Type: application/json" \
  "$CF_API/zones/$ZONE_ID/dns_records" \
  -d '{"type":"A","name":"sub","content":"1.2.3.4","proxied":false,"ttl":1}'
# proxied: true = orange cloud (CDN), false = gray cloud (DNS only)

Update / Delete record

# Update
curl -s -X PATCH -H "Authorization: Bearer $CF_TOKEN" -H "Content-Type: application/json" \
  "$CF_API/zones/$ZONE_ID/dns_records/RECORD_ID" \
  -d '{"content":"new.ip","proxied":true}'
# Delete
curl -s -X DELETE -H "Authorization: Bearer $CF_TOKEN" \
  "$CF_API/zones/$ZONE_ID/dns_records/RECORD_ID"

SSL / TLS

Set SSL mode

# mode: off | flexible | full | strict
curl -s -X PATCH -H "Authorization: Bearer $CF_TOKEN" -H "Content-Type: application/json" \
  "$CF_API/zones/$ZONE_ID/settings/ssl" -d '{"value":"strict"}'

Create Origin Certificate (15-year, for Full Strict)

curl -s -X POST -H "Authorization: Bearer $CF_TOKEN" -H "Content-Type: application/json" \
  "$CF_API/certificates" \
  -d '{"hostnames":["DOMAIN.COM","*.DOMAIN.COM"],"requested_validity":5475,"request_type":"origin-rsa","csr":""}'
# Save: certificate field → origin.crt, private_key → origin.key
# NEVER display private_key in chat

Workers

Deploy via Wrangler

export CLOUDFLARE_API_TOKEN=$CF_TOKEN  # set env var, don't put in wrangler.toml
wrangler deploy

Deploy via API

curl -s -X PUT -H "Authorization: Bearer $CF_TOKEN" \
  -F "metadata={\"main_module\":\"worker.js\"};type=application/json" \
  -F "worker.js=@worker.js;type=application/javascript+module" \
  "$CF_API/accounts/$CF_ACCOUNT/workers/scripts/WORKER_NAME"

Secrets

# Use wrangler (interactive, never echoes to terminal)
wrangler secret put SECRET_NAME

Pages

wrangler pages project create MY_PROJECT
wrangler pages deploy ./dist --project-name MY_PROJECT

R2 Object Storage

wrangler r2 bucket create BUCKET_NAME
wrangler r2 object put BUCKET/path/file.txt --file ./local.txt
wrangler r2 object list BUCKET_NAME

D1 Database

wrangler d1 create MY_DB
wrangler d1 execute MY_DB --command "SELECT * FROM users"
wrangler d1 execute MY_DB --file schema.sql
wrangler d1 export MY_DB --output backup.sql

KV

wrangler kv namespace create MY_KV
wrangler kv key put --namespace-id=NS_ID "key" "value"
wrangler kv key get --namespace-id=NS_ID "key"

Multi-Account

.cloudflare/credentials.json supports multiple accounts:

{
  "default": "personal",
  "accounts": {
    "personal": { "label": "Personal", "api_token": "***", "account_id": "***" },
    "work":     { "label": "Work",     "api_token": "***", "account_id": "***" }
  }
}

Switch: load_cf_creds(account="work")

To add an account, ask the user for token + account ID, then append to the file using Write tool.


Wrangler Config Template

# wrangler.toml — safe to commit (no secrets)
name = "my-app"
main = "src/index.ts"
compatibility_date = "2024-01-01"

[[d1_databases]]
binding = "DB"
database_name = "my-database"
database_id = "DATABASE_ID"   # not secret

[[r2_buckets]]
binding = "BUCKET"
bucket_name = "my-bucket"

[[kv_namespaces]]
binding = "KV"
id = "NAMESPACE_ID"           # not secret

Secrets go in wrangler secret put — never in wrangler.toml.


Reference

  • API reference: https://developers.cloudflare.com/api/
  • Wrangler: https://developers.cloudflare.com/workers/wrangler/
  • Workers: https://developers.cloudflare.com/workers/
  • R2: https://developers.cloudflare.com/r2/
  • D1: https://developers.cloudflare.com/d1/

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.