Codapult ships with multiple layers of security enabled by default — HTTP headers, rate limiting, auth guards, input validation, and data privacy controls. No extra configuration is needed for the baseline protections.
HTTP Security Headers
The following headers are set on all responses via next.config.ts:
| Header | Value | Purpose |
| --------------------------- | ---------------------------------------------- | ----------------------------------- |
| X-Content-Type-Options | nosniff | Prevents MIME-type sniffing |
| X-Frame-Options | DENY | Blocks clickjacking via iframes |
| Strict-Transport-Security | max-age=63072000; includeSubDomains; preload | Enforces HTTPS |
| Referrer-Policy | strict-origin-when-cross-origin | Limits referrer information leakage |
| Permissions-Policy | Restrictive defaults | Disables unused browser APIs |
| X-DNS-Prefetch-Control | on | Controls DNS prefetching behavior |
Rate Limiting
A sliding-window rate limiter (src/lib/rate-limit.ts) protects against abuse. It is applied to:
- Auth endpoints (sign in, sign up, magic link, 2FA)
- AI chat API
- Billing and checkout routes
- Admin actions
- GraphQL endpoint
- Plugin catch-all routes
Usage
import { checkRateLimit } from '@/lib/rate-limit';
const { allowed, remaining, resetAt } = checkRateLimit(`api:${session.user.id}`, {
limit: 30,
windowSeconds: 60,
});
if (!allowed) {
return NextResponse.json(
{ error: 'Too many requests. Please wait a moment.' },
{
status: 429,
headers: {
'Retry-After': String(Math.ceil((resetAt - Date.now()) / 1000)),
},
},
);
}
Rate-limit state is stored in memory by default. For multi-instance deployments, switch to Redis-backed storage.
Auth Guards
Server-side guards in src/lib/guards.ts protect server actions and API routes. Use these instead of writing manual session checks:
| Guard | Description |
| ---------------------------------- | --------------------------------------------------------------------------- |
| requireAuth() | Ensures the user is authenticated. Throws if no session. |
| requireOrgPermission(permission) | Checks the user has a specific permission within their active organization. |
| requireOrgMembership(orgId) | Verifies the user is a member of the given organization. |
| requireOrgAdmin(orgId) | Verifies the user is an admin of the given organization. |
Example
import { requireAuth, requireOrgPermission } from '@/lib/guards';
export async function updateProject(data: unknown) {
const { session, organization } = await requireAuth();
await requireOrgPermission('projects:write');
// Safe to proceed — user is authenticated and authorized
}
Input Validation
All server actions and API routes validate input with Zod schemas defined in src/lib/validation.ts. Never trust raw user input:
import { updateProfileSchema } from '@/lib/validation';
export async function updateProfile(data: unknown) {
const validated = updateProfileSchema.parse(data);
// Use validated.name, validated.email, etc.
}
Zod throws a ZodError on invalid input, which is caught by the error boundary and returned as a structured error response.
Branding CSS Sanitization
When applying custom branding (white-label colors and class names), Codapult sanitizes all values before injection:
brandingToCssVars()— validates color values against an allowlist regex before injecting them viadangerouslySetInnerHTML. Only valid CSS color formats are accepted.sanitizeCustomClass()— ensures custom class names contain only safe characters (letters, digits, hyphens, underscores).
This prevents CSS injection attacks through the branding configuration.
GDPR Compliance
Codapult includes built-in data privacy controls in src/lib/gdpr/:
| Endpoint | Method | Description |
| --------------------- | -------- | ------------------------------------------------------------ |
| /api/account/export | GET | Exports all user data as a downloadable JSON file |
| /api/account/delete | DELETE | Permanently deletes the user account and all associated data |
Both endpoints require authentication and only operate on the authenticated user's own data.
API Keys
Personal API keys (src/lib/api-keys.ts) allow programmatic access to the API:
- Keys are hashed before storage — the raw key is shown once at creation and cannot be retrieved later
- Each key is scoped to a single user
- Keys can be revoked from the dashboard settings
SCIM Provisioning
For enterprise customers, Codapult supports SCIM (System for Cross-domain Identity Management) in src/lib/scim/. This enables automated user provisioning and deprovisioning from identity providers like Okta, Azure AD, and OneLogin.
Secrets Management
Follow these rules to prevent accidental secret exposure:
- Never use
NEXT_PUBLIC_prefix for secrets. Variables with this prefix are bundled into client-side JavaScript and visible to anyone. - Access env vars through
src/lib/config.tsfor type-safe, validated access on the server. - Error responses must not expose internals. Always return
{ error: string }— never stack traces, file paths, or database details.
Checklist
Use this checklist when adding new features:
- [ ] Auth guard (
requireAuthorrequireOrgPermission) on every protected route/action - [ ] Zod validation on all user input
- [ ] Rate limiting on public-facing endpoints
- [ ] No secrets in
NEXT_PUBLIC_variables - [ ] Error responses reveal no internal details
- [ ] File uploads validated (size, type) before storage
Next Steps
- Environment Variables — configure SSO, API keys, and security-related env vars
- Admin Panel — user management, feature flags, and audit log
- Analytics & Monitoring — error tracking with Sentry and performance monitoring