# Cloudflare — the foundation everything runs on (Kit 3)

The first kits were about your machine — launching Claude in one keystroke, color-coding your
terminals, talking instead of typing. This one starts the foundation: the layer your websites,
servers, files, and email actually sit on. I run almost everything I've built through one
Cloudflare account, and the reason is simple: it's the single front door AND the single control
panel for all of it. One place for DNS, a private door into every server, a CDN, file hosting,
and the email records — and because it's all API-driven, your AI can run the whole thing for you.

Here's the shape of it before the steps, because the shape is the point:

- Every domain you own lives in ONE Cloudflare account. That's your control plane.
- Each server runs a tiny program (`cloudflared`) that dials OUT to Cloudflare and holds the
  line open. You stop exposing ports. Traffic comes IN through that tunnel instead.
- Your AI gets two scoped keys: an API token (to manage DNS, tunnels, files) and a Cloudflare
  Access service token (to reach your servers — including SSH — without a password sitting on
  the box). Both are one-click revocable.
- Your images and downloads move to R2 (Cloudflare's object storage, no egress fees), served
  off a `cdn.` subdomain so they stop bloating your app server.

That's the foundation. Everything in the rest of the series — domains, Twilio, a real website
pushed live — bolts onto it.

## Prerequisite
- A Cloudflare account (free — cloudflare.com).
- A domain you own, added to Cloudflare (you point its nameservers at the two Cloudflare gives
  you; the dashboard walks you through it). We'll do buying domains properly in the next kit.
- Where it gets good: a VPS or any always-on machine running something on a port — a web app,
  an SSH server. A $5–6/mo VPS is plenty to start.

## How to use it (any OS)
Two ways — both end with Claude doing the setup:

- **Quick:** hit Copy, open Claude Code, paste this in, and say the line below.
- **Most reliable** (this is a real build, not a one-liner — let Claude read the whole file):
  hit Download, then tell Claude: *"read ~/Downloads/cloudflare-foundation-setup.md and set it
  up with me, one section at a time."*

Either way, say:

> "Be my Cloudflare setup partner. Walk me through the foundation one piece at a time: (1) help
> me create a SCOPED API token AND a Cloudflare Access service token — scoped wide enough to
> cover what's coming (DNS, tunnels, R2, email) but locked to only my account — and store them
> safely; (2) confirm my domain's DNS and turn on the proxy; (3) install cloudflared on my
> server and put my app behind a tunnel so it's reachable over https with no inbound ports open;
> (4) put SSH behind the Access service token so I (and you) can reach the box without a public
> SSH port. That's the foundation for today. Ask me for what you need as you go and explain each
> step."

Claude works through it with you, using the API for the parts it can do directly and giving you
the exact dashboard clicks for the parts that need your account.

## What it actually sets up — and why each piece matters

**1. One account, all your DNS — your control plane.**
Put every domain in the same Cloudflare account. Now "point shop.mydomain.com at my new app" is
one sentence to your AI, not ten minutes of clicking. The "orange cloud" toggle (proxied) is the
quiet hero: a proxied record routes that domain through Cloudflare's edge, which is what turns on
the CDN, the free certificate, and the tunnel routing — all from flipping one switch.

**2. A tunnel per server — the piece to actually understand.**
Normally, to reach an app on your server from the internet, you open a port and expose it — and
now you're a target that gets scanned all day. A Cloudflare Tunnel flips the direction: a tiny
daemon (`cloudflared`) on your box dials OUT to Cloudflare and keeps a few connections open. You
give it an ingress list — a plain map of `hostname → localhost:port`:

```
app.mydomain.com   -> http://localhost:3000
staging.mydomain.com -> http://localhost:3001
ssh.mydomain.com   -> ssh://localhost:22
(everything else)  -> http_status:404
```

One tunnel can front dozens of hostnames on a single box, each landing on its own local port.
The public gets clean https with a real certificate; your apps only ever listen on localhost.
The win: you can close inbound ports entirely. The thing that surprises people is that SSH works
the same way — `ssh://localhost:22` behind the tunnel means no public SSH port to brute-force.

**3. Cloudflare Access + a service token — how your AI gets in safely.**
This is the part most setups skip, and it's the one that makes handing the keys to an agent sane.
Cloudflare Access is a login gate that sits in FRONT of a hostname before any traffic reaches it.
Put it on your internal dashboards (your admin panels, a code-server, a database UI) so they're
not just sitting open behind a guessable URL.

Then create a service token — a non-human credential (a client ID + secret). That's what your AI
agent uses to authenticate through Access and SSH into the box headlessly. The reason this beats
dropping an SSH key on a server: it's scoped to exactly the hostnames you allow, every use is
logged, and if anything ever feels off you revoke the token in one click and it's instantly dead
— no re-keying servers, no cleanup. Your agent reaches your machines; nothing else does.

**4. Everything else — scoped now, switched on later.**
This is what makes it a foundation and not a one-off. Your image hosting (R2, Cloudflare's object
storage with no egress fees, served off a `cdn.` subdomain), your email DNS (the MX/SPF/DKIM/DMARC
records that decide inbox vs. spam), and edge code (Workers — for webhooks, file serving, small
proxies) all live in this SAME account. So scope the API token above wide enough to cover them
now — even though you won't configure them today. Then, when you get to each one, it pops into the
foundation you already laid: no rebuild, no new keys, and your AI can already reach it. You flip it
on and keep moving. (One line I hold for later: keep Workers OUT of the email-sending path —
deliverability lives on a warm, dedicated sending IP, not an edge function. Right tool per leg.)

## The two keys — scope them like keys
You're giving an agent real access, so scope it like you mean it.

- **API token** (My Profile → API Tokens → Create Token → Custom Token). Give it ONLY: Zone →
  DNS → Edit; Zone → Zone → Read; Account → Cloudflare Tunnel → Edit; Account → Workers R2
  Storage → Edit; Zone → Email Routing Rules → Edit. Limit it to your specific account and the
  specific zones you use — never "all zones." Store it in an environment variable or your OS
  secret store. Never hardcode it; never commit it.
- **Access service token** (Zero Trust → Access → Service Auth). Scope it to only the hostnames
  the agent should reach. Both keys die instantly when you delete them in the dashboard — that
  one-click kill switch is the whole reason this is safe to do.

Start with the narrowest scope that works and widen only when you hit a wall.

## If Claude needs the exact specs
API base `https://api.cloudflare.com/client/v4`, auth header `Authorization: Bearer <TOKEN>`.
DNS: `/zones/{zone_id}/dns_records`. Tunnels: `cloudflared tunnel create <name>`, then
`cloudflared tunnel route dns <name> <hostname>`, then an ingress list in the tunnel config
mapping each `hostname → service` (http://localhost:PORT or ssh://localhost:22) with a final
`service: http_status:404` catch-all; run `cloudflared` as a system service. Access: create a
self-hosted Access app per hostname, add a Service Auth policy, issue a service token (client ID
+ secret), and pass `CF-Access-Client-Id` / `CF-Access-Client-Secret` — for SSH use a
`ProxyCommand cloudflared access ssh --hostname <host>` in your SSH config. R2: create a bucket,
enable a public custom domain, CNAME `cdn` at it. Email: set MX + SPF (TXT) + DKIM + DMARC; for
forwarding, enable Email Routing and verify a destination.

## Make it stick
Run `cloudflared` as a service so the tunnel comes back by itself after a reboot. Keep a tiny
text map of which hostname points to which local port — once your agent is adding them by voice,
that map is the only thing standing between you and "wait, what's running on 3004?"

You're done when: a domain you own loads an app on your own server, over https, with no inbound
ports open; you can SSH to that box through Cloudflare with no public SSH port; and your AI has a
scoped key to manage it all. That's the foundation. From there, every new site — and every piece
we add next — is one sentence to Claude.

That's Kit 3 — the foundation. Next: buying a domain cheap and giving your AI API access to the
registrar too, so even that happens by talking.

-- Cliff (connectwithcliff.com)
