#Regulation

Posthorn offers a single gateway for self‑hosted apps to reach transactional mail services

AI & ML Reporter
4 min read

Posthorn is a Go‑based, Docker‑ready service that consolidates HTTP form, HTTP API, and SMTP ingress into one configurable gateway, forwarding all outbound mail to a provider such as Postmark, Resend, Mailgun, AWS SES, or an external SMTP relay. The project promises simpler integration and uniform handling of retries, rate limits, and security checks, but it remains a thin relay layer that still depends on the chosen provider for deliverability, reputation, and compliance.

What Posthorn claims

  • One binary, one TOML file, and a single Docker container can replace per‑application mail‑sending code.
  • It accepts three kinds of inbound traffic (HTML form posts, JSON‑API calls, and raw SMTP) and forwards every message to a configurable transactional provider.
  • Built‑in safeguards – honeypot fields, origin checks, rate limiting, optional CSRF, idempotency keys – are meant to reduce spam and accidental abuse.
  • The service is provider‑agnostic; switching from Postmark to Mailgun is just a line change in the config.

The repository’s README and the accompanying docs (see the official site) present the tool as the “unified outbound mail layer” for anyone running self‑hosted software on cloud VMs that block outbound SMTP.

{{IMAGE:1}}

What is actually new

A focused relay, not a mail server

Posthorn does not implement mailbox storage, IMAP/JMAP, or any of the reputation‑building mechanisms that traditional mail servers provide. It is essentially a thin wrapper around the HTTP APIs of the supported providers, plus a modest SMTP listener that parses MIME and hands the payload to the same internal transport code.

Minimal dependency surface

The Go module pulls in only three external libraries (a TOML parser, a UUID generator, and an LRU cache). All provider integrations are handwritten; there is no reliance on vendor SDKs. This keeps the binary small (a distroless Docker image) and reduces the attack surface.

Uniform configuration model

The TOML schema defines endpoints (the public URL path), transport (the chosen provider), and optional per‑endpoint policies (rate limits, allowed origins, required fields). Because every endpoint ultimately creates a transport.Message struct, the same retry logic, timeout handling, and logging format apply regardless of whether the request arrived via HTTP form, JSON API, or SMTP.

Practical integrations

The documentation includes ready‑made recipes for:

  • Hugo + Comentario comment forms
  • Ghost blog admin emails
  • Gitea magic‑link authentication
  • Umami digest cron jobs
  • Cloudflare Workers that trigger password‑reset emails

These examples demonstrate that the tool can be dropped into existing stacks with only a reverse‑proxy rule and a tiny config snippet.

Limitations you need to keep in mind

Area Detail
Deliverability Posthorn does not improve inbox placement; it inherits the provider’s reputation. If you switch providers, you must update DNS (SPF/DKIM/DMARC) and possibly warm‑up new IP ranges.
Statefulness Idempotency keys and retry counters live only in memory. A container restart clears them, which can lead to duplicate sends in edge cases.
TLS termination The service expects to run behind a reverse proxy (Caddy, nginx, Traefik). It does not terminate TLS itself, so you must configure TLS at the edge.
Metrics persistence Prometheus metrics are exposed, but there is no built‑in persistence for delivery logs. The roadmap mentions an SQLite log for v2, but it is not yet available.
Feature scope No support for attachments, HTML bodies, or multi‑tenant routing in the current release. Those features are slated for future versions.
Security While the inbound checks (honeypot, origin validation, rate limiting) are useful, they are configurable and can be mis‑configured. An open endpoint without allowed_origins could become an open relay.

How it fits into a typical self‑hosted stack

  1. Deploy – Pull the image from GitHub Container Registry (ghcr.io/craigmccaskill/posthorn:latest) and mount a read‑only posthorn.toml.
  2. Configure – Define one or more endpoints, pick a transport, and set security policies (e.g., allowed_origins = ["https://example.com"]).
  3. Route traffic – Point your application’s mail‑sending code to http://posthorn:8080/api/contact (form) or configure its SMTP client to use posthorn.yourdomain.com:2525.
  4. Monitor – Use /healthz for container health checks and /metrics for Prometheus scraping.

In practice, this reduces the number of API keys stored across services and eliminates duplicated retry logic. It also gives a single place to audit outbound email traffic, assuming you expose the Prometheus metrics or forward logs to a central system.

Real‑world considerations

  • Cloud providers that block outbound SMTP – Services like DigitalOcean or Lightsail often deny direct port 25 traffic. Posthorn’s SMTP listener can bind to an alternative port (e.g., 2525) and still forward via the provider’s HTTP API, keeping legacy apps functional.
  • Cost – Since the actual sending is billed by the chosen provider, Posthorn adds virtually no extra cost beyond the container’s compute footprint.
  • Compliance – Because the service does not store messages, it does not introduce new data‑retention obligations. However, you still need to ensure your provider’s policies align with GDPR, HIPAA, or other regulations relevant to your domain.

Outlook

The v1.0 release focuses on reliability and a clear configuration model. The roadmap points to a persistent submission log, better idempotency across restarts, and optional fan‑out to webhooks. Those additions would address the current statelessness and make the gateway more attractive for larger deployments.

If you already manage multiple self‑hosted services and are tired of scattering API keys and retry code, Posthorn is a pragmatic step toward centralisation. It does not replace a full‑featured mail server, nor does it solve deliverability challenges, but it does provide a tidy, auditable bridge between your apps and the transactional provider you trust.

Further reading

Comments

Loading comments...