gouef mailer logo

mailer

Composable mailer package for Go with MIME building, transports, failover, and DKIM support.
Build plain-text and HTML e-mails, attach files and inline assets, and send through SMTP, sendmail, or native transport wrappers.

Features · Usage · Security · Compatibility · Contributors

Static Badge Stable Version GitHub Release GitHub Release GoDoc Go Report Card codecov

Go package for composing e-mails.

[!TIP]

Quick start:

message := mailer.New().
  SetFrom("noreply@example.com").
  AddTo("john@example.com").
  SetSubject("Welcome").
  SetTextBody("Hello from Gouef Mailer")

smtpMailer := mailer.NewSMTPMailer("smtp.example.com", 587).
  SetAuth("smtp-user", "smtp-password").
  SetSecurity(mailer.SecurityStartTLS)

if err := smtpMailer.Send(message); err != nil {
  log.Fatal(err)
}

✨ Features

  • Fluent message builder
  • To, Cc, Bcc and Reply-To recipients
  • Plain text and HTML bodies
  • Multipart/alternative output
  • File and in-memory attachments
  • Inline attachments with Content-ID
  • Custom e-mail headers
  • Auto-generated Message-ID
  • List-Unsubscribe helpers (including One-Click)
  • SMTP failover (multiple servers)
  • Fallback mailer chaining
  • Message interceptors
  • DKIM signing (RSA-SHA256)
  • Sendmail transport
  • Native transport wrapper
  • Priority, Return-Path, Read-Receipt, Organization helpers
  • First-class Address type

🚀 Usage

📨 Build a Message

package main

import (
  "fmt"
  "log"

  "github.com/gouef/mailer"
)

func main() {
  message := mailer.New().
    SetFrom("noreply@example.com").
    AddTo("john@example.com").
    AddCc("team@example.com").
    SetReplyTo([]string{"support@example.com"}).
    SetSubject("Welcome").
    SetTextBody("Hello from text version").
    SetHtmlBody("<h1>Hello from HTML version</h1>")

  if _, err := message.AddAttachmentFromPath("./invoice.pdf"); err != nil {
    log.Fatal(err)
  }

  mimeMessage, err := message.ToMIME()
  if err != nil {
    log.Fatal(err)
  }

  fmt.Println(string(mimeMessage))
}

👤 Addresses with Display Names

message := mailer.New()

if _, err := message.SetFromAddress("noreply@example.com", "Billing Bot"); err != nil {
  log.Fatal(err)
}
if _, err := message.AddToAddress("john@example.com", "John Doe"); err != nil {
  log.Fatal(err)
}

from, err := mailer.NewAddress("billing@example.com", "Billing Team")
if err != nil {
  log.Fatal(err)
}
if _, err := message.SetFromMailbox(from); err != nil {
  log.Fatal(err)
}

to := []mailer.Address{}
toJohn, _ := mailer.NewAddress("john@example.com", "John Doe")
toJane, _ := mailer.NewAddress("jane@example.com", "Jane Doe")
to = append(to, toJohn, toJane)

if _, err := message.SetToMailboxes(to); err != nil {
  log.Fatal(err)
}

🖼️ Add Inline Image

if _, err := message.AddEmbeddedFile("./logo.png", "logo-cid"); err != nil {
  log.Fatal(err)
}

message.SetHtmlBody(`<p><img src="cid:logo-cid" alt="Logo" /></p>`)

📤 Send via SMTP

smtpMailer := mailer.NewSMTPMailer("smtp.example.com", 587).
  SetAuth("smtp-user", "smtp-password").
  SetSecurity(mailer.SecurityStartTLS)

if err := smtpMailer.Send(message); err != nil {
  log.Fatal(err)
}

If you use SMTP authentication, prefer SecurityStartTLS on port 587 or SecurityTLS on port 465. SecurityAuto will not send credentials over an unencrypted connection.

📮 Send via Sendmail

sendmail := mailer.NewSendmailMailer("/usr/sbin/sendmail")
if err := sendmail.Send(message); err != nil {
  log.Fatal(err)
}

🧰 Send via Native Transport

nativeMailer, err := mailer.NewNativeMailer()
if err != nil {
  log.Fatal(err)
}

if err := nativeMailer.Send(message); err != nil {
  log.Fatal(err)
}

🔁 SMTP Failover

smtpMailer := mailer.NewSMTPMailer("smtp-primary.example.com", 587).
  AddServer("smtp-backup.example.com", 587).
  SetAuth("smtp-user", "smtp-password").
  SetSecurity(mailer.SecurityStartTLS)

The mailer tries servers in order until sending succeeds.

🛟 Fallback Mailer

primary := mailer.NewSMTPMailer("smtp-primary.example.com", 587)
backup := mailer.NewSMTPMailer("smtp-backup.example.com", 587)

sender := mailer.NewFallbackMailer(primary, backup)
if err := sender.Send(message); err != nil {
  log.Fatal(err)
}

🪝 Interceptor

smtpMailer := mailer.NewSMTPMailer("smtp.example.com", 587).
  Use(func(m *mailer.Message) error {
    m.SetHeader("X-App", "billing")
    return nil
  })

Interceptors run before MIME generation and sending.

✍️ DKIM

pemKey := []byte(`-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----`)

signer, err := mailer.NewDKIMSigner("example.com", "mail", pemKey)
if err != nil {
  log.Fatal(err)
}

smtpMailer := mailer.NewSMTPMailer("smtp.example.com", 587).
  SetDKIMSigner(signer)

sendmail := mailer.NewSendmailMailer("/usr/sbin/sendmail").
  SetDKIMSigner(signer)

🏷️ Additional Message Metadata

message.
  SetPriority(1).
  SetReturnPath("bounce@example.com").
  SetReadReceiptTo("receipt@example.com").
  SetOrganization("Gouef").
  SetMessageID("custom-id@example.com")

For SMTPS on port 465:

smtpMailer := mailer.NewSMTPMailer("smtp.example.com", 465).
  SetAuth("smtp-user", "smtp-password").
  SetSecurity(mailer.SecurityTLS)

If you explicitly need plaintext SMTP AUTH for a trusted local relay, use SecurityNone.

🔐 Security

  • SMTP authentication is blocked on unencrypted connections unless you explicitly use SecurityNone.
  • SecurityAuto will not send credentials over plaintext SMTP.
  • Custom header names are validated before serialization.
  • Invalid header names containing :, CR, or LF are ignored.
  • Header values are normalized to a single line during MIME serialization.
  • Invalid raw address strings are not emitted into address headers.

Notes:

  • SecurityNone is an explicit opt-in for plaintext SMTP and should only be used with trusted local relays or controlled networks.
  • Attachments loaded from disk are read fully into memory.
  • The sendmail and native transports trust the selected local sendmail binary and host environment.

🧩 Compatibility Notes

The API shape follows Mail concepts where practical in Go:

  • SetFrom, AddTo, AddCc, AddBcc, SetSubject
  • SetFromAddress, AddToAddress, AddCcAddress, AddBccAddress, AddReplyToAddress
  • SetFromMailbox, AddToMailbox, AddCcMailbox, AddBccMailbox, AddReplyToMailbox
  • SetFromMailboxes (exactly one sender)
  • SetToMailboxes, SetCcMailboxes, SetBccMailboxes, SetReplyToMailboxes
  • AddToMailboxes, AddCcMailboxes, AddBccMailboxes, AddReplyToMailboxes
  • SetTextBody, SetHtmlBody
  • AddAttachmentFromPath, AddEmbeddedFile
  • AddHeader / SetHeader
  • SetListUnsubscribe, SetListUnsubscribePostOneClick
  • Use interceptor hooks
  • SetDKIMSigner

Custom headers added with AddHeader and SetHeader are validated before serialization. Invalid header names such as names containing :, CR, or LF are ignored.

SMTP transport is available through NewSMTPMailer(...).Send(message).

The package now includes both message composition and SMTP transport.

🛠️ Development

  • Edit go.mod and rename to your package module
  • Uncomment .github/workflows/tests.yml

🤝 Contributing

Read Contributing

Contributors

💬 Community

Discord

Click above to join our community on Discord!