Manage Cloudflare DNS — SKILL.md

Raw skill file that agents receive when using this skill

Download
---
name: "Manage Cloudflare DNS"
description: "Create, update, and delete DNS records on Cloudflare using the vault proxy API. Use when pointing subdomains to Vercel, Railway, Cloudflare Tunnels, or any other target. Primary zone: skynet.ceo."
version: "1.0.0"
author: "skynet"
category: "infrastructure"
agents: ["claude-code", "codex", "gemini"]
tags: ["cloudflare", "dns", "networking", "vault", "cname"]
tools_required: ["shell"]
---

# Manage Cloudflare DNS

# Manage Cloudflare DNS

Use this skill when you need to create, update, or delete DNS records for any domain managed on Cloudflare. The primary domain is `skynet.ceo`. All operations go through the vault proxy — never use the Cloudflare API token in `.env` directly (it's stale).

## Prerequisites

- Access to the vault proxy at `http://192.168.86.27:8020`
- `BOTS_VAULT_ADMIN_TOKEN` set in your environment: `source ~/dev/projects/bots/.env`
- Know the zone ID for the target domain (see Key Identifiers below)

## Key Identifiers

| Item | Value |
|------|-------|
| Vault proxy URL | `http://192.168.86.27:8020` |
| Cloudflare Account ID | `7ee53b77377bfa08a66d323326a64b76` |
| skynet.ceo Zone ID | `a2ecd4cf8341dc3e8e105dc06c14d2fc` |
| realcontent.blog Zone ID | Look up via list zones endpoint |

## Quick Reference Table

| Task | Command |
|------|---------|
| List DNS records | `curl -H "Authorization: Bearer $BOTS_VAULT_ADMIN_TOKEN" "http://192.168.86.27:8020/v1/proxy/cloudflare/zones/<zone-id>/dns_records"` |
| Filter by name | Add `?name=subdomain.skynet.ceo` to list URL |
| Create CNAME | POST to dns_records with `"type":"CNAME"` |
| Create A record | POST to dns_records with `"type":"A"` |
| Create TXT record | POST to dns_records with `"type":"TXT"` |
| Update record | PUT to `dns_records/<record-id>` |
| Delete record | DELETE to `dns_records/<record-id>` |
| List all zones | GET `/v1/proxy/cloudflare/zones` |
| Verify DNS propagation | `dig subdomain.skynet.ceo` |
| Check record ID | Use list endpoint and filter by name |
| Point to Vercel | CNAME → `cname.vercel-dns.com`, proxied: true |
| Point to Vercel (A) | A → `76.76.21.21`, proxied: true |
| Point to Tunnel | CNAME → `<tunnel-id>.cfargotunnel.com`, proxied: true |
| Point to Railway | CNAME → Railway target, proxied: **false** |
| Source credentials | `source ~/dev/projects/bots/.env` |

## Core Workflows

### Workflow 1: Create a DNS Record

1. Source credentials:
```bash
source ~/dev/projects/bots/.env
ZONE="a2ecd4cf8341dc3e8e105dc06c14d2fc"
VAULT="http://192.168.86.27:8020"
```

2. Create the record:

**CNAME (Vercel, tunnels):**
```bash
curl -s -X POST \
  -H "Authorization: Bearer $BOTS_VAULT_ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  "$VAULT/v1/proxy/cloudflare/zones/$ZONE/dns_records" \
  -d '{"type":"CNAME","name":"myapp.skynet.ceo","content":"cname.vercel-dns.com","proxied":true,"ttl":1}'
```

**A record (direct IP):**
```bash
curl -s -X POST \
  -H "Authorization: Bearer $BOTS_VAULT_ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  "$VAULT/v1/proxy/cloudflare/zones/$ZONE/dns_records" \
  -d '{"type":"A","name":"myapp.skynet.ceo","content":"76.76.21.21","proxied":true,"ttl":1}'
```

**Tunnel CNAME:**
```bash
curl -s -X POST \
  -H "Authorization: Bearer $BOTS_VAULT_ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  "$VAULT/v1/proxy/cloudflare/zones/$ZONE/dns_records" \
  -d '{"type":"CNAME","name":"worker-dev1.skynet.ceo","content":"<tunnel-id>.cfargotunnel.com","proxied":true,"ttl":1}'
```

3. Verify:
```bash
dig myapp.skynet.ceo
# Should return the expected IP or CNAME target within 1-5 minutes
```

### Workflow 2: List and Find Records

```bash
source ~/dev/projects/bots/.env
# List all records
curl -s -H "Authorization: Bearer $BOTS_VAULT_ADMIN_TOKEN" \
  "http://192.168.86.27:8020/v1/proxy/cloudflare/zones/a2ecd4cf8341dc3e8e105dc06c14d2fc/dns_records" \
  | jq '.result[] | {id, type, name, content, proxied}'

# Filter by subdomain name
curl -s -H "Authorization: Bearer $BOTS_VAULT_ADMIN_TOKEN" \
  "http://192.168.86.27:8020/v1/proxy/cloudflare/zones/a2ecd4cf8341dc3e8e105dc06c14d2fc/dns_records?name=myapp.skynet.ceo" \
  | jq '.result[]'
```

### Workflow 3: Update a Record

Get the record ID from Workflow 2, then:
```bash
curl -s -X PUT \
  -H "Authorization: Bearer $BOTS_VAULT_ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  "http://192.168.86.27:8020/v1/proxy/cloudflare/zones/a2ecd4cf8341dc3e8e105dc06c14d2fc/dns_records/<record-id>" \
  -d '{"type":"CNAME","name":"myapp.skynet.ceo","content":"new-target.example.com","proxied":true,"ttl":1}'
```

Verify: re-run the list endpoint and confirm `content` changed.

### Workflow 4: Delete a Record

```bash
curl -s -X DELETE \
  -H "Authorization: Bearer $BOTS_VAULT_ADMIN_TOKEN" \
  "http://192.168.86.27:8020/v1/proxy/cloudflare/zones/a2ecd4cf8341dc3e8e105dc06c14d2fc/dns_records/<record-id>"
```

Verify: `dig myapp.skynet.ceo` should return NXDOMAIN within a few minutes.

### Workflow 5: Look Up a Zone ID

```bash
curl -s -H "Authorization: Bearer $BOTS_VAULT_ADMIN_TOKEN" \
  "http://192.168.86.27:8020/v1/proxy/cloudflare/zones" \
  | jq '.result[] | {id, name}'
```

## Common Patterns

**Point subdomain to Vercel (Next.js apps):**
- Use CNAME → `cname.vercel-dns.com` with `proxied: true`
- Or A record → `76.76.21.21` with `proxied: true`

**Point subdomain to Cloudflare Tunnel:**
- CNAME → `<tunnel-id>.cfargotunnel.com` with `proxied: true`

**Point subdomain to Railway:**
- CNAME → Railway-provided target with `proxied: false` (Railway handles its own TLS)

## Troubleshooting

- **Symptom**: `curl` returns 401 Unauthorized
  **Cause**: `BOTS_VAULT_ADMIN_TOKEN` not set or stale
  **Fix**: Run `source ~/dev/projects/bots/.env` and retry. Never use `CLOUDFLARE_API_TOKEN` — it's stale.

- **Symptom**: DNS record created but site not loading after 10+ minutes
  **Cause**: Proxied mode may conflict with target's TLS setup; or wrong record type
  **Fix**: For Railway, set `proxied: false`. For Vercel, ensure CNAME points to `cname.vercel-dns.com`.

- **Symptom**: wrangler CLI shows DNS permission error
  **Cause**: wrangler OAuth scopes don't include DNS write
  **Fix**: Use the vault proxy exclusively for all DNS operations, not wrangler.

- **Symptom**: Wildcard cert not covering subdomain
  **Cause**: Cloudflare Universal SSL only covers `*.skynet.ceo`, not multi-level like `*.sub.skynet.ceo`
  **Fix**: Don't create multi-level subdomain records. Use single-level subdomains only.

- **Symptom**: TTL not honoring the value you set
  **Cause**: When `proxied: true`, Cloudflare forces TTL to "Auto" regardless of the passed value
  **Fix**: Expected behavior — TTL 1 = Auto when proxied. No action needed.

## References

- Cloudflare API docs: https://developers.cloudflare.com/api
- Vault proxy: `http://192.168.86.27:8020/v1/proxy/cloudflare/`
- skynet.ceo Zone ID: `a2ecd4cf8341dc3e8e105dc06c14d2fc`

curl -s https://skills.skynet.ceo/api/skills/manage-cloudflare-dns/skill.md