Email Processing Pipeline
When an email arrives at a Serverless Inbox domain it passes through a chain of AWS services before it lands in a user’s mailbox. This page explains that chain and the design choices behind it.
Scope: This page covers inbound email receiving only. Outbound sending and its feedback loop (bounces, complaints, delivery notifications) are covered separately.
Infrastructure overview
Section titled “Infrastructure overview”The receiving path is built on three AWS services acting as a reliable, ordered pipeline: SES → SNS → SQS → Lambda.
graph LR Internet["Internet (SMTP)"] SES["Amazon SES<br/>Receipt Rules"] S3_raw["S3<br/>incoming-emails/"] SNS["SNS Topic"] SQS["SQS Queue"] DLQ["SQS DLQ<br/>(14 day retention)"] Lambda["Lambda<br/>email-processor"]
Internet -->|"SMTP (TLS required)"| SES SES -->|"1. Store raw MIME"| S3_raw SES -->|"2. Publish notification"| SNS SNS --> SQS SQS -->|"max 3 retries"| DLQ SQS -->|"batch size 10"| LambdaSES first writes the full email to S3, then publishes a lightweight notification — so the raw message is always durably stored before any processing begins.
Why SNS between SES and SQS?
Section titled “Why SNS between SES and SQS?”SES receipt rules cannot publish directly to SQS. SNS provides the fan-out point: the same notification can reach multiple subscribers without changing the SES rule. In practice there is one subscriber today, but the topology is ready to add more (for example, a search indexer) without touching SES configuration.
Reliability
Section titled “Reliability”| Concern | Mechanism |
|---|---|
| Transient Lambda failures | SQS visibility timeout causes automatic redelivery |
| Persistent failures | Dead-letter queue after 3 attempts; 14-day retention |
| Partial batch failures | Only failed messages in a batch are retried, not the whole batch |
| Data loss on cold start | Raw MIME is in S3 before Lambda is ever invoked |
What the processor does
Section titled “What the processor does”The Lambda receives a batch of notifications and processes each one independently:
graph TD A["Notification arrives"] B["Identify recipients"] C["Download MIME from S3"] D["For each recipient:<br/>route to inbox or junk"] E["Store email<br/>(S3 + DynamoDB)"] F["Notify connected clients<br/>(WebSocket push)"]
A --> B --> C --> D --> E --> FIdentifying recipients
Section titled “Identifying recipients”SES delivers email to an entire domain — it does not know which tenant or account owns a given address. The processor resolves each destination address by looking it up in the database.
A few things happen during this lookup:
- Plus-addressing —
user+tag@domain.comis treated asuser@domain.comfor delivery purposes. - Alias resolution — every address is an alias pointing to an account. Inactive aliases are silently skipped.
- Deduplication — if multiple envelope addresses resolve to the same account, only one copy of the email is stored.
If an address cannot be resolved, that recipient is skipped. No delivery failure is generated by the processor itself; SES may generate a bounce at the SMTP layer.
Routing to inbox or junk
Section titled “Routing to inbox or junk”Once recipients are resolved, SES’s spam verdict determines where the email lands:
- Spam detected → placed in the junk mailbox (or inbox with the
$junkkeyword if no junk mailbox is configured), per RFC 8621 §9. - Everything else → inbox.
DKIM, SPF, and DMARC results are recorded on the email and visible in the UI, but they do not currently affect routing.
Storing the email
Section titled “Storing the email”Each recipient gets their own copy of the email stored in per-account S3 storage, plus a record in
DynamoDB. Mailbox unread counters are updated atomically. After all recipients are processed, the
original raw object in the incoming-emails/ S3 prefix is deleted as a best-effort cleanup.
Notifying connected clients
Section titled “Notifying connected clients”At the end of the batch, any accumulated change notifications are pushed to connected JMAP clients via WebSocket. This is what makes new mail appear in the UI without a page refresh.
Related topics
Section titled “Related topics”- System Architecture — how the email-processor fits into the broader platform.
- Bounce & Complaint Handling — what happens after an outbound email is sent.
- Multi-Tenancy Model — how tenant isolation works and why address lookups must be cross-tenant.
- Security Model — DKIM signing and trust boundaries.