A polished, open-source, single-publisher video platform — think a Netflix-clean brand channel rather than a community site. Built with Next.js 16 (Cache Components), Mux, Supabase (Postgres + Auth + Storage), a minimal monochrome shadcn-style UI, Tiptap authoring, and optional Polar monetization.
One click clones the repo, prompts for the Core environment variables, and
builds from apps/web. You still need a Supabase project (for the database
schema) and a Mux account first — see Deploying for the full
checklist, including the Mux webhook, scheduled-publish cron, and optional
Polar setup.
- Monorepo: Turborepo + Bun workspaces
- App: Next.js 16 (App Router,
cacheComponents, Turbopack), React 19.2 - Video: Mux (upload, playback, signed playback, captions, Mux Data)
- Database: Supabase Postgres (typed data layer, RLS, full-text search)
- Auth: Supabase Auth via
@supabase/ssr(admins + viewers, roles inapp_metadata, optional OAuth/magic link) - Storage: Supabase Storage (thumbnails + branding buckets)
- UI: Tailwind v4 + shadcn-style components, custom theme, light/dark
- Authoring: Tiptap (→ Markdown) + MDX rendering
- Monetization (optional): Polar subscriptions + one-time purchases
- Hosting: Vercel + Supabase
apps/
web/ # the single Next.js app (viewer + /admin)
packages/
db/ # Supabase client, typed repositories, generated DB types
mux/ # Mux client + helpers (upload, webhook, signed playback)
ui/ # design system (theme, components, player, editor)
config/ # shared tsconfig
supabase/
migrations/ # schema, RLS, triggers, storage buckets, stats functions
bun install
# 1. Start a local Supabase stack (requires Docker)
supabase start
# Copy the printed API URL + anon/service_role keys into apps/web/.env.local
# (a ready-to-use .env.local pointing at the default local stack is included).
cp .env.example .env.local # for non-local setups, fill in your project keys
bun run seed # creates the admin + demo content (optional)
bun run devThe schema lives in supabase/migrations. Apply it locally with
supabase db reset, and regenerate the typed client with
supabase gen types typescript --local > packages/db/src/database.types.ts.
Open http://localhost:3000. The admin lives at /admin.
The first account to sign up automatically becomes an admin. After that, manage roles from the admin Users page.
See .env.example for all environment variables. Only the
Core section is required; sign-in providers, AI moderation, and Polar
monetization all auto-enable when their variables are present.
bun run dev— start the dev serverbun run build— production buildbun run typecheck— type-check all workspacesbun run seed— seed admin + demo databun run seed:admin— seed only the admin account
- Create a Supabase project at https://supabase.com/dashboard, then push
the schema with
supabase db push(link first viasupabase link). The migrations create all tables, RLS policies, triggers, and thethumbnails/brandingstorage buckets. - Import the repo into Vercel and set the Root Directory to
apps/web. Turborepo handles the workspace build; the framework preset is Next.js. - Add environment variables from
.env.example(Core required; the rest are opt-in). Use the new Supabase API keys:NEXT_PUBLIC_SUPABASE_URL,NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY(sb_publishable_...),SUPABASE_SECRET_KEY(sb_secret_...), andNEXT_PUBLIC_APP_URL(your production URL). The legacyanon/service_rolekeys still work as a fallback. In the Supabase dashboard, addhttps://<your-domain>/auth/callbackto the Auth → URL Configuration redirect allow-list. - Mux webhook: in the Mux dashboard add a webhook pointing at
https://<your-domain>/api/mux/webhookand copy the signing secret intoMUX_WEBHOOK_SECRET. This is what marks uploaded assets as ready. - Scheduled publishing:
apps/web/vercel.jsonregisters a cron that hits/api/cron/publishevery 5 minutes. It's authorized withCRON_SECRET, so make sure that variable is set. - Polar (optional): set
POLAR_ENABLED=trueplusPOLAR_ACCESS_TOKEN(andPOLAR_SERVER=sandboxwhile testing), then add a webhook athttps://<your-domain>/api/polar/webhookfor theorder.paidandsubscription.*events and copy its signing secret intoPOLAR_WEBHOOK_SECRET. Create your subscription and one-time Products in Polar and register their product IDs under Admin → Monetization (and on paid videos). Gated videos require Mux signed playback (MUX_SIGNING_KEY_ID/MUX_SIGNING_PRIVATE_KEY).
After the first deploy, run bun run seed against your production project (or
just sign up — the first account becomes an admin).
