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.
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.
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.
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.
Have questions?
The short answers. The full ledger, with reasoning, lives in docs/faq.md.
More in the full decisions ledger.