573 lines
16 KiB
TOML
573 lines
16 KiB
TOML
# RustBox - 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"
|
|
|
|
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 examples:
|
|
# gmail, proton, zoho, outlook, fastmail, apple-mail, yahoo, hey,
|
|
# nord, github, gitlab, discord, slack, reddit, notion, minimal,
|
|
# 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
|
|
|
|
# Allow multiple IMAP accounts in one browser session.
|
|
# When disabled, RustBox hides multi-account UI, rejects adding another account,
|
|
# and realtime notifications/watchers are limited to the active account.
|
|
multi_account_enabled = true
|
|
|
|
# Security headers added by RustBox. Off by default for compatibility.
|
|
#
|
|
# When enabled:
|
|
# - HTML documents receive the full browser security policy
|
|
# (CSP, frame protection, referrer/permissions policy, COOP, nosniff).
|
|
# - CSS receives only headers that are meaningful for stylesheets.
|
|
# - JS/static/API responses receive only headers that are meaningful for
|
|
# non-document resources; private API responses use no-store.
|
|
# - HSTS is intentionally NOT emitted here. Configure HSTS at the HTTPS
|
|
# terminator/reverse proxy, which knows whether the request is really HTTPS.
|
|
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
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# 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
|
|
# -----------------------------------------------------------------------------
|
|
|
|
# Also used by the administrative diagnostics endpoint to validate forwarded
|
|
# client addresses. Do not trust arbitrary Internet-facing proxy ranges.
|
|
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
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# 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.
|
|
#
|
|
|
|
[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 / body cache
|
|
# -----------------------------------------------------------------------------
|
|
#
|
|
# Periodic synchronization complements IMAP IDLE. The currently watched folder
|
|
# can update immediately through IDLE, while this interval is used to reconcile
|
|
# counters/flags and to make progress on folders that are not actively open.
|
|
#
|
|
# Lower values make folder counters converge faster but increase IMAP traffic.
|
|
#
|
|
[sync]
|
|
|
|
# Periodic synchronization interval in seconds.
|
|
interval_secs = 30
|
|
|
|
# Maximum number of complete message bodies fetched from IMAP during one
|
|
# deep-cache/index pass. Increasing this speeds initial catch-up but increases
|
|
# IMAP traffic, disk writes and indexing CPU usage.
|
|
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"
|
|
|
|
# Global deep body cache/index policy.
|
|
#
|
|
# false (recommended production default):
|
|
# - RustBox does not force full-message caching for every account.
|
|
# - An account may still opt in through its own deep_index setting.
|
|
#
|
|
# true:
|
|
# - RustBox automatically downloads complete message bodies from IMAP and keeps
|
|
# them in the local/shared cache so the search backend can fully index content.
|
|
# - This can significantly increase IMAP bandwidth, local/S3 storage usage,
|
|
# indexing CPU and initial synchronization time for large mailboxes.
|
|
#
|
|
# This does not control IMAP IDLE itself.
|
|
deep_index = false
|
|
|
|
# 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
|
|
|
|
worker_interval_secs = 2
|
|
worker_batch_size = 100
|
|
worker_max_attempts = 12
|
|
|
|
# Optional OpenSearch HTTP authentication.
|
|
#
|
|
# username = "rustbox"
|
|
# password = "change-me"
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Administrative system diagnostics endpoint
|
|
# -----------------------------------------------------------------------------
|
|
#
|
|
# This endpoint is intended for local/cluster troubleshooting and monitoring.
|
|
# It is independent from the normal signed-in-user diagnostics UI.
|
|
#
|
|
# Security model:
|
|
# - disabled by default;
|
|
# - source IP must match allowed_networks;
|
|
# - authentication is additionally required (token or Basic Auth);
|
|
# - untrusted source network -> HTTP 403;
|
|
# - missing/invalid endpoint credentials -> HTTP 401;
|
|
# - backend health can make the diagnostic response report degraded/error.
|
|
#
|
|
# Behind a reverse proxy, configure trusted_proxies correctly. If forwarded
|
|
# client-IP headers are present but the proxy is not trusted, access fails
|
|
# closed instead of treating the proxy connection as localhost.
|
|
#
|
|
# The endpoint NEVER returns configured passwords, access keys, session keys,
|
|
# bearer tokens or full credential-bearing DSNs. Secret fields are exposed only
|
|
# as configured/not-configured state.
|
|
#
|
|
# Runtime statistics are intentionally limited to the RustBox process and
|
|
# application state. RustBox does not inspect host CPU/RAM/load or /proc for
|
|
# this endpoint, keeping it lightweight and portable across supported systems.
|
|
#
|
|
[diagnostics_endpoint]
|
|
|
|
enabled = false
|
|
|
|
# Must start with "/api/".
|
|
path = "/api/system/diagnostics"
|
|
|
|
# Default: localhost only.
|
|
# Add explicit internal management networks when required, for example:
|
|
# allowed_networks = ["127.0.0.1/32", "::1/128", "10.20.30.0/24"]
|
|
allowed_networks = ["127.0.0.1/32", "::1/128"]
|
|
|
|
# token | basic
|
|
auth = "token"
|
|
|
|
# For token authentication uncomment and set a long random secret.
|
|
# Keep it outside source control in real deployments, preferably via environment
|
|
# configuration/secrets management.
|
|
#
|
|
# token = "minimum-24-random-characters"
|
|
|
|
# For Basic authentication:
|
|
#
|
|
# auth = "basic"
|
|
# basic_username = "rustbox-monitor"
|
|
# basic_password = "minimum-16-random-characters"
|
|
|
|
# When false, session/account diagnostics avoid exposing user identifiers.
|
|
include_user_identifiers = false
|
|
|
|
# Timeout used by lightweight dependency probes.
|
|
probe_timeout_secs = 5
|
|
|
|
# Sliding security-observation window used to aggregate login failures,
|
|
# 401/403/429 responses, 5xx responses and suspicious access patterns.
|
|
security_window_secs = 900
|
|
|
|
# The report can include:
|
|
# - RustBox version, PID, start time and uptime;
|
|
# - effective configuration with secrets redacted;
|
|
# - SQL backend/version/pool/schema migration state;
|
|
# - Redis/Valkey state when configured;
|
|
# - filesystem/S3 blob backend state;
|
|
# - Tantivy/OpenSearch state and index health;
|
|
# - active sessions and IMAP IDLE/synchronization state;
|
|
# - cache/index backlog and worker state;
|
|
# - aggregate security signals such as failed logins, 401/403/429/5xx,
|
|
# brute-force-like patterns and rejected diagnostic endpoint access.
|
|
#
|
|
# It intentionally does NOT expose message bodies, passwords, auth headers,
|
|
# session secrets, S3 credentials or host-wide resource statistics.
|
|
|
|
|
|
# =============================================================================
|
|
# 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
|
|
#
|
|
# [sync]
|
|
# interval_secs = 30
|
|
# deep_index_batch_size = 10
|
|
#
|
|
# [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"
|
|
# deep_index = false
|
|
# urls = ["https://opensearch.example.net:9200"]
|
|
# index_prefix = "rustbox"
|
|
# required = true
|
|
# allow_insecure_http = false
|
|
# request_timeout_secs = 10
|
|
# 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"
|
|
# deep_index = false
|
|
# 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"
|