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/*– thePOST /api/domainsroute (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 inCF_EMAIL_WORKER_NAME. - Message storage uses Drizzle ORM (
src/db/*) with SQLite migrations indrizzle/migrations/*.sql. The UI components insrc/app/(dashboard)/*read/write through the generated Prisma‑like clients insrc/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 point –
next devlaunches the server; the first request hits an API route (e.g.,GET /api/domains) which authenticates viasrc/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.examplelists 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.