Mail Gateway
The Mail Gateway is the outbound transport worker for prepared email messages. It owns delivery attempts, provider communication, rate limiting, and result publication. It does not render templates, resolve variables, load attachments, or write delivery rows.
The service does not expose HTTP or TCP endpoints. Both directions use durable BullMQ queues in the same Valkey deployment as the backend.
Queue Endpoints
| Queue | Job name | Producer | Consumer | Purpose |
|---|---|---|---|---|
mail | send | Backend | Mail Gateway | Deliver one fully prepared message |
mail-results | result | Mail Gateway | Backend | Persist the final delivery outcome |
The backend waits for the result queue transaction to be acknowledged before considering tracked delivery persistence complete. If persistence is unavailable, the Mail Gateway retains the recorded transport result and retries publication without sending the message again.
Functionality
- Applies the configured sends-per-second limit and optional daily quota.
- Uses stable job and delivery identifiers for tracked mail.
- Records attempt progress before contacting the provider.
- Retries provider failures with exponential delay, up to five attempts.
- Reports
SENT,FAILED, orUNCERTAINoutcomes. - Marks an interrupted provider call as
UNCERTAINinstead of assuming that it failed. - Enforces the configured total attachment byte limit.
- Publishes final results until the backend acknowledges persistence.
Delivery Providers
| Environment | Provider | Connection |
|---|---|---|
| Production | AWS SES v2 | AWS SDK through Nodemailer |
| Development | Inbucket | SMTP, normally on port 2500 |
Provider SDK retries are disabled because durable retry ownership belongs to the Mail Gateway. This avoids a hidden provider retry sending a message twice.
Backend Responsibilities
Before enqueueing a mail request, the backend:
- Resolves template variables.
- Renders the subject, HTML, and plain-text content.
- Loads and serializes attachments.
- Adds tracking data when required.
- Creates or locks the tracked delivery record.
The queue message therefore contains a transport-ready snapshot. The Mail Gateway can deliver it without access to Postgres or the CMS.
Local Start
Start Valkey and Inbucket first, then run:
pnpm mail-gateway:devUse packages/mail-gateway/.env.example as the local environment template.