π₯’ TempeMail
Zero-config, multi-domain disposable email on Cloudflare Workers.
TempeMail is a self-hosted disposable email service that runs entirely on the Cloudflare edge β no VPS, no Docker, no SMTP server. One worker handles the web UI, REST API, inbound email delivery, and automatic provisioning.
Deploy in minutes with 3 environment values:
git clone https://github.com/ghufronainun/tempe-mail.git
cd tempe-mail
npm install
cp .env.example .env # fill 3 values
npm run setup # provisions everything automatically
npx wrangler deploy # go live π
Table of Contents
- Why TempeMail?
- Features
- How it works
- Quick Start
- Development
- Project Structure
- API Reference
- Configuration
- Troubleshooting
- Roadmap
- Contributing
- License
Why TempeMail?
TempeMail is a self-hosted disposable email service that runs entirely on the Cloudflare edge β no VPS, no Docker, no SMTP server. One worker handles the web UI, REST API, inbound email delivery, and automatic provisioning.
What sets it apart from other temp-mail projects:
- Multi-domain β serve disposable inboxes across any number of domains from a single worker, without extra config.
- Zero-config setup β
npm run setupprovisions D1, Email Routing, and zone catch-all rules automatically. Idempotent and re-runnable. - Deliverability diagnostics β SPF/DKIM/DMARC status for every message, which most temp-mail services don't expose.
- Agent-ready β REST API keys, webhooks, RSS feeds, SSE streams, and a built-in MCP server so AI agents can read inboxes natively.
- Self-hosted β you own the infrastructure and the data, on Cloudflare's free tier.
Features
π¬ Core
- Zero-config setup β
npm run setupprovisions D1, Email Routing, zone catch-all rules, and renderswrangler.tomlautomatically. Idempotent β safe to re-run anytime. - Multi-domain β serve disposable inboxes across 1 or N domains from a single worker.
DOMAINS=mail.example.com,mail2.example.netand you're done. - HTML email rendering β full HTML email support, rendered safely in a sandboxed iframe (
sandbox=""). Email looks exactly as the sender designed it. - OTP auto-highlight β verification codes (4β8 digit) are automatically detected and displayed prominently in monospace, so you never hunt for the code.
- Semantic tagging β emails are auto-classified into π Verification, π Security, π§ͺ Testing, or π£ Marketing based on subject and sender.
- SPF/DKIM/DMARC viewer β inspect
Received-SPFandAuthentication-Resultsheaders for every message. Gold for QA and email deliverability testing.
ποΈ UX
- Custom addresses β choose your own local part (
[email protected]) or let TempeMail generate a random one. - Auto-expire β inboxes expire after a configurable TTL (default 24h, range 1hβ168h).
- Attachment metadata β file name, type, and size shown as chips.
- Starred messages β pin important emails, persisted in localStorage.
- Filter chips β quickly filter All / Verification / Starred.
- Realtime updates β new messages appear instantly via SSE (Durable Objects).
- Dark mode β beautiful dark-first UI with light toggle.
- i18n β English and Bahasa Indonesia, toggle in one click.
- Fully responsive β mobile-first, breakpoints at 900px and 680px.
π€ Developer / AI-agent friendly
- REST API β full JSON API for scripts and automation.
- API key auth β
Authorization: Bearer tmk_...for scripts/agents (no browser session needed). Keys hashed with SHA-256, revocable. - Webhooks β POST to any URL when new mail arrives, signed with HMAC-SHA256 (
X-TempeMail-Signature). - MCP server β Model Context Protocol bridge so Claude Code / Cursor / any MCP client can read inboxes as tools (
list_inboxes,list_messages,get_message). - RSS feed per inbox β
GET /inboxes/:address/feed.xmlrequires no auth (the address itself is the secret). Poll it from any script. - SSE stream per inbox β realtime push to any client.
- Inbox search β
GET /inboxes/:address/search?q=searches subject, body, and sender. - Attachment download β (optional) full attachment bodies stored in R2 and downloadable per message. Without R2 you still get attachment metadata + the message body; only the file download is skipped.
- AGENTS.md β onboarding guide so AI coding agents can work on this repo immediately.
How it works
Sender
β
βΌ
Cloudflare MX
β (Email Routing β Worker)
βΌ
Worker email() handler
β PostalMime parses MIME
β (text + html + headers + attachments)
βΌ
D1 Database (SQLite)
ββ inboxes (address, expires_at)
ββ messages (body, body_html, spf, dkim, dmarc, ...)
ββ attachments (filename, content_type, size)
ββ sessions (browser session β inbox ownership)
β
ββββββ΄βββββ
βΌ βΌ
REST API Web UI (static assets)
β ββ index.html
ββ /api/session ββ app.js (vanilla JS, no framework)
ββ /api/inboxes
ββ /api/inboxes/:addr/messages
ββ /api/inboxes/:addr/feed.xml (RSS)
ββ /api/inboxes/:addr/events (SSE)
The flow:
- Sender emails
[email protected] - Cloudflare Email Routing delivers to the Worker's
email()handler - PostalMime parses the MIME β extracts text, HTML, headers, attachments, deliverability status
- Data is stored in D1; the inbox is auto-created if it doesn't exist
- Subscribers listening via SSE get a realtime push
- Web UI polls or streams and renders everything with OTP highlight + semantic tags
Tech stack
Quick Start
Prerequisites
- A Cloudflare account (free tier is enough)
- At least one domain managed by Cloudflare DNS
- Node.js 18+ and npm
1. Clone & install
git clone https://github.com/ghufronainun/tempe-mail.git
cd tempe-mail
npm install
2. Get your Cloudflare credentials
You need two things from Cloudflare: an API token and your account ID.
Create an API token
- Go to the Cloudflare dashboard and log in
- Click your profile icon (top-right) β My Profile β API Tokens
- Click Create Token
- Choose Create Custom Token (or use the "Edit zone DNS" template as a starting point)
- Give the token a name like
tempe-mail - Add these permissions (all needed for one-command provisioning):
- Account β Workers Scripts β Edit
- Account β Workers D1 β Edit
- Account β Workers R2 Storage β Edit
- Account β Email Routing Addresses β Edit
- Account β Email Routing Rules β Edit
- Zone β Zone β Read (for all zones that host your mail domains)
- Zone β DNS β Edit (optional, for automatic DNS records)
- Zone β Email Routing β Edit
- Under Zone Resources, choose All zones (or select the specific zones you'll use)
- Click Continue to summary β Create Token
- Copy the token immediately β it's shown only once. It looks like
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
β οΈ Treat this token like a password. Anyone with it can modify your Cloudflare resources. It is stored in
.env(gitignored) β never commit it.
Find your Account ID
- Go to the Cloudflare dashboard
- Look at the sidebar on the left β your Account ID is listed right below "Workers & Pages"
- Alternatively: click any domain β scroll down in the right sidebar β "Account ID"
- It's a 32-character hex string like
a1b2c3d4e5f60718293a4b5c6d7e8f90
3. Configure
cp .env.example .env
Edit .env β only 3 values are required:
CLOUDFLARE_API_TOKEN=your_cf_api_token # Workers + D1 + Email Routing + Zone permissions
CLOUDFLARE_ACCOUNT_ID=your_account_id # dashboard β Workers & Pages β account ID
DOMAINS=mail.example.com # one or more, comma-separated
WEB_HOST=temp.example.com # subdomain for the web UI
Multi-domain? Just add more:
DOMAINS=mail.example.com,mail2.example.net,mail3.example.org
4. Setup & deploy
npm run setup # creates D1, applies schema, enables Email Routing,
# sets catch-all β worker for every domain, renders wrangler.toml
npx wrangler deploy # pushes the worker + static assets to the edge
Open https://temp.example.com and start receiving email. π
Expected setup output:
π¦ Setting up D1 database...
Created D1 database: 12345678-...
π Applying schema...
Schema applied.
π Discovering zone IDs...
mail.example.com β zone abcdef...
π§ Provisioning Email Routing...
mail.example.com: catch-all β worker:tempe-mail (enabled)
π Rendering wrangler.toml...
wrangler.toml rendered.
β
Setup complete!
1. Review wrangler.toml
2. Run: npx wrangler deploy
3. Open: https://temp.example.com
Optional: enable R2 attachment downloads
R2 is optional β without it TempeMail works fully (metadata + body), only file downloads are skipped. To enable:
- Enable R2 in your account: Cloudflare dashboard β R2 β Get started (free tier available)
- Create the bucket:
npx wrangler r2 bucket create tempe-mail-attachments - Uncomment the
[[r2_buckets]]block inwrangler.toml, fill in your bucket name, then re-deploy:npx wrangler deploy
Optional: manual setup (if the script hits permission limits)
If your API token lacks D1 or Email Routing permissions, npm run setup may skip those steps. You can do them manually:
-
Create the D1 database:
npx wrangler d1 create tempe-mail-dbCopy the
database_idfrom the output. -
Apply the schema:
npx wrangler d1 execute tempe-mail-db --remote --file=src/db/schema.sql -
Create the R2 bucket (optional β for attachment downloads):
npx wrangler r2 bucket create tempe-mail-attachments -
Fill in
wrangler.toml: replace theREPLACED_BY_SETUPplaceholders with your real database ID and routes, and uncomment the[[r2_buckets]]block. -
Enable Email Routing in the dashboard: Cloudflare dashboard β your domain β Email β Email Routing β enable it, add a catch-all rule pointing to the
tempe-mailworker. -
Deploy:
npx wrangler deploy
Note: Email Worker bindings (
[email]) are always configured via the dashboard/API β not wrangler.toml. That's whywranglermay warn about the
Development
npm run dev # applies schema to local D1, then starts wrangler dev (port 8787)
npm test # run all 77 tests (Vitest)
npm run typecheck # strict TypeScript check
Local dev guide:
npm run devβ starts the worker locally athttp://localhost:8787- The D1 schema is applied automatically to the local database on startup (no more
no such tableerrors) - Inbound email is simulated via unit/E2E tests (
tests/email-handler-e2e.test.ts) - The scheduled purge trigger can be fired manually:
curl "http://localhost:8787/cdn-cgi/local/scheduled"
wrangler devis for development only. Production runs on Cloudflare's edge viawrangler deployβ your machine/VPS is completely uninvolved.
Project Structure
tempe-mail/
βββ src/
β βββ index.ts # Worker entry: fetch() + email() + scheduled purge
β βββ env.ts # Environment bindings interface
β βββ email/
β β βββ ingest.ts # MIME parsing (PostalMime) β text/html/headers/attachments
β β βββ headers.ts # SPF/DKIM/DMARC extraction from raw headers
β βββ api/
β β βββ routes.ts # Hono REST API (session, inboxes, messages)
β β βββ rss.ts # Per-inbox RSS 2.0 feeds
β β βββ tagging.ts # Semantic classifier (content rules)
β β βββ realtime.ts # SSE endpoint β Durable Object
β βββ cf/
β β βββ zones-loader.ts # CF_ZONE_MAP env β domainβzone map
β β βββ routing.ts # Email Routing provisioning (setup-time)
β βββ db/
β β βββ schema.sql # D1 schema (inboxes, messages, attachments, sessions)
β β βββ realtime-room.ts # Durable Object: SSE room per inbox
β βββ web/ # Frontend (vanilla, dark theme)
β βββ index.html
β βββ app.js # Rendering, polling/SSE, OTP, tags, i18n
β βββ styles.css # Responsive, dark/light, 3 breakpoints
β βββ i18n/{en,id}.js # String dictionaries
βββ scripts/
β βββ setup.mjs # Zero-config provisioning script
β βββ dev.mjs # Dev helper (auto-schema + wrangler dev)
βββ tests/ # Vitest: 5 files, 77 tests
βββ .github/workflows/ci.yml # CI: typecheck + tests on push/PR
βββ .env.example # Environment template
βββ wrangler.toml # Generated by setup (placeholders committed)
βββ README.md
βββ AGENTS.md # AI-agent onboarding guide
βββ API.md # Full API reference
βββ LICENSE # MIT
API Reference
Authentication
Browser sessions via x-session-id header. Create one:
POST /api/session
β { "sessionId": "uuid-v4" }
All inbox/message endpoints require the header. Inbox ownership is enforced β you can only read messages from inboxes your session owns.
Endpoints
Example: create inbox
curl -X POST https://temp.example.com/api/inboxes \
-H "x-session-id: <sessionId>" \
-H "Content-Type: application/json" \
-d '{"localPart": "myalias", "ttlHours": 48}'
{
"address": "[email protected]",
"domain": "mail.example.com",
"ttlHours": 48
}
Full reference: API.md Β· Live docs: /docs
Configuration
All values are read at setup time. Secrets stay in .env (gitignored) β never commit them.
Troubleshooting
Roadmap
See AGENTS.md and the plan document for details.
β Phase 2 β done
- REST API key authentication (no browser session needed)
- Webhooks (POST to a URL on new mail, HMAC-signed)
- MCP server (Model Context Protocol) for AI agents
- Full attachment download (R2 storage)
- Inbox search
π Planned
- Frontend UI for API keys, webhooks, and search (backends are API-ready)
- Rate limiting per key
- Multiple webhook events (message deleted, inbox expired)
Contributing
Contributions are welcome! Open an issue or PR.
- Start with CONTRIBUTING.md β setup, conventions, quality gate
- All code must pass:
npm run typecheckandnpm test - Follow the Code of Conduct
- Found a vulnerability? See SECURITY.md β please report privately
- Use the issue/PR templates in
.github/
License
MIT Β© 2026 masantoid, TempeMail contributors.
Derived from hirotomasato/tempik. TempeMail has been rewritten from scratch with multi-domain support, deliverability diagnostics, zero-config provisioning, and an original UI. This project is distributed under the MIT License β see the LICENSE file for the full text.