The existing guide is frontend/onboarding-focused; a session audit of the last 14 days showed the hot re-read files are almost all server-side (image.service.ts read 131×, plus model/redis/meili/orchestrator/jobs/ metrics across many worktrees) with no layout map to find them. Adds a one-line-per-subsystem map of src/server (tRPC, images, models, search, redis/cache, orchestrator, auth, jobs, metrics, db, telemetry, health) pointing at the actual hot files, and flags that image.service.ts is ~7.9K lines (grep it, don't read end-to-end). All file paths and anchors (X-Search-Actor, getOrchestratorToken, HEALTHCHECK_TIMEOUT/Promise.all in health.ts, createCachedObject, etc.) verified against the tree before writing. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
12 KiB
Civitai Development Guide
How to work with us
We use markdown documents to discuss plans. Documentation goes in the docs/ folder.
Inline Comments
Occasionally, we comment back and forth as we make plans. Comments from us, are marked with @dev: and you can leave comments as well with @ai:. Please make comments inline in the document. If there are actions are requested in my comments, please take them.
New Comment Marking: When you add new comments, use an asterisk after the mention (e.g., @justin:* or @meta:*). Once you reply or acknowledge a comment, remove the asterisk so that I know it's been seen. Note: Sometimes I might forget to add the asterisk to my new comments, so please check all comments regardless of marking.
Example
@dev: This comment has been processed (asterisk removed)
@ai: Of course
@dev:* This is a new comment that needs attention
Tech Stack Overview
Core Technologies
- Framework: Next.js 14 with TypeScript
- UI Library: Mantine v7
- Styling: Tailwind CSS + SCSS Modules
- Database: PostgreSQL with Prisma ORM
- API: tRPC
- State Management: Zustand
- Authentication: NextAuth
- Search: Meilisearch
- Image Processing: Sharp
Additional Libraries
- React Query (Tanstack Query) for data fetching
- React Hook Form with Zod validation
- Tiptap for rich text editing
- Chart.js for data visualization
- Stripe/Paddle/PayPal for payments
Build Commands
Development
Always use the /dev-server skill to manage dev servers. Never use pnpm run dev directly.
Build & Deploy
pnpm run build # Production build
Code Quality
pnpm run typecheck # Run TypeScript type checking
pnpm run lint # Run ESLint
pnpm run prettier:check # Check Prettier formatting
pnpm run prettier:write # Auto-fix Prettier formatting
Testing
pnpm test # Run Playwright tests
pnpm run test:ui # Run tests with UI
Database
pnpm run db:migrate:empty # Create an empty migration file
CRITICAL: We do NOT use prisma migrate deploy. Migrations are applied manually.
- Migration files in
prisma/migrations/exist for review/history but are never auto-run - Each environment's DB is updated by a human running the SQL directly (psql, retool, etc.)
- The
_prisma_migrationstable is not the source of truth — do not rely on it - When you add a new migration: write the SQL, commit it, and surface to the user that it needs to be applied manually to wherever they want it (preview / staging / prod)
- Never suggest
prisma migrate deploy,prisma migrate resolve, or any auto-apply path
Release (requires user permission)
pnpm run release # Patch release (0.0.x) - default
pnpm run release:minor # Minor release (0.x.0)
pnpm run release:major # Major release (x.0.0)
IMPORTANT: Never run release commands without explicit user approval. These commands bump the version, push tags, and rebase the release branch.
Server-Side Architecture Map
src/server/ holds the most-edited (and largest) code in the repo. Read the specific file before changing it — several are huge, so grep within them rather than reading end-to-end (services/image.service.ts is ~7.9K lines).
- tRPC API —
trpc.ts(root router + procedure helpers),createContext.ts,middleware.trpc.ts,routers/(~93 per-domain routers),controllers/,schema/(zod input contracts),selectors/(Prismaselectfragments). - Images —
services/image.service.ts(~7.9K lines; the hot feed path —getInfiniteImages,getAllImages, NSFW/own-content merge). API surfacesrc/pages/api/v1/images/index.ts; index syncsearch-index/images.search-index.ts. - Models —
services/model.service.ts,search-index/models.search-index.ts. - Search (Meilisearch) —
meilisearch/client.ts(tags requests withX-Search-Actor),meilisearch/cleanup.ts,search-index/base.search-index.ts(shared sync engine). - Redis / caching —
redis/client.ts(clients incl. sysRedis),redis/caches.ts(createCachedObjectdefs + TTLs, e.g.imageMetaCache,tagIdsForImagesCache),utils/cache-helpers.ts. - Orchestrator (generation) —
orchestrator/get-orchestrator-token.ts(getOrchestratorToken),services/orchestrator/orchestrator.service.ts. - Auth —
auth/next-auth-options.ts,auth/session-user.ts,auth/token-refresh.ts. - Jobs (cron) —
jobs/job.ts(runner) + individual jobsjobs/*.ts(e.g.entity-moderation.ts,search-index-sync.ts). - Metrics / analytics —
metrics/*.metrics.ts(ClickHouse-backed entity metrics),clickhouse/. - DB —
db/db-helpers.ts(raw pg-pool config:connectionTimeoutMillis, labeled pool gauges), Prisma client; schemaprisma/schema.prisma. Migrations are applied manually — see the Database rule above. - Telemetry —
src/instrumentation.node.ts(OTEL: Prisma/Redis/HTTP auto-instrumentation + customwithSpan()fromutils/otel-helpers.ts),schema/track.schema.ts(ClickHouse action/event tags),prom/client.ts. - Health —
src/pages/api/health.tsruns sub-checks underPromise.all; a single slow check (e.g.searchMetrics) can exceed the kubelet probe budget.HEALTHCHECK_TIMEOUTenv gates it. - Other server domains —
games/(new-order/ratings),webhooks/,paddle/+coinbase/(payments),notifications/,signals/,rewards/; S3 helpers atsrc/utils/s3-utils.ts.
Component Standards
File Structure
src/
├── components/ # React components
│ ├── ComponentName/ # Component folder
│ │ ├── ComponentName.tsx
│ │ ├── ComponentName.module.scss # Optional SCSS module
│ │ └── utils.ts # Component utilities
├── hooks/ # Custom React hooks
├── server/ # Server-side code
├── utils/ # Shared utilities
└── store/ # Zustand stores
Component Patterns
1. Mantine Components
import { Button, Group, Text } from '@mantine/core';
import { IconBolt } from '@tabler/icons-react';
2. Tailwind Classes with clsx
import clsx from 'clsx';
<div className={clsx('flex items-center gap-2', conditionalClass && 'bg-blue-500')} />
3. SCSS Modules (when needed)
import styles from './Component.module.scss';
<div className={styles.container} />
4. TypeScript Patterns
- Use type imports when possible:
import type { ButtonProps } from '@mantine/core' - Define Props interfaces for components
- Use enums from
~/shared/utils/prisma/enums
Coding Standards
Imports Order
- External libraries (React, Mantine, etc.)
- Internal components (~/components/...)
- Hooks (~/hooks/...)
- Server/API code (~/server/...)
- Utils and helpers (~/utils/...)
- Types and enums
- Styles
State Management
- Use Zustand for global state
- Use React Query for server state
- Use React Hook Form for forms
API Calls
import { trpc } from '~/utils/trpc';
const { data, isLoading } = trpc.user.getProfile.useQuery();
Authentication
import { useCurrentUser } from '~/hooks/useCurrentUser';
const currentUser = useCurrentUser();
Environment Setup
Required Environment Variables
- Database connection strings
- Authentication providers
- S3/CloudFlare credentials
- Payment provider keys
- Search service endpoints
Local Development
- Install dependencies:
pnpm install - Generate Prisma client:
pnpm run db:generate - Start dev server: Use
/dev-serverskill
Important Notes
- Read the full file before editing. Plan all changes, then make ONE complete edit. If you've edited a file 3+ times, stop and re-read the user's requirements.
- When the user corrects you, stop and re-read their message. Quote back what they asked for and confirm before proceeding.
- Every few turns, re-read the original request to make sure you haven't drifted from the goal.
- Act sooner. Don't read more than 3-5 files before making a change. Get a basic understanding, make the change, then iterate.
- When stuck, summarize what you've tried and ask the user for guidance instead of retrying the same approach.
- Re-read the user's last message before responding. Follow through on every instruction completely.
- After 2 consecutive tool failures, stop and change your approach entirely. Explain what failed and try a different strategy.
Performance
- Use dynamic imports for heavy components
- Implement virtual scrolling for large lists
- Optimize images with Next.js Image component
Security
- Never commit secrets or API keys
- Use environment variables
- Sanitize user input with sanitize-html
- Follow authentication best practices
Before Committing
- Run type checking:
pnpm run typecheck - Run linting:
pnpm run lint - Format code:
pnpm run prettier:write - Test changes locally
Common Patterns
Infinite Scroll
Use MasonryGrid or virtual scrolling components with React Query infinite queries.
Modals
Use Mantine modals with proper accessibility and keyboard handling.
Dialog Registry System
The project uses a dialog-registry system for managing modals:
- Register dialogs in
src/components/Dialog/dialog-registry.tsordialog-registry2.ts - Use
DialogProviderfor context-based modal management RoutedDialogProviderfor URL-based modal state- Access dialogs through the registry for consistent modal handling across the app
Forms
Use React Hook Form with Zod schemas for validation.
File Uploads
Use the S3 upload hooks and providers in the codebase.
Image Handling
Use EdgeImage component for optimized image loading with CDN support.
Debug Endpoints (src/pages/api/testing/*)
src/pages/api/testing/*.ts is the convention for hidden debug endpoints. Each endpoint is guarded by WEBHOOK_TOKEN (via WebhookEndpoint(...), which checks the ?token= query param) and exposes a handful of POST actions for experimenting with a feature without paying real money or hand-editing the DB.
To use one: read the endpoint's source file directly — the top-of-file comment documents the available actions and required params, and the zod schema is the authoritative contract. Agents should never need a wrapper skill; cURL with ?token=$WEBHOOK_TOKEN appended to the URL is enough.
When adding a new debug endpoint:
- Drop it at
src/pages/api/testing/<feature>.ts - Use
WebhookEndpoint(handler)for auth - Lead the file with a block comment listing each action + its params + a one-line description (see
src/pages/api/testing/referrals.tsfor the pattern) - Scope every destructive action to a single
userId/refereeIdper call so a misuse can't cascade
Feature Documentation
Feature-specific documentation lives in docs/features/. Before implementing a feature, check if documentation exists:
Core Systems Reference
| System | Documentation |
|---|---|
| Image Resources | docs/features/image-resources.md |
| NSFW Filtering | docs/features/nsfw-filtering.md |
| Buzz Accounts | docs/features/buzz-accounts.md |
| Notifications | docs/features/notifications.md |
| Metrics/Analytics | docs/features/metrics-analytics.md |
| Bitwise Flags | docs/features/bitwise-flags.md |
| Civitai LLM Client | docs/features/civitai-llm-client.md |
Troubleshooting
Memory Issues
Use cross-env NODE_OPTIONS with increased memory:
pnpm run dev-debug # Includes --max_old_space_size=8192
Build Failures
- Clear .next folder
- Clear node_modules and reinstall
- Check for circular dependencies
- Ensure all environment variables are set
Database Issues
- Check connection string
- Apply pending migrations manually (we do NOT use
prisma migrate deploy— see Database section above) - Regenerate client:
pnpm run db:generate