> ## Documentation Index
> Fetch the complete documentation index at: https://docs.xem.email/llms.txt
> Use this file to discover all available pages before exploring further.

# Cloudflare mailbox Worker

This Worker gives one Xem installation a customer-owned inbound mailbox. Cloudflare Email Routing invokes the Worker, private R2 stores raw MIME, bodies, and attachments, and D1 stores the mailbox index, UID state, flags, and attachment metadata. Xem's backend calls the signed Worker API. The browser never receives the shared secret or a Cloudflare API token.

The implementation has a 10 MiB raw-message limit and accepts at most 32 attachments per message. It does not automatically delete received, pending, or quarantined mail. One deployment serves the exact address configured in `MAILBOX_ADDRESS`; use a separate Worker, D1 database, R2 bucket, and secret for each mailbox that needs isolation.

## Account setup

The step-by-step guide, including what is free on Cloudflare and what is not, is [Receive mail on your domain with Cloudflare](https://docs.xem.email/guides/cloudflare-inbox.md). In short:

[![Deploy to Cloudflare](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/mailxem/mail/tree/undefined/devops/cloudflare-mailbox)

The button creates the D1 database and R2 bucket, asks for `MAILBOX_ADDRESS` and `MAILBOX_API_SECRET` (declared in `.dev.vars.example`), and runs `pnpm run deploy`, which deploys and then applies the D1 migration.

Without the button, install Node.js 24 and pnpm 10.7.1, then from this directory:

1. `pnpm install --frozen-lockfile` and `pnpm wrangler login`.
2. `cp .dev.vars.example .dev.vars`, then set `MAILBOX_ADDRESS` to the complete mailbox address and `MAILBOX_API_SECRET` to a unique random value of 32–4096 characters (`openssl rand -hex 32`).
3. `pnpm wrangler deploy --secrets-file .dev.vars`. Wrangler creates the `xem-cloudflare-mailbox` D1 database and R2 bucket if they don't exist and writes the database ID back to `wrangler.jsonc`.
4. `pnpm wrangler d1 migrations apply MAILBOX_DB --remote`.
5. Create a Cloudflare Email Routing rule for that exact address whose action sends to this Worker. `MAILBOX_ADDRESS` must match it exactly, ignoring case.
6. In **Settings → SMTP senders → Connect Cloudflare inbox**, enter the Worker URL, exact mailbox address, and the same shared secret. Xem confirms the signed `/v1/mailbox/identity` response before saving. It returns protocol `version: 1` only after the D1 migration is available.

Do not make the R2 bucket public. Xem does not need an R2 or D1 API token; all content access stays behind the signed Worker API.

For local development, `.dev.vars` also feeds `pnpm wrangler dev`. Apply the migration locally with `pnpm wrangler d1 migrations apply MAILBOX_DB --local`, then deliver a test message with `curl -X POST "http://localhost:8787/cdn-cgi/handler/email?from=a@example.net&to=<MAILBOX_ADDRESS>" --data-binary @message.eml`.

## Storage and recovery

Ingestion first writes `pending/<id>.eml`, then parses MIME and writes deterministic message objects before atomically committing the D1 message and attachment rows. It deletes the pending object only after that commit. A per-minute scheduled handler retries one pending object per run and advances a private cursor so a transient failure cannot block the backlog; transient failures remain pending without an expiry. Malformed MIME and messages with more than 32 attachments move to `quarantine/<id>.eml` for operator inspection and manual recovery; they are never automatically deleted.

Inspect the `pending/` and `quarantine/` prefixes through the authenticated Cloudflare dashboard or an account-scoped S3-compatible R2 client. Do not make the bucket public to inspect it. After correcting a parser or operational problem, copy the quarantined raw object and its `from`, `to`, and `receivedAt` custom metadata back to its original `pending/<id>.eml` key and let the scheduled handler retry it. Byte-identical messages with the same normalized envelope sender and recipient produce the same deterministic ID and deduplicate; distinct bytes remain separate messages.

The mailbox exposes only `INBOX`, supports metadata search across subject, sender, recipients, and RFC Message-ID, and permits only independent read (`\\Seen`) and starred (`\\Flagged`) changes. It has no expunge or delete API.

## Validation and limits

```sh theme={"dark"}
pnpm install --frozen-lockfile
pnpm run types
pnpm test
pnpm run check
pnpm run deploy:check
```

Cloudflare bills and limits Workers, Email Routing, R2, and D1 separately. Check the current account plan and product documentation before production use. D1 databases are documented at 500 MB each on the Free plan and 10 GB each on paid plans; Worker CPU and request quotas also constrain parsing and API access.

This directory is a local implementation. Its tests and Wrangler dry run do not prove a live Cloudflare deployment, Email Routing delivery, DNS readiness, or receipt from external providers such as Google.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.