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
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. SecurityAutowill 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:
SecurityNoneis 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
sendmailbinary and host environment.
🧩 Compatibility Notes
The API shape follows Mail concepts where practical in Go:
SetFrom,AddTo,AddCc,AddBcc,SetSubjectSetFromAddress,AddToAddress,AddCcAddress,AddBccAddress,AddReplyToAddressSetFromMailbox,AddToMailbox,AddCcMailbox,AddBccMailbox,AddReplyToMailboxSetFromMailboxes(exactly one sender)SetToMailboxes,SetCcMailboxes,SetBccMailboxes,SetReplyToMailboxesAddToMailboxes,AddCcMailboxes,AddBccMailboxes,AddReplyToMailboxesSetTextBody,SetHtmlBodyAddAttachmentFromPath,AddEmbeddedFileAddHeader/SetHeaderSetListUnsubscribe,SetListUnsubscribePostOneClickUseinterceptor hooksSetDKIMSigner
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
Click above to join our community on Discord!