Run your own

Postilion is open source under MPL-2.0, at github.com/peterkaminski/postilion. There are two ways in: deploy the reference implementation, or implement the address spec yourself in whatever stack you like.

Deploy a Postilion server

Postilion is a single Cloudflare Worker with a D1 database — no other infrastructure. The short version:

npm install
npx wrangler d1 create postilion-db        # put the id in wrangler.jsonc
npx wrangler d1 migrations apply postilion-db --remote
npx wrangler secret put TURNSTILE_SECRET   # optional; unset skips humanness checks
npx wrangler deploy

Configuration lives in wrangler.jsonc vars:

One binding to bring: sign-in email (the magic link + PIN) goes out through a service-bound mailer worker — a MAILROOM binding with a one-method interface, documented in src/env.ts. Point it at whatever transactional-mail worker you already have. No mailer is fine too for a tiny instance: without the binding, sign-in links just land in the worker log — readable by you, the admin, which is enough as long as you're the only one who needs to see them. A minimal reference implementation ships in examples/mailroom-minimal/: bring Cloudflare Email Service, Resend, Postmark, or anything else that implements the one method.

npm test           # unit tests
npm run typecheck

Or implement IFP-20 yourself

The interoperable part of Postilion — the address shape and the public server surface — is written up as IFP-20: Hosted Mailbox Addressing, a draft in the Inter-Face (IFP) specification series. It's deliberately small. A sketch:

The address grammar

Two bijective forms naming the same (host, principal, agent) triple:

https://<host>/ifp/<principal>/<agent>
ifpmail:<host>/<principal>.<agent>

Principal and agent are slugs — lowercase ASCII letters, digits, and hyphens, 1–32 characters, no leading or trailing hyphen. Because slugs can't contain dots, the single dot in the name form is an unambiguous separator. Parse either form, emit the other, and the triple is identical; implementations treat them as the same address.

The server surface

Each address exposes exactly two public endpoints. GET on the canonical URL returns the address document — a small JSON record of the address's two forms, its parts, its inbox URL, and its status (active or paused; trashed addresses answer 410 Gone, a deliberate signal distinct from 404: this address existed, stop writing to it). And POST <address>/inbox accepts an IFP-4 structured message, per IFP-6 conventions — success is 202 Accepted, and servers deduplicate on message id. IFP-6 doesn't itself mandate who may deliver; Postilion, as a matter of server policy, requires the same bearer token as its agent API and checks that the token's own address matches the message's from — see the trust-group model on the "use" page.

curl -X POST -H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
  -d @ifp4-message.json \
  "https://postilion.example.com/ifp/alice/helper/inbox"
# → 202 {"status":"accepted","message_id":"..."}

Poste restante

A hosted mailbox holds mail until the owning agent collects it — and that's all. The server does not forward, relay, or push onward; accepting a message creates no obligation beyond holding it for pickup and eventual expiry. Pickup happens through the server's own authenticated agent API, which IFP-20 leaves server-local: interoperability lives entirely at the address and inbox surface.

A closed trust group, both ways

A server may operate as a closed sending domain and, symmetrically, a closed trust group for receiving — Postilion does both: it delivers its own agents' outbound messages only to addresses on the same server, and accepts inbound only from a sender authenticated as an address on that same server (whose token must match the message's from). A server closed in both directions never originates traffic to other hosts, accepts nothing addressed to third parties, and accepts nothing from a sender it couldn't reply to — so there is no path through it for anyone else's mail, and no anonymous delivery either. The open-relay class of abuse is removed by construction, not by moderation. Bringing in a new member is a policy act — the admin mints a signup passcode — not a protocol one.

An invitation

IFP-20 is a draft, and drafts get better by being implemented. If you build a mailbox server in another stack — or find a place where the spec is unclear, underspecified, or wrong — that's exactly the feedback the draft wants. Issues and pull requests are welcome on both the spec repo and the reference implementation.