SMTP email provider
Generic SMTP provider implementing email.ProviderPort against any STARTTLS-capable SMTP server.
Overview
The SMTP provider speaks the SMTP protocol directly, so any standards-compliant server works without a vendor SDK. Postfix, Exim, Microsoft Exchange, an internal corporate relay, or the SMTP submission endpoint that every transactional ESP exposes all connect through the same code path. The provider does not implement vendor-specific features such as webhook events, suppression lists, or per-recipient personalisation tags. SupportsBulkSending returns false, because plain SMTP has no native bulk API.
The provider has one hard requirement that determines which servers it can reach. It dials with the go-mail TLSMandatory policy, which requires the server to offer a STARTTLS upgrade and aborts the connection when the server does not. There is no knob to relax this. The provider never negotiates implicit TLS (port 465) and never sends in plaintext (port 25 without STARTTLS). Use the submission port 587 with a relay that advertises STARTTLS. A stock MailHog or MailCatcher instance speaks plain SMTP without STARTTLS, so this provider cannot reach it as a sink.
Authentication is mandatory in practice for any real relay. The provider sets SMTPAuthAutoDiscover, which selects PLAIN or LOGIN based on what the server advertises, and the exchange happens over the upgraded STARTTLS channel. Supply a username and password unless the relay accepts unauthenticated submission from your network.
The provider holds one persistent connection. It establishes the connection on the first send, resets it between messages, and reconnects on failure, so repeated sends and SendBulk reuse a single dialled socket. The provider satisfies email.ProviderPort, emits OpenTelemetry email.provider.smtp.send.total and email.provider.smtp.send.duration metrics, and exposes liveness and readiness through the health probe. A built-in rate limiter of 10 sends per second with a burst of 20 blocks before each send, so a misconfigured loop does not flood a shared relay.
Requirements
- A reachable SMTP server that advertises a
STARTTLSupgrade. - Network egress to the configured
Host:Port. - Credentials (
Username/Password) for any authenticating relay. Leave blank only when the relay accepts unauthenticated submission from your network.
The provider lives in its own Go module, piko.sh/piko/wdk/email/email_provider_smtp. Add that module as a dependency to import it. The provider is pure Go, so it needs no build tag, no CGO, and no system libraries, and it runs in interpreted dev-i mode.
Configuration
import (
"context"
"os"
"piko.sh/piko/wdk/email/email_provider_smtp"
)
provider, err := email_provider_smtp.NewSMTPProvider(ctx, email_provider_smtp.SMTPProviderArgs{
Host: "smtp.example.com", // required
Port: 587, // required, 1 to 65535
Username: os.Getenv("SMTP_USERNAME"), // SMTP AUTH username
Password: os.Getenv("SMTP_PASSWORD"), // SMTP AUTH password
FromEmail: "[email protected]", // default From address
})
if err != nil {
return err
}
The constructor validates that Host is non-empty and that Port is between 1 and 65535. It returns an error otherwise. The constructor prepares the provider but does not dial. The first send establishes the connection. No public option changes the rate limit of 10 sends per second with a burst of 20.
Bootstrap
ssr := piko.New(
piko.WithEmailProvider("smtp", provider),
piko.WithDefaultEmailProvider("smtp"),
)
WithEmailProvider is the only lifecycle step. The container registers the provider for graceful shutdown, so it closes the persistent SMTP connection when the server stops. There is no Close call to make yourself.
Sending mail
After bootstrap, a handler builds and sends through the default email service. The builder dials lazily, so the first send opens the persistent connection.
builder, err := email.NewEmailBuilderFromDefault()
if err != nil {
return err
}
err = builder.
To("[email protected]").
Subject("Hello").
BodyHTML("<p>Hi!</p>").
Do(ctx)
The builder comes from piko.sh/piko/wdk/email. See the Email API reference for the full builder surface.
See also
Other email providers:
- SendGrid, vendor with broad analytics surface.
- Postmark, transactional-only, fast delivery.
- SES, cheapest at scale if already on AWS.
- Mailgun, strong EU presence, flexible routing rules.
- Mailchimp Transactional, the former Mandrill.
- Gmail, Gmail SMTP with app passwords (dev / low-volume).
Framework docs:
- Email API reference, every type and method on the email service.
External:
- RFC 5321: SMTP, protocol specification.
- RFC 3207: SMTP over TLS, the
STARTTLSextension this provider requires.