Skip to content
Conduit

mcp server

postern

Self-hostable email for humans and agents on Cloudflare: send, receive, search, MCP, IMAP, and webmail. Identity-bound credentials (v1.4).

skyphusion-labs/posterncommunitylistedunreviewednot running here yet

Set up

Runs on your computer. Pick your agent and operating system.

This project does not publish an install method we can turn into a config. Follow the README instead.

README

synced Oct 14, 2026

postern

Release CI npm @skyphusion/postern-mcp PyPI postern-client

Email, for humans and agents. Postern is a self-hostable mailbox on Cloudflare: it sends and receives mail, stores every message in a searchable store, and exposes one structured API that agents and human clients (IMAP/webmail) both speak. Cloudflare Email is the default transport on each seam, never a hard dependency.

Humans and agents each send as themselves, under per-identity credentials rather than a shared mailbox (see docs/SEND-IDENTITIES.md), and agents speak the mailbox natively over MCP (@skyphusion/postern-mcp) as a first-class door, not an afterthought.

From a fresh clone, with only your own domain, you can deploy it, send a message, and receive + read it back. Outbound send needs Cloudflare Email Sending on the Workers Paid plan (USD 5/month); inbound via Email Routing is free-plan OK. See DEPLOY.md for the clean-install quickstart and inbound/smoke.mjs for the scripted v1.0 acceptance smoke (issue #25).

Try it live

Live read-only demo: https://demo.posternonline.com (landing) or webmail. Log in with the demo token below. Apex posternonline.com 301s to the demo host. Sitemap: demo.posternonline.com/sitemap.xml.

Mail any address @posternonline.com and watch it appear in the mailbox. That is the product working: Email Routing -> worker -> D1 store.

The demo token is READ-scoped and public by design. Here is what that means.

It can read. It lists messages, reads one, walks a thread, searches, downloads an attachment, and lists folders.

It cannot write. The API scope wall refuses send, reply and delete. It also refuses the three routes that change stored state: mark read, set flags, and move between folders. Those need the organize scope, which this token does not carry. Webmail hides compose for it.

A read token cannot change anything in the mailbox. If you run your own Postern, a read token you issue has the same limits.

The demo mailbox is shared and public, so do not mail anything private.

MCP config for agents:

POSTERN_API_URL=https://demo.posternonline.com
POSTERN_API_TOKEN=a5fb2da1a13ab468adc1d147272126a4a977f3db44ace5c961ce87d26e0055d8

Run your own in about 10 minutes: follow DEPLOY.md. No Cloudflare account yet? DEPLOY.md section 0 runs the whole mailbox locally under wrangler dev first.

Six surfaces in one repo (one store, one API):

PathRole
inbound/Core Cloudflare Worker: ingest, store (D1 + R2 + Vectorize), mailbox API, send
relay/Optional Go SMTP daemon: loopback ingest, submission 587/465, BYO dispatch
mcp/MCP server for agents (@skyphusion/postern-mcp on npm)
webmail/Browser UI embedded at /webmail: read, plus compose/reply when the token is send-capable
imap/IMAP proxy (read, \Seen, delete) for Thunderbird / mutt / iOS Mail
clients/python/Stdlib HTTP client + CLI (postern-client on PyPI)
flowchart TD
    subgraph transports[Transport seams]
        cfIn[CF Email Routing]
        cfOut[CF Email Sending]
        relay[postern-relay SMTP]
    end

    subgraph core[inbound Worker]
        store[(D1 + R2 + Vectorize)]
        api[Mailbox API + RPC]
    end

    subgraph clients[Clients]
        webmail[webmail]
        imap[imap]
        mcp[mcp]
        py[clients/python]
    end

    cfIn --> store
    relay --> store
    api --> cfOut
    api --> relay
    webmail --> api
    imap --> api
    mcp --> api
    py --> api

See docs/architecture.md for the full visual map (inbound/outbound sequences, client doors). docs/CONTRACT.md is the authoritative data model; docs/INTEGRATION.md covers RPC + REST caller setup.

Email for humans, too: webmail and IMAP

Agents speak the structured API; humans get two doors onto the same mailbox, both clients of that API (never a second store):

  • Webmail (webmail/): a single self-contained page (vanilla HTML/CSS/JS, no build step) served by the worker at /webmail. Paste your API origin and token and browse the mailbox (list, read, threads, search); compose and reply unlock when the token is send-capable.
  • IMAP proxy (imap/): a small Twisted server that fronts the API as IMAP (read, the \Seen read/unread flag, and delete via a both-scoped token), so Thunderbird / mutt / iOS Mail can open the mailbox too.

Webmail adds compose/reply when the token is send-capable; IMAP adds read/unread (\Seen) and delete via a both-scoped token. Both are API clients, never a second store, and all sending funnels through the structured API.

An HTML email rendered in the webmail (safely, in a sandboxed iframe; no scripts, no remote trackers running):

The inbox list, a message read view, and search:

Read a message (trust verdict + attachments)Search the mailbox

The shots above use synthetic example data. See webmail/README.md for setup and the security model (BYO-token, token in sessionStorage only, no innerHTML of message content, locked-down CSP).

Quick start

Full steps in DEPLOY.md. In short:

cd inbound                                   # everything below runs from inbound/
npm ci                                       # installs the pinned wrangler; before any npx wrangler
npx wrangler login
npx wrangler d1 create postern --binding DB  # writes database_id into the existing DB block
npx wrangler r2 bucket create postern-attachments --binding ATTACHMENTS
# edit wrangler.jsonc: DEFAULT_FROM / ALLOWED_FROM_DOMAIN, and set
# "workers_dev": true (default is false: no reachable URL to smoke without it)
npx wrangler d1 migrations apply postern --remote
npm run deploy                               # first deploy creates the Worker
npx wrangler secret put POSTERN_API_TOKEN    # openssl rand -hex 32

Then route inbound mail to the Worker (Email Routing -> Routing Rules -> catch-all to the Worker), and run the smoke (see DEPLOY.md).

Client packages

After deploy, connect agents and scripts without cloning the repo. Both packages talk to the same token-gated mailbox API; they are clients of the store, not a second copy of it.

PackageRegistryInstallDocs
@skyphusion/postern-mcpnpmnpx -y @skyphusion/postern-mcpmcp/README.md
postern-clientPyPIpip install postern-clientclients/python/README.md

Configure with your deployed origin and token:

export POSTERN_API_URL=https://postern.<your-subdomain>.workers.dev
export POSTERN_API_TOKEN=<your token>

<your token> is the POSTERN_API_TOKEN you set at deploy (full access). For a read-only client, give it a POSTERN_API_TOKEN_READ value instead (see "Scoped tokens" in DEPLOY.md); for several people or agents, one per-identity token each (docs/SEND-IDENTITIES.md).

MCP (Cursor / Claude Code): add an MCP server entry with command: npx, args: ["-y", "@skyphusion/postern-mcp"], and the env vars above in env. Send tools (mailbox_send, mailbox_reply) register only when POSTERN_SEND_TOKEN is also set (opt-in; see docs/SEND-IDENTITIES.md).

Python CLI: postern ping, postern list, postern search, postern send, and the rest; see clients/python/README.md.

Release tags: postern-mcp-v* (npm CI) and GitHub Release v* matching clients/python/pyproject.toml (PyPI CI). See docs/INTEGRATION.md.

Auth

  • Same-account Workers: the MailboxService RPC entrypoint (or legacy EmailService alias), tokenless.
  • Everyone else: Authorization: Bearer <POSTERN_API_TOKEN>, constant-time compared.
  • Transports (/ingest, relay /dispatch) use a separate POSTERN_TRANSPORT_TOKEN, never the API token, so an API-token leak cannot inject mail and vice versa.

Relay (optional, bring-your-own-SMTP)

Go 1.25+ (what relay/go.mod requires; an older Go with the default GOTOOLCHAIN=auto downloads the pinned toolchain, one set to GOTOOLCHAIN=local refuses to build):

cd relay
go build -o skyphusion-email-relay .   # the name the unit in relay/systemd/ runs

Configure via env (no values are baked in; every variable is in relay/skyphusion-email-relay.env.example, systemd bring-up in relay/README.md). For local SMTP-only producers (cron, backups), set POSTERN_INGEST_URL (your worker's /ingest) and POSTERN_TRANSPORT_TOKEN: each message is stored in the mailbox as received, addressed by the envelope RCPT TO. DEFAULT_FROM / FROM_DOMAIN apply only to the legacy EMAIL_WORKER_URL path, which sends the message instead of storing it and rewrites an off-domain From to DEFAULT_FROM, keeping the original as Reply-To.

Conventions

No em/en-dashes in source, commits, or docs. Commits use conventional-commits (feat(inbound): ..., fix(relay): ...).

Merges to main require the ci, coverage, CodeQL, and code_quality checks green, with the branch up to date with main (#419). There are 0 required approvals and no CODEOWNERS: this is a solo-maintainer project, so review theater would be dishonest gatekeeping; the checks above are the actual gate.


Operating the reference deployment (skyphusion)

These notes are specific to the maintainers' own deployment and are not required to run Postern. A stranger should follow DEPLOY.md instead.

The reference instance sends from skyphusion.org (and .net), both onboarded to Email Sending, and deploys the worker to Cloudflare via CI on a pushed SemVer tag (v*); a bare merge to main runs CI only and ships nothing. No secrets live in the tree: POSTERN_API_TOKEN is a Worker secret, untouched by deploy.

Who this is for

Self-hosters, agent builders, and mail admins who want a mailbox they own on Cloudflare: one searchable store, one API for humans and agents, webmail and IMAP included.

Links

License

The server core in this repository is AGPL-3.0-only: Postern is software you self-host, and if you run it as a network service for others, you must offer them the complete corresponding source under the same license.

Three client-side components are MIT-licensed so they are trivial to embed in any codebase: the MCP server @skyphusion/postern-mcp, the Python client postern-client, and the IMAP proxy posternimap (imap/). Each ships its own LICENSE and metadata, which govern that component.

See NOTICE for the short version and PRIVACY.md for what self-hosting means for data (short version: Skyphusion Labs operates no mail service, so we hold none of your mail; the one instance it runs is the public demo mailbox, and PRIVACY.md says what that holds).

↑↓ navigate openesc close