# 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"