mail

package
v0.3.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 23, 2026 License: Apache-2.0 Imports: 10 Imported by: 0

Documentation

Overview

Package mail defines email messages and the Sender contract implemented by provider modules such as mail/resend and mail/smtp (ADR-0025).

Stability: stable (ADR-0015, ADR-0054).

Index

Constants

View Source
const RedactedAddress = "[email]"

RedactedAddress replaces email addresses in text redacted by RedactAddresses.

Variables

View Source
var ErrRejected = errors.New("mail: rejected")

ErrRejected marks a send that will never succeed as it is, such as an invalid message, an unverified sender domain or a refused recipient. Providers wrap it; the jobs mail worker stops retrying such sends. Temporary failures (network errors, rate limits, provider outages) must not wrap it.

View Source
var ErrSuppressed = fmt.Errorf("%w: every recipient is on the suppression list", ErrRejected)

ErrSuppressed marks a message none of whose recipients may receive email because they are on the suppression list: addresses that bounced permanently or complained (ADR-0062). It wraps ErrRejected, so the jobs mail worker cancels the send instead of retrying it.

Functions

func NormalizeAddress

func NormalizeAddress(email string) string

NormalizeAddress returns email as suppression lists store it: without surrounding spaces and in lower case. Mailbox names are case-sensitive in theory but not at any provider in practice, and a list that missed a differently capitalized address would keep sending to it.

func RedactAddresses

func RedactAddresses(text string) string

RedactAddresses returns text with everything shaped like an email address replaced by RedactedAddress. Providers and senders apply it to server replies before they become errors: SMTP servers and APIs often echo the recipient ("550 5.1.1 <jane@example.com>: Recipient address rejected"), and errors are shown in job runs and logged, where addresses must not appear.

Types

type Address

type Address struct {
	Name  string
	Email string
}

An Address is an email address with an optional display name.

func (Address) String

func (a Address) String() string

String formats the address for an email header, quoting the name if needed.

type Brand

type Brand struct {
	// Name is the product's name, shown as the wordmark when LogoURL is empty
	// and always in the footer.
	Name string
	// URL is where the name links and the address in the footer; empty for
	// neither.
	URL string
	// LogoURL is the absolute URL of a logo shown in place of the wordmark,
	// up to 32px high. Empty shows the name.
	LogoURL string
	// SupportEmail ends the footer with where to write; empty omits it.
	SupportEmail string
	// Footer is one more line for the footer, such as a postal address or
	// why the recipient gets the email. Empty omits it.
	Footer string
}

Brand is what every email an app sends has in common: the name at the top and in the footer, where it links, an optional logo, and how to reach support. Apps build one in internal/app and pass it to the modules' NewBrandedEmails; their own emails go through Brand.Message (ADR-0078).

func (Brand) Message

func (b Brand) Message(to, subject, category string, e Email) Message

Message returns a message to to with subject and the category tag, with e rendered by b. From is left for the sender's defaults.

func (Brand) Render

func (b Brand) Render(e Email) (text, html string)

Render returns e as plain text and as HTML in b's layout: a wordmark, one card with the message, a footer. The HTML is one 560px column of tables with inline styles, so it reads the same in Gmail, Outlook and Apple Mail, in light colours whatever the client's theme.

type Button

type Button struct {
	Label string
	URL   string
}

A Button is a call to action: the label on the button and the address it opens, which is repeated as a link for clients that don't show buttons.

type Defaults

type Defaults struct {
	FromName  config.Value[string]
	FromEmail config.Value[string]
	// ReplyTo is one email address; an empty value means no reply-to.
	ReplyTo config.Value[string]
}

Defaults are live sender values applied to messages that leave them empty. Each is read on every send, so runtime settings apply immediately. A nil field is ignored.

type Email

type Email struct {
	// Preheader is the summary inbox lists show after the subject. It is not
	// shown in the opened email.
	Preheader string
	// Title is the heading.
	Title string
	// Paragraphs are the message, before the code or button.
	Paragraphs []string
	// Code is a one-time code, shown large in its own block. CodeLabel is
	// its caption ("Verification code") and CodeNote the line under the
	// block ("It expires in 15 minutes.").
	Code      string
	CodeLabel string
	CodeNote  string
	// Button is the main action; the zero value shows none.
	Button Button
	// Closing paragraphs come last, set smaller: "If you didn't do this…".
	Closing []string
}

An Email is the content of one transactional message, rendered by Brand.Render as HTML and plain text. Every field is optional; the parts appear in the order they are declared.

type Message

type Message struct {
	From    Address
	To      []Address
	ReplyTo []Address
	Subject string
	// Text and HTML are alternative bodies; at least one is required.
	Text string
	HTML string
	// IdempotencyKey lets providers drop duplicate sends, for example when a
	// background job is retried. Jobs use their job ID.
	IdempotencyKey string
	// Tags are provider metadata such as a message category.
	Tags map[string]string
}

A Message is an email to send. Add fields by setting them; the zero value of every optional field means "not set".

func (Message) Validate

func (m Message) Validate() error

Validate reports whether m has a sender, at least one valid recipient, a subject without line breaks and a body.

type Sender

type Sender interface {
	Send(ctx context.Context, m Message) error
}

A Sender delivers email. Implementations must be safe for concurrent use, respect ctx cancellation and return an error when delivery fails.

func WithDefaults

func WithDefaults(next Sender, d Defaults) Sender

WithDefaults returns a Sender that fills an empty From and ReplyTo from d, then sends through next. Messages that set them keep their own.

func WithSuppressionList

func WithSuppressionList(next Sender, list SuppressionList) Sender

WithSuppressionList returns a Sender that removes suppressed recipients from each message before sending it through next. A message left without recipients isn't sent and returns ErrSuppressed; a list that can't be read returns its error, which is temporary, so the send is retried rather than delivered to an address that may be suppressed. Reply-to addresses aren't checked: they receive nothing.

Wrap the sender the mail worker delivers through, so the check happens when the email is sent rather than when it is queued.

type SenderFunc

type SenderFunc func(ctx context.Context, m Message) error

SenderFunc adapts a function to the Sender interface.

func (SenderFunc) Send

func (f SenderFunc) Send(ctx context.Context, m Message) error

Send calls f(ctx, m).

type SuppressionList

type SuppressionList interface {
	// Suppressed returns the addresses among emails, each normalized with
	// [NormalizeAddress], that are on the list. An error means the list
	// couldn't be read.
	Suppressed(ctx context.Context, emails []string) ([]string, error)
}

A SuppressionList holds addresses that must not receive email, such as those that bounced permanently or marked a message as spam. Implementations must be safe for concurrent use.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL