The Problem

Organizations that want a self‑hosted inbox with their own domain must stitch together DNS, email routing, storage, and a UI. Doing this manually requires separate Cloudflare configuration, R2 bucket handling, and a custom front‑end, which is error‑prone and hard to maintain.

What This Does

mailflare delivers a single‑page Next.js application that lets admins add a domain, create mailboxes, and manage messages while the back‑end talks directly to Cloudflare APIs.

  • Domain onboarding lives in src/app/api/domains/* – the POST /api/domains route (src/app/api/domains/route.ts) calls Cloudflare to enable inbound routing and provisioning of a sending sub‑domain.
  • Mailbox CRUD is handled by src/app/api/accounts/[id]/mailboxes/route.ts, which creates a Cloudflare Email Routing rule that forwards mail to the worker named in CF_EMAIL_WORKER_NAME.
  • Message storage uses Drizzle ORM (src/db/*) with SQLite migrations in drizzle/migrations/*.sql. The UI components in src/app/(dashboard)/* read/write through the generated Prisma‑like clients in src/db/schema/index.ts.

All UI pages are under src/app/(admin), src/app/(auth), and src/app/(dashboard), each with a layout.tsx that pulls the current session from src/app/api/auth/me/route.ts.

How It Is Wired

npm run dev  → next dev (next.config.ts)  
   └─ pages → src/app/* (App Router)  
        ├─ UI components (tsx) → call fetch('/api/...')  
        └─ API routes (src/app/api/**/*.ts)  
             ├─ auth routes → src/app/api/auth/* (session JWT)  
             ├─ domain routes → src/app/api/domains/*  
             │    └─ utils/cloudflare.ts (calls Cloudflare REST)  
             ├─ mailbox routes → src/app/api/accounts/[id]/mailboxes/*  
             │    └─ db access → src/db/* (drizzle)  
             └─ message routes (inbox, drafts, etc.) → src/app/api/*  
                  └─ attachment storage → src/app/api/.../route.ts → R2 SDK (cloudflare)  

Database init: src/db/index.ts → drizzle ORM → SQLite file (local)  
Migrations: npm run db:migrate:local → drizzle/migrations/*.sql → schema in src/db/schema/index.ts  
  • Entry pointnext dev launches the server; the first request hits an API route (e.g., GET /api/domains) which authenticates via src/app/api/auth/me/route.ts.
  • Core effect owners
  • src/app/api/domains/* – external Cloudflare side effects (DNS, routing).
  • src/app/api/accounts/[id]/mailboxes/* – creates Cloudflare routing rules and writes DB rows.
  • src/app/api/*/attachments/* – uploads to R2; the only module that touches the object store.
  • Blast radius – Cloudflare‑related utils are centralized in src/app/api/*/utils.ts; changes here affect every domain/mailbox operation. The DB layer (src/db/*) is thin, so schema changes are low‑risk. No circular dependencies were found; the graph is a clear star with API routes as leaves.

How To Use It

# Clone the repo
git clone https://github.com/moses-y/mailflare
cd mailflare

# Install deps
npm ci

# Copy example env and add your Cloudflare credentials
cp .dev.vars.example .dev.vars
# Edit .dev.vars: set CF_TOKEN (or CF_API_KEY + CF_EMAIL) and CF_EMAIL_WORKER_NAME

# Initialise local SQLite DB
npm run db:migrate:local

# Start dev server
npm run dev

The README’s “Setup” section is the source of truth; no Dockerfile or Makefile exists, so the above commands are the only supported workflow.

Real‑World Use

A SaaS provider can run mailflare in a Cloudflare Workers environment, point CF_TOKEN at a dedicated Cloudflare account, and programmatically add customer domains via the /api/domains endpoint. Incoming mail lands in R2, is indexed in the SQLite DB, and appears instantly in the provider’s dashboard UI for support agents.

Code Health & Issues

  • Medium – No test files – repository contains zero *.test.* files; code paths are unverified by automated tests.
  • Low – No CI for lint or type‑check – GitHub Actions workflow (.github/workflows/deploy-update.yml) only builds a deployment, not a lint/type step.
  • Low – Secrets in example file.dev.vars.example lists required env vars but does not enforce presence; missing validation could cause runtime failures.
  • Low – Single‑language lock – All server‑side logic is TypeScript; no polyglot concerns.

No license file is present beyond the generic LICENSE header, but the repo includes a LICENSE placeholder; verify that the intended license is correctly populated before commercial use.

The Bottom Line

mailflare provides a functional, Cloudflare‑backed email UI with domain provisioning and R2 attachment storage, built on a clean Next.js + Drizzle stack. It is usable out‑of‑the‑box for internal tooling, but the lack of automated tests and limited CI coverage mean any production deployment should be accompanied by a thorough QA effort and possibly added test suites. Ideal for teams comfortable with TypeScript and Cloudflare APIs who need a customizable mailbox solution without building the plumbing from scratch.