# einherjar/smtp [![version](https://img.shields.io/badge/version-v1.0.0-5C4EE5?style=flat-square)](https://code.nochebuena.dev/einherjar/smtp) [![license](https://img.shields.io/badge/license-AGPL--3.0-22863A?style=flat-square)](LICENSE) [![go](https://img.shields.io/badge/Go-1.26+-00ADD8?style=flat-square&logo=go&logoColor=white)](https://go.dev) [![health](https://img.shields.io/badge/health-degraded-E36209?style=flat-square)]() > A raven sent from Valhalla reaches its destination. The sender does not wait at the window. `code.nochebuena.dev/einherjar/smtp` is the email sender component of the Einherjar framework. It is built entirely on the Go standard library (`net/smtp`, `mime/multipart`, `html/template`) with no external dependencies. When `EINHERJAR_SMTP_HOST` is empty, `New` returns a silent no-op — email absence never blocks a transaction, user registration, or order placement. Health priority is `LevelDegraded`. --- ## Usage ### Setup ```go import "code.nochebuena.dev/einherjar/smtp" mailer := smtp.New(logger, smtp.DefaultConfig()) launcher.Append(mailer) // OnInit verifies credentials; OnStop is a no-op health.Register(mailer) // dial check; LevelDegraded ``` When `cfg.Host` is empty, `New` returns a no-op that logs every `Send` call at debug level and always returns nil. No code changes are needed between environments. ### Sending a plain-text email ```go err := mailer.Send(ctx, smtp.Message{ To: []string{"user@example.com"}, Subject: "Welcome to the service", Body: "Your account is ready.", }) ``` ### Sending HTML with attachments ```go err := mailer.Send(ctx, smtp.Message{ To: []string{"user@example.com"}, CC: []string{"support@example.com"}, BCC: []string{"audit@example.com"}, // envelope only — never in headers Subject: "Your invoice", Body: renderedHTML, ContentType: "text/html", Attachments: []smtp.Attachment{ { Name: "invoice.pdf", ContentType: "application/pdf", Data: pdfReader, // consumed once; do not reuse }, }, }) ``` ### Template rendering Template rendering is intentionally separate from `Send`. Render to a string first, then assign to `Message.Body`. This keeps the transport and rendering concerns independent. ```go tmpl, err := smtp.ParseFS(os.DirFS("templates"), "*.html") if err != nil { return err } body, err := tmpl.Render("welcome.html", map[string]any{ "Name": user.Name, "URL": activationURL, }) if err != nil { return err } err = mailer.Send(ctx, smtp.Message{ To: []string{user.Email}, Subject: "Activate your account", Body: body, ContentType: "text/html", }) ``` --- ## Environment variables | Variable | Required | Default | Description | |---|---|---|---| | `EINHERJAR_SMTP_HOST` | No | `""` | SMTP host. Empty → no-op sender | | `EINHERJAR_SMTP_PORT` | No | `587` | SMTP port | | `EINHERJAR_SMTP_USER` | No | `""` | Auth username | | `EINHERJAR_SMTP_PASSWORD` | No | `""` | Auth password | | `EINHERJAR_SMTP_FROM` | No | `""` | Default From address | --- ## Dependency graph ``` contracts (zero dependencies) ↑ core ↑ smtp (contracts, core, stdlib only) ↑ your app ``` No external dependencies beyond the Go standard library. --- ## Verification ```bash cd smtp/ go build ./... go vet ./... go test ./... gofmt -l . ``` --- > *The message matters. The raven is only the path.* > *Make the path reliable. Do not let it stop the battle.*