Files
RustBox/packaging/rustbox.toml
T
Mateusz Gruszczyński beae94e932
RustBox CI / rustbox (push) Canceled after 0s
v0.2.23
2026-11-04 20:44:31 +01:00

532 lines
16 KiB
TOML

# RustBox 0.2.16 - packaged production configuration (DEB/RPM)
#
# This file contains all supported RustBox configuration options.
# Commented options are optional and do not change the packaged defaults.
# -----------------------------------------------------------------------------
# Server
# -----------------------------------------------------------------------------
host = "0.0.0.0"
port = 3001
environment = "production"
# Secure attribute of session cookies: auto | always | never.
# auto (default) sets Secure only when the browser reached RustBox over HTTPS:
# X-Forwarded-Proto / Forwarded: proto= / X-Forwarded-Ssl from a trusted proxy,
# otherwise the scheme of the browser's Origin/Referer. Plain-HTTP installations
# therefore keep working. Use "always" behind HTTPS if your proxy strips headers.
cookie_secure = "auto"
data_dir = "/var/lib/rustbox"
log_dir = "/var/log/rustbox"
# trace | debug | info | warn | error | off
# RUST_LOG overrides this value when set.
log_level = "warn"
# Optional reverse-proxy subpath, e.g. "/mail".
# base_path = "/mail"
# -----------------------------------------------------------------------------
# UI defaults
# -----------------------------------------------------------------------------
# system | dark | light
default_theme = "system"
# Built-in styles (full list in config.example.toml), e.g.:
# gmail, outlook, apple-mail, fastmail, github, slack, notion, nord,
# dracula, catppuccin, tokyo-night, rose-pine, paper, terminal, aqua,
# material, retro and Roundcube variants.
default_interface_skin = "apple-mail"
# Optional product features. Disabled features are removed from the API/UI.
sieve_enabled = true
diagnostics_enabled = true
about_enabled = true
# Content-type-aware browser security headers. Off by default for compatibility.
security_headers_enabled = false
# -----------------------------------------------------------------------------
# Database
# -----------------------------------------------------------------------------
# sqlite | mysql | pgsql
database_backend = "sqlite"
# SQLite defaults to data_dir/rustbox.sqlite.
# MySQL/PostgreSQL require database_url.
#
# PostgreSQL:
# database_url = "postgres://rustbox:secret@db.example.com/rustbox"
#
# MySQL:
# database_url = "mysql://rustbox:secret@db.example.com/rustbox"
# -----------------------------------------------------------------------------
# Cluster
# -----------------------------------------------------------------------------
cluster_mode = false
# cluster_mode=true:
# - requires MySQL or PostgreSQL
# - local Tantivy cannot be used
# - filesystem blob storage must be genuinely shared between all nodes,
# or S3-compatible storage must be used
# - pgp_enabled must currently be false
#
# data_dir is not required as persistent node state when all required state
# lives in SQL/shared blob storage/external search.
# -----------------------------------------------------------------------------
# Sessions
# -----------------------------------------------------------------------------
# database | redis
session_backend = "database"
session_timeout_hours = 72
remember_me_enabled = true
remember_me_days = 30
# Required when sessions/database state must be decryptable by multiple nodes.
# All RustBox nodes must use the same 32-byte base64-encoded key.
#
# session_encryption_key = "BASE64_32_BYTE_KEY"
# Direct Redis/Valkey:
# redis_url = "redis://redis.example.com:6379/"
# Redis Sentinel (or a compatible Redis deployment):
# redis_sentinel_nodes = [
# "sentinel-a:26379",
# "sentinel-b:26379",
# "sentinel-c:26379",
# ]
# redis_sentinel_master = "mymaster"
# redis_sentinel_password = "sentinel-secret"
# redis_password = "secret"
# -----------------------------------------------------------------------------
# IMAP / SMTP
# -----------------------------------------------------------------------------
imap_host = "localhost"
imap_port = 993
smtp_host = "localhost"
smtp_port = 587
tls_enabled = true
# Certificate and hostname verification.
# Packaged default keeps the existing local-mail-server behavior.
tls_verify = false
# Maximum raw attachment size in MiB.
max_attachment_size_mb = 50
# Maximum complete RFC822/MIME message size in MiB.
max_message_size_mb = 100
# 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
# "Undo Send" window in seconds (0 disables; max 120). See config.example.toml.
undo_send_window_secs = 8
# -----------------------------------------------------------------------------
# ManageSieve
# -----------------------------------------------------------------------------
# If sieve_host is omitted, the IMAP host from the authenticated session
# is used.
#
# sieve_host = "mail.example.com"
sieve_port = 4190
# starttls | implicit | none
sieve_tls_mode = "none"
sieve_allow_insecure_auth = true
sieve_timeout_secs = 10
# -----------------------------------------------------------------------------
# Reverse proxy
# -----------------------------------------------------------------------------
trusted_proxies = "127.0.0.1,::1"
# -----------------------------------------------------------------------------
# Special IMAP folders
# -----------------------------------------------------------------------------
# Optional server-wide defaults used when an account has no override
# and the named folder exists.
# default_sent_folder = "Sent"
# default_drafts_folder = "Drafts"
# default_trash_folder = "Trash"
default_junk_folder = "Spam"
# default_archive_folder = "Archive"
# -----------------------------------------------------------------------------
# Outgoing mail headers
# -----------------------------------------------------------------------------
# Additional headers added to every outgoing message.
# Format: "Header-Name: value"
#
# Structural/MIME headers and CR/LF injection are rejected.
#
# Example:
# outgoing_headers = [
# "X-Organization: Example Corp",
# ]
outgoing_headers = [
"X-Mailer: RustBox (https://git.linuxiarz.pl/gru/RustBox)",
]
# -----------------------------------------------------------------------------
# OpenPGP
# -----------------------------------------------------------------------------
pgp_enabled = true
pgp_wkd_enabled = true
pgp_hkp_enabled = false
pgp_hkp_url = "https://keys.openpgp.org"
pgp_network_timeout_secs = 10
pgp_max_key_bytes = 1048576
# Packaged default preserves the existing package behavior.
pgp_allow_private_export = true
# Current cluster deployments require:
#
# pgp_enabled = false
# -----------------------------------------------------------------------------
# Login brute-force protection
# -----------------------------------------------------------------------------
# Login brute-force protection (POST /api/auth/login). Enabled by default.
# Environment overrides use a double underscore, e.g.
# LOGIN_RATE_LIMIT__ACCOUNT_MAX_FAILURES=10 or LOGIN_RATE_LIMIT__ENABLED=false.
# Client IPs are taken from X-Forwarded-For only when the peer is in trusted_proxies.
# State is kept in memory on each node: behind a load balancer every node counts
# separately, so use sticky routing or lower thresholds in cluster mode.
[login_rate_limit]
enabled = true
# Per-IP token bucket: up to ip_burst attempts at once, then one new attempt
# every ip_replenish_secs seconds. Requests over the limit get HTTP 429.
ip_burst = 5
ip_replenish_secs = 12
# Failed logins counted inside failure_window_secs before a lockout.
# account_max_failures: per mailbox (case-insensitive), from any IP.
# ip_max_failures: per client IP, across all mailboxes (password spraying).
# 0 disables that lockout type.
account_max_failures = 5
ip_max_failures = 20
failure_window_secs = 900
# First lockout length; every consecutive lockout of the same key doubles it
# up to max_lockout_secs. Locked attempts are refused before contacting IMAP.
# Trade-off: an attacker can keep a known mailbox locked for webmail logins
# (IMAP/SMTP clients are unaffected), so keep max_lockout_secs moderate.
lockout_secs = 300
max_lockout_secs = 3600
# IPs/CIDRs exempt from the per-IP lockout (account lockouts still apply).
exempt_networks = []
# Memory cap per tracking table (accounts, IPs).
max_tracked_keys = 100000
[storage]
# filesystem | s3
backend = "filesystem"
# Filesystem backend.
path = "/var/lib/rustbox/blobs"
# Set true only when every RustBox node sees the same filesystem.
shared = false
# Optional object/key prefix.
prefix = ""
request_timeout_secs = 30
connect_timeout_secs = 5
max_retries = 3
retry_timeout_secs = 60
# --- S3-compatible backend ----------------------------------------------------
#
# Example:
#
# backend = "s3"
#
# bucket = "rustbox"
# region = "eu-central-1"
#
# Custom endpoint for Garage, MinIO, SILO, Ceph RGW, OpenStack Swift s3api, etc.
# AWS S3 normally does not require endpoint.
#
# endpoint = "https://s3.example.net"
#
# Enable for S3 implementations requiring path-style addressing.
# path_style = false
#
# Explicitly allow an unencrypted HTTP endpoint.
# Intended primarily for trusted development/internal networks.
# allow_http = false
#
# Credentials may be configured here or through supported AWS environment
# variables.
#
# access_key_id = "..."
# secret_access_key = "..."
# session_token = "..."
#
# Server-side encryption:
#
# server_side_encryption = "AES256"
#
# or:
#
# server_side_encryption = "aws:kms"
# kms_key_id = "..."
# -----------------------------------------------------------------------------
# IMAP synchronization / automatic body cache
# -----------------------------------------------------------------------------
[sync]
# Periodic fallback/background IMAP reconciliation. Active folders use IMAP IDLE,
# while this pass refreshes counters/flags/headers in the rest of the mailbox and
# also runs automatic deep indexing when enabled. Lower values increase IMAP load.
interval_secs = 30
# Maximum full message bodies fetched from IMAP per periodic pass when deep index is
# effective. Larger batches catch up faster but increase bandwidth, application database/
# search-storage usage and indexing CPU/I/O bursts.
deep_index_batch_size = 10
# -----------------------------------------------------------------------------
# Search
# -----------------------------------------------------------------------------
#
# Search is independent from both SQL and blob storage.
#
# local_tantivy:
# - default packaged single-node backend
# - cannot be used in cluster mode
#
# opensearch:
# - recommended backend for cluster deployments
#
# disabled:
# - completely disables search indexing
#
[search]
# disabled | local_tantivy | opensearch
backend = "local_tantivy"
# Local Tantivy path.
path = "/var/lib/rustbox/search"
# OpenSearch endpoints:
#
# urls = [
# "https://opensearch.example.net:9200",
# ]
index_prefix = "rustbox"
# If true, inability to use the configured search backend is treated
# as a required-service failure.
required = false
# Permit plain HTTP OpenSearch endpoints.
# Intended primarily for trusted development/internal networks.
allow_insecure_http = false
request_timeout_secs = 10
# Server-wide automatic full-body cache/index switch. Production default is false
# because enabling it proactively downloads and stores full message bodies. When
# true, all accounts are deep-indexed; when false, an account may still opt in via
# its existing deep_index preference.
deep_index = false
# Search outbox worker cadence/batch; these do NOT control IMAP body downloads.
worker_interval_secs = 2
worker_batch_size = 100
worker_max_attempts = 12
# Optional OpenSearch HTTP authentication.
#
# username = "rustbox"
# password = "change-me"
# =============================================================================
# CLUSTER EXAMPLES
# =============================================================================
#
# Examples below are comments only. They do not affect the packaged defaults.
# -----------------------------------------------------------------------------
# PostgreSQL + S3-compatible storage + OpenSearch
# -----------------------------------------------------------------------------
#
# database_backend = "pgsql"
# database_url = "postgres://rustbox:change-me@db.internal/rustbox"
#
# cluster_mode = true
#
# session_backend = "database"
# session_encryption_key = "BASE64_32_BYTE_KEY"
#
# pgp_enabled = false
#
# [storage]
# backend = "s3"
# bucket = "rustbox"
# region = "eu-central-1"
# endpoint = "https://s3.example.net"
# path_style = false
# allow_http = false
# prefix = ""
# request_timeout_secs = 30
# connect_timeout_secs = 5
# max_retries = 3
# retry_timeout_secs = 60
#
# [search]
# backend = "opensearch"
# urls = ["https://opensearch.example.net:9200"]
# index_prefix = "rustbox"
# required = true
# allow_insecure_http = false
# request_timeout_secs = 10
# deep_index = false
# worker_interval_secs = 2
# worker_batch_size = 100
# worker_max_attempts = 12
# username = "rustbox"
# password = "change-me"
# -----------------------------------------------------------------------------
# MySQL + shared NFS/CephFS/GlusterFS + OpenSearch
# -----------------------------------------------------------------------------
#
# database_backend = "mysql"
# database_url = "mysql://rustbox:change-me@db.internal/rustbox"
#
# cluster_mode = true
#
# session_backend = "database"
# session_encryption_key = "BASE64_32_BYTE_KEY"
#
# pgp_enabled = false
#
# [storage]
# backend = "filesystem"
# path = "/mnt/rustbox-shared"
# shared = true
# prefix = ""
#
# [search]
# backend = "opensearch"
# urls = ["https://opensearch.example.net:9200"]
# index_prefix = "rustbox"
# required = true
# -----------------------------------------------------------------------------
# Cluster sessions using Redis/Valkey
# -----------------------------------------------------------------------------
#
# session_backend = "redis"
# session_encryption_key = "BASE64_32_BYTE_KEY"
#
# redis_url = "redis://valkey.internal:6379/"
#
# Or Sentinel:
#
# redis_sentinel_nodes = [
# "sentinel-a:26379",
# "sentinel-b:26379",
# "sentinel-c:26379",
# ]
# redis_sentinel_master = "mymaster"
# redis_sentinel_password = "sentinel-secret"
# redis_password = "secret"
# -----------------------------------------------------------------------------
# Blob storage
# -----------------------------------------------------------------------------
#
# Blob storage is independent from database_backend.
#
# PostgreSQL/MySQL can use a local filesystem in single-node mode.
#
# Cluster deployments must use:
# - S3-compatible object storage, or
# - a genuinely shared POSIX filesystem such as NFS/CephFS/GlusterFS.
#
# -----------------------------------------------------------------------------
# Administrator system diagnostics endpoint
# -----------------------------------------------------------------------------
# Disabled by default. When enabled, the endpoint performs live probes of SQL,
# session persistence/Redis, filesystem/S3, Tantivy/OpenSearch and reports
# process/runtime/IMAP-IDLE/security telemetry. Returned configuration is redacted:
# passwords, auth tokens, S3 secrets, session keys and URL credentials are never emitted.
# Access is an AND condition: source IP/CIDR must be allowed and token/basic auth must pass.
[diagnostics_endpoint]
enabled = false
path = "/api/system/diagnostics"
# Fail-safe default. Add only monitoring/admin networks that really need access.
allowed_networks = ["127.0.0.1/32", "::1/128"]
auth = "token" # token | basic
# Prefer injecting these with a secret manager/environment rather than storing here.
# token = "at-least-24-random-characters"
# basic_username = "rustbox-monitor"
# basic_password = "long-random-password"
include_user_identifiers = false
probe_timeout_secs = 5
security_window_secs = 900