Mateusz Gruszczyński c160f25e4c
RustBox CI / rustbox (push) Canceled after 0s
v0.2.28
2026-11-06 12:30:57 +01:00
2026-10-31 14:36:07 +01:00
2026-11-01 20:40:40 +01:00
2026-11-06 12:30:57 +01:00
2026-10-31 14:36:07 +01:00
2026-11-04 23:34:09 +01:00
2026-11-04 20:44:31 +01:00
2026-11-06 12:30:57 +01:00
2026-11-06 12:30:57 +01:00
2026-10-31 00:37:16 +01:00
2026-11-01 20:40:40 +01:00
2026-10-29 15:24:59 +01:00
2026-10-30 14:42:01 +01:00
2026-11-01 20:40:40 +01:00
2026-11-06 12:30:57 +01:00
2026-11-06 12:30:57 +01:00
2026-11-06 12:30:57 +01:00
2026-11-04 14:59:03 +01:00
2026-11-04 23:34:09 +01:00
2026-11-01 20:40:40 +01:00
2026-11-05 08:33:13 +01:00
2026-11-05 08:33:13 +01:00
2026-10-29 15:24:59 +01:00
2026-11-05 08:33:13 +01:00
2026-11-06 12:30:57 +01:00
2026-11-06 12:30:57 +01:00
2026-11-06 12:30:57 +01:00
2026-10-31 22:36:28 +01:00
2026-10-31 14:36:07 +01:00

RustBox 0.2.28

Fast, self-hosted webmail written in Rust. RustBox connects to any IMAP/SMTP server and ships as one executable with the whole interface built in, so there is no Node.js, PHP or separate frontend to deploy.

RustBox

Highlights

  • Three-pane mail client with folder tree, threads, bulk actions, drag and drop, snooze, undo send and undo move.
  • HTML, plain-text and Markdown composer with inline images, drafts, identities, signatures and OpenPGP.
  • Server-side filters (Sieve) with a visual editor, exceptions, templates and Apply now for mail that is already in a folder.
  • Calendar with reminders and ICS import/export, contacts with groups and vCard.
  • Realtime new-mail updates and desktop notifications.
  • 43 interface styles, from Gmail- and Outlook-inspired to Dracula and Windows XP, plus a fully adjustable custom style, light/dark/time-of-day themes and a mobile layout.
  • SQLite, MySQL or PostgreSQL; local disk, shared filesystem or S3 storage; built-in search or OpenSearch; optional cluster mode.
  • English and Polish interface.

The complete list, with screenshots, is in docs/FEATURES.md.

Filters Dracula style
Calendar Windows XP style

Quick start

# development server with config.dev.toml
./dev.sh
# then open http://127.0.0.1:3001

Production build and systemd installation:

./scripts/build-release.sh
sudo ./scripts/install-systemd.sh
sudo systemctl enable --now rustbox

Docker Compose examples (SQLite; PostgreSQL + S3 + OpenSearch; MySQL + NFS + OpenSearch; HA with Garage) are in docker/.

Documentation

Document Contents
Features Everything RustBox does, with screenshots
Storage and search Backends, shared storage, migrations, failure modes
System diagnostics Administrator diagnostics endpoint
IMAP connection pool How IMAP sessions are reused
Docker Compose deployments
Releasing · Validation Release process and checks
Implementation status Detailed technical status
Changelog Changes per version
Database migrations Schema baseline and upgrades

Configure mail servers

Mail endpoints are configured by the administrator, not on the login screen. Users enter only email and password.

Login brute-force protection is on by default and configured in the [login_rate_limit] section (or LOGIN_RATE_LIMIT__* environment variables): a per-IP token bucket on the login endpoint plus failed-attempt lockouts per mailbox and per client IP with exponential back-off. Locked attempts are answered with 429 and Retry-After without contacting the IMAP server. Counters are in memory per node. See config.example.toml for every option.

Session cookies get the Secure attribute according to cookie_secure (default auto): only when the browser reached RustBox over HTTPS, detected from a trusted proxy's X-Forwarded-Proto/Forwarded/X-Forwarded-Ssl header or the browser's Origin/Referer. Plain-HTTP installations keep working; set cookie_secure = "always" behind an HTTPS proxy that does not forward the scheme.

Edit config.dev.toml before the first run:

default_theme = "system" # system | dark | light
default_interface_skin = "rustbox" # rustbox | gmail | outlook | apple-mail | github | slack | dracula | catppuccin | tokyo-night | paper | terminal | aqua | material | retro | winxp | osx | ios6 | win7 | frutiger-aero | beos | ... (full list in config.example.toml)
log_level = "info" # RustBox app level; dependencies default to WARN. RUST_LOG can override the full filter.
# Recommended dev filter: warn,rustbox=debug,tower_http=info
# Avoid a global RUST_LOG=debug/info unless diagnosing a dependency: third-party
# crates may emit internal filesystem/source paths in verbose records.
# Mail hosts may be omitted: IMAP defaults to localhost:993 and SMTP to localhost:587.
# imap_host = "localhost"
# smtp_host = "localhost"
tls_enabled = true
# tls_verify = true # omitted: false only for localhost/loopback, true for remote hosts
# Upload limit for one raw attachment.
max_attachment_size_mb = 10
# Hard limit for the complete rendered RFC822/MIME message submitted to the MTA.
max_message_size_mb = 50
# Memory guard for messages with attachments. Sending a message or saving an IMAP
# draft snapshot loads every attachment into RAM and the MIME builder keeps several
# copies (about 3-4x the message size). Attachment sets that cannot fit
# max_message_size_mb are rejected with 413 before any file is read, and at most
# max_concurrent_message_builds requests may hold attachments in memory at once;
# the others wait up to message_build_wait_secs and then get 503.
# Rough peak RAM = max_concurrent_message_builds x 4 x max_message_size_mb.
max_concurrent_message_builds = 4
message_build_wait_secs = 30

# Optional features exposed by the server/UI.
sieve_enabled = true
diagnostics_enabled = true
about_enabled = true
# Adds content-type-aware browser security headers when enabled: full document policy on HTML, private no-store/CORP/anti-framing policy on /api, Referrer-Policy on HTML/CSS, and nosniff broadly.
security_headers_enabled = false

# Optional; defaults to imap_host. Ignored when sieve_enabled=false.
# sieve_host = "mail.example.com"
sieve_port = 4190
sieve_tls_mode = "starttls"
sieve_allow_insecure_auth = false

# Optional administrator-owned headers added to every outgoing message.
# Structural/MIME headers such as From, To, Subject and Content-Type are rejected.
outgoing_headers = [
  "X-Company-Mailer: RustBox",
]

If the mail host settings are omitted, RustBox uses localhost with IMAP 993 and SMTP 587. TLS is enabled by default. Certificate and hostname verification are skipped automatically only for localhost/loopback unless tls_verify is set explicitly.

Mail-size limits are intentionally separate. max_attachment_size_mb applies to each raw uploaded file, while max_message_size_mb is checked against the final RFC822/MIME byte stream immediately before SMTP submission. The latter therefore includes message bodies, headers, MIME boundaries, base64 expansion, inline CID parts and OpenPGP transformations. The composer displays only an estimate; the server-side final-MIME check is authoritative.

Attachments are streamed to blob storage on upload, but sending a message or saving an IMAP draft snapshot has to hold them in memory while the MIME message is built. RustBox therefore (1) rejects an attachment set whose base64-encoded size cannot fit max_message_size_mb with 413 before any file is read into memory, and also while an upload is still streaming, and (2) allows only max_concurrent_message_builds such operations at the same time; further requests wait up to message_build_wait_secs and then receive 503. Peak RAM for these operations is roughly max_concurrent_message_builds x 4 x max_message_size_mb. Fully streamed MIME assembly is not implemented.

RustBox separates three concerns: SQL metadata, blob storage and search. The SQL engine does not decide whether data_dir may be used. A single-node PostgreSQL/MySQL deployment may still use filesystem blobs and local Tantivy. In cluster mode, local node state is rejected unless the configured backend is explicitly shared.

Blob backends:

  • storage.backend="filesystem" — local disk in single-node mode; set storage.shared=true only for a real shared POSIX mount such as NFS, CephFS or GlusterFS.
  • storage.backend="s3" — AWS S3 or compatible APIs such as MinIO/Ceph RGW.

Physical blob deletion is durable across all blob backends. RustBox records deletion work in the per-account SQL blob_delete_outbox in the same transaction that removes attachment metadata, attempts the delete immediately, and retries temporary failures in the background with leases/backoff. This covers local disk, shared POSIX/NFS and S3-compatible storage without permanently dead-lettering failed cleanup.

Search backends:

  • search.backend="local_tantivy" — single-node.
  • search.backend="opensearch" — cluster-capable. Index mutations are written first to a durable per-tenant SQL outbox and processed with retry/backoff/dead-letter handling.
  • search.backend="disabled" — no full-text index.

In a cluster using shared SQL + S3/shared POSIX + OpenSearch/disabled search + pgp_enabled=false, data_dir is not required as persistent node state. See docker/README.md for ready Compose variants.

Administrator-only deep runtime diagnostics can be enabled separately under [diagnostics_endpoint]. It is disabled and loopback-only by default, supports a network allowlist plus token or Basic Auth, redacts secrets, and probes SQL/Redis/storage/search/session/runtime/security state. See docs/SYSTEM_DIAGNOSTICS.md.

Database, storage, search and cluster traffic

database_backend = "sqlite" | "mysql" | "pgsql" selects only the authoritative SQL database. It does not force or forbid local blob/search storage. In particular, PostgreSQL/MySQL are valid in a single-node installation together with storage.backend="filesystem" and search.backend="local_tantivy".

session_backend = "database" stores sessions in the selected SQL database. session_backend = "redis" moves only session persistence to Redis/Redis Sentinel. Mailbox metadata and cached message bodies remain in SQL.

data_dir is persistent node state only when a selected local feature actually needs it: default SQLite, implicit filesystem storage, implicit local Tantivy, or local OpenPGP keys. In cluster mode it is not required when those features are replaced with cluster-safe backends. Cluster validation is capability-based:

  • SQL: shared MySQL/PostgreSQL; SQLite is rejected;
  • blobs: S3-compatible storage, or filesystem with storage.shared=true on a real NFS/CephFS/GlusterFS mount;
  • search: OpenSearch or disabled; local Tantivy is rejected;
  • OpenPGP: remains disabled until a cluster-safe key backend exists;
  • all nodes use the same session_encryption_key.

Example stateless cluster:

database_backend = "pgsql"
database_url = "postgres://rustbox:secret@db/rustbox"
session_backend = "database"
session_encryption_key = "<shared-key>"
cluster_mode = true
pgp_enabled = false

[storage]
backend = "s3"
bucket = "rustbox"
region = "eu-central-1"
# endpoint = "https://s3.example.com" # MinIO/Ceph RGW/etc.

[sync]
interval_secs = 30
deep_index_batch_size = 10

[search]
backend = "opensearch"
urls = ["https://opensearch.example.com:9200"]
required = true
# Production-safe default: do not proactively download every full message body.
deep_index = false

Shared POSIX is also supported without S3:

[storage]
backend = "filesystem"
path = "/mnt/rustbox-shared"
shared = true

Existing attachment files can be migrated to the configured storage backend without deleting the source files:

rustbox storage migrate --from filesystem:/old/data/root --to configured --dry-run
rustbox storage migrate --from filesystem:/old/data/root --to configured --verify

The SQL row is switched to the logical object key only after upload and size/checksum verification. Search state is rebuildable from SQL:

rustbox search reindex

Ready Docker examples are in docker/: standalone SQLite, PostgreSQL + SILO (S3-compatible) + OpenSearch, and MySQL + NFS + OpenSearch.

Database schema upgrades run automatically at startup. Databases older than the consolidated baseline (V052) must first be opened once with RustBox 0.2.22. See migrations/README.md for the schema history; V055 is the next migration number.

ManageSieve

Supported modes:

  • starttls — recommended;
  • implicit — TLS immediately after connect;
  • none — plaintext transport; reusable credential authentication remains blocked unless sieve_allow_insecure_auth = true is explicitly enabled.

When sieve_host is not configured, RustBox uses the authenticated session's IMAP host. Sieve uses the same session credentials; passwords are not exposed to the browser after login.

Native installation

./scripts/build-release.sh
sudo ./scripts/install-systemd.sh
sudo systemctl enable --now rustbox
sudo journalctl -u rustbox -f

For public access put an HTTPS reverse proxy in front of RustBox. The production example binds to 127.0.0.1:3001.

Development

Requirements: Rust toolchain with Cargo. OpenPGP key generation/sign/encrypt/decrypt is implemented with rPGP. Changing the passphrase of an existing private key additionally requires GnuPG (gpg) on the server; override the executable with RUSTBOX_PGP_GPG_BINARY if needed.

./dev.sh

Then open:

http://127.0.0.1:3001

To use another configuration:

RUSTBOX_CONFIG=/path/to/rustbox.toml ./dev.sh

Normal Cargo commands work from the repository root:

cargo run
cargo test
cargo build --release

Project layout

src/            Rust application (Axum server, IMAP/SMTP, Sieve, storage, search)
src/web/        Browser interface: index.html, css/, js/, i18n/, icons.svg
migrations/     SQL schemas for SQLite, MySQL and PostgreSQL
docker/         Compose examples
packaging/      systemd unit, logrotate and production config
scripts/        Build, install, release and Roundcube import helpers
docs/           Documentation and screenshots

build.rs bundles the CSS and JavaScript modules listed in src/web/css/manifest.txt and src/web/js/manifest.txt; the bundles, HTML, translations and icons are embedded in the executable.

License

Copyright © 2026 Mateusz Gruszczyński (linuxiarz.pl).

RustBox is dual-licensed:

  • GNU Affero General Public License v3 (LICENSE) — free to use, modify and share, also in companies. If you run a modified version as a network service, offer its source code to its users.
  • Commercial license — for organisations that prefer different terms, for example to keep their changes private. Paid; terms by agreement. Contact: linuxiarz@linuxiarz.pl. Contacting the author is voluntary and never required for use that follows the AGPL.

Details: LICENSING.md. The first version of RustBox was created as a fork of Oxi (MIT License); see NOTICE.md.

S
Description
No description provided
Readme AGPL-3.0
14 MiB
0 Stars 1 Watchers 0 Forks
Languages
Rust 64.2%
JavaScript 18%
CSS 8.7%
Python 4.7%
HTML 4.2%
Other 0.2%