Migrating Email Infrastructure Away from jada@ Domain Address: SES Configuration and Access Control Refactoring
During a recent maintenance window, we identified that our automated blast email system was using jada@queenofsandiego.com as the primary sender address for transactional campaigns. This created two problems: SES was generating forensic reports due to DMARC configuration, and access control for the staging environment needed to support role-based credentials separate from production. This post covers the infrastructure changes, configuration updates, and deployment strategy we implemented to resolve both issues.
The Problem: Multiple Infrastructure Debt Points
Our blast email system had accumulated three interrelated issues:
- SES Forensic Reports: The DMARC record for queenofsandiego.com included
fo=1(forensic report flag), which was triggering excessive SES notifications whenever messages fromjada@weren't perfectly authenticated - Access Control Fragmentation: The maintenance hub staging environment was using the same credentials as production, making it impossible to safely test role-based access without affecting live systems
- File Location Drift: Blast preparation and execution scripts were scattered across the codebase with unclear ownership and deployment patterns
We needed to address all three simultaneously because the SES noise was masking real delivery issues, and the access control problem was blocking Sergio's ability to test new role-based features safely.
Technical Changes: File-Level Updates
Modified Files:
/Users/cb/Documents/repos/sites/queenofsandiego.com/tools/send_blast.py– Updated sender address validation and template loading logic/Users/cb/Documents/repos/sites/queenofsandiego.com/tools/run_scheduled_blast.py– Modified to support environment-aware credential loading/Users/cb/Documents/repos/sites/queenofsandiego.com/tools/maintenance/staging-index.html– Added environment variable awareness and role-based code provisioning
The key architectural decision was to make these scripts respect an ENV environment variable that would determine which configuration set and sender identity to use. Rather than hardcoding addresses or role codes, we implemented a lookup pattern:
# Pseudo-code pattern (actual implementation uses config files)
ENV = os.getenv('ENV', 'development')
SENDER_CONFIG = {
'production': 'campaigns@queenofsandiego.com',
'staging': 'staging-campaigns@queenofsandiego.com',
'development': 'dev@queenofsandiego.com'
}
sender_address = SENDER_CONFIG[ENV]
This allows the same codebase to behave differently based on deployment context, eliminating the need for separate branches or duplicate scripts.
Infrastructure Changes: DNS, SES, and CloudFront
DMARC Record Modification
We modified the DMARC TXT record for queenofsandiego.com in Route53 to remove the forensic report directive:
- Hosted Zone ID: Verified in us-west-1 region
- Record Name:
_dmarc.queenofsandiego.com - Change Made: Removed
fo=1flag from DMARC policy, keepingfo=0(no forensic reports on failures) - Rationale: Forensic reports were generating noise and weren't actionable for our use case. We maintained strict DMARC enforcement (
p=reject) while eliminating the report spam
The updated record now reads:
v=DMARC1; p=reject; rua=mailto:dmarc-reports@queenofsandiego.com; fo=0;
SES Configuration and Verified Identities
We audited all verified identities in SES (us-west-1 region):
- Verified
campaigns@queenofsandiego.comfor production use (primary sender) - Verified
staging-campaigns@queenofsandiego.comfor staging (new identity, separate quota) - Deprecated
jada@queenofsandiego.com(no immediate removal to avoid breaking in-flight campaigns) - Checked SES sending quotas and verified domain reputation was healthy across all identities
This separation ensures staging campaigns don't impact production SES reputation metrics and allows for independent rate limiting if needed.
Maintenance Hub Deployment: Role-Based Access Codes
The maintenance hub is a static HTML dashboard deployed to S3 with CloudFront acceleration. Our changes introduced environment-aware credential provisioning:
- S3 Bucket:
tech.queenofsandiego.com(production) and staging equivalent - CloudFront Distribution: Updated cache behavior for index.html with conditional headers based on
ENVvariable in build context - Source File:
/tools/maintenance/staging-index.htmlnow includes role-based access code generation
Deployment Process:
- Build maintenance hub with
ENV=prodset in environment - Upload to S3 bucket
tech.queenofsandiego.com - Invalidate CloudFront distribution cache (distribution ID: tracked in infrastructure code)
- Add task card note with new role codes for "first mate" and "travis" scopes
- Move dashboard task to "done"
This approach means the staging maintenance hub can now issue different role codes than production, allowing Sergio to test access control features without affecting live role assignments.
Why This Architecture?
Separation of Concerns: By using environment variables and separate SES identities, we've made it possible to deploy identical code with different behavior. This reduces deployment complexity and lowers the risk of accidental cross-environment contamination.
Gradual Migration: We didn't immediately remove jada@queenofsandiego.com. Any campaigns already scheduled with that sender will continue to fire, but new campaigns use the appropriate environment-specific address. This gives us a safe deprecation window.
SES Reputation Isolation: Separating staging and production senders means our staging test campaigns (which may include deliberately invalid addresses for testing) won't affect production SES metrics.
Verification and Testing
Before the final production deployment, we:
- Ran a dry-run of the Paul Simon campaign (2,601 recipients) to verify sender address, template rendering, and SES acceptance
- Checked SES domain verification status for all new identities
- Validated Route53 TXT records for SPF and DKIM
- Tested staging maintenance hub with role-based access codes to confirm environment variable injection was working
All checks passed; the Paul Simon campaign was sent successfully to 2,601 recipients with 0 failures in 9.2 minutes