{"owner":"useplunk","repo":"plunk","hasSkills":true,"hasMcp":false,"mcpConfig":null,"found":["CLAUDE.md"],"skills":{"CLAUDE.md":"# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Project Overview\n\nPlunk is a Turborepo monorepo containing multiple applications and shared packages for a platform service. The project\nuses Yarn workspaces with Node.js 20+ requirement.\n\n## Scale & Performance Requirements\n\n**CRITICAL**: This service operates at high scale with a large amount of contacts being added every day. All code\nchanges must consider:\n\n- **Database Performance**: Queries must be optimized for large datasets (1M+ rows). Avoid N+1 queries, use proper\n  indexes, and prefer cursor-based pagination over offset-based.\n- **Memory Efficiency**: Never load large datasets into memory. Always use streaming or batch processing with reasonable\n  limits.\n- **Asynchronous Operations**: Heavy computations (counts, aggregations, bulk updates) should be offloaded to background\n  jobs via BullMQ, not executed synchronously in API requests.\n- **Caching Strategy**: Frequently accessed computed values should be cached or stored as materialized data to avoid\n  repeated expensive queries.\n- **Query Optimization**: Be mindful of JSON field queries (Contact.data) - these require GIN indexes. Test query plans\n  with EXPLAIN ANALYZE.\n- **API Response Times**: Target < 200ms for read operations, < 500ms for write operations. Use timeouts and circuit\n  breakers.\n\nWhen implementing features that query or process contacts, segments, or campaigns:\n\n1. Always consider performance with millions of contacts\n2. Use pagination with reasonable defaults (20-100 items)\n3. Implement background jobs for bulk operations\n4. Add database indexes for new query patterns\n5. Cache computed values that don't need real-time accuracy\n\n## Development Commands\n\n### Environment Setup\n\n- **Start services**: `yarn services:up` - Starts PostgreSQL, Redis, Minio, and Browserless via Docker Compose\n- **Build shared packages**: `yarn build --filter=\"@plunk/shared\"` - Required before running apps\n\n### Development\n\n- **Start all apps**: `yarn dev` - Starts all apps including API server and worker process\n- **Start specific app**: `yarn dev --filter=\"<app-name>\"` (e.g., `yarn dev --filter=\"web\"`)\n- **Start API only (server)**: `yarn workspace api dev:server` - API server without worker\n- **Start API only (worker)**: `yarn workspace api dev:worker` - Worker process only\n- **Build all**: `yarn build`\n- **Lint all**: `yarn lint`\n- **Clean all**: `yarn clean` - Removes node_modules, .turbo, and build artifacts\n\n**Note**: The API's `dev` script automatically runs both the server and worker process using `concurrently`. If you need\nto run them separately (e.g., for debugging), use `dev:server` and `dev:worker` individually.\n\n### Database (Prisma)\n\n- **Generate client**: `yarn workspace @plunk/db db:generate`\n- **Run migrations (dev)**: `yarn workspace @plunk/db migrate:dev`\n- **Deploy migrations (prod)**: `yarn workspace @plunk/db migrate:prod`\n\n## Architecture\n\n### Applications (`apps/`)\n\n- **api**: Express.js API server with TypeScript (ESM), uses @overnightjs/core\n  - HTTP API endpoints for the platform\n  - Background cron jobs (workflow processor, domain verification)\n  - **Worker process** (separate): BullMQ worker for processing email, campaign, and workflow queues\n- **web**: Next.js app (Pages Router) - Main platform (next-app.useplunk.com)\n- **landing**: Next.js app (Pages Router) - Marketing site (www.useplunk.com)\n- **wiki**: Next.js app - Documentation site (docs.useplunk.com)\n\n### Background Job Architecture\n\nThe API uses BullMQ (backed by Redis) for asynchronous job processing:\n\n- **API Server** creates jobs and adds them to queues (email, campaign, workflow)\n- **Worker Process** (`apps/api/src/jobs/worker.ts`) consumes jobs from queues\n- Jobs are processed with retry logic, rate limiting, and concurrency control\n- Worker runs separately for scalability and fault isolation (can scale workers independently)\n\n### Shared Packages (`packages/`)\n\n- **@plunk/db**: Prisma schema and client\n- **@plunk/ui**: ShadCN-based UI library with Radix UI + Tailwind\n- **@plunk/shared**: Common utilities and business logic\n- **@plunk/types**: TypeScript type definitions\n- **@plunk/email**: React-email templates\n- **@plunk/notifications**: Notification system\n\n## Key Technologies\n\n- **Frontend**: React 19, Next.js 15.3, Tailwind CSS, Framer Motion\n- **Backend**: Express.js, Prisma, Redis (ioredis), Stripe\n- **UI Library**: Radix UI primitives, ShadCN components\n- **Authentication**: JWT with bcrypt\n\n## Code Standards\n\n### Import Organization\n\nESLint enforces import order: builtin → external → internal → parent → sibling with alphabetical sorting and newlines\nbetween groups.\n\n### TypeScript\n\n- Consistent type imports preferred: `import type { ... }`\n- Unused vars allowed with `_` prefix\n- Strict type checking enabled across all packages\n- Try to avoid inline types in favor of shared types in `@plunk/types`\n\n### Component Structure\n\n- UI components in `packages/ui/src/components/`\n- App-specific components in `apps/<app>/src/components/`\n- Atomic design pattern: atoms → molecules hierarchy\n\n## Environment Variables\n\n**Configuration File Setup:**\n\n- **Development**: Copy `.env.example` to `.env` at the repository root and fill in your values\n- **All apps** (API, web, landing, wiki) load environment variables from the root `.env` file\n- **Production**: Environment variables are injected by Docker/orchestration systems (no .env file needed)\n\nRequired for builds and deployment (see turbo.json and .env.example):\n\n**Build Time:**\n\n- Database: `DATABASE_URL`, `DIRECT_DATABASE_URL` (for Prisma client generation)\n- Standard: `NODE_ENV`\n\n**Runtime:**\n\n- Security: `JWT_SECRET`\n- Database: `DATABASE_URL`, `DIRECT_DATABASE_URL`\n- Infrastructure: `REDIS_URL`\n- **Application URLs** (injected at runtime into Next.js apps): `API_URI`, `DASHBOARD_URI`, `LANDING_URI`, `WIKI_URI` (\n  optional)\n- S3-compatible Storage (Minio): `S3_ENDPOINT`, `S3_ACCESS_KEY_ID`, `S3_ACCESS_KEY_SECRET`, `S3_BUCKET`,\n  `S3_PUBLIC_URL`, `S3_FORCE_PATH_STYLE`\n- AWS SES: `AWS_SES_REGION`, `AWS_SES_ACCESS_KEY_ID`, `AWS_SES_SECRET_ACCESS_KEY`, `SES_CONFIGURATION_SET`,\n  `SES_CONFIGURATION_SET_NO_TRACKING`\n- OAuth (optional): `GITHUB_OAUTH_CLIENT`, `GITHUB_OAUTH_SECRET`, `GOOGLE_OAUTH_CLIENT`, `GOOGLE_OAUTH_SECRET`\n- Stripe (optional): `STRIPE_SK`, `STRIPE_WEBHOOK_SECRET`, `STRIPE_PRICE_ONBOARDING`, `STRIPE_PRICE_EMAIL_USAGE`,\n  `STRIPE_METER_EVENT_NAME`\n- Notifications (optional): `NTFY_URL` (ntfy.sh topic URL or self-hosted server for system notifications)\n- Platform Email Notifications (optional): `PLUNK_API_KEY` (enables email notifications to users for critical events like\n  project disabled, billing limits, etc. If not set, only ntfy notifications are sent)\n- Self-hosting User Management (optional):\n  - `DISABLE_SIGNUPS` (default: false) - When set to true, prevents new user signups via the API\n  - `VERIFY_EMAIL_ON_SIGNUP` (default: false) - When set to true, validates emails on signup for disposable domains,\n    plus-addressing, domain existence, and MX records\n- Security (optional): `AUTO_PROJECT_DISABLE` (default: true) - Controls whether projects are automatically disabled when\n  bounce/complaint rate thresholds are exceeded\n- Attachment Limits (optional):\n  - `MAX_ATTACHMENT_SIZE_MB` (default: 10) - Maximum total attachment size in megabytes per email. AWS SES supports up to 40 MB.\n  - `MAX_ATTACHMENTS_COUNT` (default: 10) - Maximum number of attachments per email\n- Phishing Detection (optional):\n  - `OPENROUTER_API_KEY` - API key for OpenRouter (enables phishing detection)\n  - `OPENROUTER_MODEL` (default: anthropic/claude-3-haiku) - LLM model to use for content analysis\n  - `PHISHING_DETECTION_SAMPLE_RATE` (default: 0.1) - Percentage of emails to check (0.0-1.0, e.g., 0.1 = 10%)\n  - `PHISHING_CONFIDENCE_THRESHOLD` (default: 95) - Minimum confidence percentage (0-100) to auto-disable project for single detection\n  - `PHISHING_CUMULATIVE_THRESHOLD` (default: 3) - Number of phishing detections within time window to trigger auto-disable\n  - `PHISHING_CUMULATIVE_WINDOW_MS` (default: 3600000) - Time window in milliseconds for cumulative tracking (default 1 hour)\n\n**Important Notes:**\n\n- **Development**: All environment variables are loaded from the root `.env` file (monorepo-wide)\n- **Production**: The application URLs (`API_URI`, `DASHBOARD_URI`, etc.) are injected at Docker container startup. This\n  allows the same Docker image to be used across different environments by simply changing environment variables at\n  runtime\n- **Frontend Variables**: Next.js apps use `NEXT_PUBLIC_*` prefixed variables that are embedded at build time for\n  client-side access\n\n## Environment Variable Changes\n\nWhen you add, rename, remove, or change the default/behaviour of any environment variable, you MUST update all THREE of the following in the same change:\n\n1. `apps/api/.env.example` — local development defaults\n2. `.env.self-host.example` — self-hosting / production template\n3. `apps/wiki/content/docs/self-hosting/environment-variables.mdx` — user-facing reference\n\nRules:\n\n- If the variable already exists in any file, **modify** its line/row/description — do not duplicate or leave a stale entry.\n- Keep section/category names consistent across all three files (e.g. \"AWS SES\", \"Phishing Detection\").\n- For dev-only or self-host-only variables, still mention them in the wiki and note the scope; only skip the example file where the variable is genuinely never applicable.\n- When in doubt about whether a variable belongs in `apps/api/.env.example` (development), include it commented out with a short note.\n\n## Plugins\n\nThere are two plugins installed for you to use.\n\n- frontend-design: This plugin can help you to create polished user interfaces. Use it when working on design-related tasks.\n- superpowers: This plugin can help you with advanced tasks such as refactorings, new features or architectural changes. Use it when you need extra assistance beyond basic coding.\n"},"files":{"CLAUDE.md":"# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Project Overview\n\nPlunk is a Turborepo monorepo containing multiple applications and shared packages for a platform service. The project\nuses Yarn workspaces with Node.js 20+ requirement.\n\n## Scale & Performance Requirements\n\n**CRITICAL**: This service operates at high scale with a large amount of contacts being added every day. All code\nchanges must consider:\n\n- **Database Performance**: Queries must be optimized for large datasets (1M+ rows). Avoid N+1 queries, use proper\n  indexes, and prefer cursor-based pagination over offset-based.\n- **Memory Efficiency**: Never load large datasets into memory. Always use streaming or batch processing with reasonable\n  limits.\n- **Asynchronous Operations**: Heavy computations (counts, aggregations, bulk updates) should be offloaded to background\n  jobs via BullMQ, not executed synchronously in API requests.\n- **Caching Strategy**: Frequently accessed computed values should be cached or stored as materialized data to avoid\n  repeated expensive queries.\n- **Query Optimization**: Be mindful of JSON field queries (Contact.data) - these require GIN indexes. Test query plans\n  with EXPLAIN ANALYZE.\n- **API Response Times**: Target < 200ms for read operations, < 500ms for write operations. Use timeouts and circuit\n  breakers.\n\nWhen implementing features that query or process contacts, segments, or campaigns:\n\n1. Always consider performance with millions of contacts\n2. Use pagination with reasonable defaults (20-100 items)\n3. Implement background jobs for bulk operations\n4. Add database indexes for new query patterns\n5. Cache computed values that don't need real-time accuracy\n\n## Development Commands\n\n### Environment Setup\n\n- **Start services**: `yarn services:up` - Starts PostgreSQL, Redis, Minio, and Browserless via Docker Compose\n- **Build shared packages**: `yarn build --filter=\"@plunk/shared\"` - Required before running apps\n\n### Development\n\n- **Start all apps**: `yarn dev` - Starts all apps including API server and worker process\n- **Start specific app**: `yarn dev --filter=\"<app-name>\"` (e.g., `yarn dev --filter=\"web\"`)\n- **Start API only (server)**: `yarn workspace api dev:server` - API server without worker\n- **Start API only (worker)**: `yarn workspace api dev:worker` - Worker process only\n- **Build all**: `yarn build`\n- **Lint all**: `yarn lint`\n- **Clean all**: `yarn clean` - Removes node_modules, .turbo, and build artifacts\n\n**Note**: The API's `dev` script automatically runs both the server and worker process using `concurrently`. If you need\nto run them separately (e.g., for debugging), use `dev:server` and `dev:worker` individually.\n\n### Database (Prisma)\n\n- **Generate client**: `yarn workspace @plunk/db db:generate`\n- **Run migrations (dev)**: `yarn workspace @plunk/db migrate:dev`\n- **Deploy migrations (prod)**: `yarn workspace @plunk/db migrate:prod`\n\n## Architecture\n\n### Applications (`apps/`)\n\n- **api**: Express.js API server with TypeScript (ESM), uses @overnightjs/core\n  - HTTP API endpoints for the platform\n  - Background cron jobs (workflow processor, domain verification)\n  - **Worker process** (separate): BullMQ worker for processing email, campaign, and workflow queues\n- **web**: Next.js app (Pages Router) - Main platform (next-app.useplunk.com)\n- **landing**: Next.js app (Pages Router) - Marketing site (www.useplunk.com)\n- **wiki**: Next.js app - Documentation site (docs.useplunk.com)\n\n### Background Job Architecture\n\nThe API uses BullMQ (backed by Redis) for asynchronous job processing:\n\n- **API Server** creates jobs and adds them to queues (email, campaign, workflow)\n- **Worker Process** (`apps/api/src/jobs/worker.ts`) consumes jobs from queues\n- Jobs are processed with retry logic, rate limiting, and concurrency control\n- Worker runs separately for scalability and fault isolation (can scale workers independently)\n\n### Shared Packages (`packages/`)\n\n- **@plunk/db**: Prisma schema and client\n- **@plunk/ui**: ShadCN-based UI library with Radix UI + Tailwind\n- **@plunk/shared**: Common utilities and business logic\n- **@plunk/types**: TypeScript type definitions\n- **@plunk/email**: React-email templates\n- **@plunk/notifications**: Notification system\n\n## Key Technologies\n\n- **Frontend**: React 19, Next.js 15.3, Tailwind CSS, Framer Motion\n- **Backend**: Express.js, Prisma, Redis (ioredis), Stripe\n- **UI Library**: Radix UI primitives, ShadCN components\n- **Authentication**: JWT with bcrypt\n\n## Code Standards\n\n### Import Organization\n\nESLint enforces import order: builtin → external → internal → parent → sibling with alphabetical sorting and newlines\nbetween groups.\n\n### TypeScript\n\n- Consistent type imports preferred: `import type { ... }`\n- Unused vars allowed with `_` prefix\n- Strict type checking enabled across all packages\n- Try to avoid inline types in favor of shared types in `@plunk/types`\n\n### Component Structure\n\n- UI components in `packages/ui/src/components/`\n- App-specific components in `apps/<app>/src/components/`\n- Atomic design pattern: atoms → molecules hierarchy\n\n## Environment Variables\n\n**Configuration File Setup:**\n\n- **Development**: Copy `.env.example` to `.env` at the repository root and fill in your values\n- **All apps** (API, web, landing, wiki) load environment variables from the root `.env` file\n- **Production**: Environment variables are injected by Docker/orchestration systems (no .env file needed)\n\nRequired for builds and deployment (see turbo.json and .env.example):\n\n**Build Time:**\n\n- Database: `DATABASE_URL`, `DIRECT_DATABASE_URL` (for Prisma client generation)\n- Standard: `NODE_ENV`\n\n**Runtime:**\n\n- Security: `JWT_SECRET`\n- Database: `DATABASE_URL`, `DIRECT_DATABASE_URL`\n- Infrastructure: `REDIS_URL`\n- **Application URLs** (injected at runtime into Next.js apps): `API_URI`, `DASHBOARD_URI`, `LANDING_URI`, `WIKI_URI` (\n  optional)\n- S3-compatible Storage (Minio): `S3_ENDPOINT`, `S3_ACCESS_KEY_ID`, `S3_ACCESS_KEY_SECRET`, `S3_BUCKET`,\n  `S3_PUBLIC_URL`, `S3_FORCE_PATH_STYLE`\n- AWS SES: `AWS_SES_REGION`, `AWS_SES_ACCESS_KEY_ID`, `AWS_SES_SECRET_ACCESS_KEY`, `SES_CONFIGURATION_SET`,\n  `SES_CONFIGURATION_SET_NO_TRACKING`\n- OAuth (optional): `GITHUB_OAUTH_CLIENT`, `GITHUB_OAUTH_SECRET`, `GOOGLE_OAUTH_CLIENT`, `GOOGLE_OAUTH_SECRET`\n- Stripe (optional): `STRIPE_SK`, `STRIPE_WEBHOOK_SECRET`, `STRIPE_PRICE_ONBOARDING`, `STRIPE_PRICE_EMAIL_USAGE`,\n  `STRIPE_METER_EVENT_NAME`\n- Notifications (optional): `NTFY_URL` (ntfy.sh topic URL or self-hosted server for system notifications)\n- Platform Email Notifications (optional): `PLUNK_API_KEY` (enables email notifications to users for critical events like\n  project disabled, billing limits, etc. If not set, only ntfy notifications are sent)\n- Self-hosting User Management (optional):\n  - `DISABLE_SIGNUPS` (default: false) - When set to true, prevents new user signups via the API\n  - `VERIFY_EMAIL_ON_SIGNUP` (default: false) - When set to true, validates emails on signup for disposable domains,\n    plus-addressing, domain existence, and MX records\n- Security (optional): `AUTO_PROJECT_DISABLE` (default: true) - Controls whether projects are automatically disabled when\n  bounce/complaint rate thresholds are exceeded\n- Attachment Limits (optional):\n  - `MAX_ATTACHMENT_SIZE_MB` (default: 10) - Maximum total attachment size in megabytes per email. AWS SES supports up to 40 MB.\n  - `MAX_ATTACHMENTS_COUNT` (default: 10) - Maximum number of attachments per email\n- Phishing Detection (optional):\n  - `OPENROUTER_API_KEY` - API key for OpenRouter (enables phishing detection)\n  - `OPENROUTER_MODEL` (default: anthropic/claude-3-haiku) - LLM model to use for content analysis\n  - `PHISHING_DETECTION_SAMPLE_RATE` (default: 0.1) - Percentage of emails to check (0.0-1.0, e.g., 0.1 = 10%)\n  - `PHISHING_CONFIDENCE_THRESHOLD` (default: 95) - Minimum confidence percentage (0-100) to auto-disable project for single detection\n  - `PHISHING_CUMULATIVE_THRESHOLD` (default: 3) - Number of phishing detections within time window to trigger auto-disable\n  - `PHISHING_CUMULATIVE_WINDOW_MS` (default: 3600000) - Time window in milliseconds for cumulative tracking (default 1 hour)\n\n**Important Notes:**\n\n- **Development**: All environment variables are loaded from the root `.env` file (monorepo-wide)\n- **Production**: The application URLs (`API_URI`, `DASHBOARD_URI`, etc.) are injected at Docker container startup. This\n  allows the same Docker image to be used across different environments by simply changing environment variables at\n  runtime\n- **Frontend Variables**: Next.js apps use `NEXT_PUBLIC_*` prefixed variables that are embedded at build time for\n  client-side access\n\n## Environment Variable Changes\n\nWhen you add, rename, remove, or change the default/behaviour of any environment variable, you MUST update all THREE of the following in the same change:\n\n1. `apps/api/.env.example` — local development defaults\n2. `.env.self-host.example` — self-hosting / production template\n3. `apps/wiki/content/docs/self-hosting/environment-variables.mdx` — user-facing reference\n\nRules:\n\n- If the variable already exists in any file, **modify** its line/row/description — do not duplicate or leave a stale entry.\n- Keep section/category names consistent across all three files (e.g. \"AWS SES\", \"Phishing Detection\").\n- For dev-only or self-host-only variables, still mention them in the wiki and note the scope; only skip the example file where the variable is genuinely never applicable.\n- When in doubt about whether a variable belongs in `apps/api/.env.example` (development), include it commented out with a short note.\n\n## Plugins\n\nThere are two plugins installed for you to use.\n\n- frontend-design: This plugin can help you to create polished user interfaces. Use it when working on design-related tasks.\n- superpowers: This plugin can help you with advanced tasks such as refactorings, new features or architectural changes. Use it when you need extra assistance beyond basic coding.\n"},"items":[{"name":"CLAUDE.md","path":"CLAUDE.md","title":"CLAUDE.md","content":"# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Project Overview\n\nPlunk is a Turborepo monorepo containing multiple applications and shared packages for a platform service. The project\nuses Yarn workspaces with Node.js 20+ requirement.\n\n## Scale & Performance Requirements\n\n**CRITICAL**: This service operates at high scale with a large amount of contacts being added every day. All code\nchanges must consider:\n\n- **Database Performance**: Queries must be optimized for large datasets (1M+ rows). Avoid N+1 queries, use proper\n  indexes, and prefer cursor-based pagination over offset-based.\n- **Memory Efficiency**: Never load large datasets into memory. Always use streaming or batch processing with reasonable\n  limits.\n- **Asynchronous Operations**: Heavy computations (counts, aggregations, bulk updates) should be offloaded to background\n  jobs via BullMQ, not executed synchronously in API requests.\n- **Caching Strategy**: Frequently accessed computed values should be cached or stored as materialized data to avoid\n  repeated expensive queries.\n- **Query Optimization**: Be mindful of JSON field queries (Contact.data) - these require GIN indexes. Test query plans\n  with EXPLAIN ANALYZE.\n- **API Response Times**: Target < 200ms for read operations, < 500ms for write operations. Use timeouts and circuit\n  breakers.\n\nWhen implementing features that query or process contacts, segments, or campaigns:\n\n1. Always consider performance with millions of contacts\n2. Use pagination with reasonable defaults (20-100 items)\n3. Implement background jobs for bulk operations\n4. Add database indexes for new query patterns\n5. Cache computed values that don't need real-time accuracy\n\n## Development Commands\n\n### Environment Setup\n\n- **Start services**: `yarn services:up` - Starts PostgreSQL, Redis, Minio, and Browserless via Docker Compose\n- **Build shared packages**: `yarn build --filter=\"@plunk/shared\"` - Required before running apps\n\n### Development\n\n- **Start all apps**: `yarn dev` - Starts all apps including API server and worker process\n- **Start specific app**: `yarn dev --filter=\"<app-name>\"` (e.g., `yarn dev --filter=\"web\"`)\n- **Start API only (server)**: `yarn workspace api dev:server` - API server without worker\n- **Start API only (worker)**: `yarn workspace api dev:worker` - Worker process only\n- **Build all**: `yarn build`\n- **Lint all**: `yarn lint`\n- **Clean all**: `yarn clean` - Removes node_modules, .turbo, and build artifacts\n\n**Note**: The API's `dev` script automatically runs both the server and worker process using `concurrently`. If you need\nto run them separately (e.g., for debugging), use `dev:server` and `dev:worker` individually.\n\n### Database (Prisma)\n\n- **Generate client**: `yarn workspace @plunk/db db:generate`\n- **Run migrations (dev)**: `yarn workspace @plunk/db migrate:dev`\n- **Deploy migrations (prod)**: `yarn workspace @plunk/db migrate:prod`\n\n## Architecture\n\n### Applications (`apps/`)\n\n- **api**: Express.js API server with TypeScript (ESM), uses @overnightjs/core\n  - HTTP API endpoints for the platform\n  - Background cron jobs (workflow processor, domain verification)\n  - **Worker process** (separate): BullMQ worker for processing email, campaign, and workflow queues\n- **web**: Next.js app (Pages Router) - Main platform (next-app.useplunk.com)\n- **landing**: Next.js app (Pages Router) - Marketing site (www.useplunk.com)\n- **wiki**: Next.js app - Documentation site (docs.useplunk.com)\n\n### Background Job Architecture\n\nThe API uses BullMQ (backed by Redis) for asynchronous job processing:\n\n- **API Server** creates jobs and adds them to queues (email, campaign, workflow)\n- **Worker Process** (`apps/api/src/jobs/worker.ts`) consumes jobs from queues\n- Jobs are processed with retry logic, rate limiting, and concurrency control\n- Worker runs separately for scalability and fault isolation (can scale workers independently)\n\n### Shared Packages (`packages/`)\n\n- **@plunk/db**: Prisma schema and client\n- **@plunk/ui**: ShadCN-based UI library with Radix UI + Tailwind\n- **@plunk/shared**: Common utilities and business logic\n- **@plunk/types**: TypeScript type definitions\n- **@plunk/email**: React-email templates\n- **@plunk/notifications**: Notification system\n\n## Key Technologies\n\n- **Frontend**: React 19, Next.js 15.3, Tailwind CSS, Framer Motion\n- **Backend**: Express.js, Prisma, Redis (ioredis), Stripe\n- **UI Library**: Radix UI primitives, ShadCN components\n- **Authentication**: JWT with bcrypt\n\n## Code Standards\n\n### Import Organization\n\nESLint enforces import order: builtin → external → internal → parent → sibling with alphabetical sorting and newlines\nbetween groups.\n\n### TypeScript\n\n- Consistent type imports preferred: `import type { ... }`\n- Unused vars allowed with `_` prefix\n- Strict type checking enabled across all packages\n- Try to avoid inline types in favor of shared types in `@plunk/types`\n\n### Component Structure\n\n- UI components in `packages/ui/src/components/`\n- App-specific components in `apps/<app>/src/components/`\n- Atomic design pattern: atoms → molecules hierarchy\n\n## Environment Variables\n\n**Configuration File Setup:**\n\n- **Development**: Copy `.env.example` to `.env` at the repository root and fill in your values\n- **All apps** (API, web, landing, wiki) load environment variables from the root `.env` file\n- **Production**: Environment variables are injected by Docker/orchestration systems (no .env file needed)\n\nRequired for builds and deployment (see turbo.json and .env.example):\n\n**Build Time:**\n\n- Database: `DATABASE_URL`, `DIRECT_DATABASE_URL` (for Prisma client generation)\n- Standard: `NODE_ENV`\n\n**Runtime:**\n\n- Security: `JWT_SECRET`\n- Database: `DATABASE_URL`, `DIRECT_DATABASE_URL`\n- Infrastructure: `REDIS_URL`\n- **Application URLs** (injected at runtime into Next.js apps): `API_URI`, `DASHBOARD_URI`, `LANDING_URI`, `WIKI_URI` (\n  optional)\n- S3-compatible Storage (Minio): `S3_ENDPOINT`, `S3_ACCESS_KEY_ID`, `S3_ACCESS_KEY_SECRET`, `S3_BUCKET`,\n  `S3_PUBLIC_URL`, `S3_FORCE_PATH_STYLE`\n- AWS SES: `AWS_SES_REGION`, `AWS_SES_ACCESS_KEY_ID`, `AWS_SES_SECRET_ACCESS_KEY`, `SES_CONFIGURATION_SET`,\n  `SES_CONFIGURATION_SET_NO_TRACKING`\n- OAuth (optional): `GITHUB_OAUTH_CLIENT`, `GITHUB_OAUTH_SECRET`, `GOOGLE_OAUTH_CLIENT`, `GOOGLE_OAUTH_SECRET`\n- Stripe (optional): `STRIPE_SK`, `STRIPE_WEBHOOK_SECRET`, `STRIPE_PRICE_ONBOARDING`, `STRIPE_PRICE_EMAIL_USAGE`,\n  `STRIPE_METER_EVENT_NAME`\n- Notifications (optional): `NTFY_URL` (ntfy.sh topic URL or self-hosted server for system notifications)\n- Platform Email Notifications (optional): `PLUNK_API_KEY` (enables email notifications to users for critical events like\n  project disabled, billing limits, etc. If not set, only ntfy notifications are sent)\n- Self-hosting User Management (optional):\n  - `DISABLE_SIGNUPS` (default: false) - When set to true, prevents new user signups via the API\n  - `VERIFY_EMAIL_ON_SIGNUP` (default: false) - When set to true, validates emails on signup for disposable domains,\n    plus-addressing, domain existence, and MX records\n- Security (optional): `AUTO_PROJECT_DISABLE` (default: true) - Controls whether projects are automatically disabled when\n  bounce/complaint rate thresholds are exceeded\n- Attachment Limits (optional):\n  - `MAX_ATTACHMENT_SIZE_MB` (default: 10) - Maximum total attachment size in megabytes per email. AWS SES supports up to 40 MB.\n  - `MAX_ATTACHMENTS_COUNT` (default: 10) - Maximum number of attachments per email\n- Phishing Detection (optional):\n  - `OPENROUTER_API_KEY` - API key for OpenRouter (enables phishing detection)\n  - `OPENROUTER_MODEL` (default: anthropic/claude-3-haiku) - LLM model to use for content analysis\n  - `PHISHING_DETECTION_SAMPLE_RATE` (default: 0.1) - Percentage of emails to check (0.0-1.0, e.g., 0.1 = 10%)\n  - `PHISHING_CONFIDENCE_THRESHOLD` (default: 95) - Minimum confidence percentage (0-100) to auto-disable project for single detection\n  - `PHISHING_CUMULATIVE_THRESHOLD` (default: 3) - Number of phishing detections within time window to trigger auto-disable\n  - `PHISHING_CUMULATIVE_WINDOW_MS` (default: 3600000) - Time window in milliseconds for cumulative tracking (default 1 hour)\n\n**Important Notes:**\n\n- **Development**: All environment variables are loaded from the root `.env` file (monorepo-wide)\n- **Production**: The application URLs (`API_URI`, `DASHBOARD_URI`, etc.) are injected at Docker container startup. This\n  allows the same Docker image to be used across different environments by simply changing environment variables at\n  runtime\n- **Frontend Variables**: Next.js apps use `NEXT_PUBLIC_*` prefixed variables that are embedded at build time for\n  client-side access\n\n## Environment Variable Changes\n\nWhen you add, rename, remove, or change the default/behaviour of any environment variable, you MUST update all THREE of the following in the same change:\n\n1. `apps/api/.env.example` — local development defaults\n2. `.env.self-host.example` — self-hosting / production template\n3. `apps/wiki/content/docs/self-hosting/environment-variables.mdx` — user-facing reference\n\nRules:\n\n- If the variable already exists in any file, **modify** its line/row/description — do not duplicate or leave a stale entry.\n- Keep section/category names consistent across all three files (e.g. \"AWS SES\", \"Phishing Detection\").\n- For dev-only or self-host-only variables, still mention them in the wiki and note the scope; only skip the example file where the variable is genuinely never applicable.\n- When in doubt about whether a variable belongs in `apps/api/.env.example` (development), include it commented out with a short note.\n\n## Plugins\n\nThere are two plugins installed for you to use.\n\n- frontend-design: This plugin can help you to create polished user interfaces. Use it when working on design-related tasks.\n- superpowers: This plugin can help you with advanced tasks such as refactorings, new features or architectural changes. Use it when you need extra assistance beyond basic coding.\n","category":"root","tokens":2515}]}