Contributing Guide & Monorepo Architecture
Learn how to set up the EmitMail monorepo locally, understand the single-app multi-service architecture, and contribute across the API, Worker, Dashboard, and Documentation.
Welcome to the EmitMail open-source contributor guide! EmitMail is built as a cohesive, single-monorepo application powering high-deliverability transactional email dispatch, 2048-bit RSA DKIM generation, and an inbound SMTP mailbox engine.
Whether you are fixing a bug, adding a new feature to the Backend API, optimizing the Background Worker, polishing the Cloudflare Kumo Dashboard, or improving our Documentation, this guide will help you get up and running in minutes.
1. Monorepo Architecture Overview
EmitMail is organized as a pnpm workspace containing four applications. The packages/ directories are workspace scaffolding and are not currently shared implementation packages.
emitmail/
├── apps/
│ ├── api/ # Express REST API, Prisma ORM, Inbound SMTP receiver (Port 2525)
│ ├── worker/ # Decoupled BullMQ delivery engine, direct MX transport (Port 25)
│ ├── dashboard/ # Next.js 16 UI, @cloudflare/kumo design system, React 19
│ └── docs/ # Next.js 16 + Fumadocs documentation engine (Port 3001)
├── packages/ # Workspace scaffolding; currently no shared package implementation
├── docker/ # Production & development Dockerfiles
├── scripts/ # Deliverability tests and queue simulation scripts
└── docker-compose.dev.yml # Local development PostgreSQL & Redis infrastructureHow the Single Monorepo Works Together
┌─────────────────────────────────────────────────────────────────────────────┐
│ EmitMail Monorepo │
│ │
│ ┌────────────────────┐ ┌────────────────────────────┐ │
│ │ apps/dashboard │ ──(HTTP/REST)───► │ apps/api │ │
│ │ (Next.js + Kumo) │ │ (Express + Prisma) │ │
│ │ Port 3000 │ │ Port 4000 │ │
│ └────────────────────┘ └──────┬──────────────┬──────┘ │
│ │ │ │
│ (BullMQ │ │ (Prisma) │
│ Jobs) │ │ │
│ ▼ ▼ │
│ ┌────────────────────┐ ┌──────────────┐ ┌───────────┐ │
│ │ apps/docs │ │ Redis │ │ PostgreSQL│ │
│ │ (Fumadocs Engine) │ │ Port 6379 │ │ Port 5432 │ │
│ │ Port 3001 │ └──────┬───────┘ └─────▲─────┘ │
│ └────────────────────┘ │ │ │
│ (Pulls │ │ │
│ Jobs) │ │ Updates │
│ ▼ │ Status │
│ ┌──────────────┐ │ │
│ │ apps/worker │───────┘ │
│ │ (SMTP Engine)│ │
│ └──────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘2. Quickstart: Local Environment Setup
Prerequisites
Ensure you have installed:
- Node.js:
v20.xorv22.x(LTS recommended) - pnpm:
v9.xor higher (corepack enable && corepack prepare pnpm@latest --activate) - Docker & Docker Compose: For local PostgreSQL 16 and Redis 7
- Git: For version control
Clone the Repository
git clone https://github.com/omkarbhosale-dev/emitmail.git
cd emitmailInstall Workspace Dependencies
Install dependencies across the entire monorepo using pnpm:
pnpm installConfigure Environment Variables
Copy the example .env.example file to .env:
cp .env.example .envReview the values in .env.example. It contains safe placeholders, not production-ready secrets. For local development, docker-compose.dev.yml provides PostgreSQL and Redis; set the API and worker connection variables to match that compose file before starting services.
Start Local PostgreSQL & Redis
Launch the containerized development database and Redis instances:
pnpm dev:dockerVerify that both containers are running healthy:
docker compose -f docker-compose.dev.yml psInitialize and Seed Database
Generate the Prisma client, push the schema to PostgreSQL, and seed the default administrator:
# Generate Prisma Client
pnpm db:generate
# Push schema to local Postgres
pnpm db:push
# Seed the administrator (temporary password: emitmail)
pnpm db:seedSign in as admin with temporary password emitmail, then change it in dashboard Settings before using protected endpoints.
3. Running Services Locally
You can run individual services depending on which part of EmitMail you are developing:
| Command | Service | URL / Port | Purpose |
|---|---|---|---|
pnpm dev:api | Backend API | http://localhost:4000 | Express REST API, Inbound SMTP on port 2525 |
pnpm dev:worker | Delivery Worker | Console (Background) | BullMQ worker consuming queue & dispatching emails |
pnpm dev:dashboard | Dashboard UI | http://localhost:3000 | Next.js 16 App Router + Cloudflare Kumo components |
pnpm dev:docs | Documentation | http://localhost:3001 | Fumadocs MDX documentation site |
Initial Admin Account
The seeded admin credentials are admin / emitmail. Change the temporary password in dashboard Settings before using protected endpoints.
4. Explore Component Contribution Guides
Explore our detailed guides for each component of the EmitMail application:
Backend API (apps/api)
Express endpoints, Prisma models, 2048-bit DKIM crypto, Inbound SMTP server, and Resend-compatible API.
Background Worker (apps/worker)
BullMQ worker architecture, Nodemailer direct MX transport, DNS resolution, and retry loops.
Dashboard UI (apps/dashboard)
Next.js 16, React 19, Tailwind CSS v4, and Cloudflare Kumo design tokens (DESIGN.md).
Documentation (apps/docs)
Fumadocs MDX setup, component library, sidebar organization, and writing style guide.
5. Contribution & Pull Request Workflow
We follow standard GitHub Flow for all contributions:
1. Branching Strategy
Create a descriptive branch from main:
feat/feature-namefor new capabilitiesfix/bug-descriptionfor bug fixesdocs/page-namefor documentation updatesperf/optimizationfor performance enhancements
git checkout -b feat/custom-webhook-headers2. Conventional Commit Guidelines
We use Conventional Commits to generate automated changelogs:
feat: add support for custom webhook headersfix: handle 2048-bit multi-chunk DKIM TXT strings in resolverdocs: add contributing guide for backend api and workerrefactor: extract mx resolution logic into dedicated service
3. Verification Checklist Before Submitting a PR
Before opening a pull request, ensure:
pnpm build:apicompiles with 0 TypeScript errors.pnpm build:workercompiles with 0 TypeScript errors.pnpm build:dashboardbuilds successfully.pnpm build:docsgenerates all static MDX pages without errors.- All environment variables have clear documentation in
.env.example.