Skip to content

feat: ingest Amazon SES bounces and complaints via SNS - #58

Merged
rajat1saxena merged 4 commits into
mainfrom
feature/ses-feedback
Oct 18, 2026
Merged

rajat1saxena merged 4 commits into
mainfrom
feature/ses-feedback

Conversation

@clbotdev

@clbotdev clbotdev commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Closes #33

Summary

  • SNS verification (adapters/sns.ts): signature versions 1 (SHA1) and 2 (SHA256), exact sns.<region>.amazonaws.com host and /SimpleNotificationService-*.pem path checks, redirect: "error", 5 s timeout, certificate cache. A failed certificate fetch returns 503 so SNS retries.
  • SES adapter (adapters/ses.ts): the topic ARN is checked before any fetch. The subscription is confirmed automatically, via a validated SubscribeURL. Event mapping is a pure function. mail.headers is stripped before storage, and Delivery payloads are dropped once processed. Raw events are kept only for Bounce, Complaint, Reject, DeliveryDelay and RenderingFailure.
  • Route hooks: optional per-adapter body cap (512 KB for SES → 413), control-message handling and storage redaction. The other four providers behave as before.
  • Correlation: SES sends carry X-SES-MESSAGE-TAGS: sendlit_message_id=msg_…, and matching by that tag is now tried first. The SES message id is parsed from the SMTP 250 Ok <id> reply. Before this, the whole reply line was stored, so matching by SES message id never worked.
  • Connections: SES connections are set up with expectedTopicArn instead of a credential. New subscription_confirmed_at column (migration 0005). Changing the topic clears the confirmation; an unsubscribe surfaces as sns_unsubscribed.
  • Serializer: the three copies (team REST, org REST, MCP) are now one shared function. The out-of-date provider lists in provider-capabilities.ts and api-contract are fixed.
  • Web: SNS topic ARN field and subscription status in the delivery feedback dialog.
  • Docs: REST/MCP docs updated, plus a new Self-Hosting → Amazon SES bounces and complaints guide.

Event mapping

SES SendLit
Bounce Permanent hard_bounce (suppressed if on the SES suppression list)
Bounce Transient / Undetermined soft_bounce
Complaint complaint (not-spam ignored)
Delivery / DeliveryDelay delivered / delayed
Reject rejected
Rendering Failure failed
Send / Open / Click / Subscription ignored

Test plan

  • API typecheck clean; 489 API tests pass (new sns, ses, route, receipt, connection, outbound, sequence and MCP tests)
  • Web typecheck clean; docs site builds
  • Browser smoke test: invalid ARN rejected; valid ARN saves and the dialog shows "Waiting for SNS subscription"; Test returns feedback_subscription_unconfirmed
  • Live endpoint: a message from the wrong topic → 401; a body over 512 KB → 413
  • End-to-end against real AWS (ap-southeast-1, configuration set + SNS topic, webhook exposed via Tailscale Funnel):
    • SNS subscription confirmed automatically (real Amazon signature, version 1)
    • SES message ids parsed cleanly from the SMTP 250 Ok <id> reply
    • Mailbox simulator: success@ → delivered; bounce@ → hard bounce (550 5.1.1), suppressed; complaint@ → delivered + complaint, suppressed
    • SES returned the sendlit_message_id tag in mail.tags for every event, and every event matched its email
    • mail.headers stripped before storage; Delivery payloads dropped after processing; bounce/complaint payloads kept
    • A new send to the bounced address was blocked with 422 recipient_suppressed; the connection turned healthy
  • Identity notifications (guide's Option B) not yet tested against real AWS. They don't carry message tags, so matching falls back to the SES message id and recipient

Notes

  • AWS China regions (amazonaws.com.cn) aren't supported, because of the exact host check the issue specifies.
  • feedbackRequiredForOrganizationDelivery is unchanged for SES.

🤖 Generated with Claude Code

rajat1saxena and others added 4 commits October 16, 2026 20:37
- SNS signature verification (v1/v2) with strict cert URL checks,
  no redirects, cert caching and 503 on transient cert fetch failures
- SES adapter: topic ARN pinning, automatic subscription confirmation,
  pure event mapping, mail.headers redaction, Delivery payloads dropped
  after processing
- Correlate by X-SES-MESSAGE-TAGS sendlit_message_id tag first; parse the
  SES message id from the SMTP "250 Ok <id>" reply
- SES connections are armed by expectedTopicArn instead of a credential;
  add subscription_confirmed_at (migration 0005)
- Shared feedback connection serializer for team/org REST and MCP
- Web dialog, REST/MCP docs and a self-hosting setup guide

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…in SES guide

- Regression test for sequence sends through SES: X-SES-MESSAGE-TAGS must
  be merged with the List-Unsubscribe headers, and the bare SES id stored
- The SNS console's create page has no signature-version option; note that
  the default (v1) works and point to the CLI for v2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Amazon SES bounce and complaint ingestion via SNS

2 participants