Email Infrastructure
Deploy AWS SES email sending for AuthGate using Pulumi. This creates the SES domain identity, DKIM signing, MAIL FROM configuration, and an IAM user scoped to your domain.
Overview
AuthGate uses AWS SES to send transactional emails — verification codes and password reset links — for both the dashboard and the proxy email/password flow. The infrastructure is defined as code in the infra/ package using Pulumi.
The Pulumi stack creates:
- SES Domain Identity — registers your domain with SES
- DKIM — enables DomainKeys Identified Mail signing for deliverability
- MAIL FROM — configures a custom bounce-handling subdomain (
mail.yourdomain.com) - IAM User + Access Key — scoped credentials that can only send from
*@yourdomain.comand*@*.yourdomain.com
Prerequisites
- Pulumi CLI installed
- AWS CLI configured with credentials that can manage SES and IAM
- A domain you control (e.g.
authgate.dev) with access to its DNS records - Node.js 20+ and pnpm 9+
Deploy with Pulumi
From the repository root, initialize the stack and deploy:
# Navigate to the infra package
cd infra
# Create a new stack (e.g. "dev" or "prod")
pulumi stack init dev
# Configure your domain and enable SES
pulumi config set authgate:sesDomain yourdomain.com
pulumi config set authgate:enableSes true
# Build and deploy
pnpm build
pulumi up
Pulumi will show a preview of the resources to be created. Confirm to proceed. Once complete, the stack outputs will display:
sesSmtpAccessKeyId— the IAM access key ID for SESsesSmtpSecretAccessKey— the IAM secret access key (marked as secret)sesDomain— the configured domainsesDnsRecords— all DNS records you need to add
To view the secret access key:
pulumi stack output sesSmtpSecretAccessKey --show-secrets
Configure DNS records
After deploying, Pulumi outputs the DNS records you need to add at your domain registrar. You can view them at any time with:
pulumi stack output sesDnsRecords --json
SES domain verification
Add a TXT record to prove you own the domain:
| Type | Name | Value |
|---|---|---|
| TXT | _amazonses.yourdomain.com | (value from Pulumi output) |
DKIM signing
Add three CNAME records for DKIM email authentication. Pulumi outputs these as an array:
| Type | Name | Value |
|---|---|---|
| CNAME | {token1}._domainkey.yourdomain.com | {token1}.dkim.amazonses.com |
| CNAME | {token2}._domainkey.yourdomain.com | {token2}.dkim.amazonses.com |
| CNAME | {token3}._domainkey.yourdomain.com | {token3}.dkim.amazonses.com |
MAIL FROM subdomain
Add an MX and a TXT record for the mail. subdomain used for bounce handling:
| Type | Name | Value |
|---|---|---|
| MX | mail.yourdomain.com | 10 feedback-smtp.us-east-1.amazonses.com |
| TXT | mail.yourdomain.com | v=spf1 include:amazonses.com ~all |
DNS propagation can take up to 72 hours, but typically completes within a few minutes. SES will not verify the domain until the TXT record is resolvable.
Set environment variables
Add the SES credentials to your apps/web/.env.local:
AWS_SES_REGION=us-east-1
AWS_SES_ACCESS_KEY_ID=your_access_key_id
AWS_SES_SECRET_ACCESS_KEY=your_secret_access_key
Replace the values with the actual outputs from your Pulumi stack. The EMAIL_FROM variable is optional — it defaults to noreply@authgate.dev and is used as the fallback sender for dashboard emails.
# Optional — override the default dashboard sender address
EMAIL_FROM="AuthGate" <noreply@yourdomain.com>
Verify domain
After adding DNS records, verify that SES has picked them up:
- Open the AWS SES Console and check that your domain shows Verified status
- Check the DKIM status shows Success for all three CNAME records
- Check the MAIL FROM domain shows Verified
New SES accounts start in sandbox mode, which only allows sending to verified email addresses. To send to any address, request production access through the AWS console.
Per-project branding
AuthGate automatically brands emails per project. When an end user signs up or resets their password through the proxy flow, emails are sent from the project's subdomain:
"My App" <noreply@my-app.authgate.dev>
The sender name and subdomain are derived from the project's name and slug configured in the dashboard. No additional SES or DNS setup is needed for subdomains — the IAM policy allows sending from *@*.yourdomain.com automatically.
Dashboard emails (admin signup/reset) use the global EMAIL_FROM address or default to noreply@authgate.dev.
Infrastructure reference
The infrastructure code lives in the infra/ package:
| File | Description |
|---|---|
infra/src/index.ts | Entry point — wires SES + IAM together, exports stack outputs |
infra/src/config.ts | Reads Pulumi config (authgate:sesDomain, authgate:enableSes) |
infra/src/ses.ts | Creates SES domain identity, DKIM, MAIL FROM, and outputs DNS records |
infra/src/iam.ts | Creates IAM user + access key scoped to *@domain and *@*.domain |
Stack configuration
| Key | Default | Description |
|---|---|---|
authgate:sesDomain | authgate.dev | The domain to register with SES |
authgate:enableSes | false | Set to true to create SES resources |
Stack outputs
| Output | Description |
|---|---|
sesSmtpAccessKeyId | IAM access key ID for the SES sending user |
sesSmtpSecretAccessKey | IAM secret access key (secret) |
sesDomain | The configured SES domain |
sesDnsRecords | Object containing all DNS records to configure |
Destroying resources
To tear down the SES infrastructure:
cd infra
pulumi destroy --stack dev
This removes the SES domain identity, DKIM, MAIL FROM, IAM user, and access key. You should also remove the corresponding DNS records from your registrar.