Codapult's behavior is controlled through a small set of TypeScript configuration files. These are the files you'll edit most when making the product your own.
| File | Purpose |
|---|---|
src/config/app.ts | Brand identity and company settings |
src/config/navigation.ts | Dashboard and admin sidebar items |
src/config/marketing.ts | Client-safe landing page data: stats, testimonials, module cards |
src/config/marketing-pricing.ts | Server-side pricing, checkout links, plugin catalog |
src/config/competitor-comparison.ts | Compare and vs page data |
src/config/env.ts | Typed env/AI settings, feature toggles (env.features), auth, payments, and providers |
App identity
Edit src/config/app.ts to set your product identity:
const appUrl = process.env.NEXT_PUBLIC_APP_URL ?? 'http://localhost:3000';
export const appConfig = {
appUrl,
serverUrl: process.env.VERCEL_URL ? `https://${process.env.VERCEL_URL}` : appUrl,
appName: process.env.NEXT_PUBLIC_APP_NAME ?? 'my-app',
brand: {
name: 'My App',
description: 'The best project management tool for teams',
logo: '/logo.svg',
showBuiltWithBadge: true,
icons: {
favicon: '/favicon.ico',
svg: '/icon.svg',
apple: '/apple-icon.png',
pwa192: '/icon-192.png',
pwa512: '/icon-512.png',
},
},
// ...
};
| Property | Source | Description |
|---|---|---|
appUrl | NEXT_PUBLIC_APP_URL env var | Canonical public URL (auth callbacks, emails, SEO). Defaults to http://localhost:3000 for dev. |
serverUrl | VERCEL_URL → appUrl | SSR self-reference URL for server-side fetches (tRPC). On Vercel, uses the deployment URL; otherwise falls back to appUrl. |
appName | NEXT_PUBLIC_APP_NAME env var | Technical project name (lowercase). Used for OTEL service name and machine identifiers. |
brand.name | Hardcoded in source | Display name (proper case). Shown in sidebar, emails, meta tags, PWA manifest. Edit directly in app.ts. |
brand.description | Hardcoded in source | Short product description for SEO meta tags. |
brand.logo | Hardcoded in source | Path to logo image (relative to /public). |
brand.showBuiltWithBadge | Hardcoded in source | Shows or hides the small "Built with" badge in the marketing footer. |
brand.icons | Hardcoded in source | Brand icon paths: favicon (.ico), svg, apple (180×180), pwa192, pwa512. Wired into <head> metadata and PWA manifest. Drop your files at these paths in public/ (favicon.ico lives in src/app/) or change the paths here. |
The brand.name display name propagates to manifest.ts, layout.tsx metadata, and email templates automatically. The appUrl is used everywhere: robots.ts, sitemap.ts, auth callbacks, Stripe redirects, invite links, and email links. The serverUrl is used by tRPC for SSR data fetching — on Vercel preview deployments it points to the correct deployment URL instead of the production domain.
Feature toggles
All feature toggles live in environment variables (prefix ENABLE_*). They are surfaced server-side via env.features in src/config/env.ts, and both the navigation and src/proxy.ts use the same source to hide UI and block routes.
All toggles default to true. Set the corresponding env var to false to disable the feature — its routes return 404 and its sidebar/footer entries disappear.
| Env var | Feature | Default |
|---|---|---|
ENABLE_AI_CORE | Shared AI gateway and server-side AI modules | true |
ENABLE_AI_CHAT | AI chat assistant (Vercel AI SDK, streaming) | true |
ENABLE_TEAMS | Organizations, multi-tenancy, team invitations | true |
ENABLE_BLOG | MDX blog with i18n, tags, and authors | true |
ENABLE_HELP_CENTER | Help center / documentation (MDX) | true |
ENABLE_WAITLIST | Waitlist with email confirmation | true |
ENABLE_REFERRALS | Referral / affiliate program | true |
ENABLE_ANALYTICS | Self-serve analytics dashboard | true |
ENABLE_WORKFLOWS | Event-triggered workflow automation | true |
ENABLE_WEBHOOKS | Outgoing webhooks (HMAC signed) | true |
ENABLE_AUDIT_LOG | User-facing activity / audit log | true |
ENABLE_REPORTS | Scheduled email reports | true |
ENABLE_FEATURE_REQUESTS | Public feature request voting board | true |
ENABLE_CHANGELOG | In-app "What's new" changelog widget | true |
ENABLE_ONBOARDING | Interactive onboarding tours | true |
ENABLE_BRANDING | Per-org white-labeling | true |
ENABLE_EXPERIMENTS | A/B testing framework | true |
ENABLE_DRIP_CAMPAIGNS | Email drip campaigns | true |
ENABLE_TWO_FACTOR | TOTP-based two-factor authentication | true |
ENABLE_PLUGINS | Premium plugin marketplace (/plugins, /dashboard/plugins) | true |
ENABLE_API_DOCS | Interactive OpenAPI reference page (/docs/api) | true |
Each ENABLE_* flag is independent — disabling auth (AUTH_PROVIDER="none") does not cascade to other features. AI modules are the exception: ENABLE_AI_CORE controls the shared gateway, while ENABLE_AI_CHAT, ENABLE_AI_RAG, ENABLE_AI_AGENTS, ENABLE_AI_BATCH, and ENABLE_AI_PLAYGROUND control individual surfaces and require the core. RAG remains opt-in because indexing incurs embedding cost. The only other dependency is twoFactor, which is meaningless without an auth provider and is therefore force-disabled when AUTH_PROVIDER="none".
Auth configuration
Sign-in methods are controlled by environment variables:
| Variable | Default | Description |
|---|---|---|
AUTH_MAGIC_LINK | "true" | Passwordless email sign-in via magic link. Set to "false" to disable. |
AUTH_PASSKEYS | "true" | WebAuthn / passkey authentication. Set to "false" to disable. |
Both are read via env.auth.magicLink and env.auth.passkeys in src/config/env.ts.
OAuth providers appear automatically when both <PROVIDER>_CLIENT_ID and <PROVIDER>_CLIENT_SECRET env vars are set (supported: google, github, apple, discord, twitter, microsoft).
Two-factor authentication (TOTP) is controlled by ENABLE_TWO_FACTOR — see Feature toggles.
AI configuration
AI runtime configuration is environment-based and is exposed server-side through env.ai in src/config/env.ts:
AI_DEFAULT_MODEL="gpt-5-mini"
AI_DEFAULT_TEMPERATURE="0.7"
AI_DEFAULT_MAX_TOKENS="4096"
AI_DEFAULT_TOP_P="1"
AI_ALLOWED_MODELS="gpt-5-mini,gpt-5.4-mini,claude-sonnet-5,gemini-3.7-flash"
ENABLE_AI_CORE="true"
ENABLE_AI_CHAT="true"
ENABLE_AI_RAG="false"
ENABLE_AI_CORE enables the shared gateway and server-side AI modules. ENABLE_AI_CHAT controls only the chat page and /api/ai/chat; agents, batch, playground, and RAG use separate flags and require the core. RAG is opt-in. The system prompt is maintained as DEFAULT_AI_SYSTEM_PROMPT in src/lib/ai/system-prompt.ts, not in app.ts or an environment variable.
For the complete list of AI settings and RAG limits, see AI Features and Environment Variables.
Company links
Set company contact details used in the Privacy Policy, Terms of Service, and footer (src/config/app.ts):
company: {
contactEmail: 'support@myapp.com',
githubUrl: 'https://github.com/myorg/myapp',
},
| Property | Type | Description |
|---|---|---|
contactEmail | string | Contact email used in legal pages (Privacy Policy, Terms) |
githubUrl | string | GitHub URL shown in the footer. Hidden if empty |
Navigation
Edit src/config/navigation.ts to customize sidebar items for the dashboard and admin panel.
Each item has a href, label, icon (from Lucide), and an optional featureKey:
import type { Env } from '@/config/env';
export interface NavItem {
href: string;
label: string;
icon: LucideIcon;
featureKey?: keyof Env['features'];
}
When featureKey is set, the item is automatically hidden if that feature is disabled in env.features (see the Feature toggles table).
Dashboard sidebar
export const dashboardNavItems: NavItem[] = [
{ href: '/dashboard', label: 'Dashboard', icon: LayoutDashboard },
{ href: '/dashboard/ai/chat', label: 'AI Chat', icon: MessageSquare, featureKey: 'aiChat' },
{ href: '/dashboard/analytics', label: 'Analytics', icon: BarChart3, featureKey: 'analytics' },
{ href: '/dashboard/billing', label: 'Billing', icon: CreditCard },
// ...add your own items here
];
Admin sidebar
export const adminNavItems: NavItem[] = [
{ href: '/admin', label: 'Overview', icon: ShieldCheck },
{ href: '/admin/users', label: 'Users', icon: Users },
{ href: '/admin/subscriptions', label: 'Subscriptions', icon: CreditCard },
{ href: '/admin/feature-flags', label: 'Feature Flags', icon: Flag },
// ...
];
To add a custom page, create the route in src/app/[locale]/(protected)/(dashboard)/dashboard/your-page/page.tsx and add a corresponding entry to dashboardNavItems.
Marketing configuration
The shipped marketing routes describe Codapult. Replace them with your own product's claims, pricing, screenshots, legal copy, and docs before deploying a customer-facing SaaS.
Edit these files together:
| File | What it controls |
|---|---|
src/app/[locale]/(public)/(marketing)/page.tsx | Landing page section order |
src/components/marketing/* | Landing, pricing, plugins, navbar, footer, CTA sections |
src/config/marketing.ts | Client-safe landing data: stats, module cards, testimonials |
src/config/marketing-pricing.ts | Pricing tiers, checkout links, premium plugin cards |
src/config/competitor-comparison.ts | /compare, /compare/[competitor] data |
messages/*.json | Localized marketing copy and CTA labels |
content/blog/, content/docs/ | Public MDX content and RSS/help-center pages |
If you do not need a surface at launch, disable it with the matching ENABLE_* env var where available and remove it from navigation, sitemap, and footer links.
Pricing tiers
export const pricingTiers: PricingTier[] = [
{
key: 'starter',
...getPriceInfo(49, 99),
link: checkoutHref(env.checkout.starter, env.checkout.variants.starter, 'starter', '/sign-up'),
featureKeys: ['tierFeatureProject1', 'tierFeatureUpdates6', 'tierFeatureCommunity'],
},
// ... pro (featured), enterprise
];
Feature labels are stored in messages/en.json under the "ProductPricing" namespace, so they support i18n. The key maps to a translation key for the tier name.
Set featured: true on a tier to visually highlight it. The link property is created by checkoutHref() in src/config/marketing-pricing.ts: variant IDs route through /api/checkout?product=<key>, direct checkout URLs open externally, and the fallback is /sign-up.
Premium plugins
Premium plugins are configured in the premiumPlugins record and pluginBundle object in src/config/marketing-pricing.ts. Each plugin has a link created from a direct checkout URL (CHECKOUT_URL_PLUGIN_*) or a provider variant ID (CHECKOUT_VARIANT_PLUGIN_*).
When set, plugin cards on the pricing page and the /plugins page show purchase buttons. Without these env vars, buttons link to /plugins.
Testimonials
export const testimonials: Testimonial[] = [
{
name: 'Alex Chen',
role: 'Founder, StartupXYZ',
content: 'Codapult saved me weeks of setup time.',
initials: 'AC',
},
// ...add real customer quotes
];
Stats, modules, and comparison pages
The stats array powers the "by the numbers" section on the landing page. moduleCategories powers the module showcase. Comparison pages read from src/config/competitor-comparison.ts; edit or remove those entries so they match your product's real feature matrix and competitors.
Typed env var access
src/config/env.ts provides a typed env object for reading environment variables in server code:
import { env } from '@/config/env';
// Type-safe access with defaults
env.auth.provider; // 'better-auth' | 'kinde' | 'none'
env.payments.provider; // 'stripe' | 'lemonsqueezy'
env.storage.provider; // 'local' | 's3' | 'r2'
env.notifications.transport; // 'poll' | 'sse' | 'ws'
env.ai.vectorStoreProvider; // 'sqlite' | 'memory'
env.sso.product; // string — SSO product identifier
env.defaultMonthlyCredits; // number — monthly AI credits
env.payments.stripe.connectFeePercent; // number — Stripe Connect fee
env.payments.stripe.secretKey; // string
env.db.turso.url; // string
Important: Server-side code should use env.* for adapter/provider settings and appConfig.* for app identity (appUrl, serverUrl, appName, brand). Never read process.env directly for values available through these objects.
This file is hand-maintained. When adding a new environment variable, add a Zod field to the schema and a corresponding property to the env object in the same file.
For the full list of environment variables and their descriptions, see the Environment Variables reference.