Skip to content

Email Domain Migration: interaction-design.org → ixdf.org

Executive Summary

Goal: Migrate email sending infrastructure from mailgun.interaction-design.org to mailgun.ixdf.org while maintaining email deliverability and avoiding DMARC authentication failures.

Key Challenge: During the gradual warm-up period, emails are sent through both old and new mailers. The sender email domain must dynamically match the mailer domain to pass DMARC authentication.

Solution: Dynamic email domain transformation in MailNotification that aligns the From address domain with the selected mailer.


Architecture Overview

Email Flow

text
Notification Created

MailNotification::toMail()

build() creates MailMessage with sender (e.g., mads@interaction-design.org)

applySpecificMailerIfNeeded()
    ├─ Selects mailer based on:
    │   1. UseSpecificMailer attribute (explicit override)
    │   2. IxdfOrgMailerFeature lottery (warm-up percentage)

alignSenderEmailDomainWithMailer()
    ├─ If mailer = mailgun-ixdf-org → transform @interaction-design.org → @ixdf.org
    └─ If mailer = mailgun → keep @interaction-design.org (or reverse if needed)

Email sent with aligned From address

DMARC Authentication

For emails to pass DMARC, the following must align:

ComponentOld MailerNew Mailer
Sending Servermailgun.interaction-design.orgmailgun.ixdf.org
DKIM Signs Forinteraction-design.orgixdf.org
SPF Authenticatesmailgun.interaction-design.orgmailgun.ixdf.org
From Address@interaction-design.org@ixdf.org

Mismatch = DMARC Failure: If mailgun.ixdf.org sends email with From: mads@interaction-design.org, Gmail rejects with:

5.7.26 Unauthenticated email from interaction-design.org is not accepted due to domain's DMARC policy

Migration Plan (Two Phases)

Phase 1: Domain Warm-up (2025-12-12 to 2026-03-12)

Objective: Build reputation for mailgun.ixdf.org gradually.

Implementation: IxdfOrgMailerFeature uses exponential warm-up schedule:

PhaseDays%Emails/day (~7k total)
Baseline0-151-4%70-280
Early16-305-10%350-700
Growth31-5014-28%980-1,960
Mid-ramp51-7035-65%2,450-4,550
Final71-9075-100%5,250-7,000

Dynamic Domain Transformation: During this phase, alignSenderEmailDomainWithMailer() ensures:

  • Emails via mailgun-ixdf-org use @ixdf.org From addresses
  • Emails via mailgun use @interaction-design.org From addresses

Phase 2: Sender Address Migration (After 2026-03-12)

Objective: Update all sender email constants to @ixdf.org.

Files to Update:

  • app/Notifications/Senders/MadsLocalGroups.phpmads.soegaard.lg@ixdf.org
  • app/Notifications/Senders/MadsAccounting.phpaccounting@ixdf.org
  • app/Notifications/Senders/MadsMarketingMailbox.phpmads.soegaard@ixdf.org
  • app/Notifications/Senders/MadsReferralPrograms.phpmads.soegaard.p@ixdf.org
  • app/Notifications/Senders/RikkeMemberSupport.phprikke.friis.dam@ixdf.org
  • app/Notifications/Senders/HelloAtInteractionDesignFoundation.phphello@ixdf.org

Cleanup: Remove alignSenderEmailDomainWithMailer() method and @todo-by 2026-03-12 markers.


Key Components

IxdfOrgMailerFeature

Location: app/Modules/Notification/Features/IxdfOrgMailerFeature.php

Purpose: Controls gradual rollout of new mailer using Laravel Pennant feature flags.

Key Constants:

php
public const string OUTDATED_MAILER = 'mailgun';
public const string NEW_MAILER = 'mailgun-ixdf-org';

Key Methods:

  • shouldUseNewMailer() - Returns true if current request should use new mailer
  • getMailerName() - Returns appropriate mailer name based on feature flag
  • getRolloutPercentage() - Returns current warm-up percentage

MailNotification

Location: app/Notifications/Support/Mail/MailNotification.php

Key Methods:

  1. applySpecificMailerIfNeeded() - Selects mailer and calls domain alignment
  2. alignSenderEmailDomainWithMailer() - Transforms From address domain

Domain Transformation Logic:

php
private function alignSenderEmailDomainWithMailer(MailMessage $message, string $mailer): void
{
    $useNewDomain = $mailer === IxdfOrgMailerFeature::NEW_MAILER;
    $oldDomain = '@interaction-design.org';
    $newDomain = '@ixdf.org';

    if ($message->from !== [] && \is_string($message->from[0])) {
        $message->from[0] = $useNewDomain
            ? str_replace($oldDomain, $newDomain, $message->from[0])
            : str_replace($newDomain, $oldDomain, $message->from[0]);
    }
}

EmailSender Interface

Location: app/Notifications/Senders/EmailSender.php

Implementations:

  • MadsLocalGroups - Local Groups emails
  • MadsAccounting - Accounting/billing emails
  • MadsMarketingMailbox - Marketing emails
  • MadsReferralPrograms - Referral program emails
  • RikkeMemberSupport - Member support emails
  • HelloAtInteractionDesignFoundation - Generic IxDF emails
  • ArbitrarySender - Dynamic sender (external emails)
  • IxDFMember - Member-initiated emails

Monitoring & Verification

Key Metrics During Warm-up

MetricTargetAction if Exceeded
Bounce rate<2%Pause warm-up
Spam complaints<0.1%Investigate immediately
Delivery rate>95%Review Mailgun logs
Open rateCompare to baselineMonitor trends

Verification Steps

  1. Check Mailgun Logs:

    • Verify DKIM/SPF/DMARC pass for emails via new mailer
    • Monitor bounce rates by mailer
  2. Test Email Delivery:

    php
    // Trigger test notification
    $member->notify(new LocalGroupTestNotification());
  3. Verify Domain Alignment:

    php
    // In test environment
    $notification = new SomeNotification();
    $message = $notification->toMail($recipient);
    
    // Check mailer and from address alignment
    assert($message->mailer === 'mailgun-ixdf-org');
    assert(str_contains($message->from[0], '@ixdf.org'));

Emergency Response

If deliverability issues occur:

  1. Immediate: Set WARMUP_START_DATE to future date (reverts all traffic to old mailer)
  2. Investigate: Check Mailgun logs for bounce/complaint patterns
  3. Resume: Adjust schedule if needed, restart warm-up

  • app/Modules/Notification/Features/IxdfOrgMailerFeature.php - Feature flag logic
  • app/Notifications/Support/Mail/MailNotification.php - Domain alignment
  • app/Notifications/Support/Mail/MailMessage.php - Mail message builder
  • app/Modules/Notification/Attributes/UseSpecificMailer.php - Override attribute
  • tests/Unit/Notifications/Support/Mail/MailMessageMailerSelectionTest.php - Tests
  • config/mail.php - Mailer configuration

Known Limitations

Mailboxes Must Exist for Both Domains

During Phase 1 warm-up, emails may be sent from either @interaction-design.org or @ixdf.org addresses depending on which mailer is selected. Both mailboxes must exist in Google Workspace to receive replies:

Sender ClassOld EmailNew Email (must exist)
MadsLocalGroupsmads.soegaard.lg@interaction-design.orgmads.soegaard.lg@ixdf.org
MadsAccountingaccounting@interaction-design.orgaccounting@ixdf.org
RikkeMemberSupportrikke.friis.dam@interaction-design.orgrikke.friis.dam@ixdf.org
HelloAtInteractionDesignFoundationhello@interaction-design.orghello@ixdf.org

Cross-Domain DKIM Not Supported

Mailgun cannot sign emails for domains outside its configured hierarchy. For example, mailgun.ixdf.org cannot DKIM-sign for @interaction-design.org. This is why dynamic domain transformation is required during the warm-up period.


  • #26640 - Main email domain migration tracking issue
  • #26665 - Smooth mail domain migration implementation
  • #26766 - Local Groups sender email migration to @ixdf.org