OnALead · Ops Runbook · RB-001

Cloudflare Config & Deploy — the Docs Hub Manual

Everything needed to operate docs.onalead.com: one-time account setup, publishing content, and every supported deployment pipeline — interactive, unattended/CI, AI-agent, raw API, and point-and-click.

PROJECT docs-hub ACCOUNT 190223caefe5ae738493d4efac78b57d DIRECT URL docs-hub-cyq.pages.dev UPDATED 2026-06-12
01

Architecture

The main site (www.onalead.com) stays on GoDaddy, untouched. A single CNAME record hands the docs subdomain to a Cloudflare Pages project. Every deploy publishes to Cloudflare's global edge; DNS never changes again after setup.

        [ GoDaddy DNS — apex authority, unchanged ]
                        |
        +---------------+-----------------------+
        |                                       |
  (www.onalead.com)                     (docs.onalead.com)
        |                                       |
  [ GoDaddy Host ]            CNAME docs → docs-hub-cyq.pages.dev
                                                |
                                  [ Cloudflare Pages — Free plan ]
                                                |
                              deploys via CLI / CI / agent / API / web
Why not NS subdomain delegation? Cloudflare's "subdomain setup" (delegating docs.onalead.com as its own zone via NS records) is an Enterprise-plan feature. The CNAME-backed Pages custom domain delivers the same isolation, zero cost, and edge delivery on the Free plan.
02

One-Time Setup

StepActionStatus
2.1Create Pages project docs-hub (production branch main)done
2.2First deployment → live at https://docs-hub-cyq.pages.devdone
2.3Bind custom domain docs.onalead.com to the projectdone
2.4GoDaddy DNS: add CNAME · docs · docs-hub-cyq.pages.dev (TTL 600)manual
2.5Cloudflare auto-validates the domain & issues the TLS certificateauto

Steps 2.1–2.3 were performed with Wrangler + the Cloudflare API and never need repeating. If the project must ever be recreated from scratch:

shell · one-time provisioning
# authenticate (interactive, opens browser)
npx wrangler login

# create project + first deploy + bind domain
npx wrangler pages project create docs-hub --production-branch=main
npx wrangler pages deploy ./dist --project-name=docs-hub
curl -X POST "https://api.cloudflare.com/client/v4/accounts/190223caefe5ae738493d4efac78b57d/pages/projects/docs-hub/domains" \
  -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"docs.onalead.com"}'
03

Publishing Content

The repo lives at D:\Users\JJKramer\repos\onalead-docs. The deployment bundle root is dist/ — whatever is in that folder is the website.

onalead-docs/
├── dist/                        ← deployment bundle root
│   ├── index.html               → docs.onalead.com/
│   └── resources/
│       └── my-doc.html          → docs.onalead.com/resources/my-doc.html
├── package.json                 ← npm run deploy
└── wrangler.jsonc               ← project name + output dir

To publish a new resource: drop the HTML file into dist/resources/, add a link in dist/index.html, and deploy with any pipeline below.

URL normalization Pages serves "pretty URLs": requesting /resources/my-doc.html returns a 308 redirect to /resources/my-doc. Both forms work — link either one.
04

API Token — the Key to Unattended Deploys

Interactive wrangler login uses a browser OAuth session that expires. Every unattended pipeline (CI, agents, scripts, raw API) instead uses a long-lived, narrowly-scoped API token.

Create the token

  1. Cloudflare dashboard → My Profile → API Tokens → Create Token.
  2. Use the custom token form with exactly one permission: Account · Cloudflare Pages · Edit, scoped to this account.
  3. Name it for its consumer (e.g. ado-docs-hub-deploy) so it can be rotated independently.
  4. Copy the token once — it is never shown again.

Use the token

Wrangler (and the Pages API) read two environment variables. Set them and no browser login is ever needed:

environment
CLOUDFLARE_API_TOKEN=<token>
CLOUDFLARE_ACCOUNT_ID=190223caefe5ae738493d4efac78b57d
Security rules Store the token only in secret stores (ADO secret variables, GitHub encrypted secrets, local env) — never in the repo, never in wrangler.jsonc. One permission, one account, one consumer per token. Rotate from the same dashboard page; revoking takes effect immediately.
05

Pipeline A — Wrangler CLI (Local)

The everyday workflow for a human at a terminal. From the onalead-docs repo:

shell · interactive deploy
# one-time per machine (browser OAuth)
npx wrangler login

# deploy — also available as: npm run deploy
npx wrangler pages deploy ./dist --project-name=docs-hub --branch=main

Output ends with a unique preview URL per deployment (e.g. https://c943062f.docs-hub-cyq.pages.dev) plus the production alias. Deploys typically complete in under 10 seconds; only changed files upload.

For unattended local scripts (scheduled tasks, build hooks), skip login entirely and set the two environment variables from §04 — the same command then runs headless.

06

Pipeline B — Azure DevOps (CI)

GitOps for teams already in ADO: push the onalead-docs repo to an ADO project, and every merge to main ships to the edge automatically.

Setup

  1. Push the repo to ADO and create a pipeline from azure-pipelines.yml below.
  2. Pipeline → Variables → add CLOUDFLARE_API_TOKEN with the value from §04, marked "Keep this value secret." (Or put it in a Library variable group.)
azure-pipelines.yml
trigger:
  branches:
    include: [main]

pool:
  vmImage: ubuntu-latest

steps:
  - task: NodeTool@0
    inputs:
      versionSpec: '20.x'
    displayName: Install Node

  - script: |
      npx wrangler pages deploy ./dist --project-name=docs-hub --branch=main
    displayName: Deploy to Cloudflare Pages
    env:
      CLOUDFLARE_API_TOKEN: $(CLOUDFLARE_API_TOKEN)  # secret pipeline variable
      CLOUDFLARE_ACCOUNT_ID: 190223caefe5ae738493d4efac78b57d
Branch previews for free Pass --branch=$(Build.SourceBranchName) instead of --branch=main on PR builds and Cloudflare creates an isolated preview URL per branch — review the rendered docs before merging.
07

Pipeline C — GitHub Actions (CI)

If the repo lives on GitHub instead, the official wrangler-action does the same job. Add CLOUDFLARE_API_TOKEN as an encrypted repo secret (Settings → Secrets and variables → Actions).

.github/workflows/deploy.yml
name: Deploy docs hub
on:
  push: { branches: [main] }

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: cloudflare/wrangler-action@v3
        with:
          apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          accountId: 190223caefe5ae738493d4efac78b57d
          command: pages deploy ./dist --project-name=docs-hub --branch=main

Alternatively, Cloudflare's dashboard offers a native Git integration (Workers & Pages → docs-hub → Settings → Builds) that connects directly to a GitHub/GitLab repo and builds on every push — no workflow file at all. Note: native Git integration supports GitHub and GitLab only, not Azure Repos — which is why ADO uses the Wrangler step in §06.

08

Pipeline D — Claude & AI Agents (MCP / CLI)

Two ways to let an AI agent publish docs autonomously, from simplest to most integrated:

D1 · Claude Code + Wrangler (works today)

Claude Code drives the same Wrangler CLI a human would. With the §04 token in the environment, the agent deploys headlessly — write the HTML, drop it in dist/resources/, run the deploy, verify the URL, done in one prompt.

example agent prompt
# in D:\Users\JJKramer\repos\onalead-docs
"Add the attached writeup as a new resource page, link it from the
 index, deploy to Cloudflare Pages, and verify the live URL returns 200."

D2 · Cloudflare's native MCP servers

Cloudflare publishes remote Model Context Protocol servers that expose account operations (Workers/Pages builds, bindings, observability) as first-class tools to MCP clients like Claude Code and Claude Desktop, authenticated via OAuth:

shell · register MCP server with Claude Code
# Workers/Pages bindings & resources
claude mcp add cloudflare-bindings -- npx mcp-remote https://bindings.mcp.cloudflare.com/sse

# build & deployment insights
claude mcp add cloudflare-builds -- npx mcp-remote https://builds.mcp.cloudflare.com/sse

On first use the browser opens for Cloudflare OAuth; afterwards the agent can inspect projects, builds, and logs through tool calls. The full catalog of servers is at github.com/cloudflare/mcp-server-cloudflare.

Recommended split Use D1 (Wrangler + token) for the deploy itself — it is deterministic and CI-identical. Use D2 (MCP) when the agent needs to observe the account: list deployments, read build logs, debug a failed rollout.
09

Pipeline E — Raw REST API

Everything Wrangler does is a documented HTTP API — useful for custom tooling or platforms with no Node runtime. All calls authenticate with Authorization: Bearer $CLOUDFLARE_API_TOKEN.

Read operations

shell · inspect via API
BASE="https://api.cloudflare.com/client/v4/accounts/190223caefe5ae738493d4efac78b57d"

# project details, custom-domain status, deployment history
curl -s "$BASE/pages/projects/docs-hub"             -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
curl -s "$BASE/pages/projects/docs-hub/domains"     -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
curl -s "$BASE/pages/projects/docs-hub/deployments" -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN"

Management operations

shell · manage via API
# rollback production to a previous deployment
curl -X POST "$BASE/pages/projects/docs-hub/deployments/<DEPLOYMENT_ID>/rollback" \
  -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN"

# delete a deployment
curl -X DELETE "$BASE/pages/projects/docs-hub/deployments/<DEPLOYMENT_ID>" \
  -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
Creating deployments by raw API Direct-upload deployments via bare HTTP require building a file-hash manifest and a multipart upload protocol. It is documented but fiddly — Wrangler is the supported wrapper around exactly this API, so for creating deployments prefer wrangler pages deploy even inside custom tooling (shell out or use the wrangler npm package programmatically).
10

Pipeline F — Web Upload (Dashboard)

Zero-tooling fallback: drag-and-drop in the browser. Good for emergencies or non-technical edits; the trade-off is no git history of what shipped.

  1. Open dash.cloudflare.com → Workers & Pages and select docs-hub.
  2. Click Create deployment (Deployments tab).
  3. Choose the main branch (production) and drag the contents of dist/ — or click "select from computer → upload folder" and pick the dist folder itself.
  4. Save and deploy. The edge updates in seconds.
Upload the whole bundle A web upload replaces the deployment's file set — always upload all of dist/, not just the one changed file, or everything else 404s. Afterwards, sync the same change back into the git repo so the next CLI/CI deploy doesn't silently revert it.
11

Pipeline Comparison

PipelineAuthTriggerBest for
A · Wrangler CLIOAuth login or tokennpm run deployDay-to-day human deploys default
B · Azure DevOpsAPI token (secret var)Merge to mainTeam GitOps in the existing ADO org
C · GitHub Actions / native GitAPI token / GitHub OAuthPush to mainGitOps if the repo moves to GitHub
D · Claude / MCPAPI token (D1) · OAuth (D2)Natural-language promptAgent-authored docs, autonomous publishing
E · Raw REST APIAPI tokenAny HTTP clientCustom tooling, rollbacks, status checks
F · Web uploadDashboard loginDrag & dropEmergency / non-technical fallback

All six publish to the same project — they can be mixed freely. The only coordination rule: keep dist/ in git as the source of truth (see the §10 caveat).

12

Troubleshooting

SymptomCause & fix
docs.onalead.com doesn't resolveGoDaddy CNAME missing or still propagating (TTL 600 ⇒ ≤10 min). Verify with nslookup -type=CNAME docs.onalead.com 8.8.8.8.
Custom domain stuck on initializing / pendingCloudflare validates over HTTP once the CNAME resolves; allow up to ~1 h. Check Workers & Pages → docs-hub → Custom domains.
TLS certificate error on first visitCertificate issuance follows domain validation by a few minutes. If >1 h, remove and re-add the custom domain.
308 redirect on .html URLsNormal — Pages pretty-URL behavior (§03). Both URL forms serve the page.
wrangler: not authenticatedOAuth session expired. Re-run npx wrangler login, or set CLOUDFLARE_API_TOKEN to go tokenized/headless permanently.
CI fails with Authentication error [code 10000]Token revoked, expired, or missing the Cloudflare Pages · Edit permission. Reissue per §04.
Page 404s after a web uploadPartial upload replaced the bundle (§10). Redeploy the full dist/ from git: npm run deploy.
Wrong content live / bad deployInstant rollback: dashboard → Deployments → ⋯ → Rollback to this deployment, or the API call in §09.