# Email

`mail.Message` represents an envelope and its text/HTML content. `mail.Sender`
validates messages before dispatching to an explicit `mail.Transport`. Delivery
is synchronous and context-aware. Construction performs no network activity.

```go
sender := mail.Sender{Transport: mail.SMTP{
    Address: "smtp.example.com:587",
    Username: smtpUsername,
    Password: smtpPassword,
}}
err := sender.Send(ctx, mail.Message{
    From: "App <app@example.com>",
    To: []string{"recipient@example.com"},
    Subject: "Account invitation",
    Text: "Your account is ready.",
    HTML: "<p>Your account is ready.</p>",
})
```

Credentials and recipients come from application configuration or trusted data.
The account starter wires recovery delivery in `project/accounts.go`; the
[authentication guide](https://cicadel.fauzaanu.com/en/dev/topics/authentication/index.md) lists its environment settings.

## Ask your agent

```text
Read https://cicadel.fauzaanu.com/en/dev/topics/email/index.md.
Add email delivery for [event] using Cicadel mail.Sender.
Read SMTP credentials from secret configuration, send after successful persistence,
and test recipients and message content with an in-memory transport.
```

## Message and MIME contract

Messages support `To`, `Cc`, `Bcc`, `ReplyTo`, plain text, HTML alternatives,
attachments and extension headers. Bcc addresses are used only for the SMTP
envelope and never appear in serialized MIME. `Message.Bytes()` validates and
serializes the message with Date, Message-ID, UTF-8 headers, quoted-printable
bodies, multipart alternatives and base64 attachments. MIME parts use CRLF and
standard line wrapping. The sender mailbox and recipient mailboxes are parsed,
and CR/LF/NUL header injection is rejected.

```go
message.Attachments = []mail.Attachment{{
    Filename: "report.csv",
    ContentType: "text/csv",
    Data: csvBytes,
}}
message.Headers = map[string]string{"X-Application": "reports"}
```

Attachment names cannot include path separators. Bytes are application-provided;
this API does not read arbitrary filesystem paths. Extra headers accept X-*
headers, Message-ID, In-Reply-To and References; they cannot override envelope,
MIME or Bcc fields. Duplicate case-insensitive header names are rejected. Callers
own attachment size limits and must not mutate messages during a send.

Use `mail.RenderComponent(ctx, message, content)` to render compiled typed UI into
the HTML body; supply `message.Text` explicitly for a text alternative. Existing
`mail.Render` remains a compatibility helper for separate Go text and escaped
HTML templates. Neither helper converts arbitrary user HTML into trusted markup.
Email client CSS support differs from browser CSS; tailor email components to
the intended email clients.

## SMTP and batches

`SMTP.Encryption` defaults to `starttls`, requiring STARTTLS and certificate
verification. `tls` supports implicit TLS, commonly port 465. A cloned
`TLSConfig` can supply a private certificate pool; disabling verification is
rejected and the minimum TLS version is 1.2. Credentials are sent only after TLS.
Explicit `plain` mode is limited to an actual loopback destination and refuses
credentials; it is for local mail capture tools.

The default timeout is 30 seconds for the batch. Context cancellation closes the
connection, including while awaiting an SMTP response. `Sender.SendMany` validates
the full input before delivery and reuses one SMTP connection when supported.
It returns the number accepted before an error. A failed QUIT after all DATA
commands were accepted does not make those messages failed sends. A connection
failure during DATA acknowledgement can leave delivery uncertain; the caller
owns its retry/idempotency policy. The framework does not silently retry or
silently swallow delivery errors.

## Development backends

| Backend | Behavior |
| --- | --- |
| `mail.Capture` | Thread-safe in-memory outbox with deep copies on write and read |
| `&mail.Console{Writer: writer}` | Serialized MIME to an explicit writer, with synchronized writes |
| `mail.File{Directory: path}` | One mode-0600 `.eml` file per message in a private directory |
| `mail.Discard{}` | Explicitly validate without delivery |
| Custom `mail.Transport` | Implement `Send(context.Context, mail.Message) error` |

Console/file messages include private bodies and reset links. Select them
explicitly for development; the accounts starter uses SMTP. Writers, filesystems,
credentials and provider accounts remain application-owned. No application email
is sent by tests: the SMTP tests use isolated loopback servers with locally
created certificates, and account tests inject a capture callback.

`mail/delivery_test.go` verifies MIME parsing, attachment decoding, Bcc privacy,
header rejection, outbox isolation, file permissions, STARTTLS/implicit TLS,
connection reuse, partial delivery, failed upgrades and cancellation. Run the
package under the race detector alongside the framework CI suite.
