Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 83 additions & 79 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,138 +5,142 @@
<h1 align="center">RustMail</h1>

<p align="center">
A fast, feature-rich SMTP mail catcher built in Rust.<br>
Single binary. Persistent storage. Modern UI. CI-ready.
A fast, self-hosted SMTP mail catcher for development and testing.<br>
Capture outbound email, inspect it in a modern UI, and assert on it in CI.
</p>

<p align="center">
<a href="https://github.com/rustmailapp/rustmail/actions/workflows/ci.yml"><img src="https://github.com/rustmailapp/rustmail/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
<a href="https://hub.docker.com/r/smyile/rustmail"><img src="https://img.shields.io/docker/pulls/smyile/rustmail?logo=docker&label=pulls" alt="Docker Pulls"></a>
<a href="https://github.com/rustmailapp/rustmail/releases/latest"><img src="https://img.shields.io/github/v/release/rustmailapp/rustmail?logo=github" alt="Latest Release"></a>
<a href="#license"><img src="https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-blue.svg" alt="License"></a>
</p>

<p align="center">
<code>~7 MB binary</code> · <code>28 KB JS gzipped</code> · <code>zero runtime dependencies</code>
<a href="https://docs.rustmail.app">Documentation</a> · <a href="https://docs.rustmail.app/api/">API Reference</a> · <a href="https://hub.docker.com/r/smyile/rustmail">Docker Hub</a>
</p>

<table align="center">
<tr>
<td align="center"><strong>Web UI</strong></td>
<td align="center"><strong>TUI</strong></td>
</tr>
<tr>
<td><img src="docs/public/screenshot-web.png" alt="RustMail Web UI" width="450" /></td>
<td><img src="docs/public/screenshot-tui.png" alt="RustMail TUI" width="450" /></td>
</tr>
</table>

<p align="center">
Inspired by <a href="https://github.com/mailhog/MailHog">MailHog</a> and <a href="https://github.com/tweedegolf/mailcrab">MailCrab</a>
<img src="docs/public/screenshot-web.png" alt="RustMail Web UI" width="720" />
</p>

## Install
## Quick Start

```sh
brew install rustmailapp/rustmail/rustmail
docker run -p 1025:1025 -p 8025:8025 smyile/rustmail:latest
```

Or on Arch Linux (AUR):
Point your app's SMTP at `localhost:1025`, then open [localhost:8025](http://localhost:8025). Emails appear in real time.

```sh
yay -S rustmail-bin
### Docker Compose

```yaml
services:
rustmail:
image: smyile/rustmail:latest
ports:
- "1025:1025"
- "8025:8025"
volumes:
- rustmail-data:/data
restart: unless-stopped

volumes:
rustmail-data:
```

Or with Docker:
## Install

### Homebrew

```sh
docker run -p 1025:1025 -p 8025:8025 smyile/rustmail:latest
brew install rustmailapp/rustmail/rustmail
```

Or from source:
### Arch Linux (AUR)

```sh
git clone https://github.com/rustmailapp/rustmail
cd rustmail && make build
yay -S rustmail-bin
```

## Quick Start
### From Source

```sh
rustmail
git clone https://github.com/rustmailapp/rustmail
cd rustmail && make build
```

Point your app's SMTP at `localhost:1025`, then open [localhost:8025](http://localhost:8025). Emails show up in real time.
### Pre-built Binaries

## Features
Download from [GitHub Releases](https://github.com/rustmailapp/rustmail/releases/latest) — Linux (x86_64, aarch64, armv7 — glibc + musl), macOS (Intel + Apple Silicon), and multi-arch Docker images.

### Core
## Features

- **Persistent storage** — SQLite-backed, emails survive restarts (or `--ephemeral` for CI)
- **Full-text search** — FTS5 across subject, body, sender, and recipients
- **Real-time updates** — WebSocket pushes new emails to the UI instantly
- **Modern UI** — dark-mode-first, looks and feels like a real email client
| | |
|---|---|
| **Persistent storage** | SQLite-backed, emails survive restarts. `--ephemeral` for CI. |
| **Full-text search** | FTS5 across subject, body, sender, and recipients. |
| **Real-time UI** | WebSocket push — new email appears instantly. Dark/light mode, keyboard shortcuts. |
| **CI-native** | REST assertion endpoints, CLI assert mode, and a first-party GitHub Action. |
| **Single binary** | Frontend embedded at compile time. ~7 MB, zero runtime dependencies. |
| **Auth header display** | Parses DKIM, SPF, DMARC, and ARC headers with color-coded status badges. |
| **Webhooks** | Fire-and-forget POST on every new message. |
| **Email release** | Forward captured emails to a real SMTP server. |
| **Export** | Download any email as EML or JSON. |
| **Retention policies** | Auto-purge by age (`--retention`) or count (`--max-messages`). |
| **TUI** | Optional terminal UI client for Neovim and headless workflows. |

### CI/CD
## GitHub Action

- **REST assertion endpoints** — `GET /api/v1/assert/count?min=1&subject=Welcome`
- **CLI assert mode** — `rustmail assert --min-count=2 --subject="Welcome" --timeout=30s`
- **GitHub Action** — two-step start + assert, works on Linux and macOS runners
- **Ephemeral mode** — in-memory DB for test pipelines, no cleanup needed
```yaml
- uses: rustmailapp/rustmail-action@v1

### Advanced
- run: npm test # your app sends emails to localhost:1025

- **DKIM/SPF/DMARC/ARC display** — parses authentication headers, color-coded status badges (no other local catcher does this)
- **Webhook notifications** — fire-and-forget POST on new email via `--webhook-url`
- **Email release** — forward captured emails to a real SMTP server
- **Export** — download as EML or JSON
- uses: rustmailapp/rustmail-action@v1
with:
mode: assert
assert-count: 1
assert-subject: "Welcome"
```

### Platform Support
## CLI Assert Mode

Pre-built binaries and multi-arch Docker images for every major platform:
Run as an ephemeral mail catcher that exits with a status code — for CI without the GitHub Action:

| Platform | Architectures | Format |
|----------|--------------|--------|
| **Linux** | x86_64, aarch64, armv7 | gnu + musl (static) |
| **macOS** | x86_64 (Intel), aarch64 (Apple Silicon) | native |
| **Docker** | amd64, arm64, arm/v7 | multi-arch manifest |
```sh
rustmail assert --min-count=2 --subject="Welcome" --timeout=30s
```

Static musl builds run on Alpine, distroless, and any Linux — no glibc required.
## Configuration

### Developer Experience
Configure via CLI flags, `RUSTMAIL_*` environment variables, or a TOML config file:

- **Keyboard shortcuts** — `j`/`k` navigate, `d` delete, `D` clear all, `/` search, `Esc` close
- **Dark/light theme** — toggle with localStorage persistence
- **Flexible config** — CLI flags, `RUSTMAIL_*` env vars, or TOML config file
- **Retention policies** — `--retention` (hours) and `--max-messages` with automatic enforcement
```sh
rustmail serve --bind 0.0.0.0 --smtp-port 2525 --http-port 9025
rustmail serve --retention 24 --max-messages 1000
rustmail serve --webhook-url https://hooks.example.com/email
```

## GitHub Action
See the full [configuration reference](https://docs.rustmail.app/configuration/cli-flags).

```yaml
- name: Start RustMail
uses: rustmailapp/rustmail-action@v1
## TUI

- name: Run tests (your app sends emails to localhost:1025)
run: npm test
<p align="center">
<img src="docs/public/screenshot-tui.png" alt="RustMail TUI" width="600" />
</p>

- name: Assert emails were sent
uses: rustmailapp/rustmail-action@v1
with:
mode: assert
assert-count: 1
assert-subject: "Welcome"
```
A terminal UI that connects to a running RustMail instance. Also available as a [Neovim plugin](https://docs.rustmail.app/integrations/neovim).

## Documentation

Full docs at [docs.rustmail.app](https://docs.rustmail.app) ([source](docs/)):
Full docs at [docs.rustmail.app](https://docs.rustmail.app):

- [Getting Started](docs/getting-started/introduction.md)
- [Configuration](docs/configuration/cli-flags.md)
- [CI Integration](docs/ci-integration/rest-assertions.md)
- [Features](docs/features/webhooks.md) — Webhooks, Export, Release, Email Auth, WebSocket
- [Integrations](docs/integrations/neovim.md) — Neovim Plugin
- [API Reference](docs/api/index.md)
- [Docker](docs/getting-started/docker.md)
- [Architecture](docs/getting-started/architecture.md)
- [Getting Started](https://docs.rustmail.app/getting-started/introduction)
- [Docker](https://docs.rustmail.app/getting-started/docker)
- [Configuration](https://docs.rustmail.app/configuration/cli-flags)
- [CI Integration](https://docs.rustmail.app/ci-integration/rest-assertions)
- [API Reference](https://docs.rustmail.app/api/)

## License

Expand Down
102 changes: 102 additions & 0 deletions docker/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# RustMail

A fast, feature-rich SMTP mail catcher built in Rust. Single binary. Persistent storage. Modern UI. CI-ready.

**GitHub:** [rustmailapp/rustmail](https://github.com/rustmailapp/rustmail) · **Docs:** [docs.rustmail.app](https://docs.rustmail.app)

## Quick Start

```sh
docker run -p 1025:1025 -p 8025:8025 smyile/rustmail:latest
```

Point your app's SMTP at `localhost:1025`, then open [localhost:8025](http://localhost:8025). Emails show up in real time.

## Docker Compose

```yaml
services:
rustmail:
image: smyile/rustmail:latest
ports:
- "1025:1025"
- "8025:8025"
security_opt:
- no-new-privileges:true
volumes:
- rustmail-data:/data
restart: unless-stopped

volumes:
rustmail-data:
```

## Supported Architectures

| Architecture | Tag |
|---|---|
| `linux/amd64` | `latest` |
| `linux/arm64` | `latest` |
| `linux/arm/v7` | `latest` |

Multi-arch manifest — Docker automatically pulls the correct image for your platform.

## Persistence

The image stores emails at `/data/rustmail.db` by default. Mount a volume to `/data` to persist emails across container restarts.

For ephemeral (CI) usage, skip the volume:

```sh
docker run -p 1025:1025 -p 8025:8025 -e RUSTMAIL_EPHEMERAL=true smyile/rustmail:latest
```

## Environment Variables

All configuration is done via `RUSTMAIL_*` environment variables:

| Variable | Default | Description |
|---|---|---|
| `RUSTMAIL_BIND` | `0.0.0.0` | IP address to bind listeners to |
| `RUSTMAIL_SMTP_PORT` | `1025` | SMTP listener port |
| `RUSTMAIL_HTTP_PORT` | `8025` | HTTP and WebSocket port |
| `RUSTMAIL_DB_PATH` | `/data/rustmail.db` | Path to SQLite database file |
| `RUSTMAIL_RETENTION` | `0` | Auto-delete messages after N hours (`0` = keep forever) |
| `RUSTMAIL_MAX_MESSAGES` | `0` | Max messages to retain (`0` = unlimited) |
| `RUSTMAIL_MAX_MESSAGE_SIZE` | `10485760` | Max accepted message size in bytes (10 MB) |
| `RUSTMAIL_EPHEMERAL` | `false` | Use in-memory SQLite (no data written to disk) |
| `RUSTMAIL_WEBHOOK_URL` | — | URL to POST on every new message |
| `RUSTMAIL_LOG_LEVEL` | `info` | Log verbosity: `trace`, `debug`, `info`, `warn`, `error` |
| `RUSTMAIL_RELEASE_HOST` | — | Allowed SMTP target for email release (`host:port`) |

## Ports

| Port | Protocol | Description |
|---|---|---|
| `1025` | TCP | SMTP server |
| `8025` | TCP | HTTP API, WebSocket, and Web UI |

## Features

- **Persistent storage** — SQLite-backed, emails survive restarts
- **Full-text search** — FTS5 across subject, body, sender, and recipients
- **Real-time updates** — WebSocket pushes new emails to the UI instantly
- **Modern UI** — dark-mode-first, looks and feels like a real email client
- **DKIM/SPF/DMARC/ARC display** — parses authentication headers with color-coded badges
- **REST assertion endpoints** — `GET /api/v1/assert/count?min=1&subject=Welcome`
- **Webhook notifications** — fire-and-forget POST on new email
- **Email release** — forward captured emails to a real SMTP server
- **Export** — download as EML or JSON
- **Retention policies** — auto-purge by age or count

## Image Details

- **Base image:** `alpine:3.21`
- **Runs as:** non-root user `rustmail`
- **Healthcheck:** built-in (HTTP check every 30s)
- **Volume:** `/data`
- **Security:** `no-new-privileges` recommended

## License

Licensed under either of [MIT](https://github.com/rustmailapp/rustmail/blob/master/LICENSE-MIT) or [Apache 2.0](https://github.com/rustmailapp/rustmail/blob/master/LICENSE-APACHE), at your option.
3 changes: 2 additions & 1 deletion docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ const reference: DefaultTheme.SidebarItem[] = [
{ text: "WebSocket", link: "/features/websocket" },
{ text: "Keyboard Shortcuts", link: "/features/keyboard-shortcuts" },
{ text: "Email Authentication", link: "/features/email-auth" },
{ text: "Terminal UI", link: "/features/tui" },
],
},
{
Expand Down Expand Up @@ -151,7 +152,7 @@ export default defineConfig({

footer: {
message: "Released under the MIT / Apache 2.0 License.",
copyright: "Copyright © 2026 Davide Tacchini",
copyright: "Copyright © 2026 Smyile",
},
},
});
4 changes: 3 additions & 1 deletion docs/ci-integration/github-action.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,11 @@ By default the action downloads the latest RustMail release. Pin to a specific v
```yaml
- uses: rustmailapp/rustmail-action@v1
with:
version: v0.1.0
version: v0.2.1 # pin to a release tag
```

Check [GitHub Releases](https://github.com/rustmailapp/rustmail/releases) for available versions.

::: tip
For non-GitHub CI systems, use the [CLI Assert Mode](/ci-integration/cli-assert) or [REST Assertions](/ci-integration/rest-assertions) directly.
:::
Loading