Skip to content

feat: production-ready hardening, TLS SMTP, pluggable content, docs - #8

Merged
christiangda merged 1 commit into
mainfrom
feat/production-ready-hardening
Jul 11, 2026
Merged

feat: production-ready hardening, TLS SMTP, pluggable content, docs#8
christiangda merged 1 commit into
mainfrom
feat/production-ready-hardening

Conversation

@christiangda

Copy link
Copy Markdown
Contributor

Summary

Makes mailer production-ready: fixes a concurrency deadlock, adds TLS to the SMTP transport, makes MailContent usable by external backends, hardens validation, and adds documentation, tests, and public-library tooling.

Highlights

Correctness & concurrency

  • Fixed a deadlock. Enqueue previously held an exclusive mutex during a blocking channel send, so a full queue could block Start/Stop indefinitely (reproducible via the old TestMailService_EnqueueBlocksWhenFull, which hung for 60s). The service now uses an RWMutex + atomic started flag; the queue is closed exactly once under the write lock while Enqueue holds the read lock — no send-on-closed-channel, no deadlock.
  • Start/Stop are now idempotent and safe for concurrent use.

Pluggable backends actually work

  • MailContent now exposes read accessors (FromName, FromAddress, ToName, ToAddress, MimeType, Subject, Body). Previously all fields were unexported with no getters, so any external MailerService implementation could not read the message it was asked to send.

SMTP transport (security)

  • TLS support: implicit TLS/SMTPS (ImplicitTLS, implied on port 465) and opportunistic/required STARTTLS (RequireTLS). Without this, PLAIN auth against a real server fails (stdlib refuses plaintext auth) — the transport was effectively unusable for authenticated servers.
  • Configurable DialTimeout, LocalName (EHLO), custom TLSConfig; full 1–65535 port range; CRLF line endings + UTF-8 Content-Type; errors wrap their cause (errors.Is/errors.As).

Validation & injection safety

  • Rejects CR/LF/NUL in header fields (SMTP header/command injection).
  • Address limits raised to RFC-compatible bounds (254); body limit raised to 256 KiB.

Service ergonomics

  • MailServiceConfig.QueueSize (queue independent of worker count) and per-message Timeout.

Tests, docs, tooling

  • In-process SMTP test server exercising plain, STARTTLS, and implicit-TLS paths; concurrency tests under -race; validation/getter coverage. ~92% statement coverage (gate in .testcoverage.yml passes).
  • Rewritten README (install, quick start, config tables, custom backends), removed the retired Go Report Card badge, new docs/ guides + examples.
  • Added Makefile, .golangci.yaml (lint clean), DEVELOPMENT_GUIDELINES.md, CHANGELOG.md; expanded SECURITY.md.

Compatibility

Pre-1.0. Additive API (accessors, new config fields). Behavioral changes: SMTP now negotiates TLS, SMTPPort allowlist replaced by a range check, and validation bounds widened — all documented in CHANGELOG.md.

Verification

gofmt -l .          # clean
go vet ./...        # clean
go test -race ./... # pass
golangci-lint run   # 0 issues
go-test-coverage    # PASS (92.1%)

🤖 Generated with Claude Code

Core correctness and production readiness:

- Fix a deadlock where Enqueue held an exclusive lock during a blocking
  channel send, which could block Start/Stop when the queue was full. The
  service now uses an RWMutex plus an atomic started flag; the queue is
  closed exactly once under the write lock while Enqueue holds the read lock.
- Add exported read accessors to MailContent (FromName, FromAddress, ToName,
  ToAddress, MimeType, Subject, Body) so external MailerService backends can
  actually read message fields.
- Reject CR/LF/NUL in header fields at build time (SMTP header-injection
  protection); raise address limits to RFC-compatible bounds and the body
  limit to 256 KiB.

SMTP transport:

- Add TLS: implicit TLS/SMTPS (ImplicitTLS, implied on 465) and
  opportunistic/required STARTTLS (RequireTLS).
- Configurable DialTimeout, LocalName (EHLO), and custom TLSConfig; accept the
  full 1-65535 port range; render messages with CRLF and a UTF-8 Content-Type;
  wrap underlying errors via MailerError.Err/Unwrap.

Service:

- Add MailServiceConfig.QueueSize and a per-message Timeout; make Start/Stop
  idempotent and safe for concurrent use.

Tests, docs, and tooling:

- Add an in-process SMTP test server (plain, STARTTLS, implicit TLS),
  concurrency tests under -race, and validation/getter coverage (~92%).
- Rewrite README (install, quick start, config tables, custom backends);
  add docs/ guides and examples; expand SECURITY.md.
- Add Makefile, .golangci.yaml, DEVELOPMENT_GUIDELINES.md, CHANGELOG.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@christiangda christiangda self-assigned this Jul 11, 2026
@christiangda
christiangda merged commit d2f59af into main Jul 11, 2026
1 check passed
@christiangda
christiangda deleted the feat/production-ready-hardening branch July 11, 2026 13:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant