Files
civitai__civitai/CLAUDE.md
T
Zachary Lowden 21d6fd3de6 docs(claude): add server-side architecture map to CLAUDE.md (#2368)
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>
2026-05-30 19:47:06 -05:00

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_migrations table 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 APItrpc.ts (root router + procedure helpers), createContext.ts, middleware.trpc.ts, routers/ (~93 per-domain routers), controllers/, schema/ (zod input contracts), selectors/ (Prisma select fragments).
  • Imagesservices/image.service.ts (~7.9K lines; the hot feed path — getInfiniteImages, getAllImages, NSFW/own-content merge). API surface src/pages/api/v1/images/index.ts; index sync search-index/images.search-index.ts.
  • Modelsservices/model.service.ts, search-index/models.search-index.ts.
  • Search (Meilisearch)meilisearch/client.ts (tags requests with X-Search-Actor), meilisearch/cleanup.ts, search-index/base.search-index.ts (shared sync engine).
  • Redis / cachingredis/client.ts (clients incl. sysRedis), redis/caches.ts (createCachedObject defs + TTLs, e.g. imageMetaCache, tagIdsForImagesCache), utils/cache-helpers.ts.
  • Orchestrator (generation)orchestrator/get-orchestrator-token.ts (getOrchestratorToken), services/orchestrator/orchestrator.service.ts.
  • Authauth/next-auth-options.ts, auth/session-user.ts, auth/token-refresh.ts.
  • Jobs (cron)jobs/job.ts (runner) + individual jobs jobs/*.ts (e.g. entity-moderation.ts, search-index-sync.ts).
  • Metrics / analyticsmetrics/*.metrics.ts (ClickHouse-backed entity metrics), clickhouse/.
  • DBdb/db-helpers.ts (raw pg-pool config: connectionTimeoutMillis, labeled pool gauges), Prisma client; schema prisma/schema.prisma. Migrations are applied manually — see the Database rule above.
  • Telemetrysrc/instrumentation.node.ts (OTEL: Prisma/Redis/HTTP auto-instrumentation + custom withSpan() from utils/otel-helpers.ts), schema/track.schema.ts (ClickHouse action/event tags), prom/client.ts.
  • Healthsrc/pages/api/health.ts runs sub-checks under Promise.all; a single slow check (e.g. searchMetrics) can exceed the kubelet probe budget. HEALTHCHECK_TIMEOUT env gates it.
  • Other server domainsgames/ (new-order/ratings), webhooks/, paddle/ + coinbase/ (payments), notifications/, signals/, rewards/; S3 helpers at src/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

  1. External libraries (React, Mantine, etc.)
  2. Internal components (~/components/...)
  3. Hooks (~/hooks/...)
  4. Server/API code (~/server/...)
  5. Utils and helpers (~/utils/...)
  6. Types and enums
  7. 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

  1. Install dependencies: pnpm install
  2. Generate Prisma client: pnpm run db:generate
  3. Start dev server: Use /dev-server skill

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

  1. Run type checking: pnpm run typecheck
  2. Run linting: pnpm run lint
  3. Format code: pnpm run prettier:write
  4. 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.ts or dialog-registry2.ts
  • Use DialogProvider for context-based modal management
  • RoutedDialogProvider for 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:

  1. Drop it at src/pages/api/testing/<feature>.ts
  2. Use WebhookEndpoint(handler) for auth
  3. Lead the file with a block comment listing each action + its params + a one-line description (see src/pages/api/testing/referrals.ts for the pattern)
  4. Scope every destructive action to a single userId/refereeId per 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

  1. Clear .next folder
  2. Clear node_modules and reinstall
  3. Check for circular dependencies
  4. Ensure all environment variables are set

Database Issues

  1. Check connection string
  2. Apply pending migrations manually (we do NOT use prisma migrate deploy — see Database section above)
  3. Regenerate client: pnpm run db:generate