229 lines
11 KiB
Markdown
229 lines
11 KiB
Markdown
# RustBox 0.1.2
|
|
|
|
RustBox is a webmail derived from `c0h1b4/oxi`. The consumed Oxi revision is recorded in `UPSTREAM_BASE`.
|
|
|
|
The repository is one Cargo application. The RustBox browser interface lives under `src/web/`, uses HTML/CSS/vanilla JavaScript, and is embedded into the Rust executable at build time. There is no TypeScript, React, Next.js, Node.js or Bun build step.
|
|
|
|
## Layout
|
|
|
|
```text
|
|
rustbox/
|
|
├── Cargo.toml
|
|
├── build.rs
|
|
├── src/
|
|
│ ├── main.rs
|
|
│ ├── ui.rs
|
|
│ └── web/
|
|
│ ├── index.html
|
|
│ ├── js/
|
|
│ │ ├── manifest.txt
|
|
│ │ └── *.js
|
|
│ ├── icons.svg
|
|
│ └── css/
|
|
├── migrations/
|
|
├── tests/
|
|
├── examples/
|
|
├── docs/
|
|
├── packaging/
|
|
├── scripts/
|
|
├── config.dev.toml
|
|
├── config.example.toml
|
|
├── UPSTREAM_BASE
|
|
└── dev.sh
|
|
```
|
|
|
|
`build.rs` combines the CSS modules from `src/web/css/manifest.txt` and JavaScript modules from `src/web/js/manifest.txt`. Cargo embeds the generated CSS/JS bundles, HTML and SVG sprite in the RustBox executable. There is no standalone source `src/web/app.js`; `src/web/` is the single source of truth and Cargo generates the embedded bundles in `OUT_DIR`.
|
|
|
|
## Configure mail servers
|
|
|
|
Mail endpoints are configured by the administrator, not on the login screen. Users enter only email and password.
|
|
|
|
Edit `config.dev.toml` before the first run:
|
|
|
|
```toml
|
|
default_theme = "system" # system | dark | light
|
|
default_interface_skin = "rustbox" # rustbox | gmail | proton | zoho | outlook | fastmail | apple-mail | yahoo | hey | nord | github | gitlab | discord | slack | reddit | notion | ...
|
|
log_level = "info" # trace | debug | info | warn | error | off; RUST_LOG overrides it
|
|
# 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
|
|
|
|
# Optional; defaults to imap_host.
|
|
# 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.
|
|
|
|
## Database, cache, sessions and cluster traffic
|
|
|
|
RustBox 0.1.2 uses one SQL backend for the whole application. `database_backend = "sqlite" | "mysql" | "pgsql"` selects where account data is stored and, by default, where persistent login sessions are stored. SQLite defaults to `data_dir/rustbox.sqlite`; MySQL and PostgreSQL require `database_url`.
|
|
|
|
Mailbox metadata and cached message bodies are stored in that SQL backend. With a shared MySQL/PostgreSQL database the persistent mailbox cache is therefore shared by all application nodes; Redis is not used for mailbox cache data.
|
|
|
|
The per-user directory `data_dir/<user_hash>/` is not the active account database when MySQL/PostgreSQL is configured. It can contain filesystem-backed data:
|
|
|
|
- `attachments/` — draft attachments; in a cluster `data_dir` must be mounted from shared storage at the same path on every node;
|
|
- `tantivy/` — built-in full-text index; it is single-node only and must be disabled in cluster mode;
|
|
- `pgp/` — OpenPGP private-key material; OpenPGP must be disabled in cluster mode;
|
|
- legacy `db.sqlite` — migration input only. After a successful 0.1.0 import it is retained as `db.sqlite.pre-0.1.1.bak`.
|
|
|
|
For a multi-node deployment use a shared MySQL/PostgreSQL database, the same `session_encryption_key` on every node, and shared `data_dir` for draft attachments. RustBox 0.1.2 validates this explicitly with `cluster_shared_data_dir = true`. The built-in Tantivy backend has no cross-node writer coordination, so cluster mode requires `search_index_backend = "disabled"`. SQL-backed metadata/header search remains available; deep full-body Tantivy search is unavailable in cluster mode. An external search/index backend is not implemented in 0.1.2.
|
|
|
|
`session_backend = "database"` stores sessions in the selected application database. `session_backend = "redis"` uses Redis instead; Redis Sentinel is supported through the Sentinel settings. Redis only replaces session persistence, not the application database or mailbox cache.
|
|
|
|
```toml
|
|
# Single node defaults.
|
|
database_backend = "sqlite"
|
|
session_backend = "database"
|
|
search_index_backend = "tantivy"
|
|
pgp_enabled = true
|
|
|
|
# Multi-node PostgreSQL example:
|
|
# database_backend = "pgsql"
|
|
# database_url = "postgres://rustbox:secret@db/rustbox"
|
|
# session_backend = "database" # or redis
|
|
# session_encryption_key = "<base64-32-byte-key>"
|
|
# cluster_mode = true
|
|
# cluster_shared_data_dir = true
|
|
# data_dir = "/srv/rustbox-shared" # same mounted path on every node
|
|
# search_index_backend = "disabled"
|
|
# pgp_enabled = false
|
|
|
|
# Multi-node MySQL example (MySQL 8.0.13+):
|
|
# database_backend = "mysql"
|
|
# database_url = "mysql://rustbox:secret@db/rustbox"
|
|
# session_backend = "database" # or redis
|
|
# session_encryption_key = "<base64-32-byte-key>"
|
|
# cluster_mode = true
|
|
# cluster_shared_data_dir = true
|
|
# data_dir = "/srv/rustbox-shared"
|
|
# search_index_backend = "disabled"
|
|
# pgp_enabled = false
|
|
|
|
# Optional Redis session persistence; application data still uses database_backend.
|
|
# session_backend = "redis"
|
|
# redis_url = "redis://redis:6379/"
|
|
|
|
# Sentinel alternative to redis_url:
|
|
# redis_sentinel_nodes = ["sentinel-a:26379", "sentinel-b:26379", "sentinel-c:26379"]
|
|
# redis_sentinel_master = "mymaster"
|
|
# redis_sentinel_password = "sentinel-secret"
|
|
# redis_password = "secret"
|
|
```
|
|
|
|
All executable application SQL is centralized by dialect under `src/queries/{sqlite,mysql,pgsql}/queries.rs`. Each engine has one complete baseline in `migrations/{sqlite,mysql,pgsql}/schema.sql`, including the session table. The squashed baseline is schema version 46 and the next schema migration number is V047 for all engines. Redis requires no SQL migration. See `migrations/README.md` and `docs/DB-AND-CLUSTER-AUDIT-0.1.2.md`.
|
|
|
|
|
|
## 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.
|
|
|
|
```bash
|
|
./dev.sh
|
|
```
|
|
|
|
Then open:
|
|
|
|
```text
|
|
http://127.0.0.1:3001
|
|
```
|
|
|
|
To use another configuration:
|
|
|
|
```bash
|
|
RUSTBOX_CONFIG=/path/to/rustbox.toml ./dev.sh
|
|
```
|
|
|
|
Normal Cargo commands work from the repository root:
|
|
|
|
```bash
|
|
cargo run
|
|
cargo test
|
|
cargo build --release
|
|
```
|
|
|
|
## UI
|
|
|
|
RustBox uses a compact three-pane mail layout: folders, message list and reader. The current internal baseline includes configurable special IMAP folders, collapsible folder hierarchy, folder CRUD/subscriptions, move/copy/bulk actions, drag-and-drop message moves, collapsible threads, raw headers/source download, labels, advanced/saved search, quota warnings, contacts/groups/vCard, calendar/ICS import, identities/signatures with automatic reply identity selection, HTML/plain/Markdown compose modes, CID inline images, and ManageSieve raw + visual rule editing with negation, dynamically addable conditions/actions and nested AND/OR chaining. OpenPGP includes per-user key management, identity binding, PGP/MIME + inline sign/encrypt/decrypt/verify, WKD/HKP discovery and private-key passphrase management.
|
|
|
|
IMAP IDLE events are delivered to the browser over the embedded WebSocket endpoint for realtime refresh and optional desktop notifications. Notification preferences include sound, folder selection and Do Not Disturb hours.
|
|
|
|
Appearance preferences are persisted per account and include light/dark/system theme, density, preview position, UI scale, multiple color presets, custom accent/background/panel/text colors, font, row height, pane widths, radius, contrast and visibility toggles. Administrators can set the global light/dark mode with `default_theme = "system" | "dark" | "light"` and the global interface style with `default_interface_skin = "rustbox" | "gmail" | "proton" | "zoho" | "outlook" | ...` in TOML. The global interface style is used on the login screen and by accounts set to **Global default**. Built-in interface styles include RustBox, Roundcube variants, Clean, Graphite, High contrast, Gmail/Proton/Zoho/Outlook/Fastmail/Apple Mail/Yahoo/HEY/GitHub/GitLab/Discord/Slack/Reddit/Notion-inspired profiles, Nord and Minimal. Predefined styles can optionally unlock the per-account UI scale control. The mailbox toolbar also exposes persisted date/sender/subject/size sorting, and remote images can be restricted to saved contacts.
|
|
|
|
The native RustBox interface intentionally uses flat surfaces, small radii and one SVG icon set. Compatibility skins may reproduce upstream visual treatments such as gradients, while toolbar icons remain SVG rather than Unicode glyphs.
|
|
|
|
## 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
|
|
|
|
```bash
|
|
./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`.
|
|
|
|
## Following Oxi
|
|
|
|
Initialize Git after extracting the release archive:
|
|
|
|
```bash
|
|
git init
|
|
git add .
|
|
git commit -m "RustBox 0.1.2 release"
|
|
./scripts/setup-upstream.sh
|
|
```
|
|
|
|
Inspect newer Oxi changes:
|
|
|
|
```bash
|
|
./scripts/oxi-status.sh
|
|
```
|
|
|
|
Import the mapped Oxi backend delta:
|
|
|
|
```bash
|
|
./scripts/sync-upstream.sh
|
|
```
|
|
|
|
Oxi `backend/...` paths are mapped into RustBox's flat Cargo tree. Oxi frontend sources are never copied. Relevant upstream frontend behavior is reviewed and reimplemented in `src/web/`. `UPSTREAM_BASE` advances only after `./scripts/finish-upstream.sh` completes review and the build gate.
|
|
|
|
To port one backend commit without advancing the baseline:
|
|
|
|
```bash
|
|
./scripts/oxi-port-commit.sh <oxi-commit>
|
|
```
|
|
|
|
See `docs/UPSTREAM.md` for details.
|
|
|
|
## Attribution
|
|
|
|
See `NOTICE.md`. The supplied Oxi snapshot did not contain an authoritative `LICENSE` file; verify the exact upstream licensing terms before public redistribution.
|