Environment Variables
Names only — values live in Vercel (production) and .env.local (dev), never in git. Grouped by what they power.
| Variable | Used for |
|---|
NEXT_PUBLIC_SUPABASE_URL | Supabase project URL (browser, server, admin clients, middleware) |
NEXT_PUBLIC_SUPABASE_ANON_KEY | Supabase anon key (RLS still applies) |
SUPABASE_SERVICE_ROLE_KEY | Service-role key for the admin client, crons and notifications — server only |
SURFVISION_MODAL_URL | Server-only. SurfVision Modal cloud endpoint — the failover and default backend (the old public NEXT_PUBLIC_MODAL_API_URL is read only as a fallback; delete it) |
STREAM_SIGNING_SECRET | HMAC secret for signed video-stream URLs. Throws in production if unset |
| Variable | Used for |
|---|
SURFVISION_PRIMARY_URL | Server-only. Self-hosted GPU backend, preferred over Modal when reachable |
CF_ACCESS_CLIENT_ID / CF_ACCESS_CLIENT_SECRET | Cloudflare Access service token that unlocks the tunnel to the primary |
SURFVISION_CLEAN_VIDEO | When true, analyses ship clean re-encodes and overlays stay live layers |
SURFVISION_API_TOKEN | Server-only. Sent to both backends as Authorization: Bearer … when set |
| Variable | Used for |
|---|
GOOGLE_AI_API_KEY | Transcribes guided-review voice notes into captions (POST /api/reviews/[id]/build). No video is ever sent |
GEMINI_MODEL | Model name; defaults to gemini-3.8-flash |
| Variable | Used for |
|---|
STRIPE_SECRET_KEY | Stripe API key |
STRIPE_WEBHOOK_SECRET | Verifies incoming webhooks |
STRIPE_PRICE_INSTRUCTOR_BASE / STRIPE_PRICE_INSTRUCTOR_SEAT | Price ids: Instructor base ($19/mo) and seat ($9/mo) |
STRIPE_PRICE_PRO_COACH_BASE / STRIPE_PRICE_PRO_COACH_SEAT | Price ids: Pro Coach base ($79/mo) and seat ($25/mo) |
| Variable | Used for |
|---|
RESEND_API_KEY / RESEND_FROM_EMAIL | Transactional email; every send is guarded, so email is optional |
NOTIF_UNSUBSCRIBE_SECRET | Signs one-click unsubscribe links (falls back to the service-role key) |
APNS_KEY_ID / APNS_TEAM_ID / APNS_PRIVATE_KEY / APNS_BUNDLE_ID / APNS_ENVIRONMENT | iPhone push (APNs JWT signing) |
VAPID_PUBLIC_KEY / VAPID_PRIVATE_KEY / VAPID_SUBJECT / NEXT_PUBLIC_VAPID_PUBLIC_KEY | Browser Web Push |
TWILIO_ACCOUNT_SID / TWILIO_AUTH_TOKEN / TWILIO_FROM_NUMBER / TWILIO_MESSAGING_SERVICE_SID / TWILIO_SMS_WEBHOOK_URL | SMS and phone verification |
ADMIN_ALERT_EMAIL / ADMIN_ALERTS_ENABLED | Where operator alerts and the daily admin digest go; kill switch |
| Variable | Used for |
|---|
FRAMEIO_CLIENT_ID / FRAMEIO_CLIENT_SECRET / FRAMEIO_REDIRECT_URI | Adobe IMS OAuth for Frame.io import |
| Variable | Used for |
|---|
NEXT_PUBLIC_APP_URL | Canonical origin for checkout returns, invite links, emails |
NEXT_PUBLIC_SITE_URL | Legacy alias, used by the coach-guide PDF builder |
CRON_SECRET | Bearer secret every cron route requires. Fails closed if unset |
INTERNAL_API_SECRET | Server-to-server bearer (billing cache, notification deliver) |
COACH_DEFAULT_TIMEZONE / NEXT_PUBLIC_COACH_TIMEZONE | Timezone fallbacks for CSV imports and schedule display (America/Barbados) |
| Variable | Used for |
|---|
SENTRY_DSN / NEXT_PUBLIC_SENTRY_DSN / SENTRY_TRACES_SAMPLE_RATE / NEXT_PUBLIC_SENTRY_TRACES_SAMPLE_RATE | Error reporting and tracing |
NEXT_PUBLIC_POSTHOG_KEY / NEXT_PUBLIC_POSTHOG_HOST / POSTHOG_KEY | Product analytics (no-op when unset); also carries the Send-to-TV feature flag |
NEXT_PUBLIC_MAPBOX_TOKEN | GPS track maps on wearable sessions |
DEBUG / NEXT_PUBLIC_DEBUG | Verbose logging in dev |
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
SURFVISION_MODAL_URL=https://your-surfvision-endpoint.modal.run
STREAM_SIGNING_SECRET=generate-a-long-random-secret
GOOGLE_AI_API_KEY=your-google-ai-key
NEXT_PUBLIC_MAPBOX_TOKEN=your-mapbox-token
NEXT_PUBLIC_APP_URL=http://localhost:3000
CRON_SECRET=another-long-random-secret