MuseMVP Docs
Admin

Runtime Settings

Store payment and storage credentials in the database. Admins edit them at /app/admin/settings.

Payment gateway credentials and S3 storage credentials live in the app_setting table. Admins maintain them at /app/admin/settings. Runtime code reads only the database for those two domains — there is no environment-variable fallback.

Mail, authentication, Turnstile, and NEXT_PUBLIC_AVATARS_PROXY_URL stay in env.

First admin

Registration still uses env-based mail (OTP). Create the first user with pnpm muse:user:create, then promote them with pnpm muse:user:role. After that, open /app/admin/settings.

What stays in env vs what moves to the admin page

SurfaceSourceWhy
Site URL, database, Better Auth, OAuth, TurnstileEnvNeeded before any admin session exists
Mail (MAIL_PROVIDER, Resend / Cloudflare)EnvRegistration OTP must work with an empty settings table
MUSE_SETTINGS_SECRETEnvWrapping key for encrypted settings; never stored in the database
NEXT_PUBLIC_AVATARS_PROXY_URLEnvClient components build avatar URLs in the browser
Payment gateways, webhook secrets, default gatewayAdmin settingsServer-only secrets; encrypted at rest
Checkout product IDsAdmin settings (billing.productMap)Checkout source of truth
S3 access key, secret, endpoint, avatars bucketAdmin settingsServer-only secrets; encrypted at rest

First-deploy order

Set BETTER_AUTH_SECRET, MUSE_SETTINGS_SECRET, database, and mail env vars. MUSE_SETTINGS_SECRET must be at least 32 characters.

Apply migrations (pnpm drizzle:migrate) so app_setting and app_setting_revision exist.

Create and promote an admin (pnpm muse:user:create, then pnpm muse:user:role).

Open /app/admin/settings. Save gateway credentials, checkout product IDs, and S3 settings. Use Test connection before going live.

The billing tab keeps the Default gateway card at the top. Each provider (Creem, Stripe, Dodo, Waffo) starts collapsed. Expand a provider to edit its credentials and that gateway's checkout product IDs (Pro monthly / yearly / Lifetime) in the same panel. Badges show Default, Configured / Not configured, mapped product-ID count, and Unsaved.

Optional: pnpm muse:settings:import copies leftover local gateway/storage credentials into the database once. Runtime never reads those env vars afterward. Checkout product IDs are entered in Admin → Settings, not env.

Encryption

Secret fields are stored as unprefixed base64url ciphertext. Each row uses HKDF-SHA256 (info bound to scope:key) plus AES-256-GCM with additional data bound to the same identity. Moving ciphertext to another field fails decryption. cipher_version records the algorithm; 0 is plaintext for non-secret fields.

Capability fallback

If no gateway is stored, checkout CTAs on signed-in billing pages are disabled and launch returns a readable error. If S3 is missing, avatar/file APIs return STORAGE_ENV_MISSING (503) and the storage page shows the empty state. The public homepage stays static and does not query settings.

Cache

Settings are cached in-process and keyed by app_setting_revision. Other instances refresh within 30 seconds.

On this page