diff options
| -rw-r--r-- | README.md | 61 |
1 files changed, 61 insertions, 0 deletions
diff --git a/README.md b/README.md new file mode 100644 index 0000000..0aa78f7 --- /dev/null +++ b/README.md @@ -0,0 +1,61 @@ +# ingest + +Recieve HTTP-enveloped mail and route it as LMTP to local Unix socket. + +Cloudflare Email Workers can only reach a cf tunnel over HTTP, so the +message has to be wrapped in an HTTP request to get through. On the +server side, `ingest` unwraps it and hands it to an LMTP-capable MDA +such as Dovecot. + +## Quickstart + + cat <<EOF >/etc/ingest.env + INGEST_TOKEN=$(uuidgen) + LMTP_SOCK=/var/run/dovecot/lmtp + LISTEN_ADDR=127.0.0.1:1234 + RUST_LOG=info + EOF + + cargo build --release + cp target/release/ingest /usr/local/bin/ + cp mail-ingest.service /etc/systemd/system/ + systemctl daemon-reload + systemctl enable --now mail-ingest.service + +## Example client +```ts +export default { + async email(message, env, ctx) { + const res = await fetch("https://example.com/inject", { + method: "POST", + headers: { + "authorization": `Bearer ${env.INGEST_TOKEN}`, + "content-type": "message/rfc822", + "x-envelope-from": message.from, + "x-envelope-to": message.to, + }, + body: message.raw, + }); + + if (!res.ok) { + const detail = await res.text().catch(() => ""); + throw new Error(`ingest ${res.status} ${detail.slice(0, 200)}`); + } + }, +}; +``` + +## Response codes + +| Status | Meaning | Client action | +|---|---|---| +| `204 No Content` | Message accepted by the MDA | Done | +| `401 Unauthorized` | Missing or wrong bearer token | Fix config; do not retry | +| `413 Payload Too Large` | Body exceeds 26 MiB | Do not retry | +| `422 Unprocessable Entity` | LMTP replied with a permanent error (5xx), e.g. unknown recipient | Do not retry; bounce | +| `503 Service Unavailable` | LMTP unreachable or replied with a temporary error (4xx) | Retry later | + +`ingest` itself never retries. When a Worker throws on a non-2xx +response, Cloudflare returns a temporary error to the sending MTA, +which keeps the message in the sender's queue for a few days. (This +behaviour is not documented by Cloudflare; verified against Gmail.) |
