The project's README from github.com/boringcomputers/goshen-email. As one markdown file: README.md.
Email inboxes for AI agents. Each agent gets a real address, threads, attachments, and an API key that reaches only its own mail. People see the same mail in a dashboard, where they can reply, check delivery, and release quarantined messages.
Hosted service · Documentation · API reference · Contributing · License

flowchart LR
agent["Agent or app"] -->|"API key or MCP"| api["API Worker"]
person["Person"] --> dashboard["Dashboard"]
dashboard --> api
api --> db[("PostgreSQL through Hyperdrive")]
api --> r2[("R2: raw mail and attachments")]
api <-->|"managed domains"| cloudflare["Cloudflare Email Routing and Sending"]
api <-->|"custom domains"| gateway["SMTP gateway: Postfix, Rspamd, ClamAV"]
| Path | Contents |
|---|---|
apps/email |
The API, a Cloudflare Worker. It stores mailboxes, sends and receives mail, and serves the REST API, the older RPC API, and hosted MCP. |
apps/email-dashboard |
The dashboard, homepage, and docs site. It runs as a Cloudflare Worker, or as a Node server for local or VPS hosting. |
apps/email-web |
A redesigned dashboard on Pluto's design system. It calls the dashboard's /api routes, holds no credentials, and is not deployed yet. |
apps/email-gateway |
The SMTP gateway for domains at any DNS provider, packaged with Docker. |
packages/email-client |
Private API client shared by the CLI and MCP server. Not published. |
packages/email-cli |
Command-line client that prints JSON. |
packages/email-mcp |
MCP server over stdio. |
ops/email |
The gateway's Docker Compose, Caddy, and Rspamd configuration, plus reviewed SQL for the hosted database. |
ops/autumn |
Plan definitions for hosted billing. |
docs |
Operator guides and the OpenAPI document. |
You need Node.js 24 and pnpm 10.15.1. The local fixture runs the API and the dashboard against an in-memory PostgreSQL database (PGlite). Routing, domain checks, sending, and file storage are test doubles, so you need no accounts or credentials, and no mail leaves your machine.
git clone https://github.com/boringcomputers/goshen-email.git
cd goshen-email
pnpm install --frozen-lockfile
pnpm build
pnpm --filter @bezalel/email exec tsx test/dashboard-fixture.ts
Open http://127.0.0.1:3038/app and sign in with the fixture password,
fixture-dashboard-password-fixture-dashboard-password-. Create an inbox with
the username hello, then deliver a message to it from a second terminal:
curl -X POST http://127.0.0.1:3039/receive \
-H 'Content-Type: application/json' \
-d '{"inboxId":"[email protected]","subject":"Hello from the fixture"}'
The message appears in the inbox. Mail you send or reply with from the
dashboard stays inside the fixture; curl http://127.0.0.1:3039/sends lists it.
The fixture listens on loopback only. Don't deploy it.
Goshen Email runs on Cloudflare. A deployment needs:
apps/email/wrangler.jsonc and apps/email-dashboard/wrangler.jsonc contain
the hosted service's account ID, resource IDs, routes, and domains. Replace
them with your own before you deploy. The Worker guide
lists the API Worker's settings. In the dashboard's file:
MAIL_API service binding at your API Worker's name, and set
MAIL_WORKER_URL to that Worker's HTTPS origin.DASHBOARD_PUBLIC_URL and routes to your dashboard's domain.NATIVE_MAIL_API service binding and the NATIVE_MAIL_*
variables. They connect the hosted dashboard to a separate Bezalel
deployment.apps/email/.env as DATABASE_URL and run pnpm migrate. It applies the
full schema and is safe to rerun on upgrades.pnpm deploy:worker, then pnpm deploy:dashboard. Deploy the
API first, because a newer dashboard calls operations an older API rejects.Triage needs a TypeSafe API key.
Billing uses Autumn and Stripe; without AUTUMN_SECRET_KEY
the API runs unmetered.
The CI workflow in this repository deploys the hosted service when a commit
lands on main. In a fork, remove the deploy job from
.github/workflows/ci.yml or point it at your own Cloudflare secrets.
cp apps/email-dashboard/.env.example apps/email-dashboard/.env
openssl rand -hex 32
Save the generated value as DASHBOARD_PASSWORD. Set MAIL_WORKER_URL to your
API Worker's HTTPS origin and MAIL_API_TOKEN to its platform token. Don't
reuse the token as the password.
pnpm build
pnpm start
Open http://127.0.0.1:3031/app. The Worker token stays on the dashboard server. Sessions expire after eight hours, signing out revokes them, and a restart signs everyone out.
To serve it remotely, put it behind an HTTPS reverse proxy that preserves the
Host header, and set DASHBOARD_PUBLIC_URL to the exact public origin. Set
HOST=0.0.0.0 if the proxy runs outside the dashboard's container. List the
proxy's IP addresses in DASHBOARD_TRUSTED_PROXY_IPS and have the proxy
overwrite X-Real-IP with the client's IP, so login throttling applies per
client. Requests from a trusted proxy without a valid X-Real-IP can't sign in.
Create a key under API keys in the dashboard, then:
export GOSHENEMAIL_BASE_URL="https://api.goshenemail.com" # or your own API Worker
export GOSHENEMAIL_API_KEY="bze_..."
curl "$GOSHENEMAIL_BASE_URL/v1/inboxes" \
-H "Authorization: Bearer $GOSHENEMAIL_API_KEY" \
-H 'Content-Type: application/json' \
-d '{"username":"research","displayName":"Research agent"}'
The quickstart continues with sending and reading replies. The developer guide covers the CLI and MCP. The CLI and the stdio MCP server aren't on npm yet, so build them from this checkout.
The project started inside Bezalel. The internal workspace packages
(@bezalel/email, @bezalel/email-dashboard, @bezalel/email-gateway) and the
account key prefix bze_ keep names from that time.
| Command | What it does |
|---|---|
pnpm build |
Builds every package. The Workers build with Wrangler dry runs, so nothing deploys. |
pnpm check |
Type-checks the workspace and fails if generated API files or docs are stale. |
pnpm test |
Runs every package's tests on PGlite with test doubles for Cloudflare, R2, and SMTP. |
pnpm test:postgres |
Runs the API tests through the production PostgreSQL driver. Needs TEST_DATABASE_URL; see the database guide. |
pnpm source:check |
Checks files extracted from Bezalel against the hashes in SOURCE.json. |
pnpm api:generate |
Regenerates the OpenAPI document, client types, and CLI and MCP schemas after a contract change. |
pnpm docs:generate |
Rebuilds the docs site from apps/email-dashboard/docs/ and the OpenAPI document. |
pnpm dev |
Starts the Node dashboard with file watching on port 3031. |
pnpm dev:worker |
Starts the API Worker locally on port 8788. It needs a local PostgreSQL database. |
Local tests can't prove live delivery, DNS, Cloudflare routing, Hyperdrive, R2, queues, or cron. The SMTP runbook's deployment canary checks those against a real deployment.
The public docs at goshenemail.com/docs cover the API from an agent developer's side. The guides here cover running it.
| Guide | Covers |
|---|---|
| Worker setup | Cloudflare setup, secrets, delivery tracking, suppression, quarantine, and failure handling |
| Database | PostgreSQL, Hyperdrive, migrations, and local databases |
| SMTP gateway | Customer domains, Postfix, Rspamd, ClamAV, TLS, and deployment canaries |
| Accounts | Passwordless sign-up and sign-in |
| Cloudflare Access | Optional invitation-only sign-in |
| Developer tools | API keys, REST, CLI, and MCP |
| RPC API | The older RPC interface, mailbox provisioning, and webhooks |
| Inbox setup | The first-inbox flow for new accounts |
| Settings | Organization, profile, and notification settings |
| Triage | Categories, needs-reply, urgency, and what data leaves the Worker |
| Pricing | Hosted plans and Autumn billing |
| Dashboard UX and design system | Dashboard behavior and visual tokens |
| Web app | The Pluto-design dashboard: local runs, checks, and rollout |
The native-*, verification, and evidence files in docs/ record the
hosted deployment's releases and the original extraction.
Goshen Email began as the email service inside Bezalel, a private Boring Computers project. SOURCE.md records the extracted revision and how fixes move between the two projects.
Read CONTRIBUTING.md before opening a pull request. Report vulnerabilities privately as described in SECURITY.md, never in a public issue. Everyone taking part follows the code of conduct.
Goshen Email uses the Functional Source License 1.1 with an Apache 2.0 future license, FSL-1.1-ALv2. You can use, modify, and self-host it for any purpose, including inside a business. You can't offer it, or anything built from it, in a commercial product or service that competes with Goshen Email. Each release becomes available under Apache 2.0 two years after we publish it.
We published earlier commits under AGPL-3.0-only, and copies taken from them keep that license.