$0 forever No build step Mobile-first

Scaffold

A DRY, replicable system for building mobile-first sites on Cloudflare’s free tier. One template, one scaffold script, one deploy path — every new site starts identical and stays cheap forever.

Prompt the Builder Start a site

The Infra

Every piece of the stack, what it does, and what the free tier gives you. Nothing here costs a cent.

Tap the architecture

One diagram, the whole system. Tap any node to see what it does.

BROWSER your phone PAGES static CDN FUNCTION /api/* D1 + KV state LLM API brains SEARCH live web R2 media GITHUB push → live

Tap a node

Each box is one billable thing that bills $0. Pick one to see its job.

Cloudflare Pages

Hosts the static site — HTML, CSS, JS — on a global CDN. This is the default home for every Scaffold site.

500 builds/mo · unlimited bandwidth · 20,000 files/deploy

Pages Functions / Workers

Server code only where a site truly needs it: the chat proxy, the agent dispatcher, secret-key calls. Everything else stays static.

100,000 req/day · 10 ms CPU/req · 128 MB memory

KV

Tiny key-value reads at the edge. Chat transcripts, caches, feature flags — anything small and read-heavy.

100K reads/day · 1K writes/day · 1 GB

D1

SQLite for structured state that needs queries and strong consistency — agent task records, app data with relations.

5M reads/day · 100K writes/day · 5 GB

R2

Object storage for media: photos, uploads, files too big or too binary for a database.

Free tier available — verify the current allowance in Cloudflare docs

OpenAI-compatible LLM API

The brains: chat replies and agent reasoning through one server-side key. Any OpenAI-compatible endpoint plugs in.

Allowances vary by provider — verify the free tier when you add your key

Brave Search API

Gives the agent live web research: the Function calls search, hands concise results back to the model.

Includes a free monthly credit — verify current terms

Limits: Pages + Functions from FRAMEWORK.md. KV/D1 figures are 2026-09 verified values (see the meter below). R2/LLM/Search allowances live with their providers — check at setup.

Patterns

Two battle-tested recipes. Copy the shape, not the code — each site owns its copy.

Data-driven static

No backend

A scheduled publisher regenerates data/*.json; the site renders it. Scores, reports, rosters — anything that changes on a schedule but reads the same for everyone.

# publisher (cron): atomic write, then deploy
write("data/site.json.tmp", json)
rename("data/site.json.tmp", "data/site.json")
git push  # Pages rebuilds; data + site update together

// js/app.js — the whole "backend"
const data = await getJSON("data/site.json");
renderCards(data); // zero Functions, zero DB

Rules: atomic writes, versioned JSON per deploy, publisher failures never delete the last good file.

LLM chat

Drop-in widget

Multi-model chat with one shared conversation: the widget has a model picker, every model reads and writes the same transcript, context, and progress. The key never leaves the server.

// POST /api/chat → OpenAI-compatible SSE stream
await fetch("/api/chat", {
  method: "POST",
  body: JSON.stringify({ conversation_id: id, model, message })
});
// server: allowlisted model id → upstream → stream → persist

Ships research-first: the default system prompt checks existing ideas (GitHub, Reddit, HN) before proposing, and never invents citations.

Free-tier meter

Will it stay $0? Punch in your numbers — the bars show usage against the free limits. Rough planning heuristics, not measured figures.

Limits hardcoded as 2026-09 verified values. Static page views ride unlimited bandwidth — they cost nothing.

Decide

The framework’s judgment, as tools. Answer honestly.

Do I need a Pages Function?

KV vs D1 vs R2 vs static JSON?

Ship-it checklist

Run this before every deploy. Your ticks are saved on this device.

Ready to ship0%

Sites

Apps built the Scaffold way. Every one started as the same template.

Moves

In development

Mobile fantasy-football companion: waiver-target cards, positional roster view with injury status, and a static site.json data flow — the data-driven-static pattern in production.

No public URL yet.

Scaffold

You are here

The framework’s own home: infra map, patterns, free-tier meter, decision tools, and the Builder. Static, except the Builder’s one API.

This page.

Start a site

One command. Thirty seconds to live.

# 1. Scaffold
./bin/new-site my-site "My Site"

# 2. Develop
cd sites/my-site && python3 -m http.server 8000
# …edit index.html / css/app.css / js/app.js…

# 3. Deploy — connect the Pages project to Git once,
#    then every push to main is live in ~30s.
git push origin main

The DRY contract

1. One template. Every site begins as a copy of templates/mobile-site/. Shared improvements go into the template — never copy-pasted between sites.

2. No build step by default. Plain HTML/CSS/JS deploys as-is. Add a bundler only when a site outgrows it — most never will.

3. Mobile-first is encoded, not remembered. Viewport, safe areas, 44px targets, 100dvh, dark mode — all in the template’s tokens. New sites inherit them.

4. One deploy path. Git-connected Pages project: push to main → live. wrangler pages deploy is the fallback, not a second workflow.

5. Static until proven dynamic. Data-driven sites publish JSON; Pages Functions cover the rest. No servers, no containers, nothing to keep awake.

Builder

Prompt the agent. It builds a new app or a feature on an existing one, then lands it as a pull request with a live preview — you approve before anything merges.

1 — Pick a target

2 — Describe it