Skip to content
Development
Skill

/email-infrastructure

Email delivery infrastructure - DNS authentication (SPF/DKIM/DMARC), subdomain isolation, provider abstraction, template systems, bounce handling, warmup strategy, and deliverability monitoring.

From plugin
vibecosystem
534200 skills138 agents7 hooks
Install
$ npx -y skills add vibeeval/vibecosystem --skill email-infrastructure --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/email-infrastructure

Context preview

The summary Claude sees to decide when to auto-load this skill.

Email delivery infrastructure - DNS authentication (SPF/DKIM/DMARC), subdomain isolation, provider abstraction, template systems, bounce handling, warmup strategy, and deliverability monitoring.

SKILL.md

email-infrastructure.SKILL.md
name: email-infrastructure
description: Email delivery infrastructure - DNS authentication (SPF/DKIM/DMARC), subdomain isolation, provider abstraction, template systems, bounce handling, warmup strategy, and deliverability monitoring.

Email Infrastructure

Production email delivery requires DNS authentication, domain isolation, and provider-agnostic architecture. A single misconfiguration can land your entire domain in spam.

GOOD vs BAD: Domain Strategy

BAD:  Send bulk marketing from example.com
      → Spam complaints tank your main domain reputation
      → Transactional emails (password reset, receipts) start landing in spam
      → Recovery takes weeks of warmup

GOOD: Subdomain isolation with separate reputations
      → mail.example.com        for transactional (password reset, receipts, 2FA)
      → notify.example.com      for product notifications (comments, mentions)
      → marketing.example.com   for bulk campaigns (newsletters, promotions)
      → Each subdomain has independent reputation — one bad campaign does not poison the rest

DNS Authentication: SPF + DKIM + DMARC

# SPF — declare which servers can send from your domain
mail.example.com     TXT  "v=spf1 include:_spf.provider.com ~all"

# DKIM — cryptographic signature on every email
selector._domainkey.mail.example.com  TXT  "v=DKIM1; k=rsa; p=MIGf..."

# DMARC — policy for failed authentication (progressive rollout)
# Week 1-2: monitor only
_dmarc.mail.example.com  TXT  "v=DMARC1; p=none; rua=mailto:dmarc@example.com"

# Week 3-4: quarantine suspicious emails
_dmarc.mail.example.com  TXT  "v=DMARC1; p=quarantine; pct=25; rua=mailto:dmarc@example.com"

# Week 5+: reject after confidence builds
_dmarc.mail.example.com  TXT  "v=DMARC1; p=reject; rua=mailto:dmarc@example.com"

Never jump straight to `p=reject`. The progressive rollout catches misconfigurations before they block legitimate mail.

Email Provider Abstraction

// Swap Resend, SES, Postmark, or Mailgun without touching business logic

interface EmailProvider {
  send(message: EmailMessage): Promise<EmailResult>
  sendBatch(messages: EmailMessage[]): Promise<EmailResult[]>
}

interface EmailMessage {
  from: string
  to: string | string[]
  subject: string
  html: string
  text?: string
  replyTo?: string
  headers?: Record<string, string>
  tags?: Record<string, string>
}

interface EmailResult {
  id: string
  status: 'sent' | 'queued' | 'failed'
  error?: string
}

// Factory selects provider from config — no hardcoded vendor
function createEmailProvider(config: { provider: string }): EmailProvider {
  switch (config.provider) {
    case 'resend':    return new ResendProvider()
    case 'ses':       return new SESProvider()
    case 'postmark':  return new PostmarkProvider()
    default:          throw new Error(`Unknown email provider: ${config.provider}`)
  }
}

Transactional vs Marketing Separation

interface EmailService {
  sendTransactional(message: EmailMessage): Promise<EmailResult>
  sendMarketing(message: EmailMessage): Promise<EmailResult>
}

class ProductionEmailService implements EmailService {
  constructor(
    private transactional: EmailProvider,  // high-deliverability provider
    private marketing: EmailProvider       // bulk-optimized provider
  ) {}

  async sendTransactional(message: EmailMessage): Promise<EmailResult> {
    // Transactional: password reset, receipts, 2FA — must arrive instantly
    // Use mail.example.com subdomain, high-priority provider
    return this.transactional.send({
      ...message,
      from: `noreply@mail.example.com`,
      headers: { 'X-Priority': '1' }
    })
  }

  async sendMarketing(message: EmailMessage): Promise<EmailResult> {
    // Marketing: newsletters, promotions — rate-limited, includes unsubscribe
    // Use marketing.example.com subdomain, bulk provider
    return this.marketing.send({
      ...message,
      from: `hello@marketing.example.com`,
      headers: { 'List-Unsubscribe': `<https://example.com/unsubscribe>` }
    })
  }
}

Template System (MJML)

// MJML compiles to responsive HTML that works across all email clients
// Compile at build time, not runtime

import mjml2html from 'mjml'

const mjmlTemplate = `
<mjml>
  <mj-head>
    <mj-attributes>
      <mj-all font-family="system-ui, -apple-system, sans-serif" />
      <mj-text font-size="16px" line-height="1.5" color="#1a1a1a" />
    </mj-attributes>
    <mj-style>
      @media (prefers-color-scheme: dark) {
        .dark-bg { background-color: #1a1a1a !important; }
        .dark-text { color: #e5e5e5 !important; }
      }
    </mj-style>
  </mj-head>
  <mj-body>
    <mj-section css-class="dark-bg">
      <mj-column>
        <mj-text css-class="dark-text">Hello {{name}},</mj-text>
        <mj-text css-class="dark-text">{{body}}</mj-text>
        <mj-button href="{{actionUrl}}" background-color="#2563eb">
          {{actionLabel}}
        </mj-button>
      </mj-column>
    </mj-section>
  </mj-body>
</mjml>
`

function escapeHtml(str: string): string {
  return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;')
}

function compileTemplate(mjml: string, vars: Record<string, string>): string {
  let compiled = mjml
  for (const [key, value] of Object.entries(vars)) {
    compiled = compiled.replaceAll(`{{${key}}}`, escapeHtml(value))
  }
  const { html, errors } = mjml2html(compiled)
  if (errors.length > 0) {
    throw new Error(`MJML compilation errors: ${errors.map(e => e.message).join(', ')}`)
  }
  return html
}

Bounce and Complaint Handling

// Webhook handler for provider callbacks (bounces, complaints, deliveries)

interface BounceEvent {
  type: 'bounce' | 'complaint' | 'delivery'
  email: string
  reason?: string
  timestamp: string
}

async function handleEmailWebhook(event: BounceEvent): Promise<void> {
  switch (event.type) {
    case 'bounce':
      // Hard bou
Read more
Ships withvibecosystem

Your AI software team. Built on Claude Code. vibecosystem turns Claude Code into a full AI software team — 138 specialized agents that plan, build, review, test, and learn from every mistake. No configuration needed — just install and code.

Get the whole plugin

Other skills on vibecosystem.