Application Configuration
A deep dive into src/config/index.ts: what each config item does, where it takes effect, and suggested change patterns.
src/config/index.ts is MuseMVP's runtime configuration entry point. It centrally manages i18n, auth behavior, storage, payments, and analytics.
If you want to "change product behavior," this file is usually your first stop.
What This Page Covers
This page only covers code-level configuration in src/config/index.ts. For bootstrap secrets (database, OAuth, mail, MUSE_SETTINGS_SECRET), see /docs/quick-build/environment-variables. Payment gateway and S3 credentials are not env vars — configure them at Runtime Settings.
Configuration Guide
i18n: Internationalization
Key fields:
i18n.enabledi18n.locales.en|zh(appName/summary/description/keywords/docsAlias)i18n.defaultLocalei18n.defaultCurrencyi18n.localePrefixi18n.localeCookieName
Practical impact:
docsAliasis used as the docs page title suffix (for example,xxx | Docs).- Header and footer labels live in
home.json, not in this locale bundle. localePrefixaffects next-intl routing behavior and the language-hiding strategy of docs sources.
Suggestion When Updating Copy
Update en and zh in sync to avoid mismatches between bilingual navigation and footer text.
users: New User Onboarding Redirect and Billing Master Switch
Key field:
users.enableSetup
Current behavior:
enableSetup=true: newly registered users (logged-in users who have not completed onboarding) are redirected to/setup.
auth: Registration Methods and Session Lifecycle
Key fields:
auth.gates.allowRegisterauth.gates.allowSocialSignInauth.gates.allowPasswordSignInauth.gates.allowTwoFactorAuthauth.lifecycle.redirectAfterLoginauth.lifecycle.redirectAfterSignOutauth.lifecycle.redirectWhenSessionExpiredauth.lifecycle.sessionTtlSeconds
Behavior highlights:
- When
allowRegister=false,/auth/registerredirects to/auth/login. allowPasswordSignInandallowSocialSignInmainly control frontend entry visibility.allowTwoFactorAuthmainly controls whether the 2FA block is shown on the settings page.sessionTtlSecondsis written directly into Better-Authsession.expiresIn.
Easy-to-Misunderstand Point
allowTwoFactorAuth is not a backend plugin master switch. The 2FA plugin is still registered; this flag mainly controls whether the frontend exposes the entry point.
mails: Sender Identity and Brand Logo
Key fields:
mails.frommails.links.logo
Behavior highlights:
- If
mails.fromis only an email address, it will be automatically assembled into the format"AppName" <email@...>at send time. mails.links.logois used for the logo in email templates. By default it usespublic/icon.pngunder the project root, and you can also switch to a custom external image URL with faster access.
ui: Themes, SaaS Shell, and Contact Delivery
ui is the most frequently changed section, so it is best understood in blocks.
ui: {
enabledThemes: ["light", "dark", "system"],
defaultTheme: "light",
saas: { enabled: true, cookieInfo: { showBox: false } },
contactForm: {
to: "[email protected]",
subject: "MuseMVP contact form message",
},
}Behavior highlights:
ui.saas.enabled=falsecloses entries to both auth and saas areas (related layouts redirect directly to home).- The signed-in shell lives under
src/modules/layout/. See App Layout Customization. contactForm.to/subjectare the contact form delivery address and subject. They do not hide/contact.- Header and footer links are not config flags. Edit
SwitchHeader,SwitchFooter,site-links.ts, andhome.json. - Docs, blog, legal, features, changelog, and contact stay reachable unless you remove the nav link and the route file.
No page-visibility matrix
There is no ui.docs.enabled, ui.blog.enabled, ui.legal.allowMap, headerConfig, or footerConfig. Hide a public page by deleting its link in the header/footer components.
storage: Cloudflare R2
S3 credentials and the avatars bucket live in Admin → Runtime settings, not in config/index.ts.
Key fields that remain in config:
storage.proxyUrls.avatarsFile(fromNEXT_PUBLIC_AVATARS_PROXY_URL)
Behavior highlights:
- The upload API checks stored S3 credentials; missing values return
STORAGE_ENV_MISSING. NEXT_PUBLIC_AVATARS_PROXY_URLstays in env because client components build avatar URLs in the browser. Trailing/is stripped before URL composition.
payments: Billing Master Switch and Product Catalog Mapping
Key fields:
payments.enableBillingpayments.enableFreepayments.enableEnterprisepayments.productCatalog
productCatalog uses stable app plan IDs (pro_monthly, pro_yearly, lifetime) for the public pricing UI and checkout requests. Provider product IDs come from Admin → Runtime settings (billing.productMap).
Behavior highlights:
- Checkout uses the stored product map; missing mappings throw a readable error.
pricing-desc-usage.tsalso supports an optionaloriginalAmountfield for compare-at pricing display. It affects only the pricing UI; checkout still usesamount.enableBilling=falsedisables the pricing entry and related billing flow branches. The/pricingroute redirects home. The homepagePricingSectionreturnsnull. Homepage section order is the JSX list in(home)/page.tsx, not a config flag object.
Important Boundary
The default gateway and gateway API keys live in Admin → Runtime settings, not in config/index.ts and not in env. See Runtime Settings.
analytics: Analytics Script Injection
Key fields:
analytics.googleAnalyticsIdanalytics.baiduTongjiId
Behavior highlights:
- Analytics scripts are injected only in non-development environments.
- If an ID is empty, the corresponding script is not rendered.
Post-Change Verification Checklist
pnpm type-check
pnpm buildConfirm Header / Footer copy renders correctly in both en and zh.
Confirm login/register/sign-out redirects match auth.lifecycle config.
Confirm checkout product IDs for each gateway you use are saved in Admin → Runtime settings (billing.productMap).
If originalAmount is configured, confirm the pricing card shows the strikethrough original price while checkout still uses amount.
Confirm avatar upload matches stored S3 settings plus NEXT_PUBLIC_AVATARS_PROXY_URL.