ADR-063: Email Infrastructure and Alias Configuration
Status: Accepted Date: 2026-01-11 Deciders: Hal Casteel (CTO) Related ADRs:
- ADR-009: GCP Infrastructure Architecture - DNS and networking
- ADR-020: Security Hardening - Security contact requirements
Context
CODITECT requires functional email addresses for pilot launch operations including:
- User support communications
- Pilot program coordination
- Security vulnerability reports
- Privacy/GDPR requests
- Transactional emails (invitations, purchase confirmations, password resets)
The primary corporate domain is az1.ai with all emails routing to 1@az1.ai. CODITECT product emails need to be available at coditect.ai domain with forwarding to az1.ai.
Decision
Email Architecture
CODITECT Product Emails (coditect.ai)
│
│ Alias/Forward
▼
AZ1.AI Corporate Email (1@az1.ai)
│
│ Google Workspace
▼
Primary Inbox (Google Workspace)
Required Email Aliases
| CODITECT Address | Forwards To | Purpose |
|---|---|---|
support@coditect.ai | 1@az1.ai | User support tickets |
pilot@coditect.ai | 1@az1.ai | Pilot program coordination |
security@coditect.ai | 1@az1.ai | Security vulnerability reports |
privacy@coditect.ai | 1@az1.ai | Privacy/GDPR requests |
noreply@coditect.ai | N/A (SendGrid) | Transactional emails (outbound only) |
admin@coditect.ai | 1@az1.ai | Administrative communications |
billing@coditect.ai | 1@az1.ai | Billing inquiries |
feedback@coditect.ai | 1@az1.ai | Product feedback |
Transactional Email (Outbound)
Provider: SendGrid
Sender: CODITECT <noreply@coditect.ai>
Use Cases:
- Team invitation emails
- Purchase confirmation
- License activation
- Password reset
- Workstation provisioning notifications
Implementation
Step 1: Configure DNS Records for coditect.ai
MX Records (for receiving - optional if using forwarding service):
coditect.ai. IN MX 10 mx1.improvmx.com.
coditect.ai. IN MX 20 mx2.improvmx.com.
SPF Record (for sending via SendGrid):
coditect.ai. IN TXT "v=spf1 include:sendgrid.net include:_spf.google.com ~all"
DKIM Record (SendGrid-provided):
s1._domainkey.coditect.ai. IN CNAME s1.domainkey.u12345678.wl.sendgrid.net.
s2._domainkey.coditect.ai. IN CNAME s2.domainkey.u12345678.wl.sendgrid.net.
DMARC Record:
_dmarc.coditect.ai. IN TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc@coditect.ai; pct=100"
Step 2: Configure Email Forwarding
Option A: ImprovMX (Recommended for aliases)
- Sign up at https://improvmx.com
- Add domain:
coditect.ai - Verify domain ownership via DNS TXT record
- Add aliases:
support@coditect.ai → 1@az1.ai
pilot@coditect.ai → 1@az1.ai
security@coditect.ai → 1@az1.ai
privacy@coditect.ai → 1@az1.ai
admin@coditect.ai → 1@az1.ai
billing@coditect.ai → 1@az1.ai
feedback@coditect.ai → 1@az1.ai - Add MX records to DNS
Option B: Google Workspace Routing (if coditect.ai added to Workspace)
- Admin Console → Apps → Google Workspace → Gmail → Routing
- Add routing rule for each alias
- Configure delivery to
1@az1.ai
Step 3: Configure SendGrid for Transactional Email
1. Create SendGrid Account
# Store API key in Secret Manager
echo -n "SG.xxx..." | gcloud secrets create sendgrid-api-key \
--data-file=- \
--replication-policy=automatic \
--project coditect-cloud-infra
2. Authenticate Domain in SendGrid
- Add coditect.ai to SendGrid
- Configure CNAME records for DKIM
- Verify domain authentication
3. Configure Django Backend
# backend/coditect_license/settings.py
# Email Configuration
EMAIL_BACKEND = os.environ.get(
'EMAIL_BACKEND',
'django.core.mail.backends.smtp.EmailBackend'
)
# SendGrid Settings
SENDGRID_API_KEY = os.environ.get('SENDGRID_API_KEY')
EMAIL_HOST = 'smtp.sendgrid.net'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = 'apikey'
EMAIL_HOST_PASSWORD = SENDGRID_API_KEY
# Default sender
DEFAULT_FROM_EMAIL = 'CODITECT <noreply@coditect.ai>'
SERVER_EMAIL = 'CODITECT System <system@coditect.ai>'
4. Kubernetes Secret
# kubernetes/base/secrets.yaml
apiVersion: v1
kind: Secret
metadata:
name: email-secrets
namespace: coditect-dev
type: Opaque
stringData:
SENDGRID_API_KEY: ${SENDGRID_API_KEY}
EMAIL_FROM: "CODITECT <noreply@coditect.ai>"
Step 4: Email Service Implementation
# backend/core/services/email.py
from django.conf import settings
from django.core.mail import send_mail
from django.template.loader import render_to_string
import logging
logger = logging.getLogger(__name__)
class EmailService:
"""Email service for CODITECT transactional emails."""
@staticmethod
def send_invitation_email(
to_email: str,
inviter_name: str,
team_name: str,
accept_url: str
) -> bool:
"""Send team invitation email."""
subject = f"{inviter_name} invited you to join {team_name} on CODITECT"
context = {
'inviter_name': inviter_name,
'team_name': team_name,
'accept_url': accept_url,
}
html_message = render_to_string('emails/invitation.html', context)
plain_message = render_to_string('emails/invitation.txt', context)
try:
send_mail(
subject=subject,
message=plain_message,
from_email=settings.DEFAULT_FROM_EMAIL,
recipient_list=[to_email],
html_message=html_message,
)
logger.info(f"Invitation email sent to {to_email}")
return True
except Exception as e:
logger.error(f"Failed to send invitation email: {e}")
return False
@staticmethod
def send_welcome_email(to_email: str, user_name: str) -> bool:
"""Send welcome email after registration."""
# Implementation similar to above
pass
@staticmethod
def send_workstation_ready_email(
to_email: str,
workstation_name: str,
dashboard_url: str
) -> bool:
"""Send notification when workstation is ready."""
# Implementation similar to above
pass
Step 5: Verification Checklist
# 1. Verify DNS records
dig MX coditect.ai
dig TXT coditect.ai
dig TXT _dmarc.coditect.ai
dig CNAME s1._domainkey.coditect.ai
# 2. Test email forwarding
echo "Test from CLI" | mail -s "Test forwarding" support@coditect.ai
# 3. Test SendGrid sending
python -c "
from django.core.mail import send_mail
send_mail(
'Test Email',
'This is a test from CODITECT.',
'noreply@coditect.ai',
['test@example.com'],
)
"
# 4. Check email deliverability
# Use https://www.mail-tester.com/ to verify SPF/DKIM/DMARC
Email Templates
Location
backend/templates/emails/
├── base.html # Base template with branding
├── invitation.html # Team invitation
├── invitation.txt # Plain text version
├── welcome.html # Welcome after signup
├── workstation-ready.html # Workstation provisioned
├── purchase-confirm.html # License purchase confirmation
└── password-reset.html # Password reset link
Template Example (invitation.html)
{% extends "emails/base.html" %}
{% block content %}
<h1>You've been invited to CODITECT</h1>
<p>{{ inviter_name }} has invited you to join <strong>{{ team_name }}</strong> on CODITECT.</p>
<p>
<a href="{{ accept_url }}" class="button">Accept Invitation</a>
</p>
<p>This invitation expires in 7 days.</p>
{% endblock %}
Security Considerations
- No Email Credentials in Code - All secrets in GCP Secret Manager
- SPF/DKIM/DMARC - Prevents email spoofing and phishing
- TLS Required - All email transmission encrypted
- Rate Limiting - SendGrid handles abuse prevention
- Audit Logging - All transactional emails logged
- Unsubscribe Headers - Required for CAN-SPAM compliance
Consequences
Positive
- Single inbox (1@az1.ai) for all CODITECT emails - simplified management
- Professional email addresses for each function (support@, security@, etc.)
- SendGrid handles deliverability, reputation, and scaling
- Email authentication prevents spoofing
Negative
- Dependency on external services (ImprovMX, SendGrid)
- Monthly cost for SendGrid (~$15-50/month for pilot scale)
- DNS propagation delays when making changes
Risks
- SendGrid service outage would block transactional emails
- Mitigation: Email queue with retry logic, fallback to SMTP
Monitoring
- SendGrid Dashboard - Delivery rates, bounces, spam reports
- GCP Cloud Monitoring - Email service errors in application logs
- ImprovMX Dashboard - Forwarding success/failures
Cost
| Service | Monthly Cost | Notes |
|---|---|---|
| ImprovMX | Free (up to 25 aliases) | Or $9/mo for premium |
| SendGrid | Free (100 emails/day) | Or $15/mo for 40K/mo |
| Total | $0-24/month | Pilot scale |
References
Document Owner: Hal Casteel, Founder/CEO/CTO, AZ1.AI INC Last Updated: January 11, 2026