Skip to main content

System Emails Architecture

Philosophy: System Emails Don't Need Database Tracking

System emails (password resets, email verifications, security alerts) are platform infrastructure, not customer data. They should be:

  1. Independent - Not tied to any organization or domain in the database
  2. Reliable - Always work, even if database has issues
  3. Simple - No complex verification or tracking overhead
  4. Fast - Skip database lookups and validation
  5. Secure - Configuration from environment, not user-controlled database

Why System Emails Are Different

Customer Emails

  • Belong to an organization
  • Use customer's verified domain
  • Tracked in database (Message, MessageEvent)
  • Subject to rate limits and verification
  • Can be paused/disabled per customer
  • Need audit trail and analytics

System Emails

  • Belong to the platform (Postchi)
  • Use brand domain (postchi.io)
  • NOT tracked in database
  • High priority, no rate limits
  • Always enabled
  • Logged to application logs only

Architecture

┌─────────────────────────────────────────────────────┐
│ Password Reset Request │
└────────────────┬────────────────────────────────────┘


┌─────────────────────────────────────────────────────┐
│ password-reset.service.ts │
│ ├─ Generate token (database tracked) │
│ ├─ Get config from ENV (not database) │
│ │ └─ SYSTEM_DKIM_PRIVATE_KEY │
│ └─ Queue to systemEmailQueue │
└────────────────┬────────────────────────────────────┘


┌─────────────────────────────────────────────────────┐
│ System Email Queue (Redis) │
│ ├─ Separate from customer queue │
│ ├─ Higher priority │
│ └─ More retry attempts │
└────────────────┬────────────────────────────────────┘


┌─────────────────────────────────────────────────────┐
│ system-email.worker.ts │
│ ├─ NO database lookups │
│ ├─ NO message tracking │
│ ├─ Direct SMTP send │
│ └─ Log to application logs │
└────────────────┬────────────────────────────────────┘


┌─────────────────────────────────────────────────────┐
│ SMTP Server (Direct Send) │
│ ├─ From: noreply@postchi.io │
│ ├─ DKIM signed with postchi.io keys │
│ └─ Dedicated brand IP (recommended) │
└─────────────────────────────────────────────────────┘

Configuration

Environment Variables (.env)

# System Email Configuration (for postchi.io brand emails)
SYSTEM_DKIM_SELECTOR=default
SYSTEM_DKIM_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----
...your private key here...
-----END PRIVATE KEY-----"
DASHBOARD_URL=http://localhost:5173

No Database Configuration Required ✅

System emails bypass domain verification:

  • ❌ No Domain record needed for postchi.io
  • ❌ No organization ownership
  • ❌ No verification status checks
  • ✅ Configuration from environment only

Benefits

1. Reliability

  • Works even if database is down
  • No domain verification blocking sends
  • No complex query chains

2. Performance

  • Skip database lookups
  • Skip message tracking overhead
  • Direct queue → SMTP

3. Security

  • Configuration in environment (not user-controlled database)
  • Can't be tampered with by customers
  • Separate DKIM keys for brand

4. Simplicity

  • Less code
  • Fewer failure points
  • Easier to debug

5. Scalability

  • Separate queue = independent scaling
  • Can scale system email worker independently
  • No customer volume impact

What Gets Tracked

Tracked in Database:

  • ✅ Password reset tokens (PasswordResetToken table)
  • ✅ User password changes (audit log)
  • ✅ Session invalidation

NOT Tracked in Database:

  • ❌ System email messages
  • ❌ System email events
  • ❌ System email delivery status

Tracked in Application Logs:

  • ✅ Email queued (with RFC Message-ID)
  • ✅ Email sent successfully
  • ✅ Email failed (with error)
  • ✅ SMTP response codes

Example: Password Reset Flow

// 1. User requests password reset
POST /v1/auth/forgot-password
{
"email": "user@example.com"
}

// 2. Service generates token (database)
await prisma.passwordResetToken.create({
token: "crypto-random-token",
userId: user.id,
expiresAt: oneHourFromNow
});

// 3. Service gets config from ENV (NOT database)
const systemDomain = {
domain: "postchi.io",
dkimSelector: process.env.SYSTEM_DKIM_SELECTOR,
dkimPrivateKey: process.env.SYSTEM_DKIM_PRIVATE_KEY
};

// 4. Queue email (NO database insert)
await systemEmailQueue.add("send-email", {
from: { email: "noreply@postchi.io", name: "Postchi" },
to: [user.email],
subject: "Reset Your Password",
html: emailTemplate,
dkimSelector: systemDomain.dkimSelector,
dkimPrivateKey: systemDomain.dkimPrivateKey
});

// 5. Worker sends directly (NO database tracking)
const transporter = createSmtpTransporter(dkimConfig);
await transporter.sendMail(mailOptions);

// 6. Log result (NO database insert)
logger.info({ email, smtpMessageId }, "Password reset email sent");

Comparison with Customer Emails

AspectCustomer EmailsSystem Emails
Domain SourceDatabase (Domain table)Environment (SYSTEM_*)
VerificationRequired (DNS + verification)None
OrganizationCustomer's org IDNone
Message TrackingYes (Message table)No
Event TrackingYes (MessageEvent table)No
Queueemail-sendingsystem-email
PriorityNormalHighest
Rate LimitsYesNo
Sendercustomer@customerdomain.comnoreply@postchi.io
DKIM KeysCustomer's keys (in DB)System keys (in ENV)
Database Queries5-10 per email0 per email

DNS Configuration

System emails require DNS configuration for postchi.io:

# SPF
postchi.io. TXT "v=spf1 ip4:YOUR_IP -all"

# DKIM
default._domainkey.postchi.io. TXT "v=DKIM1; k=rsa; p=YOUR_PUBLIC_KEY"

# DMARC
_dmarc.postchi.io. TXT "v=DMARC1; p=quarantine; ..."

See DNS-SETUP-POSTCHI-IO.md for complete instructions.

Future System Emails

This architecture supports all platform emails:

Already Implemented:

  • ✅ Password reset

Easy to Add:

  • 📧 Email verification (signup)
  • 📧 Two-factor authentication codes
  • 📧 Security alerts (suspicious login)
  • 📧 Billing notifications
  • 📧 Service announcements
  • 📧 Account suspension notices

All follow the same pattern:

  1. Data in database (tokens, audit logs)
  2. Email config from ENV
  3. Queue to systemEmailQueue
  4. Worker sends directly
  5. No message tracking

Monitoring

Application Logs

All system emails are logged:

{
"level": "info",
"message": "Password reset email queued to system email queue",
"email": "user@example.com",
"rfcMessageId": "<1234567890.abc@postchi.io>"
}
{
"level": "info",
"message": "System email sent successfully",
"rfcMessageId": "<1234567890.abc@postchi.io>",
"smtpMessageId": "abc123@smtp.server",
"to": ["user@example.com"]
}

Queue Monitoring

Monitor system email queue health:

  • Queue depth
  • Processing rate
  • Failed jobs
  • Average latency

External Monitoring

  • Google Postmaster Tools (reputation)
  • Microsoft SNDS (reputation)
  • Bounce rate tracking (via SMTP logs)
  • Complaint rate tracking (via feedback loops)

Error Handling

System emails have robust error handling:

// In worker
try {
await transporter.sendMail(mailOptions);
logger.info({ ... }, "System email sent successfully");
return { success: true };
} catch (error) {
logger.error({ error, to: data.to }, "Failed to send system email");
throw error; // BullMQ will retry
}

Retry Strategy:

  • 5 attempts (vs 3 for customer emails)
  • Exponential backoff
  • 1 second initial delay

Security Considerations

✅ Secure:

  • DKIM keys in environment (not database)
  • Can't be modified by customers
  • Separate from customer keys
  • Environment-only configuration

🛡️ Best Practices:

  • Use dedicated IP for brand
  • Monitor for abuse
  • Rate limit forgot-password endpoint
  • Use DMARC with strict alignment
  • Rotate DKIM keys annually

Conclusion

System emails are platform infrastructure, not customer data. By keeping them separate from the customer email pipeline, we achieve:

  • Higher reliability (fewer dependencies)
  • Better performance (no database overhead)
  • Stronger security (environment-only config)
  • Simpler code (less complexity)
  • Independent scaling (separate queue/worker)

This architecture prioritizes the critical nature of system emails while keeping customer email tracking robust and feature-rich.