Goal
Match Resend's outbound onboarding UX. After a user delegates their domain via DNS records, agent mail goes out as:
From: agent@inbox.mnexa.ai
mailed-by: send.inbox.mnexa.ai
signed-by: inbox.mnexa.ai
No via e2a display-name shim. Users never see AWS / SES — they paste ~5 DNS records into their DNS provider and watch chips turn green.
Today's state
The deployment-level shared-sender pattern works for deliverability but renders as:
From: agent@inbox.mnexa.ai via e2a <agent@send.e2a.dev>
mailed-by: mail.send.e2a.dev
signed-by: send.e2a.dev
Gmail surfaces the "via e2a" anywhere From-domain ≠ DKIM-domain. Mail delivers fine (SES's deployment DKIM aligns with the deployment From-domain), but the visible header doesn't match the customer's brand.
The per-domain DKIM keypair work in PR #112 (commit f022ae5) generates a keypair per domain and exposes the public key in DNS, but the actual deliverability win requires SES to be the signer, not us. See Resend-style comparison below.
The gap
| Piece |
Status |
What's needed |
| SES per-customer identity |
Not built |
Call SES `CreateEmailIdentity` at domain registration → SES returns 3 DKIM tokens. Store identity ARN + tokens. |
| Custom MAIL FROM |
Not built |
Call `PutEmailIdentityMailFromAttributes` with `send.{customerDomain}`. SES returns 1 MX + 1 SPF TXT for the customer to publish. |
| Verification poller / webhook |
Not built |
SES emits SNS events when an identity flips to `SUCCESS`. Subscribe to SNS or poll `GetEmailIdentity` periodically — mark the domain "ready to send" only after SES confirms. |
| Sender rewrite |
Partial |
Drop the "via e2a" display-name shim. Set `From: bot@customerDomain` + envelope MAIL FROM = `bounce@send.customerDomain` when the domain is fully delegated. Fall back to today's shared-domain path when not. |
| Onboarding UI |
Partial |
Domain detail page renders the ~5 DNS records (3 DKIM CNAMEs + 1 MX + 1 SPF TXT + the existing inbound MX), each with a live "found / missing" chip. |
The per-domain DKIM code from PR #112 becomes either deleted (SES handles signing once delegated) or kept as a self-hosted fallback. Recommend delete to keep one path.
Target user flow
What a user onboarding `inbox.mnexa.ai` would see:
- Add domain — enter `inbox.mnexa.ai`
- One screen lists 5 DNS records to paste, each labeled with purpose:
- DKIM 1 of 3 (CNAME)
- DKIM 2 of 3 (CNAME)
- DKIM 3 of 3 (CNAME)
- Outbound mail return path (MX on `send.inbox.mnexa.ai`)
- Outbound mail SPF (TXT on `send.inbox.mnexa.ai`)
- Plus the existing inbound MX record
- Real-time status — e2a polls SES + DNS, each record's chip turns green as DNS propagates.
- All green → "Ready to send" chip appears. Agent mail goes out as `From: agent@inbox.mnexa.ai`, signed by `inbox.mnexa.ai`, no shim.
What they don't see: AWS, SES, the identity ARN, the DKIM tokens themselves. The CNAME targets are `*.dkim.amazonses.com` and similar — opaque infrastructure details from their perspective.
Recommended PR split
- SES management plumbing (backend-only, no UX change). New `internal/sesidentity` package wrapping the SES v2 API. DB migration for `domains.ses_identity_arn` + `ses_dkim_tokens` + `mail_from_status`. Background verification poller. — Load-bearing piece, build first.
- Onboarding UI revamp. Domain detail page shows the 5-record DNS checklist with live status. Replaces today's single-TXT verification step.
- Sender cutover. Change `From` + envelope MAIL FROM when domain is fully delegated. Keep shared-domain agents on today's path. Delete the self-signing code in `outbound.Sender`.
Footguns
- SES sandbox. New SES accounts start in sandbox mode (can only send to pre-verified addresses). The current production deployment is presumably already out (since mail is delivering), but worth confirming. Re-deploying into a fresh AWS account = one-time AWS support ticket + ~24h wait. Doesn't affect users, only deployment.
- Region scoping. SES identities are region-scoped. Fine for single-region deploys; multi-region needs identity replication.
- Bounce / complaint handling. SES emits SNS notifications for bounces and complaints. Need to subscribe + handle (suppression list) — currently we let SES handle it pool-wide, but per-customer reputation will require per-customer handling.
- IAM. Deployment role needs `ses:CreateEmailIdentity`, `ses:GetEmailIdentity`, `ses:PutEmailIdentityMailFromAttributes`, `ses:DeleteEmailIdentity`.
Reference: how Resend achieves this
When a user onboards `mnexa.ai` with Resend, they're given CNAMEs that delegate DKIM signing authority to Resend's keys (`*.dkim.resend.com` style). Resend signs with `d=mnexa.ai` because their DNS lookup for `resend._domainkey.mnexa.ai` resolves through the CNAME chain to their key material. SPF aligns under `send.mnexa.ai` (a subdomain the customer CNAMEs to Resend's infra), which is why Gmail shows `mailed-by: send.mnexa.ai`. Same architectural pattern works for us via SES.
Effort estimate
~5–8 days of focused work for a usable v1, broken across the three PRs above. PR #1 is the meaty chunk (SES SDK integration + state machine for verification); #2 is mostly UI work; #3 is mechanical.
Goal
Match Resend's outbound onboarding UX. After a user delegates their domain via DNS records, agent mail goes out as:
No
via e2adisplay-name shim. Users never see AWS / SES — they paste ~5 DNS records into their DNS provider and watch chips turn green.Today's state
The deployment-level shared-sender pattern works for deliverability but renders as:
Gmail surfaces the "via e2a" anywhere From-domain ≠ DKIM-domain. Mail delivers fine (SES's deployment DKIM aligns with the deployment From-domain), but the visible header doesn't match the customer's brand.
The per-domain DKIM keypair work in PR #112 (commit f022ae5) generates a keypair per domain and exposes the public key in DNS, but the actual deliverability win requires SES to be the signer, not us. See Resend-style comparison below.
The gap
The per-domain DKIM code from PR #112 becomes either deleted (SES handles signing once delegated) or kept as a self-hosted fallback. Recommend delete to keep one path.
Target user flow
What a user onboarding `inbox.mnexa.ai` would see:
What they don't see: AWS, SES, the identity ARN, the DKIM tokens themselves. The CNAME targets are `*.dkim.amazonses.com` and similar — opaque infrastructure details from their perspective.
Recommended PR split
Footguns
Reference: how Resend achieves this
When a user onboards `mnexa.ai` with Resend, they're given CNAMEs that delegate DKIM signing authority to Resend's keys (`*.dkim.resend.com` style). Resend signs with `d=mnexa.ai` because their DNS lookup for `resend._domainkey.mnexa.ai` resolves through the CNAME chain to their key material. SPF aligns under `send.mnexa.ai` (a subdomain the customer CNAMEs to Resend's infra), which is why Gmail shows `mailed-by: send.mnexa.ai`. Same architectural pattern works for us via SES.
Effort estimate
~5–8 days of focused work for a usable v1, broken across the three PRs above. PR #1 is the meaty chunk (SES SDK integration + state machine for verification); #2 is mostly UI work; #3 is mechanical.