First deploy
Walk through the first-time deploy step by step.
Assumes you've finished Cloudflare setup, D1 + R2, and Secrets.
Smoke-test locally
bun install
bun run devWeb at http://localhost:3001, api at http://localhost:3000. Hit http://localhost:3000/api/health — should return {"ok":true}.
Apply local migrations
bun run db:migrate:localDrizzle SQL under packages/db/src/migrations/ lands in your local SQLite.
Run the app once locally
http://localhost:3001/setup lets you walk the first-run wizard. This account becomes the broadcaster. Locked once setupCompletedAt flips.
Verify you can sign in, see the dashboard at /dashboard, and that /api/trpc/channel.getInfo returns your data.
First deploy
bun run deployProvisions every Cloudflare resource and uploads both Workers. ~2 minutes for the first run.
Output ends with two URLs — note them.
Smoke-test prod
curl https://tv-api.<your-account>.workers.dev/api/health
# {"ok":true}Open https://tv.<your-account>.workers.dev in a browser. The first-run wizard lives at /setup. The first person to fill it in becomes the prod broadcaster.
If you set up locally already (Step 3), and want a fresh prod state: nothing carries over from local — local D1 is separate from remote D1.
Configure GetStream webhook
GetStream dashboard → Video → Webhook URL:
https://tv-api.<your-account>.workers.dev/api/webhooks/getstreamSubscribe to:
call.live_startedcall.session_startedcall.session_endedcall.endedcall.session_participant_joinedcall.session_participant_left
Same URL for the Chat webhook subscribed to message.new.
Without these the LIVE badge never flips, viewer counts stay at 0, and the Stats page shows zeros.
See GetStream.
CI/CD on push to main
.github/workflows/deploy.yml runs after ci.yml succeeds on main. Concurrency group deploy so two pushes don't race.
Push a commit, watch GitHub Actions, verify /api/health still returns {"ok":true} after.
You're deployed. Now go configure GetStream and run the first-run wizard.