Project 01 · live · source-available
Postern
Personal email on Cloudflare’s free tier. Receiving, storage, reading and replying — on Workers, D1 and R2, with a web client I wrote.
What it is
live · source-availablePersonal email on Cloudflare’s free tier. Mail for this domain is received, stored in my own D1 database and R2 bucket, and read through a web client I wrote — roughly 1,400 lines of vanilla JavaScript, no framework and no build step. No provider holds my archive, and at personal volume it costs nothing to run.
What it is not: an SMTP server. Resend is the MTA — it terminates SMTP inbound and outbound, and it can see what passes through. Everything after that handoff is mine: storage, the index, full-text search, the client, auth, forwarding, backups. Calling this “self-hosted email” without that sentence would be a claim rather than a fact, which is the opposite of the point.
- Status
- In daily use
- Since
- August 2026 · my only mailbox
- Runs on
- Workers · D1 · R2 · Resend for SMTP
- Cost
- €0 / month · free tier throughout
-
Constraint · 10 ms of CPU
The server parses nothing
Workers Free allows 10 ms of CPU per invocation, and parsing a multi-megabyte MIME message does not fit — a handler that exhausts its CPU loses the message. So the server stores the raw
Architecture decided by a quota, not a preference.emland the browser parses it on read. The useful side effect: the server never handles a decoded body. -
Failure mode
A bug here cannot cost an email
A handler that returns without storing, forwarding or rejecting drops the mail silently, and silent loss is the worst failure a mailbox has. Every path ends in one of those three — and mail is forwarded to my existing mailbox before it is stored.
Write-ahead, applied to a mailbox -
Threat model
Bodies are untrusted code
Message bodies are code written by strangers, so they render in a sandboxed iframe with no
Cloudflare Access gates the UI; the webhook proves itself by HMACallow-scriptsunderdefault-src 'none'. Remote images are read receipts, so they are blocked per message until I ask for them. Attachments are served from my bucket and never executed.
The gaps are published too. The roadmap names what is unfinished and what is risky — the send-quota guard that does not exist yet, and a nightly backup that has never been read back. An untested backup is a belief, not a backup.