Documentation
¶
Overview ¶
Package email provides a composable, host-configured email capability.
The host owns transport credentials, sender and recipients, provenance, and durable receipt retention. Callers provide only bounded message content. Mailer starts no goroutines and opens no network connection until Send.
Index ¶
- Variables
- type FileReceiptSink
- type IProvenanceSource
- type IReceiptSink
- type ITransport
- type Mailer
- type Option
- func WithAddresses(from string, to []string) Option
- func WithClock(now func() time.Time) Option
- func WithMetrics(registerer prometheus.Registerer) Option
- func WithProvenance(source IProvenanceSource) Option
- func WithReceiptSink(sink IReceiptSink) Option
- func WithTransport(transport ITransport) Option
- type SMTPConfig
- type SMTPTransport
Constants ¶
This section is empty.
Variables ¶
var ( // ErrDeliveryFailed means the transport rejected the message before acceptance. ErrDeliveryFailed = errors.New("email: delivery failed") // ErrDeliveryUnknown means interruption left remote acceptance ambiguous. ErrDeliveryUnknown = errors.New("email: delivery outcome is unknown") // ErrInvalidTransportOutcome means a transport returned an unusable outcome. ErrInvalidTransportOutcome = errors.New("email: transport returned an invalid outcome") )
var ( // ErrNoTransport means no delivery transport was configured. ErrNoTransport = errors.New("email: transport is required") // ErrNoProvenance means no runtime provenance source was configured. ErrNoProvenance = errors.New("email: provenance source is required") // ErrNoReceiptSink means no receipt evidence sink was configured. ErrNoReceiptSink = errors.New("email: receipt sink is required") // ErrNoSender means the host did not configure one valid sender. ErrNoSender = errors.New("email: sender is required") // ErrNoRecipients means the host did not configure any valid recipients. ErrNoRecipients = errors.New("email: at least one recipient is required") // ErrHeaderInjection means an address contains a line break. ErrHeaderInjection = errors.New("email: address contains a CR or LF") )
var ( // ErrSTARTTLSRequired means the server offered no encrypted upgrade. ErrSTARTTLSRequired = errors.New("email: smtp server does not advertise STARTTLS") // ErrAuthUnsupported means credentials were configured but encrypted SMTP // offered no AUTH extension. ErrAuthUnsupported = errors.New("email: smtp server does not advertise AUTH") )
Functions ¶
This section is empty.
Types ¶
type FileReceiptSink ¶
type FileReceiptSink struct {
// contains filtered or unexported fields
}
FileReceiptSink stores one deterministic protobuf file per receipt ID. A final record atomically replaces the pre-send UNKNOWN record at the same path. It never stores message bodies, SMTP credentials, senders, or recipients.
func NewFileReceiptSink ¶
func NewFileReceiptSink(directory string) (*FileReceiptSink, error)
NewFileReceiptSink prepares a private receipt directory. Existing directories must already deny group and other access.
func (*FileReceiptSink) Record ¶
func (sink *FileReceiptSink) Record(ctx context.Context, receipt *emailv1.EmailReceipt) error
Record validates and atomically persists receipt as deterministic protobuf.
type IProvenanceSource ¶
type IProvenanceSource interface {
Snapshot(ctx context.Context) (*provenancev1.ReceiptMetadata, error)
}
IProvenanceSource observes the runtime facts attached to one send attempt. Mailer clones the returned message before assigning receipt-owned fields.
type IReceiptSink ¶
type IReceiptSink interface {
Record(ctx context.Context, receipt *emailv1.EmailReceipt) error
}
IReceiptSink records bounded receipt evidence. Implementations must treat a later record with the same receipt ID as the final replacement for the pre-send UNKNOWN attempt.
type ITransport ¶
type ITransport interface {
Send(ctx context.Context, message []byte) (emailv1.DeliveryOutcome, error)
}
ITransport delivers one complete RFC 5322 message. ACCEPTED means only that the transport accepted the message; it does not prove inbox delivery.
type Mailer ¶
type Mailer struct {
// contains filtered or unexported fields
}
Mailer renders, records, and delivers bounded email messages. Its fields are immutable after construction, and Send starts no background goroutines.
func (*Mailer) Send ¶
func (mailer *Mailer) Send(ctx context.Context, message *emailv1.EmailMessage) (*emailv1.EmailReceipt, error)
Send records an UNKNOWN attempt before network I/O, then replaces it with the final bounded outcome. A process crash between those records deliberately leaves UNKNOWN because blind retry could duplicate an accepted message.
type Option ¶
type Option func(config *mailerConfig) error
Option configures a Mailer before construction.
func WithAddresses ¶
WithAddresses configures the host-owned envelope and visible address headers.
func WithMetrics ¶
func WithMetrics(registerer prometheus.Registerer) Option
WithMetrics registers bounded send metrics with registerer.
func WithProvenance ¶
func WithProvenance(source IProvenanceSource) Option
WithProvenance configures the trusted runtime observation boundary.
func WithReceiptSink ¶
func WithReceiptSink(sink IReceiptSink) Option
WithReceiptSink configures receipt evidence retention.
func WithTransport ¶
func WithTransport(transport ITransport) Option
WithTransport configures the delivery boundary.
type SMTPConfig ¶
SMTPConfig configures a real SMTP transport. The Mailer, not the transport, owns sender and recipients; the transport reads their validated MIME headers.
type SMTPTransport ¶
type SMTPTransport struct {
// contains filtered or unexported fields
}
SMTPTransport performs one mandatory-STARTTLS SMTP conversation per Send.
func NewSMTPTransport ¶
func NewSMTPTransport(config SMTPConfig) *SMTPTransport
NewSMTPTransport returns a real SMTP transport. Configuration is validated before the first network operation in Send.
func (*SMTPTransport) Send ¶
func (transport *SMTPTransport) Send(ctx context.Context, message []byte) (emailv1.DeliveryOutcome, error)
Send delivers message with STARTTLS mandatory. Failure while closing DATA is UNKNOWN because the server may have accepted the message. Once DATA closes successfully, a later QUIT failure does not revoke ACCEPTED.