Getting started
From clone to running locally in five minutes.
Prerequisites
- Bun — the only package manager this repo uses
- A Google Cloud project, for OAuth credentials (Google sign-in is required; GitHub and emailed sign-in codes are optional extras)
- A Cloudflare account — needed to deploy, not to run locally
Setup
bun install
bun run db:migrate:local
bun run dev # http://localhost:3000
Secrets live in .dev.vars, which is gitignored. Start from the key list with
cp .dev.vars.example .dev.vars, then fill in at least BETTER_AUTH_SECRET
(openssl rand -base64 32) and the two Google OAuth values before signing in.
Env is read at boot, so restart the dev server after editing it. Keep a copy
of every key in a password manager: a key that lives only in a terminal or a
dashboard is a key that gets lost.
The dev server runs your code inside workerd with local D1 and R2
bindings, so local behavior matches production. Local state lives under
.wrangler/state, also gitignored.
If you have Claude Code, /setup does all of the above and creates your first
admin user; /google-oauth, /stripe and /deploy handle the account setup
that has to happen in a browser.
Make yourself an admin
Sign in once, then flip your row:
bunx wrangler d1 execute DB --local \
--command "UPDATE user SET role='admin' WHERE email='you@example.com';"
/admin is now reachable from the sidebar.
Before the first deploy
Work through src/config/app-config.ts — the name, canonical URL, support
address and legal entity reach the marketing pages, every transactional email
and the Terms and Privacy pages. Then src/config/plans.ts, whose product ids
are placeholders until you create a payment catalog.
Deploy
wrangler d1 create shipkit # put the id into wrangler.jsonc
wrangler r2 bucket create shipkit-files
bun run db:migrate:remote # migrations are applied by hand, never by CI
wrangler secret put BETTER_AUTH_SECRET # repeat for the other secrets
bun run deploy
CI refuses to deploy while migrations are pending, so apply them first.
ShipKit