{"id":51906284,"url":"https://github.com/skyphusion-labs/postern","last_synced_at":"2026-07-26T23:00:33.507Z","repository":{"id":365828860,"uuid":"1260681314","full_name":"skyphusion-labs/postern","owner":"skyphusion-labs","description":"Self-hostable email for humans and agents on Cloudflare: send, receive, search, MCP, IMAP, and webmail.","archived":false,"fork":false,"pushed_at":"2026-07-26T00:11:17.000Z","size":3884,"stargazers_count":4,"open_issues_count":7,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-26T00:11:42.287Z","etag":null,"topics":["agents","ai-agents","cloudflare-email","cloudflare-workers","d1","email","email-api","email-routing","imap","ldap","mcp","open-source","python","rag","self-hosted","smtp-server","vectorize","webmail"],"latest_commit_sha":null,"homepage":"https://github.com/skyphusion-labs/postern","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/skyphusion-labs.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":"NOTICE","maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-06-05T18:54:40.000Z","updated_at":"2026-07-26T00:03:47.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/skyphusion-labs/postern","commit_stats":null,"previous_names":["skyphusion-labs/postern"],"tags_count":12,"template":false,"template_full_name":null,"purl":"pkg:github/skyphusion-labs/postern","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skyphusion-labs%2Fpostern","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skyphusion-labs%2Fpostern/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skyphusion-labs%2Fpostern/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skyphusion-labs%2Fpostern/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/skyphusion-labs","download_url":"https://codeload.github.com/skyphusion-labs/postern/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skyphusion-labs%2Fpostern/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35930907,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-07-20T02:08:10.276Z","status":"online","status_checked_at":"2026-07-26T02:00:06.503Z","response_time":89,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agents","ai-agents","cloudflare-email","cloudflare-workers","d1","email","email-api","email-routing","imap","ldap","mcp","open-source","python","rag","self-hosted","smtp-server","vectorize","webmail"],"created_at":"2026-07-26T23:00:32.566Z","updated_at":"2026-07-26T23:00:33.475Z","avatar_url":"https://github.com/skyphusion-labs.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# postern\n\n[![Release](https://img.shields.io/github/v/release/skyphusion-labs/postern?label=release)](https://github.com/skyphusion-labs/postern/releases)\n[![CI](https://github.com/skyphusion-labs/postern/actions/workflows/ci.yml/badge.svg)](https://github.com/skyphusion-labs/postern/actions/workflows/ci.yml)\n[![npm @skyphusion/postern-mcp](https://img.shields.io/npm/v/@skyphusion/postern-mcp?label=postern-mcp)](https://www.npmjs.com/package/@skyphusion/postern-mcp)\n[![PyPI postern-client](https://img.shields.io/pypi/v/postern-client?label=postern-client)](https://pypi.org/project/postern-client/)\n\nEmail, for humans and agents. Postern is a self-hostable mailbox on Cloudflare:\nit sends and receives mail, stores every message in a searchable store, and\nexposes one structured API that agents and human clients (IMAP/webmail) both\nspeak. Cloudflare Email is the default transport on each seam, never a hard\ndependency.\n\nHumans and agents each **send as themselves**, under per-identity credentials\nrather than a shared mailbox (see [docs/SEND-IDENTITIES.md](docs/SEND-IDENTITIES.md)),\nand agents speak the mailbox natively over **MCP**\n([`@skyphusion/postern-mcp`](https://www.npmjs.com/package/@skyphusion/postern-mcp)) as\na first-class door, not an afterthought.\n\nFrom a fresh clone, with only your own domain, you can deploy it, send a\nmessage, and receive + read it back. Outbound send needs Cloudflare Email Sending\non the **Workers Paid** plan (USD 5/month); inbound via Email Routing is free-plan\nOK. See **[DEPLOY.md](DEPLOY.md)** for the\nclean-install quickstart and **[inbound/smoke.mjs](inbound/smoke.mjs)** for the\nscripted v1.0 acceptance smoke (issue #25).\n\n## Try it live\n\nLive read-only demo: [https://demo.posternonline.com](https://demo.posternonline.com).\nOpen `/webmail` and log in with the demo token below.\n\nMail **any** address `@posternonline.com` and watch it appear in the mailbox.\nThat is the product working: Email Routing -\u003e worker -\u003e D1 store.\n\nThe demo token is READ-scoped and public by design: send, reply, and delete are\nrefused by the API scope wall, and webmail hides compose for it. The demo mailbox\nis shared and public, so do not mail anything private.\n\nMCP config for agents:\n\n```\nPOSTERN_API_URL=https://demo.posternonline.com\nPOSTERN_API_TOKEN=a5fb2da1a13ab468adc1d147272126a4a977f3db44ace5c961ce87d26e0055d8\n```\n\nRun your own in about 10 minutes: follow [DEPLOY.md](DEPLOY.md).\n\nSix surfaces in one repo (one store, one API):\n\n| Path | Role |\n|------|------|\n| **`inbound/`** | Core Cloudflare Worker: ingest, store (D1 + R2 + Vectorize), mailbox API, send |\n| **`relay/`** | Optional Go SMTP daemon: loopback ingest, submission 587/465, BYO dispatch |\n| **`mcp/`** | MCP server for agents ([`@skyphusion/postern-mcp`](https://www.npmjs.com/package/@skyphusion/postern-mcp) on npm) |\n| **`webmail/`** | Browser UI embedded at `/webmail`: read, plus compose/reply when the token is send-capable |\n| **`imap/`** | IMAP proxy (read, `\\Seen`, delete) for Thunderbird / mutt / iOS Mail |\n| **`clients/python/`** | Stdlib HTTP client + CLI ([`postern-client`](https://pypi.org/project/postern-client/) on PyPI) |\n\n```mermaid\nflowchart TD\n    subgraph transports[Transport seams]\n        cfIn[CF Email Routing]\n        cfOut[CF Email Sending]\n        relay[postern-relay SMTP]\n    end\n\n    subgraph core[inbound Worker]\n        store[(D1 + R2 + Vectorize)]\n        api[Mailbox API + RPC]\n    end\n\n    subgraph clients[Clients]\n        webmail[webmail]\n        imap[imap]\n        mcp[mcp]\n        py[clients/python]\n    end\n\n    cfIn --\u003e store\n    relay --\u003e store\n    api --\u003e cfOut\n    api --\u003e relay\n    webmail --\u003e api\n    imap --\u003e api\n    mcp --\u003e api\n    py --\u003e api\n```\n\nSee [docs/architecture.md](docs/architecture.md) for the full visual map (inbound/outbound\nsequences, client doors). [docs/CONTRACT.md](docs/CONTRACT.md) is the authoritative data\nmodel; [docs/INTEGRATION.md](docs/INTEGRATION.md) covers RPC + REST caller setup.\n\n## Email for humans, too: webmail and IMAP\n\nAgents speak the structured API; humans get two doors onto the same mailbox, both\nclients of that API (never a second store):\n\n- **Webmail** (`webmail/`): a single self-contained page (vanilla HTML/CSS/JS, no\n  build step) served by the worker at **`/webmail`**. Paste your API origin and\n  token and browse the mailbox (list, read, threads, search); compose and reply\n  unlock when the token is send-capable.\n- **IMAP proxy** (`imap/`): a small Twisted server that fronts the API as IMAP\n  (read, the `\\Seen` read/unread flag, and delete via a `both`-scoped token), so\n  Thunderbird / mutt / iOS Mail can open the mailbox too.\n\nWebmail adds compose/reply when the token is send-capable; IMAP adds read/unread\n(`\\Seen`) and delete via a `both`-scoped token. Both are API clients, never a second\nstore, and all sending funnels through the structured API.\n\nAn HTML email rendered in the webmail (safely, in a sandboxed iframe; no scripts,\nno remote trackers running):\n\n![Postern webmail rendering an HTML email](webmail/screenshots/webmail-html-email.png)\n\nThe inbox list, a message read view, and search:\n\n![Postern webmail: inbox list, message read view, and thread](webmail/screenshots/webmail-inbox.png)\n\n| Read a message (trust verdict + attachments) | Search the mailbox |\n|---|---|\n| ![Reading a message](webmail/screenshots/webmail-message.png) | ![Searching](webmail/screenshots/webmail-search.png) |\n\n\u003e The shots above use synthetic example data. See [webmail/README.md](webmail/README.md)\n\u003e for setup and the security model (BYO-token, token in `sessionStorage` only, no\n\u003e `innerHTML` of message content, locked-down CSP).\n\n## Quick start\n\nFull steps in [DEPLOY.md](DEPLOY.md). In short:\n\n```bash\ncd inbound\nnpx wrangler d1 create postern              # paste database_id into wrangler.jsonc\nnpx wrangler r2 bucket create postern-attachments\n# edit wrangler.jsonc: database_id + DEFAULT_FROM / ALLOWED_FROM_DOMAIN, and set\n# \"workers_dev\": true (default is false: no reachable URL to smoke without it)\nnpx wrangler d1 execute postern --remote --file=schema.sql\nnpx wrangler secret put POSTERN_API_TOKEN   # openssl rand -hex 32\nnpm install \u0026\u0026 npm run deploy\n```\n\nThen route inbound mail to the Worker (Email Routing -\u003e Routing Rules -\u003e\ncatch-all to the Worker), and run the smoke (see DEPLOY.md).\n\n## Client packages\n\nAfter deploy, connect agents and scripts without cloning the repo. Both packages\ntalk to the same token-gated mailbox API; they are clients of the store, not a\nsecond copy of it.\n\n| Package | Registry | Install | Docs |\n|---------|----------|---------|------|\n| **@skyphusion/postern-mcp** | [npm](https://www.npmjs.com/package/@skyphusion/postern-mcp) | `npx -y @skyphusion/postern-mcp` | [mcp/README.md](mcp/README.md) |\n| **postern-client** | [PyPI](https://pypi.org/project/postern-client/) | `pip install postern-client` | [clients/python/README.md](clients/python/README.md) |\n\nConfigure with your deployed origin and token:\n\n```bash\nexport POSTERN_API_URL=https://postern.\u003cyour-account\u003e.workers.dev\nexport POSTERN_API_TOKEN=\u003cread-scoped token\u003e\n```\n\n**MCP (Cursor / Claude Code):** add an MCP server entry with `command: npx`,\n`args: [\"-y\", \"@skyphusion/postern-mcp\"]`, and the env vars above in `env`. Send tools\n(`mailbox_send`, `mailbox_reply`) register only when `POSTERN_SEND_TOKEN` is also\nset (opt-in; see [docs/SEND-IDENTITIES.md](docs/SEND-IDENTITIES.md)).\n\n**Python CLI:** `postern ping`, `postern list`, `postern search`, `postern send`,\nand the rest; see [clients/python/README.md](clients/python/README.md).\n\nRelease tags: `postern-mcp-v*` (npm CI) and GitHub Release `v*` matching\n`clients/python/pyproject.toml` (PyPI CI). See [docs/INTEGRATION.md](docs/INTEGRATION.md).\n\n## Auth\n\n- **Same-account Workers:** the `MailboxService` RPC entrypoint (or legacy `EmailService` alias), tokenless.\n- **Everyone else:** `Authorization: Bearer \u003cPOSTERN_API_TOKEN\u003e`, constant-time\n  compared.\n- Transports (`/ingest`, relay `/dispatch`) use a **separate**\n  `POSTERN_TRANSPORT_TOKEN`, never the API token, so an API-token leak cannot\n  inject mail and vice versa.\n\n## Relay (optional, bring-your-own-SMTP)\n\nGo \u003e= 1.22:\n\n```bash\ncd relay\ngo mod tidy\ngo build -o postern-relay .\n```\n\nConfigure via env (no values are baked in): `POSTERN_INGEST_URL` (or the legacy\n`EMAIL_WORKER_URL`), `POSTERN_TRANSPORT_TOKEN`, and `DEFAULT_FROM` / `FROM_DOMAIN`\nfor off-domain sender rewriting. The relay uses the envelope `RCPT TO` for\nrecipients; if a message's `From` is off the allowed domain it is rewritten to\n`DEFAULT_FROM` with the original preserved as `Reply-To`.\n\n## Conventions\n\nNo em/en-dashes in source, commits, or docs. Commits use conventional-commits\n(`feat(inbound): ...`, `fix(relay): ...`).\n\nMerges to `main` require the `ci`, `coverage`, CodeQL, and `code_quality` checks\ngreen, with the branch up to date with `main` (#419). There are 0 required\napprovals and no CODEOWNERS: this is a solo-maintainer project, so review theater\nwould be dishonest gatekeeping; the checks above are the actual gate.\n\n---\n\n## Operating the reference deployment (skyphusion)\n\nThese notes are specific to the maintainers' own deployment and are **not**\nrequired to run Postern. A stranger should follow [DEPLOY.md](DEPLOY.md) instead.\n\nThe reference instance sends from `skyphusion.org` (and `.net`), both onboarded\nto Email Sending, and deploys the worker to Cloudflare via CI on a pushed\nSemVer tag (`v*`); a bare merge to `main` runs CI only and ships nothing. No\nsecrets live in the tree: `POSTERN_API_TOKEN` is a Worker secret, untouched by\ndeploy.\n\n## Who this is for\n\nSelf-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.\n\n## Links\n\n- **Quickstart:** [DEPLOY.md](DEPLOY.md)\n- **Operations (backup, restore, monitoring):** [docs/OPERATIONS.md](docs/OPERATIONS.md)\n- **Skyphusion Labs:** https://skyphusion.org · **Org:** https://github.com/skyphusion-labs\n- **Related:** [prism](https://github.com/skyphusion-labs/prism) (AI playground), [search-mcp](https://github.com/skyphusion-labs/search-mcp)\n\n## License\n\nThe server core in this repository is [AGPL-3.0-only](LICENSE): Postern is software you self-host, and\nif you run it as a network service for others, you must offer them the complete corresponding source\nunder the same license.\n\nThe two published client integrations are MIT-licensed so they are trivial to embed in any codebase:\nthe MCP server [`@skyphusion/postern-mcp`](mcp/LICENSE) and the Python client\n[`postern-client`](clients/python/LICENSE). Each package ships its own `LICENSE` and metadata, which\ngovern that package.\n\nSee [NOTICE](NOTICE) for the short version and [PRIVACY.md](PRIVACY.md) for what self-hosting means for\ndata (short version: Skyphusion Labs operates nothing, so we hold none of your mail).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fskyphusion-labs%2Fpostern","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fskyphusion-labs%2Fpostern","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fskyphusion-labs%2Fpostern/lists"}