Skip to content

Inbound Delivery

Ra's al Ghul edited this page Aug 9, 2026 · 1 revision

Inbound Delivery

End-to-end flow

Cloudflare Email Routing invokes the Worker's email() handler with the raw RFC 822 message and envelope addresses. The handler:

  1. Captures the envelope sender, recipient, original sender IP when available, and receipt time.
  2. Generates a random 256-bit AES data key and 96-bit GCM IV.
  3. Encrypts the JSON envelope using AES-256-GCM.
  4. Wraps the data key with MAILBRIDGE_PUBLIC_KEY_PEM using RSA-OAEP SHA-256.
  5. Writes only the encrypted envelope to the MAIL_STORE R2 bucket.
  6. Sends { objectKey, encryptionVersion } to MAIL_QUEUE.

The Worker logs object identifiers and sizes, not raw mail contents.

Queue consumer behavior

The Worker's queue() handler processes each object reference independently:

  1. Load the encrypted object from R2.
  2. POST { encryptedPayload } to NODE_APP_URL with X-Webhook-Secret.
  3. On a successful HTTP response, delete the R2 object and acknowledge the Queue message.
  4. Retry HTTP 429, 5xx, fetch failures, and R2 deletion failures with backoff.
  5. Treat other 4xx responses as permanent, delete the stored object, and acknowledge the Queue message.

If an object is already missing, the message is acknowledged because there is nothing left to deliver.

Webhook intake and decryption

Mailbridge exposes:

POST /api/webhook/email

The endpoint requires the shared X-Webhook-Secret, rejects malformed mail fields, and decrypts v1 envelopes with the private key at MAILBRIDGE_PRIVATE_KEY_PATH. Supported encryption metadata is:

version:   v1
algorithm: RSA-OAEP-256+A256GCM

Unsupported versions, algorithms, authentication failures, and malformed plaintext are rejected. The decrypted original sender IP is used for reputation checks; the Cloudflare request IP is not mistaken for the SMTP sender.

Filtering and local delivery

After decryption Mailbridge:

  1. Checks optional Spamhaus reputation data.
  2. Runs SpamAssassin locally or through Postmark SpamCheck.
  3. Optionally invokes AI for questionable messages or as a filter fallback.
  4. Detects the GTUBE test signature.
  5. Builds Exchange-friendly spam headers and optionally tags the subject.
  6. Delivers the raw message through Nodemailer to LOCAL_MAIL_HOST:LOCAL_MAIL_PORT.

For TLS configuration and filter decisions, see Configuration Reference and Spam and Reputation Filtering.

Failure semantics

  • A local SMTP 5xx response is permanent and returned to the Worker.
  • Timeouts, connection failures, and temporary SMTP failures are stored in the encrypted local retry queue.
  • If all spam filters are unavailable, Mailbridge returns 503 unless SPAMC_FAIL_OPEN=true was explicitly configured.
  • Queue messages exceeding QUEUE_MAX_ATTEMPTS are removed after an audit event records the drop.

Clone this wiki locally