← All documentation

Provider inbound MX

Provider inbound MX

Coldfeet can terminate inbound SMTP itself (coldfeet_mx) or let an ESP (Amazon SES, Mailgun, SendGrid, Resend, Postmark, MailerSend) accept mail and replay it into the pipeline over an authenticated webhook (provider_mx).

Pure MX mode

Functionality only requires the domain’s MX change. Legitimate senders deliver to the ESP; Coldfeet filters/quarantines and relays clean mail to the final destination (Microsoft 365, Google Workspace, Exchange, etc.). Restricting the backend connector / firewall to gateway IPs is optional hardening — not a prerequisite.

Internet Sender
      │
      ▼
 MX Record  ──────────────►  ESP (SES / SendGrid / Mailgun / Resend / Postmark / MailerSend)
      │
      ▼
 Authenticated webhook  ──►  Coldfeet (scan + quarantine)
      │
      ▼
 Relay clean mail  ───────►  Final mailboxes

Why

Provider MX keeps Coldfeet’s IP off public DNS. The ESP absorbs abuse and connection load; Coldfeet still scans, applies policy, and delivers to the tenant’s smart host.

What you lose

Because the ESP has already accepted the message before Coldfeet sees it:

Unknown recipients on SendGrid/Mailgun/Resend/Postmark/MailerSend are dropped and recorded in Rejections (source=webhook). We never generate a bounce to a forged sender (backscatter). On SES, mail for domains that have not been promoted is rejected at the SES edge with 550 because receipt-rule recipients are scoped to promoted FQDNs only.

Rspamd still scores RBL/reputation against the recovered origin IP from the Received: chain.

Hard precondition

provider_mx cannot be enabled without a configured delivery destination (relay host/port, or local mailbox hosting). Without that, scanned mail would MX-lookup the ESP and loop back into the webhook ingest.

Outbound ESP relay is also blocked when any recipient domain is hosted here with provider_mx, for the same reason.

Webhook ingest also refuses recipients whose domain is still in coldfeet_mx mode, even if the signature verifies.

Size limits

Platform ceiling is 35 MB (nginx client_max_body_size 40m, Nest parser scoped to /api/v1/public/inbound and /api/v1/public/relay-events). Larger messages are rejected with the provider’s stop-retry status.

MTA-STS cutover

MTA-STS and TLS-RPT are suppressed for provider-MX domains. A Coldfeet policy naming our mail host would cause enforcing senders to refuse delivery to the ESP.

If the domain already publishes _mta-sts naming Coldfeet:

  1. Remove the _mta-sts TXT record
  2. Wait out the policy max_age (default 7 days)
  3. Then change MX to the ESP hosts shown in the onboarding wizard

Per-provider notes

ProviderMXOutbound SMTPSizeAuthstopRetryStatusInbound automation
Mailgunmxa.mailgun.org / mxb.mailgun.orgsmtp.mailgun.org / smtp.eu.mailgun.org35 MBHMAC over timestamp+token406Routes created via API
SESinbound-smtp.<region>.amazonaws.comemail-smtp.<region>.amazonaws.com35 MBSNS signature + topic ARN / bucket pin200S3 + SNS + receipt rule provisioned from IAM keys
SendGridmx.sendgrid.netsmtp.sendgrid.net35 MBECDSA via security policy; send_raw=true200Inbound Parse + security policy via API
Resendinbound-smtp.<region>.amazonaws.comsmtp.resend.com (user resend)35 MBSvix HMAC-SHA256 on ${id}.${timestamp}.${rawBody}406email.received webhook + Receiving API fetch
Postmarkinbound.postmarkapp.comsmtp.postmarkapp.com35 MBURL token + Postmark webhook IP allowlist (no HMAC)406Server InboundHookUrl + InboundDomain via API
MailerSendinbound.mailersend.netsmtp.mailersend.net35 MBHMAC-SHA256 Signature header over raw body406Inbound route webhook via API

Webhook endpoints:

Postmark inbound and bounce URLs include ?token=<high-entropy>; both token and source IP (Postmark webhook IPs below) must match.

PUBLIC_API_URL must be set (install sets it to WEB_URL/api). Coldfeet normalizes /api/api/v1 when registering notify URLs so they match the reverse-proxy routes.

Resend

Postmark

MailerSend

SES automated setup

Relay accounts need only three credentials: AWS access key ID, secret access key, and region. Coldfeet:

  1. Derives the SES SMTP username (access key ID) and password (AWS-documented HMAC)
  2. Optionally mints a least-privilege IAM user under /coldfeet/ when the pasted key can iam:CreateUser
  3. On Provision inbound, creates/reuses:
    • S3 bucket with public-access block, 7-day lifecycle on inbound/ (transient spool — not the quarantine), and a bucket policy allowing ses.amazonaws.com s3:PutObject
    • SNS topic + policy + HTTPS subscription to the notify URL (the topic ARN is recorded first, since ingest rejects notifications from any other topic — including the subscription confirmation)
    • Receipt rule set + rule (TlsPolicy: Require, S3 + SNS actions)
  4. On domain promote to provider_mx, adds the FQDN to the rule’s Recipients (spilling to a second rule past 100 recipients)
  5. On demote/remove, removes the recipient

Required IAM actions are available from Admin → Workers → Relay accounts → AWS policy JSON (GET /api/v1/admin/egress/relay-accounts/aws/policy).

Sandbox is detected during preflight/test — production accounts are assumed. Quota (Max24HourSend / MaxSendRate) is stored and used to prefill caps and pace sends.

Admin flow

  1. Admin → Workers → Relay accounts → Add relay account (full-page form)
  2. Choose provider; for SES paste access key + secret + region and Verify
  3. Optionally mint a least-privilege IAM user, then save
  4. Enable inbound MX; for SES click Preview plan then Provision inbound
  5. On the domain, configure a delivery destination first
  6. Switch Inbound MX to Provider MX and select the account
  7. Publish the wizard’s MX (and relay identity) records

Multi-account

Multiple relay accounts can coexist. Outbound selection is sticky per domain with priority failover and hourly/daily (plus provider-quota) headroom checks.