MuseMVP Docs
Configuration

Environment Variables

Find which settings you need now, where to put them, and what to configure before launch.

Use this page when you need to add a service or prepare a deployment. You do not need to fill every integration key to explore the local app with seed accounts.

Before You Start

Complete Quick Start. Init creates the local files and signing secrets for you. Keep your app's .env.example open as the field inventory, but put real values in .env.local, not the example file.

What to Configure Now

StageConfigure
First local sign-inGenerated auth/newsletter secrets; local D1; seed accounts
Brand customizationDefault locale, public support address, local site origin
Test email, OAuth, payment, or uploadOnly the credentials for the feature you are testing
DeployProduction origin, database/bindings, stable signing secrets, real mail, and every enabled integration

Real email requires Cloudflare credentials. Missing integrations can fail when used even though the homepage and seed login work. Product display settings live in packages/config/src/index.ts; credentials are environment values.

Application Environment

ConfigurationWeb Cloudflare
Local filesapps/web-cloudflare/.env.local; .dev.vars for Worker preview
DatabaseD1 binding DB; no DATABASE_URL
StorageNative R2_BUCKET; no S3 keys
MailMAIL_PROVIDER=cloudflare
pnpm muse:init --yes --app cloudflare
pnpm db:seed:d1
pnpm dev:cf

Init preserves existing values and generates missing signing secrets. Do not use --force on a configured environment. The CLI uses --app cloudflare; the app directory is web-cloudflare.

Core Variables

VariablePurpose
NEXT_PUBLIC_SITE_URLOrigin, links, and OAuth callbacks; use the real production HTTPS origin
NEXT_PUBLIC_DEFAULT_LOCALEDefault en or zh
NEXT_PUBLIC_EMAIL_SUPPORTPublic support address
BETTER_AUTH_SECRETSession signing; missing in production runtime is a hard error
BETTER_AUTH_TRUSTED_ORIGINSComma-separated additional trusted origins
BETTER_AUTH_ENABLE_TEST_UTILSAutomated test helpers; keep false normally
GITHUB_CLIENT_ID/SECRET, GOOGLE_CLIENT_ID/SECRETOptional OAuth; missing credentials hide the provider
NEXT_PUBLIC_GOOGLE_ONE_TAP_CLIENT_IDOptional Google One Tap
NEXT_PUBLIC_TURNSTILE_SITE_KEY, TURNSTILE_SECRET_KEYBoth required to enable captcha
MAIL_FROM, MAIL_FROM_NEWSLETTER, CONTACT_FORM_TOTransactional/newsletter sender and contact inbox
NEWSLETTER_TOKEN_SECRETUnsubscribe HMAC signing, without an auth-secret fallback
MUSE_SEED_EMAIL, MUSE_SEED_PASSWORDLocal seed account domain and password, not production provisioning

Mail Credentials

Set MAIL_PROVIDER=cloudflare, CLOUDFLARE_ACCOUNT_ID, and CLOUDFLARE_API_TOKEN, and verify your sending domain. Workers production rejects resend and console. Missing credentials can warn at startup but still break OTP delivery.

Billing

BILLING_DEFAULT_GATEWAY accepts muse_creem, muse_stripe, muse_dodo, or muse_waffo. Empty falls back across configured gateways. Never put secrets in NEXT_PUBLIC_*.

  • Creem: CREEM_API_KEY, CREEM_WEBHOOK_SECRET, CREEM_ENVIRONMENT
  • Stripe: STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET
  • Dodo: DODO_PAYMENTS_API_KEY, DODO_PAYMENTS_WEBHOOK_SECRET, DODO_PAYMENTS_ENVIRONMENT
  • Waffo: WAFFO_MERCHANT_ID, WAFFO_PRIVATE_KEY, WAFFO_ENVIRONMENT; optional WAFFO_DEBUG, WAFFO_CUSTOMER_PORTAL_URL
  • Products: <GATEWAY>_PRODUCT_ID_PRO_MONTHLY, <GATEWAY>_PRODUCT_ID_PRO_YEARLY, <GATEWAY>_PRODUCT_ID_LIFETIME, with CREEM, STRIPE, DODO, or WAFFO prefixes

Store a Waffo private key on one line with \n for newlines. It signs API requests; webhooks use SDK verification, without a separate Waffo webhook secret. See gateway guides for sandbox/production environment values.

Worker Bindings

Declare D1 DB and R2 R2_BUCKET in apps/web-cloudflare/wrangler.jsonc. Run pnpm cf:typegen after binding changes. Assets use session-protected /files/avatars/*. See storage.

Build and Runtime Boundaries

NEXT_PUBLIC_* values are inlined into clients and require a rebuild when changed. Inject server secrets on the target platform. Do not copy a whole local file containing seed credentials and development URLs into production.

src/env/readiness.ts collects errors/warnings, throws hard errors in production runtime, and does not abort the build phase for those same hard errors. Use SKIP_ENV_VALIDATION=true only for necessary secretless CI builds, never to hide missing production config. A successful build does not prove auth, mail, or database readiness.

Keep package ./env, the selected app schema, and .env.example aligned when adding variables. Platform differences are intentional. Never commit .env.local, .dev.vars, or real secrets.

Check Your Result

Restart the dev server after editing environment files. Workers local changes also need .dev.vars; production needs host secrets. Open the app and test the specific integration you enabled, such as a delivered email or sandbox checkout. Rebuild after changing NEXT_PUBLIC_* on a deployed app.

Common Problems

ProblemFix
Local settings changed but behavior did notCheck the selected app's file and restart its server
Workers preview differs from Next devCompare .dev.vars and bindings with .env.local, then test pnpm cf:preview
Live links still point at localhostCorrect the build-time public origin and rebuild
Production starts but a feature failsRead server/provider errors and check that feature's required values, not just build success

Follow Email, Payments, and Storage for service-specific walkthroughs.

On this page