diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cf6fdd3..e642cac 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ # Contributing to RustMail -Thank you for your interest in contributing to RustMail! This document provides guidelines and instructions for contributing. +Thank you for your interest in contributing to RustMail! ## Getting Started @@ -40,16 +40,16 @@ make dev ## Reporting Bugs -Before filing a bug report, please check [existing issues](https://github.com/rustmailapp/rustmail/issues). Then use the [Bug Report](https://github.com/rustmailapp/rustmail/issues/new?template=bug_report.yml) template — it will guide you through the required information. +Before filing a bug report, please check [existing issues](https://github.com/rustmailapp/rustmail/issues). Then use the [Bug Report](https://github.com/rustmailapp/rustmail/issues/new?template=bug_report.yml) template: it will guide you through the required information. ## Submitting Changes ### Branch Naming -- `feat/short-description` — New features -- `fix/short-description` — Bug fixes -- `refactor/short-description` — Code restructuring -- `docs/short-description` — Documentation changes +- `feat/short-description`: New features +- `fix/short-description`: Bug fixes +- `refactor/short-description`: Code restructuring +- `docs/short-description`: Documentation changes ### Code Style @@ -59,7 +59,7 @@ Before filing a bug report, please check [existing issues](https://github.com/ru - No `unwrap()` in library crates (`crates/rustmail-smtp`, `crates/rustmail-storage`, `crates/rustmail-api`) - `unwrap()` is acceptable in tests and `rustmail-server` (the binary) - Error handling: `thiserror` in libraries, `anyhow` in the binary -- Async everywhere — no blocking calls on the tokio runtime +- Async everywhere: no blocking calls on the tokio runtime **TypeScript (UI):** - Run `pnpm exec prettier --write .` before committing diff --git a/README.md b/README.md index 169baf4..949c33c 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ cd rustmail && make build ### Pre-built Binaries -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. +Download from [GitHub Releases](https://github.com/rustmailapp/rustmail/releases/latest): Linux x86_64/aarch64/armv7 (glibc and musl builds), macOS (Intel + Apple Silicon), and multi-arch Docker images. ## Features @@ -80,7 +80,7 @@ Download from [GitHub Releases](https://github.com/rustmailapp/rustmail/releases |---|---| | **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. | +| **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. | @@ -107,7 +107,7 @@ Download from [GitHub Releases](https://github.com/rustmailapp/rustmail/releases ## CLI Assert Mode -Run as an ephemeral mail catcher that exits with a status code — for CI without the GitHub Action: +Run as an ephemeral mail catcher that exits with a status code, for CI without the GitHub Action: ```sh rustmail assert --min-count=2 --subject="Welcome" --timeout=30s @@ -124,7 +124,7 @@ rustmail serve --webhook-url https://hooks.example.com/email rustmail serve --smtp-tls-cert ./certs/localhost.pem --smtp-tls-key ./certs/localhost-key.pem ``` -STARTTLS is optional and uses the same SMTP port via explicit upgrade. RustMail advertises `STARTTLS` only when both the certificate and private key are configured. +STARTTLS uses the same SMTP port via explicit upgrade. Both `--smtp-tls-cert` and `--smtp-tls-key` are required; setting only one fails startup, and `STARTTLS` is advertised only when both are set. See the full [configuration reference](https://docs.rustmail.app/configuration/cli-flags). diff --git a/SECURITY.md b/SECURITY.md index 38784ab..743895d 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -37,7 +37,7 @@ RustMail applies the following security practices: - **Dependency pinning:** Workspace-level dependency management with locked versions - **Input validation:** Bounded SMTP reads, FTS5 query sanitization, filename sanitization - **Network security:** Default bind to `127.0.0.1`, configurable via `--bind` -- **CORS:** No CORS headers are sent — browsers block all cross-origin access to the API; the UI is served same-origin +- **CORS:** No CORS headers are sent; browsers block all cross-origin access to the API, and the UI is served same-origin - **Connection timeouts:** Per-operation idle timeout (60s) and overall session cap (5 min) on SMTP connections - **Rate limiting:** Semaphore-based limits on SMTP sessions (100) and WebSocket connections (50) - **Release safeguards:** Email release requires explicit `--release-host` flag with port allowlist @@ -56,6 +56,6 @@ The following are in scope for security reports: The following are out of scope: -- RustMail is a **development tool** — it is not designed to be exposed to the public internet +- RustMail is a **development tool**: it is not designed to be exposed to the public internet - Social engineering attacks - Denial of service via legitimate high-volume email sending diff --git a/distribution.md b/distribution.md index 525f0f8..5ad886a 100644 --- a/distribution.md +++ b/distribution.md @@ -82,7 +82,7 @@ Reference: https://docs.brew.sh/Acceptable-Formulae ## Future Channels (not planned yet) -- **AUR** (Arch Linux) — PKGBUILD, straightforward for Rust binaries -- **Nix** — nixpkgs PR, similar community process to Homebrew -- **Scoop** (Windows) — JSON manifest in a bucket repo -- **Snap / Flatpak** — lower priority, Docker covers Linux well +- **AUR** (Arch Linux): PKGBUILD, straightforward for Rust binaries +- **Nix**: nixpkgs PR, similar community process to Homebrew +- **Scoop** (Windows): JSON manifest in a bucket repo +- **Snap / Flatpak**: lower priority, Docker covers Linux well diff --git a/docker/README.md b/docker/README.md index 1aa0cad..1846a28 100644 --- a/docker/README.md +++ b/docker/README.md @@ -39,7 +39,7 @@ volumes: | `linux/arm64` | `latest` | | `linux/arm/v7` | `latest` | -Multi-arch manifest — Docker automatically pulls the correct image for your platform. +Multi-arch manifest: Docker automatically pulls the correct image for your platform. ## Persistence @@ -78,10 +78,10 @@ All configuration is done via `RUSTMAIL_*` environment variables: | `1025` | TCP | SMTP server | | `8025` | TCP | HTTP API, WebSocket, and Web UI | -`STARTTLS` uses the normal SMTP port and is advertised only when both `RUSTMAIL_SMTP_TLS_CERT` and `RUSTMAIL_SMTP_TLS_KEY` are set. - ## Optional STARTTLS +STARTTLS uses the normal SMTP port via explicit upgrade. Both `RUSTMAIL_SMTP_TLS_CERT` and `RUSTMAIL_SMTP_TLS_KEY` are required; setting only one fails startup, and `STARTTLS` is advertised only when both are set. + Mount your TLS files read-only and set both environment variables: ```sh @@ -97,16 +97,16 @@ Clients must issue `EHLO`, then `STARTTLS`, and then `EHLO` again after the TLS ## 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 +- **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 diff --git a/docs/ci-integration/cli-assert.md b/docs/ci-integration/cli-assert.md index 47dd9f7..69fb6f8 100644 --- a/docs/ci-integration/cli-assert.md +++ b/docs/ci-integration/cli-assert.md @@ -36,8 +36,8 @@ rustmail assert --sender=notifications@example.com --min-count=1 | Code | Meaning | |------|---------| -| `0` | Assertion passed — required emails were received | -| `1` | Assertion failed — timeout or criteria not met | +| `0` | Assertion passed: required emails were received | +| `1` | Assertion failed: timeout or criteria not met | ## How It Works diff --git a/docs/ci-integration/github-action.md b/docs/ci-integration/github-action.md index 40d19af..ecae672 100644 --- a/docs/ci-integration/github-action.md +++ b/docs/ci-integration/github-action.md @@ -21,8 +21,8 @@ RustMail provides a GitHub Action for email assertion in CI pipelines. The action has two modes: -1. **`start`** (default) — Downloads the RustMail binary, starts an ephemeral SMTP server in the background, and waits until it's ready. -2. **`assert`** — Checks captured emails against your filters using the REST assertion API. +1. **`start`** (default): Downloads the RustMail binary, starts an ephemeral SMTP server in the background, and waits until it's ready. +2. **`assert`**: Checks captured emails against your filters using the REST assertion API. ## Inputs @@ -30,7 +30,7 @@ The action has two modes: | Input | Default | Description | |-------|---------|-------------| -| `mode` | `start` | Set to `start` (or omit — it's the default) | +| `mode` | `start` | Set to `start` (or omit, it's the default) | | `smtp-port` | `1025` | SMTP port to listen on | | `http-port` | `8025` | HTTP/API port | | `version` | `latest` | RustMail version (e.g., `v0.1.0`) | diff --git a/docs/ci-integration/rest-assertions.md b/docs/ci-integration/rest-assertions.md index 9160c75..b7c03e0 100644 --- a/docs/ci-integration/rest-assertions.md +++ b/docs/ci-integration/rest-assertions.md @@ -1,6 +1,6 @@ # REST Assertions -RustMail exposes a purpose-built assertion endpoint for CI pipelines. It returns `200 OK` when conditions are met and `417 Expectation Failed` otherwise — designed for `curl -f`. +RustMail exposes a purpose-built assertion endpoint for CI pipelines. It returns `200 OK` when conditions are met and `417 Expectation Failed` otherwise. Designed for `curl -f`. ## Endpoint @@ -37,10 +37,10 @@ curl -f "localhost:8025/api/v1/assert/count?min=1&recipient=admin@example.com" ## Response ```json -// 200 OK — assertion passed +// 200 OK: assertion passed { "ok": true, "count": 2 } -// 417 Expectation Failed — assertion failed +// 417 Expectation Failed: assertion failed { "ok": false, "count": 0, "expected_min": 1, "expected_max": null } ``` diff --git a/docs/configuration/cli-flags.md b/docs/configuration/cli-flags.md index 2a20539..376d056 100644 --- a/docs/configuration/cli-flags.md +++ b/docs/configuration/cli-flags.md @@ -21,7 +21,7 @@ Configuration is resolved in this precedence order: **CLI flags > environment va | `--release-host` | `RUSTMAIL_RELEASE_HOST` | — | Allowed SMTP target for email release in `host:port` format (e.g. `smtp.example.com:587`). Release is disabled unless set. | | `--config` | — | — | Path to an optional TOML configuration file. | -`STARTTLS` is advertised on the normal SMTP port only when both TLS paths are configured. After the client upgrades the connection, it must send `EHLO` again before continuing the session. +`STARTTLS` is advertised on the normal SMTP port only when both TLS paths are configured; setting only one fails startup. After the client upgrades the connection, it must send `EHLO` again before continuing the session. ## Examples diff --git a/docs/configuration/toml-config.md b/docs/configuration/toml-config.md index 183f0e9..61c7d5d 100644 --- a/docs/configuration/toml-config.md +++ b/docs/configuration/toml-config.md @@ -24,15 +24,15 @@ webhook_url = "https://hooks.example.com/email" release_host = "smtp.example.com:587" ``` -Configure both `smtp_tls_cert` and `smtp_tls_key` to enable optional SMTP `STARTTLS` on the existing SMTP listener. If either value is missing, RustMail fails to start. +Configure both `smtp_tls_cert` and `smtp_tls_key` to enable optional SMTP `STARTTLS` on the existing SMTP listener. Setting only one of the two fails startup. ## Precedence Configuration is resolved in this order (highest wins): -1. **CLI flags** — `--smtp-port 2525` -2. **Environment variables** — `RUSTMAIL_SMTP_PORT=2525` -3. **TOML config file** — `smtp_port = 2525` -4. **Defaults** — `1025` +1. **CLI flags**: `--smtp-port 2525` +2. **Environment variables**: `RUSTMAIL_SMTP_PORT=2525` +3. **TOML config file**: `smtp_port = 2525` +4. **Defaults**: `1025` This means you can set baseline config in a TOML file and override specific values with environment variables or CLI flags per-deployment. diff --git a/docs/features/export.md b/docs/features/export.md index 4b7d648..c9c1625 100644 --- a/docs/features/export.md +++ b/docs/features/export.md @@ -12,7 +12,7 @@ GET /api/v1/messages/{id}/export?format=eml|json ### EML (default) -Returns the raw RFC 822 message — the original bytes as received by the SMTP server. +Returns the raw RFC 822 message: the original bytes as received by the SMTP server. ```sh curl -O -J "localhost:8025/api/v1/messages/01ARZ3NDEKTSV4RRFFQ69G5FAV/export" diff --git a/docs/features/html-preview.md b/docs/features/html-preview.md index 882c18e..c2894cc 100644 --- a/docs/features/html-preview.md +++ b/docs/features/html-preview.md @@ -1,6 +1,6 @@ # HTML Preview -The message detail panel renders the email's HTML body so you see it the way a real client would — including remote images, which load automatically. +The message detail panel renders the email's HTML body so you see it the way a real client would. ## Rendering model @@ -18,10 +18,10 @@ Because the iframe has no `allow-scripts` or `allow-same-origin`, email content ## Remote images -Remote images load automatically — RustMail is a development mail catcher, so the "sender" is your own application and the priority is showing the message exactly as sent, with no extra click. Inline images referenced by `cid:` are served from the message's attachments. +Remote images load automatically. RustMail is a development mail catcher, so the "sender" is your own application and the priority is showing the message exactly as sent, with no extra click. Inline images referenced by `cid:` are served from the message's attachments. Requests for remote content are sent with `Referrer-Policy: no-referrer`, so the originating URL is not leaked. ::: warning Exposed instances -Loading remote content means opening a message triggers an outbound request to whatever URLs it references. On the default loopback bind (`127.0.0.1`) this is harmless. If you bind RustMail to a non-loopback address (`RUSTMAIL_BIND`) and let it receive untrusted mail, be aware that viewing a message will fetch remote content from the viewer's network. The API is unauthenticated by design — keep exposed instances behind your own access controls. +Loading remote content means opening a message triggers an outbound request to whatever URLs it references. On the default loopback bind (`127.0.0.1`) this is harmless. If you bind RustMail to a non-loopback address (`RUSTMAIL_BIND`) and let it receive untrusted mail, be aware that viewing a message will fetch remote content from the viewer's network. The API is unauthenticated by design: keep exposed instances behind your own access controls. ::: diff --git a/docs/features/release.md b/docs/features/release.md index e960ad6..3bbb2a6 100644 --- a/docs/features/release.md +++ b/docs/features/release.md @@ -16,7 +16,7 @@ Or via environment variable: RUSTMAIL_RELEASE_HOST=smtp.example.com:587 rustmail serve ``` -The `--release-host` value is an allowlist — only this exact host (and port, if specified) can be used as a release target. This prevents SSRF. +The `--release-host` value is an allowlist: only this exact host (and port, if specified) can be used as a release target. This prevents SSRF. ## Endpoint @@ -45,11 +45,11 @@ Success response: Release is locked down to prevent misuse: -1. **Disabled unless configured** — returns `403` if `--release-host` is not set. -2. **Host allowlist** — the `host` in the request body must exactly match the configured `--release-host` host. Mismatches return `403`. -3. **Port allowlist** — only standard SMTP ports are accepted: `25`, `465`, `587`, `2525`. Other ports return `400`. -4. **Port pinning** — if `--release-host` includes a port (e.g., `smtp.example.com:587`), the request must use that exact port. Mismatches return `403`. -5. **TLS required** — connections use `lettre::relay()` with certificate verification. TLS failures return `502`. +1. **Disabled unless configured**: returns `403` if `--release-host` is not set. +2. **Host allowlist**: the `host` in the request body must exactly match the configured `--release-host` host. Mismatches return `403`. +3. **Port allowlist**: only standard SMTP ports are accepted: `25`, `465`, `587`, `2525`. Other ports return `400`. +4. **Port pinning**: if `--release-host` includes a port (e.g., `smtp.example.com:587`), the request must use that exact port. Mismatches return `403`. +5. **TLS required**: connections use `lettre::relay()` with certificate verification. TLS failures return `502`. ## Errors diff --git a/docs/features/webhooks.md b/docs/features/webhooks.md index 1a9264a..c97888b 100644 --- a/docs/features/webhooks.md +++ b/docs/features/webhooks.md @@ -41,7 +41,7 @@ Each webhook is an HTTP POST with `Content-Type: application/json`. The body is | Field | Type | Description | |-------|------|-------------| -| `id` | string | ULID — use this to fetch the full message via the REST API | +| `id` | string | ULID: use this to fetch the full message via the REST API | | `sender` | string | Envelope sender address | | `recipients` | string | JSON-encoded array of recipient addresses | | `subject` | string \| null | Parsed subject line | @@ -54,10 +54,10 @@ Each webhook is an HTTP POST with `Content-Type: application/json`. The body is ## Behavior -- **Fire-and-forget** — the webhook is dispatched in a background task and does not block message processing. -- **5-second timeout** — if the endpoint doesn't respond within 5 seconds, the request is abandoned. -- **No retries** — failed deliveries are logged as warnings and not retried. -- **No queue** — webhooks are sent in real time as messages arrive. If the endpoint is down, those notifications are lost. +- **Fire-and-forget**: the webhook is dispatched in a background task and does not block message processing. +- **5-second timeout**: if the endpoint doesn't respond within 5 seconds, the request is abandoned. +- **No retries**: failed deliveries are logged as warnings and not retried. +- **No queue**: webhooks are sent in real time as messages arrive. If the endpoint is down, those notifications are lost. ## Example: Slack Notification diff --git a/docs/features/websocket.md b/docs/features/websocket.md index 95524ac..d50b0eb 100644 --- a/docs/features/websocket.md +++ b/docs/features/websocket.md @@ -1,6 +1,6 @@ # WebSocket -RustMail pushes real-time events over a WebSocket connection. The UI uses this for live inbox updates — you can use the same endpoint for custom integrations. +RustMail pushes real-time events over a WebSocket connection. The UI uses this for live inbox updates. You can use the same endpoint for custom integrations. ## Endpoint @@ -8,7 +8,7 @@ RustMail pushes real-time events over a WebSocket connection. The UI uses this f ws://localhost:8025/api/v1/ws ``` -The connection is **one-way push** — the server sends events to the client. Messages sent by the client are ignored. +The connection is **one-way push**: the server sends events to the client. Messages sent by the client are ignored. ## Events diff --git a/docs/getting-started/architecture.md b/docs/getting-started/architecture.md index b89de3f..787d592 100644 --- a/docs/getting-started/architecture.md +++ b/docs/getting-started/architecture.md @@ -9,7 +9,7 @@ RustMail is a Cargo workspace with five crates, each with a single responsibilit | `rustmail-smtp` | TCP listener, ESMTP handshake, emits parsed messages over a tokio broadcast channel | | `rustmail-storage` | sqlx + SQLite repository, FTS5 index, retention enforcement | | `rustmail-api` | Axum routes, WebSocket broadcast, bridges HTTP to storage and SMTP channel | -| `rustmail-server` | Binary entry point — parses config, wires all crates, embeds UI assets | +| `rustmail-server` | Binary entry point: parses config, wires all crates, embeds UI assets | | `rustmail-tui` | Terminal UI client (optional, connects to a running RustMail instance) | ## Data Flow @@ -33,11 +33,11 @@ rustmail-api | Decision | Rationale | |----------|-----------| -| Custom SMTP over samotop | A mail catcher needs minimal ESMTP — samotop is over-engineered for this use case | +| Custom SMTP over samotop | A mail catcher needs minimal ESMTP; samotop is over-engineered for this use case | | sqlx over rusqlite | Async-native, compile-time checked queries | | ULID over UUID/integer | Time-sortable without needing a `created_at` index | | SolidJS over Leptos/Yew | Contributor-friendly (JS/TS), smaller bundles, better ecosystem | -| rust-embed for UI | Single binary distribution — no separate static file server needed | +| rust-embed for UI | Single binary distribution: no separate static file server needed | | Ephemeral mode via `--ephemeral` | Same sqlx code path, just `sqlite::memory:` connection string | ## Frontend diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md index a546383..95d4b14 100644 --- a/docs/getting-started/installation.md +++ b/docs/getting-started/installation.md @@ -24,7 +24,7 @@ Or with any AUR helper (`paru -S rustmail-bin`, `pacman -S rustmail-bin` via cha ## Pre-built Binaries -Download from [GitHub Releases](https://github.com/rustmailapp/rustmail/releases/latest) — Linux (x86_64, aarch64, armv7 — glibc + musl), macOS (Intel + Apple Silicon). +Download from [GitHub Releases](https://github.com/rustmailapp/rustmail/releases/latest): Linux x86_64/aarch64/armv7 (glibc and musl builds), macOS (Intel + Apple Silicon). ## From Source diff --git a/docs/getting-started/introduction.md b/docs/getting-started/introduction.md index a642694..8ac7345 100644 --- a/docs/getting-started/introduction.md +++ b/docs/getting-started/introduction.md @@ -2,7 +2,7 @@ RustMail is a local SMTP mail catcher. Point your app's outbound email at it during development or testing, and every message lands in a web UI instead of a real inbox. -It ships as a single binary — the frontend is compiled in, so there's nothing else to install or run. +It ships as a single binary: the frontend is compiled in, so there's nothing else to install or run. ![RustMail web UI](/screenshot-web.png) @@ -10,11 +10,11 @@ It ships as a single binary — the frontend is compiled in, so there's nothing Most alternatives are either unmaintained (MailHog), minimal (MailCrab), or cloud-only (Mailtrap). RustMail is a self-hosted option that doesn't cut corners: -- **Emails stick around** — SQLite by default, survives restarts. Or `--ephemeral` for throwaway CI runs. -- **Search that works** — FTS5 across subject, body, sender, and recipients. -- **Real-time UI** — WebSocket push, dark mode, keyboard shortcuts. Feels like a proper email client. -- **CI-first** — REST assertion endpoints, a CLI assert mode, and a GitHub Action. -- **Zero runtime deps** — one binary, frontend baked in. Also ships as a ~8 MB Docker image. +- **Emails stick around**: SQLite by default, survives restarts. Or `--ephemeral` for throwaway CI runs. +- **Search that works**: FTS5 across subject, body, sender, and recipients. +- **Real-time UI**: WebSocket push, dark mode, keyboard shortcuts. Feels like a proper email client. +- **CI-first**: REST assertion endpoints, a CLI assert mode, and a GitHub Action. +- **Zero runtime deps**: one binary, frontend baked in. Also ships as a ~8 MB Docker image. ## How It Works diff --git a/docs/getting-started/quick-start.md b/docs/getting-started/quick-start.md index f5573d1..a6bcd5d 100644 --- a/docs/getting-started/quick-start.md +++ b/docs/getting-started/quick-start.md @@ -11,10 +11,10 @@ docker run -p 1025:1025 -p 8025:8025 smyile/rustmail:latest ### Binary ```sh -# Default — SMTP on 1025, UI on 8025, SQLite at ./rustmail.db +# Default: SMTP on 1025, UI on 8025, SQLite at ./rustmail.db rustmail -# Ephemeral mode for CI — in-memory, nothing written to disk +# Ephemeral mode for CI: in-memory, nothing written to disk rustmail serve --ephemeral --smtp-port 1025 --http-port 8025 ``` @@ -44,7 +44,7 @@ swaks --to test@example.com --server localhost --port 1025 --tls printf "EHLO test\r\nMAIL FROM:\r\nRCPT TO:\r\nDATA\r\nSubject: Hello\r\n\r\nTest body\r\n.\r\nQUIT\r\n" | nc localhost 1025 ``` -The email appears in the UI in real time via WebSocket push — no page refresh needed. +The email appears in the UI in real time via WebSocket push, no page refresh needed. ## What's Next? diff --git a/docs/index.md b/docs/index.md index 377a4a0..578d4bc 100644 --- a/docs/index.md +++ b/docs/index.md @@ -27,7 +27,7 @@ features: details: Full-text search (FTS5) across subject, body, sender, and recipients. - icon: '' title: Real-time inbox - details: WebSocket push — new mail shows up the moment it arrives. Dark mode included. + details: WebSocket push, so new mail shows up the moment it arrives. Dark mode included. - icon: '' title: Built for CI details: REST assertion endpoints, a CLI assert mode, and a GitHub Action for your test pipelines. diff --git a/ui/README.md b/ui/README.md index 0a3a6c0..73ead3d 100644 --- a/ui/README.md +++ b/ui/README.md @@ -1,28 +1,42 @@ -## Usage +# RustMail UI -```bash -$ npm install # or pnpm install or yarn install +The RustMail web client: SolidJS + TypeScript + Tailwind CSS, built with Vite. + +This UI is not deployed standalone. `make build` compiles it to `ui/dist`, which the `rustmail-api` crate embeds into the Rust binary via `rust-embed` and serves at the HTTP port. + +## Package Manager + +pnpm only (`pnpm-lock.yaml` is the sole lockfile). + +```sh +pnpm install ``` -### Learn more on the [Solid Website](https://solidjs.com) and come chat with us on our [Discord](https://discord.com/invite/solidjs) +## Development -## Available Scripts +Run the Vite dev server together with the Rust backend from the repository root: -In the project directory, you can run: +```sh +make dev # UI dev server + backend concurrently +make dev-ui # UI dev server only (pnpm dev) +``` -### `npm run dev` +The dev server listens on `http://localhost:3000` and proxies `/api` and the `/api/v1/ws` WebSocket to the backend on `http://localhost:8025`, so a backend must be running for data to load. -Runs the app in the development mode.
-Open [http://localhost:3000](http://localhost:3000) to view it in the browser. +## Scripts -### `npm run build` +```sh +pnpm dev # Vite dev server on :3000 +pnpm build # type-check (tsc -b) then build to ui/dist +pnpm preview # preview the production build +``` -Builds the app for production to the `dist` folder.
-It correctly bundles Solid in production mode and optimizes the build for the best performance. +## Production Build -The build is minified and the filenames include the hashes.
-Your app is ready to be deployed! +From the repository root: -## Deployment +```sh +make build +``` -Learn more about deploying your application with the [documentations](https://vite.dev/guide/static-deploy.html) +This runs `pnpm build` to produce `ui/dist`, then compiles the Rust binary with those assets embedded.