Overview
Codapult follows a standard Next.js App Router layout with additional conventions for adapters, plugins, and infrastructure code.
codapult.plugins.ts — Plugin registration config
Dockerfile — Production Docker image
docker-compose.yml — Local Docker Compose setup
src/
app/
[locale]/ — Locale segment (next-intl URL routing)
(protected)/
(dashboard)/ — Protected dashboard (settings, billing, teams, AI chat)
(admin)/ — Admin panel (users, subscriptions, feature flags)
(public)/
(marketing)/ — Public pages: landing, pricing, blog, docs, plugins, legal
(auth)/ — Sign in, sign up
invite/[token]/ — Team invitation acceptance
layout.tsx — Root layout
api/ — API routes (auth, chat, upload, webhooks, trpc, graphql)
components/
ui/ — shadcn/ui base components
marketing/ — Landing page sections
dashboard/ — Dashboard components (sidebar, team switcher)
admin/ — Admin components (tables, managers)
auth/ — Auth components (AuthForm)
lib/
db/ — Drizzle client, schema, migrations, seed
auth/ — Auth adapter (Better-Auth / Kinde)
payments/ — Payment adapter (Stripe / LemonSqueezy / Polar)
storage/ — File storage adapter (local / S3 / R2)
actions/ — Server actions (mutations)
trpc/ — tRPC v11 routers
graphql/ — GraphQL schema + resolvers
plugins/ — Plugin system (types, registry, hooks)
jobs/ — Background job adapter (in-memory / BullMQ)
ai/ — AI chat, RAG pipeline, embeddings
validation.ts — Zod schemas (shared across all actions/routes)
rate-limit.ts — Sliding-window rate limiter
guards.ts — Auth + permission guards
env-utils.ts — Env parsing helpers
config/
env.ts — Typed env var access (`env.*`)
env-schema.ts — Zod schema of env vars and startup validation
app.ts — Brand identity and company links
navigation.ts — Dashboard & admin sidebar items
marketing.ts — Client-safe landing content (stats, modules, testimonials)
marketing-pricing.ts — Server-side pricing, checkout links, plugin cards
competitor-comparison.ts — Compare and vs page data
i18n/
routing.ts — Locale list, prefix strategy (defineRouting)
navigation.ts — Locale-aware Link, useRouter, usePathname
request.ts — Server-side locale resolution
content/
blog/ — MDX blog posts (locale variants can use a slug.<locale>.mdx convention)
docs/ — MDX documentation articles
messages/ — i18n translation files
plugins/ — Community plugins directory
e2e/ — Playwright E2E tests
scripts/ — Utility scripts (seed, migrate)
infra/
terraform/ — AWS infrastructure (HCL)
pulumi/ — AWS infrastructure (TypeScript)
helm/ — Kubernetes Helm chart
Key Directories
src/app/ — Routes
Next.js App Router uses route groups (parenthesized folders) to organize routes without affecting URL paths:
[locale]/— dynamic segment that captures the active locale from the URL. Contains all user-facing routes. Managed bynext-intl; the default locale (en) has no URL prefix.[locale]/(marketing)/— public pages: landing, pricing, blog, changelog, legal pages, help center, feature requests, plugin pages, and comparison pages. No auth required.[locale]/(auth)/— sign in and sign up pages.[locale]/(protected)/(dashboard)/— all protected routes behind authentication. Includes settings, billing, team management, AI chat, onboarding, and plugin pages.[locale]/(protected)/(admin)/— admin panel with its own layout. Requiresrole: "admin". Manages users, subscriptions, feature flags, webhooks, SSO, performance metrics, and A/B tests.api/— REST and RPC endpoints (outside[locale]/). Every route follows the same pattern: auth check → rate limit → Zod validation → business logic → JSON response.
src/components/ — UI
Components are organized by domain. ui/ contains the shared shadcn/ui primitives (Button, Dialog, Table, etc.); other folders hold feature-specific components.
src/lib/ — Business Logic
Each adapter module (auth/, payments/, storage/, jobs/) contains a common interface and multiple implementations. The active implementation is selected at runtime by the corresponding env var — your application code only imports the interface.
Other modules:
actions/— server actions for form submissions and mutations. Each validates input with Zod and checks auth/permissions via guards.trpc/— type-safe API procedures organized into routers (user, billing, notifications). Client and server setup included.graphql/— optional GraphQL layer using graphql-yoga. SDL schema with resolvers.plugins/— plugin registry, types, lifecycle hooks, and marketplace catalog.ai/— AI chat with streaming, RAG pipeline, embedding generation, conversation history.jobs/— background job system with built-in jobs (send-email, webhook-retry, credit-reset, rag-index) and cron scheduling.
src/app/[locale]/(public)/(marketing)/ — Public Marketing Surface
The marketing route group is intentionally public and SEO-indexable. It includes the default Codapult sales site so you can see a complete working product page, but that copy is not generic placeholder text. Before shipping your SaaS, replace the Codapult-specific pages and content with your own product's positioning.
Use this map when customizing:
| Surface | Files to edit |
|---|---|
| Landing page order | src/app/[locale]/(public)/(marketing)/page.tsx |
| Landing sections | src/components/marketing/* |
| Landing data, stats, testimonials, module cards | src/config/marketing.ts |
| Pricing, product checkout links, plugin cards | src/config/marketing-pricing.ts |
| Compare and vs pages | src/config/competitor-comparison.ts, src/app/[locale]/(public)/(marketing)/compare/ |
| Blog and RSS content | content/blog/, src/app/(marketing)/rss.xml/route.ts, src/app/[locale]/(public)/(marketing)/rss.xml/route.ts |
| Help center docs | content/docs/, src/app/[locale]/(public)/(marketing)/docs/ |
| Legal pages | src/app/[locale]/(public)/(marketing)/privacy/page.tsx, src/app/[locale]/(public)/(marketing)/terms/page.tsx |
| Navbar and footer | src/components/marketing/Navbar.tsx, src/components/marketing/Footer.tsx, messages/*.json |
If a public surface is not part of your launch, disable it with the matching ENABLE_* env var where available, then remove its navigation links and sitemap entries.
content/ — MDX Content
Blog posts and documentation articles are MDX files with frontmatter metadata. Blog posts can support additional locales through filename conventions such as post.<locale>.mdx; the currently shipped default locale is English. The help center documentation uses category-based organization.
infra/ — Infrastructure as Code
Three deployment options are included out of the box:
- Terraform — AWS infrastructure defined in HCL
- Pulumi — AWS infrastructure defined in TypeScript
- Helm — Kubernetes chart with Deployment, Service, Ingress, HPA, worker, and Redis
Configuration Architecture
Codapult uses a three-layer config system:
1. Environment Variables (.env.local)
Secrets and per-environment settings. All variables are documented in .env.example with inline comments. Access them type-safely via src/config/env.ts:
import { env } from '@/config/env';
env.db.turso.url; // string — validated at startup
env.auth.provider; // "better-auth" | "kinde" | "none"
2. Application Config (src/config/app.ts)
Your main customization file. Controls brand identity, ai settings:
export const appConfig = {
appName: 'my-saas',
appUrl: 'https://my-saas.com',
brand: {
name: 'My SaaS',
description: 'The best SaaS product',
logo: '/logo.svg',
},
company: {
contactEmail: 'support@my-saas.com',
},
};
AI runtime settings and feature gates live in src/config/env.ts, not app.ts. Use ENABLE_AI_CORE for the shared AI runtime and ENABLE_AI_CHAT for the chat surface.
Feature toggles (which modules are enabled) are controlled via ENABLE_* environment variables — see env.features in src/config/env.ts.
3. Supporting Config Files
| File | Purpose |
|---|---|
src/config/navigation.ts | Dashboard and admin sidebar menu items |
src/config/marketing.ts | Client-safe landing content — stats, testimonials, modules |
src/config/marketing-pricing.ts | Pricing tiers, checkout links, plugin catalog |
src/config/competitor-comparison.ts | Compare and vs page data |
messages/*.json | UI translations |
Database Schema
The database schema lives in a single file: src/lib/db/schema.ts. This is the source of truth for all tables. Conventions:
- Table names: singular
snake_case(user,organization_member) - Column names:
snake_case(created_at,user_id) - Primary keys:
text('id').primaryKey()with UUID/nanoid (not auto-increment) - Timestamps:
integer('created_at', { mode: 'timestamp' })with.$defaultFn(() => new Date()) - Foreign keys: always specify
onDeletebehavior
For development, run pnpm db:push to apply the schema directly. For production databases with existing data, use pnpm db:generate to create a migration, then pnpm db:migrate to apply it safely.
Next Steps
- Authentication — sign-in methods, 2FA, and SSO configuration
- Payments & Billing — configure subscription tiers
- Customization — theming, branding, and white-label