feat(email): add plaintext SmtpTls::None for test mail sinks (BUNYIP-150) #219

Closed
nrupard wants to merge 545 commits from feat/bunyip-150-smtp-plaintext into main
Owner

What

Add an additive SmtpTls::None plaintext SMTP transport mode to EmailService, so a Mailpit-style test mail sink (plaintext, unauthenticated, port 1025) can receive bunyip-api's outbound mail. Part 1 of BUNYIP-150 (staging E2E mail sink).

Why

EmailService previously supported only Implicit (465) and Starttls (587), both of which require TLS. The E2E email-driven flows (password reset, magic link, email-change confirmation) cannot be tested without a sink that the suite can read, and the standard sink (Mailpit) speaks plaintext SMTP. This arm is the mechanism that lets staging bunyip-api deliver into that sink.

Change

  • crates/bunyip-domain/src/config.rs: add SmtpTls::None; SMTP_TLS=none parses to it; default port 1025.
  • crates/bunyip-domain/src/services/email.rs: build a no-TLS, no-credentials transport via lettre builder_dangerous for the None arm. Two #[tokio::test] constructor tests (None + a regression guard on Implicit).

Safety

Additive and prod-safe: production never sets SMTP_TLS=none and stays on Implicit. Plaintext is intended only for an internal-network test sink. Also enables a local-dev Mailpit.

Verification

just check-container green: fmt + clippy (-D warnings) + workspace --lib tests, including the two new email-transport tests.

Part of BUNYIP-150

This is the first of three PRs. Next: the docker-repo Mailpit fragment + staging SMTP repoint, then the e2e suite changes (which merge last, once staging is live). See the issue for the full sequence.

🤖 Generated with Claude Code

## What Add an additive `SmtpTls::None` plaintext SMTP transport mode to `EmailService`, so a Mailpit-style test mail sink (plaintext, unauthenticated, port 1025) can receive bunyip-api's outbound mail. Part 1 of BUNYIP-150 (staging E2E mail sink). ## Why `EmailService` previously supported only `Implicit` (465) and `Starttls` (587), both of which require TLS. The E2E email-driven flows (password reset, magic link, email-change confirmation) cannot be tested without a sink that the suite can read, and the standard sink (Mailpit) speaks plaintext SMTP. This arm is the mechanism that lets staging bunyip-api deliver into that sink. ## Change - `crates/bunyip-domain/src/config.rs`: add `SmtpTls::None`; `SMTP_TLS=none` parses to it; default port 1025. - `crates/bunyip-domain/src/services/email.rs`: build a no-TLS, no-credentials transport via lettre `builder_dangerous` for the `None` arm. Two `#[tokio::test]` constructor tests (None + a regression guard on Implicit). ## Safety Additive and prod-safe: production never sets `SMTP_TLS=none` and stays on `Implicit`. Plaintext is intended only for an internal-network test sink. Also enables a local-dev Mailpit. ## Verification `just check-container` green: fmt + clippy (`-D warnings`) + workspace `--lib` tests, including the two new email-transport tests. ## Part of BUNYIP-150 This is the first of three PRs. Next: the docker-repo Mailpit fragment + staging SMTP repoint, then the e2e suite changes (which merge last, once staging is live). See the issue for the full sequence. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(email): add plaintext SmtpTls::None for test mail sinks (BUNYIP-150)
All checks were successful
E2E / Playwright against deployment (pull_request) Successful in 26s
Check / fmt + clippy + build + tests (pull_request) Successful in 2m28s
Create release / Create release from merged PR (pull_request) Has been skipped
559b41e4a5
The EmailService SMTP transport only supported Implicit (port 465) and STARTTLS (port 587), both of which require TLS. A Mailpit-style test mail sink listens with plaintext SMTP and no auth, so staging bunyip-api could not be pointed at one to make the E2E email-driven flows (password reset, magic link, email-change confirmation) readable.

Add an additive SmtpTls::None mode: SMTP_TLS=none parses to it, defaults to port 1025, and builds a no-TLS, no-credentials transport via lettre's builder_dangerous. Production is unaffected (it never sets SMTP_TLS=none and stays on Implicit). This also enables a local-dev mail sink.

Tests are #[tokio::test] because the built AsyncSmtpTransport holds a connection pool whose Drop runs inside a tokio runtime; in production the transport always lives in the server runtime, so a runtime context here matches reality.

#BUNYIP-150

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
nrupard closed this pull request 2026-06-24 21:38:41 +02:00
All checks were successful
E2E / Playwright against deployment (pull_request) Successful in 26s
Required
Details
Check / fmt + clippy + build + tests (pull_request) Successful in 2m28s
Required
Details
Create release / Create release from merged PR (pull_request) Has been skipped

Pull request closed

Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/bunyip!219
No description provided.