All posts

Walkthroughs / 2026-09-01

From clone to deployed, start to finish

Every step between `bun install` and a live domain — what you configure, in what order, and which steps you genuinely cannot automate.

From clone to deployed, start to finish

This is the whole path, in the order the dependencies actually run. Nothing here is optional except where it says so.

1. Run it (5 minutes)

bun install
bun run db:migrate:local
bun run dev

You now have the app at localhost:3000 with local D1 and R2 — but you cannot sign in yet, because Google is the only sign-in method.

2. Google OAuth (10 minutes, mostly browser)

Create an OAuth client in Google Cloud with redirect URI http://localhost:3000/api/auth/callback/google. Put the id and secret in .dev.vars, and the id also in appConfig.googleClientId — One Tap runs in the browser and needs it public. Restart the dev server; env is read at boot.

Sign in, then promote yourself:

bunx wrangler d1 execute DB --local \
  --command "UPDATE user SET role='admin' WHERE email='you@example.com';"

3. Make it yours (30 minutes, and worth every one)

src/config/app-config.ts first: name, canonical URL, support address, legal entity. Those values reach the marketing pages, every transactional email, and the Terms and Privacy pages — the legal entity is printed verbatim, so fill it in before you take a payment.

Then src/config/plans.ts. The comment at the top explains the ladder: packs sell at list price, subscriptions below it, and plans.test.ts fails the build if a tier ever drops under a margin floor. Set CREDIT_COST_CENTS from a measurement, not a guess.

4. Payments (20 minutes)

Create the catalog — one product per tier, one recurring price per interval — and paste the ids into plans.ts, replacing the REPLACE_ME placeholders. Put the secret key and webhook secret in .dev.vars and set PAYMENT_PROVIDER.

Test locally with the Stripe CLI forwarding to your dev server:

bun run stripe:listen

Buy something with a test card and watch the ledger move. Until a provider key exists the purchase buttons say so instead of starting a checkout, so this step is safe to defer.

5. Deploy (15 minutes)

wrangler d1 create shipkit            # id → wrangler.jsonc
wrangler r2 bucket create shipkit-files
bun run db:migrate:remote
grep -v '^#' .prod.vars | grep = | wrangler secret bulk
bun run deploy

Migrations are applied by hand, never from CI — a schema change is a deliberate, watched step, and the deploy job refuses to ship code whose migrations are still pending.

6. The things only you can do

A second Google OAuth client for the production origin. A webhook endpoint in the payment dashboard pointing at <your-domain>/api/webhooks/stripe. A verified sending domain in Resend. These are browser steps with no API worth scripting — which is exactly why the repo ships Claude Code skills that walk you through them and verify the result instead of pretending they do not exist.

More posts