What is Vellum
Vellum is a testing SMTP server with a web interface designed for engineering teams that need to intercept, inspect, and analyze emails in development and staging environments without any message reaching the public network. The server receives, retains, and classifies each email according to the project it belongs to. The differentiating point is not just interception, but the per-project isolation system and the two verification models — Vellum Verified and Sentinel Verified — that evaluate the technical quality and spam risk of each message before it reaches production.
SMTP testing server for modern teams
The problem with shared environments
Tools like Mailhog — abandoned since 2020 — or Mailpit work well for strictly local development: any email the application sends appears in the inbox. The problem arises when they are moved to shared development servers where multiple projects coexist.
A shared server where all projects direct their emails to the same instance generates two real frictions. First: emails from all projects mix in a single inbox with no separation. Second: each tool restart wipes the complete history. The first problem violates an elementary zero-trust principle applied to development data: if a developer does not have access to project A, they should not see its emails either, even if they are fictitious staging data. The second turns the tool into something inconsistent for test flows that require auditing complete email sequences.
The alternative of creating a separate subdomain per project — mail-project1.example.com, mail-project2.example.com — distributes the problem but multiplies it: one instance per project, each with its own port, its own configuration, and its own maintenance. It does not scale with teams handling more than two or three simultaneous projects.
For strictly local development on a personal machine, Mailpit remains a direct and frictionless option. Its limitation appears when it moves to a shared server where multiple projects and developers coexist.
Vellum vs MailHog vs Mailpit
The three tools share the same base purpose: intercept SMTP emails in development so they never reach the public network. The differences start right there.
MailHog was the go-to option for years. Its biggest problem today is that the project has no active maintenance since 2020. No security patches, no dependency updates, no new releases. Using it in 2026 means deploying software that nobody is fixing.
Mailpit is MailHog’s direct replacement for individual use. It is actively maintained, lightweight, and installs in seconds. It also includes HTML check — a CSS/HTML compatibility analysis against more than 175 rules sourced from caniemail.com data — and optional spam scoring through SpamAssassin integration. That integration requires a separately configured and running SpamAssassin server; without it, spam analysis is unavailable. Its limit in team contexts is the same as MailHog’s: it was designed for a single developer. Moving it to a shared server reveals what it does not have — no per-project separation, no user access control, and no cross-restart persistence by default.
Vellum starts from the assumption that the development server is shared. Per-project isolation, role-based access control, and persistent storage are not features added on top — they are the base. The result is a tool that works for a team of two just as well as for a team of fifteen, with the same instance.
| MailHog | Mailpit | Vellum | |
|---|---|---|---|
| Active maintenance | ✗ abandoned 2020 | ✓ | ✓ |
| Per-project isolation | ✗ | ✗ | ✓ |
| User access control | ✗ | ✗ | ✓ (OAuth2 + invite-only) |
| Persistent storage | ✗ | partial | ✓ (BBolt) |
| Spam analysis | ✗ | ⚠ SpamAssassin (external service) | ✓ (Sentinel Verified, embedded) |
| Email quality checks | ✗ | ⚠ HTML check (CSS/HTML only) | ✓ (Vellum Verified) |
| Dark mode rendering | ✗ | limited | ✓ |
| Shared server ready | ✗ | ✗ | ✓ |
If you work alone on a local machine, Mailpit is the right choice — it is fast and frictionless. If your team shares a development server, or if you need to know whether an email template would pass a real spam filter before deploying, Vellum is the option that fills those gaps.
Vellum works perfectly on a personal machine too. Local use requires no extra configuration beyond the base docker-compose.yml. The team features are available if needed, but they don’t get in the way if not.
Per-project isolation and sender-based routing
Vellum solves isolation at the SMTP protocol level. Each project defines its authorized senders: the From addresses that belong to it. When an email arrives at the server, Vellum checks whether the message’s sender corresponds to a sender registered in some project. If the sender does not exist, the email is discarded and the operation returns an unrecognized sender error. If it exists, the email is routed to that project’s exclusive inbox.
Access to that inbox is restricted to users associated with the project. A developer who only belongs to project A cannot see project B’s emails, even if both projects share the same Vellum instance. Isolation is structural: it does not depend on team discipline but on system permissions.
Each project’s administrator can configure a storage limit in megabytes. Once reached, the oldest emails are deleted to make room for new ones. This prevents a project with high test email volume from saturating the shared server’s storage.
Authentication and access
Vellum supports multiple configurable authentication providers from the admin panel: Google, GitHub, Discord, and any generic OIDC provider. It also supports local authentication via username and password, with invitation-only access. There is no open registration: a new user can only enter if an administrator invites them to the system.
All external provider configuration — client ID, client secret, callback URLs — is managed from Vellum’s interface once the server is running. No configuration files need to be edited nor does the container need to be restarted.
Session security includes Token Family, a mechanism that invalidates the entire refresh token family if a reuse attempt is detected, and HTTP-only cookies that eliminate access to session credentials from JavaScript.
Deploying Vellum on a shared server also changes the dynamic with quality assurance teams. A QA engineer can be invited to the relevant projects and gain direct access to the email flows generated in the development environment — without asking a developer to resend anything, without triggering real deliveries to live addresses, and without accumulating service blocks from repeated sends. The project isolation and per-user authentication define exactly what each person can see: QA accesses only what belongs to their projects, with the Vellum Verified and Sentinel Verified results already attached to every message. Email flow review moves from a manual back-and-forth between disciplines to a self-service inspection that QA can run at their own pace before promoting to staging or beta.
Vellum Verified: email technical health
Vellum Verified is the analysis model that evaluates the technical integrity of each received email. It checks headers, HTML standards, deliverability metrics, and message structure. The goal is not to block any email: it is to identify what could cause a production server to reject it or classify it as unwanted.
The result is a set of checks per email, with detail of what passed and what failed. If the email has a malformed From header, HTML with deprecated attributes, or lacks a plain text version, Vellum Verified flags it with a description of the problem. The developer sees exactly what their application generated and why it might fail in a real environment.
The score Vellum Verified produces is the quality target worth working toward. An email that earns that certification is built respecting the standards that real email providers use to evaluate incoming messages.
Sentinel Verified: probabilistic spam analysis
Sentinel Verified analyzes the email’s content — subject and plain text body — and calculates the probability that a real spam filter would classify it as unwanted. It does not use fixed heuristic rules: it uses a statistical classifier trained with real spam and ham examples that produces a score between 0 and 1.
When the score exceeds the decision threshold, Sentinel identifies the message tokens that contributed most to that classification and exposes them as triggers. The developer sees which specific words or phrases in their template are activating spam signals, and can correct them before they reach an environment where rejection has real consequences.
The model lives embedded in Vellum’s binary and operates in milliseconds with minimal CPU and RAM impact. The exact logic of the classifier — multinomial Naive Bayes with Laplace smoothing and softmax conversion — is documented in detail in the second article of this series.
Vellum includes a documentation section accessible from the web interface itself. Once the server is running, the detailed guide for each module is available without consulting external sources.
Forced dark mode and viewports
Email clients apply their own treatment to message HTML. Outlook on Windows and other clients with active dark mode force a color inversion that can break templates built only for light mode. Vellum renders emails simulating that behavior: standard light mode, native dark mode, and forced dark mode with the inversion algorithm applied by clients like Outlook on Windows.
The same email can be reviewed across multiple viewports — desktop, tablet, mobile — from the same interface. This eliminates the need for external tools or real email clients to verify how the message will look under different conditions.
Deploying Vellum with Docker
Vellum is distributed as a Docker image available on Docker Hub (docker.io/gerbo67/vellum:latest) and GitHub Container Registry (ghcr.io/gerbo67/vellum:latest). The minimum functional deployment requires exposing two ports: 8025 for the web interface and 2525 for the SMTP server.
services:
vellum:
image: docker.io/gerbo67/vellum:latest
container_name: vellum
restart: unless-stopped
ports:
- "8025:8025"
- "2525:2525"
volumes:
- vellum_data:/data
environment:
VELLUM_PORT: 8025
VELLUM_SMTP_PORT: 2525
VELLUM_DB_PATH: /data/vellum.db
VELLUM_BASE_URL: http://localhost:8025
VELLUM_MAX_EMAIL_SIZE: 26214400
VELLUM_JWT_SECRET: ""
LOG_LEVEL: info
LOG_FORMAT: text
volumes:
vellum_data:
driver: local The database Vellum uses is BBolt: a single portable file that contains all projects, users, emails, and system configuration. Backups are a volume copy. No PostgreSQL, no Redis, no external dependencies.
With an empty VELLUM_JWT_SECRET the server generates a secret automatically on each start. For environments where containers restart frequently, it is worth assigning a static value so existing sessions survive the restart.
Locally, Vellum works perfectly with VELLUM_BASE_URL: http://localhost:8025. On a shared server accessible from the network, that variable must point to the real domain or IP from which developers access the interface. OAuth2 providers need that URL to correctly build their callback URLs.
Why Go and React with SSE
Vellum is built with Go on the backend and React on the frontend. Go produces a compact binary with low memory footprint, instant startup, and deployment without runtime dependencies. The resulting container is small and predictable.
Real-time inbox updating uses Server-Sent Events (SSE). When an email arrives at the SMTP server, the event propagates to the client without polling: the corresponding project’s inbox updates instantly without needing to reload the page. SSE is unidirectional — from server to client — and is exactly the communication model a reactive inbox needs: the server notifies, the client renders.