From zero to AI driven development, in under 4 mins

proof is a starter template that gives every pull request its own isolated hosted preview deployment on Cloudflare. Check the running app, mark it up, and merge ships it.

Built with

Ten choices, zero magic.

Every layer is one you can read on a flight. No custom runtimes, no hidden frameworks: the boring, proven parts, wired end to end.

Boring is a feature

The whole product fits in one screen.

No magic directories, no hidden generators, no second framework hiding in the config: what you see is what deploys. An agent reads this tree in one prompt and knows where everything lives.

Every decision and its why: docs/faq.md.

proof
├─ alchemy.run.tsone stack: providers, state, the units
├─ website.tsthe public deploy unit, sole ingress
├─ apps
│ ├─ backend
│ │ ├─ configinfra room: D1, R2, auth, limits, hosts
│ │ ├─ src/contractsthe shared, typed HTTP interface
│ │ ├─ src/controllersadapt payloads, yield services
│ │ ├─ src/domainbusiness rules as services
│ │ ├─ src/viewsrow to wire shape, signed links included
│ │ └─ migrationscommitted SQL, replayed per stage
│ └─ webTanStack Start site, typed client, auth gate
├─ docs/faq.mdevery decision, question-shaped
├─ stacks/github.tsthe one-time credential ceremony
└─ scripts/wt.tsworktrees for parallel agents
Features

What you get.

Every feature here is small, real, and in the repo. Nothing on this page is a mock of something the template does not do.

End-to-end type safety

One shared contract types the API, the browser client, and the forms. No hand-written URLs, no response decoding: a backend change that breaks a page breaks the build.

Views, not raw endpoints

A pure function projects each row into the exact shape the browser needs, signed links included, in the one place that authorizes. Storage keys never leave the backend.

Passwordless out of the box

Better Auth email codes: hashed at rest, rate-limited in the database, gated at SSR. Previews capture the code in logs, so testing never sends mail.

Cloudflare Email

Real delivery in production through the send_email binding, from your own domain. Captured to logs everywhere else, and a failed send never breaks the request.

Merge is production

Migrations ride the deploy. A readiness gate keeps the merge red until the setup checklist is whole, and the preview stage is destroyed the moment main moves.

Observability included

Cloudflare logs and tracing on both Workers, wired before you write a line. Follow a stage live from your terminal while an agent works.

Fast where it counts

This page renders on the edge with almost no client JavaScript. The backend has no servers to wake: Workers, D1, and R2 answer from the first byte.

One schema, both ends

Effect Schema validates the payload on the wire and the form in the browser, through React Hook Form. The error your user sees is the error the contract declares.

Built for agents

Worktrees per issue, hashed ports, an AGENTS.md that carries the proof brief. Parallel agents never share a checkout, and every issue ends in a proof.

Paste this into your coding agent to get started.

Paste the prompt into your agent in an empty folder: it creates the repository from the template, installs, walks through the one Cloudflare credential made by hand, and opens the first pull request with a running preview. No GitHub clicks before it, no scaffold CLI under it.

Set up the proof starter template as a new project:
https://github.com/Fawwaz-2009/proof

proof is an Effect + Cloudflare starter where every pull request deploys
its own isolated preview, and merging ships production. Work one step at
a time and explain as you go. Once inside the repository, AGENTS.md and
docs/faq.md are the rules; read them before making changes.

1. Ask for the project name and whether the GitHub repository should be
   public or private.
2. Create the repository from the template, clone it, and work from its
   root:
   gh repo create <name> --template Fawwaz-2009/proof --private --clone
   (swap --private for --public if asked).
3. Install dependencies: bun install, or npm install when bun is not
   installed.
4. Read AGENTS.md and docs/faq.md.
5. Cloudflare credential: the admin profile must hold a Cloudflare API
   token, not an OAuth login: the ceremony mints two Cloudflare API
   tokens, and OAuth logins are forbidden from minting tokens. Run bunx
   alchemy profile show --profile admin: if it shows a connected
   Cloudflare API token, continue. If not: STOP and tell me this one-time credential
   must be created by hand in the Cloudflare dashboard: My Profile ->
   API Tokens -> Create Token -> Custom Token, with these permission
   groups: Account Settings Read, Workers Scripts Write, Workers KV
   Storage Write, Workers R2 Storage Write, Workers Routes Write,
   Workers Tail Read, Workers Observability Write, D1 Write, Email
   Sending Write, Secrets Store Write, and Account API Tokens Write (a
   dashboard-born token is required: tokens created by tools like you
   cannot hold this one). Then run bunx alchemy profile edit --profile
   admin --reconfigure Cloudflare, choose the API token method, and
   paste the token when prompted. Wait until I confirm.
6. Ask for the root domain and the sender email for auth codes. Both are
   optional: previews work without them, but production sign-in cannot
   deliver email until a real sender on the domain replaces the
   placeholder. If neither exists yet, keep the placeholder and warn me
   before the merge.
7. Set the identity. It lives in exactly two places. First .env (create
   it from .env.example): APP_NAME is what people read, APP_SLUG is the
   hostname prefix. Second identity.ts at the repo root: STACK is the
   infrastructure scope; set it once ("Proof" -> the project name in
   PascalCase) and never after the first alchemy deploy (the ceremony in
   step 9 is the first one), because renaming it later orphans the
   database, bucket, and workers. Nothing else carries identity: package
   scopes are @app/*, display fallbacks are neutral, and rate-limit
   namespace ids derive from the slug. Verify: grep -rn "Proof"
   --include=*.ts apps stacks website.ts alchemy.run.ts returns nothing
   (identity.ts and this prompt aside).
8. Commit the identity on main locally, and leave main unpushed:
    pushing or merging to main ships production, and production waits
    for a real sender (steps 11 and 12). Production is exactly the
    stage named prod: only the deploy-prod workflow deploys it, and
    stage logic (email capture, hostnames) keys on the literal name,
    so never deploy another stage name to a real domain.
9. Run the setup: bun run update-stack-secrets, prefixed with
   GITHUB_OWNER, GITHUB_REPO (from git remote), APP_NAME, APP_SLUG,
   ROOT_DOMAIN (if any), and AUTH_EMAIL_FROM. It mints the
   least-privilege CI token, mints the R2 keys, and writes every repo
   secret. Verify with gh secret list. Safe to re-run whenever the
   values change; re-running rotates live credentials, so redeploy any
   deployed stage afterwards.
10. Start a worktree (bun run wt <name>) and make the first change: the
    /demo page heading (apps/web/src/routes/_authed/demo.tsx, currently
    One of everything), a new field on notes, or a request. Build it by
    the book, run bun run check, and open the pull request. It carries
    the identity commit and the change together: main has never been
    pushed, and this pull request is what turns the template into the
    app. Its description starts with a warning: merging ships
    production, and if the sender email is still the placeholder, the
    readiness check stays red on purpose and login codes cannot be
    delivered. Under it: the proof brief from AGENTS.md and the
    production checklist.
11. CI posts the preview URL on the pull request. Share it and wait for
    feedback. Fix, repeat. On previews and local dev, sign in with any
    address whose name is exactly six digits, like 123456@dev.example.com:
    the code is 123456, no email involved. Production addresses get a
    real emailed code and ignore the format.
12. To go live: add the root domain and a real sender if they were
    skipped (update .env, re-run the setup from step 9), and the
    readiness check turns green. With feedback handled and readiness
    green, merge: the first production deploy ships with the migrations
    and the real sender.
13. Share the production URL and have the user sign in with a real
    email. That sign-in is the acceptance test. From here, AGENTS.md is
    the operating manual.

Always: never commit secrets, never echo tokens back, confirm before
anything billable, and show real errors instead of papering over them.

git · bun or npm · the gh CLI · a Cloudflare account · an agent in your terminal

Prefer the manual path? The README documents every command. Or sign in and poke the live demo first: this site is the template, deployed by its own pipeline.

F.A.Q

Have questions?

The short answers. The full ledger, with reasoning, lives in docs/faq.md.

More in the full decisions ledger.