# RustBox changelog ### 0.2.22 - Added message snooze. A new `snoozed_messages` table (migration `V052`) records a wake time for a message; snoozing it (via the new "Snooze…" context-menu entry or the `b` shortcut while a message is open) hides it client-side from its folder's current view until that time, using `GET /folders/{name}/snoozed-uids` to know which UIDs are currently asleep. `POST /messages/{folder}/{uid}/snooze` and `DELETE /messages/{folder}/{uid}/snooze` set/clear a snooze; `GET /messages/snoozed` lists everything currently sleeping, shown in the new "View snoozed messages…" dialog (`Shift+B`). This is purely a local cache: it has no IMAP side effects and does not change server-side folder pagination/count queries. - Added an "Undo Send" window. New `undo_send_window_secs` config setting (default `8`, max `120`; `0` restores the previous immediate-send behavior). When enabled, submitting a message stages it in memory instead of sending it right away; the composer shows a toast with an "Undo" action and countdown, and the message is only dispatched to SMTP once the window elapses without being cancelled. `GET /messages/send/{pending_send_id}` polls status (`pending`/`dispatching`/`sent`/`cancelled`/`failed`); `DELETE /messages/send/{pending_send_id}` cancels a still-pending send. Nothing is persisted, so a server restart during the window drops the staged send the same way an in-flight SMTP connection would be lost. - Bumped the application/source release to 0.2.22. ### 0.2.21 - Added configurable login brute-force protection under `[login_rate_limit]` (env: `LOGIN_RATE_LIMIT__*`): the per-IP token bucket on `POST /api/auth/login` is now driven by `ip_burst`/`ip_replenish_secs` instead of hard-coded values, plus failed-attempt lockouts per mailbox (`account_max_failures`, any IP) and per client IP (`ip_max_failures`, password spraying) with exponential back-off (`lockout_secs` → `max_lockout_secs`). Locked attempts return `429` with `Retry-After` before any IMAP connection is opened. - The public `GET /api/auth/policy` no longer consumes the per-IP login budget. - Added `cookie_secure = "auto" | "always" | "never"` (env: `COOKIE_SECURE`). The default `auto` sets the `Secure` cookie attribute only when the browser actually reached RustBox over HTTPS (trusted-proxy `X-Forwarded-Proto`/`Forwarded`/`X-Forwarded-Ssl`, otherwise the `Origin`/`Referer` scheme). Previously `Secure` depended on `environment != "development"`, which broke logins on plain-HTTP production installs and left cookies without `Secure` in development behind HTTPS. - Added memory guards for messages with attachments. An attachment set that cannot fit `max_message_size_mb` once base64-encoded is now rejected with `413` from the stored sizes before any blob is read when sending, saving an IMAP draft snapshot or attaching a message, and while an upload is still streaming (previously the limit was only checked after the whole MIME message had been built in RAM). New `max_concurrent_message_builds` (default 4) and `message_build_wait_secs` (default 30) limit how many requests hold attachment data in memory at once; extra requests wait and then receive `503`. Environment: `MAX_CONCURRENT_MESSAGE_BUILDS`, `MESSAGE_BUILD_WAIT_SECS`. - Gmail-style message row opening: outside selection mode, Ctrl/Cmd+click a message opens it in a new browser tab, and Shift+click opens it in a small popup window; both deep-link to the exact message so the new tab/window loads it directly. Plain click and existing selection-mode shift/ctrl range/toggle behavior are unchanged. - Bumped the application/source release to 0.2.21. ### 0.2.20 - Fixed interrupted initial-backfill recovery so legacy missing-date repair does not run against an invalidated or incomplete folder cache. - Fixed the `Forward as attachment` route build failure by making `AttachMessageSourceRequest` visible at the same crate scope as the public route handler, so Axum can resolve the handler type from `routes::mod`. - Fixed messages without a usable RFC 5322 `Date` header so mailbox synchronization falls back to the IMAP `INTERNALDATE`; cached messages with missing/invalid dates are repaired when the folder is opened instead of remaining at the end of the list. - Fixed `Forward as attachment` so the server fetches the original RFC822 source from IMAP and stores it in the draft as a real `message/rfc822` `.eml` attachment. - Bumped the application/source release to 0.2.20. ### 0.2.19 - Improved vertical space management in the HTML message reader: the sandboxed message iframe now expands to the actual space remaining below the reader toolbar, message header and auxiliary bars instead of stopping at a fixed `68vh`. - Recalculate the HTML message viewport when the reader, header, attachments, security bars, window size or fullscreen state changes, avoiding an unnecessary short inner scrollbar while preserving the existing iframe sandbox. - Bumped the application/source release to 0.2.19. ### 0.2.18 - Fixed new-message sender selection so the composer prefers the configured default identity, then the identity matching the active account address, then the first available identity; the visible From field now shows the active account address instead of an ambiguous `Account default` label when no identity is selected. - Restored visible signatures/footers for new messages when no identity was explicitly marked default by using the same preferred-identity fallback for signature insertion and sending. - Added explicit composer undo/redo keyboard handling: Ctrl/Cmd+Z, Ctrl/Cmd+Shift+Z and Ctrl+Y. - Improved adaptive HTML-mail rendering for legacy/marketing templates: preserve safe CSS from `
`, retain body presentation metadata, adapt HSL/HSLA and background shorthands with URLs, enforce readable text contrast and remove the hard-coded white iframe canvas. - Suppress realtime new-mail notifications caused by intentional message moves (including Spam/Junk) by tracking the destination UID while retaining normal notifications for unrelated incoming mail. - Fixed visible-signature image materialization so cleanup waits for the in-flight CID upload instead of deleting the signature image while the composer is opening. - Added a top-bar quick account switcher that appears when more than one account is signed in to the current browser session. - Added browser-level combined notification selection in Account settings; selected accounts keep their own notification folder, sound and DND preferences. - Added administrator feature gate `multi_account_enabled`; disabling it hides multi-account controls, rejects adding a second account and limits realtime WebSocket monitoring to the active account. - Made the plain-text and HTML signature editors the same visual height. - Added a dedicated HTML signature source modal with sanitized source import, visual preview/editing, formatting controls and image insertion. - Kept the global synchronization spinner active for mailbox page loads, progressive/full-folder loading and individual message/IMAP-draft retrieval. - Added Shift-click range selection across the currently displayed message list, using the last regular/toggle selection as the anchor. - Added first-page and last-page controls to mailbox pagination. - Hide HTML inline/CID resources, including images inserted through signature management, from the reader attachment list while continuing to resolve them inside the message body. - Treat MIME parts explicitly marked `Content-Disposition: inline` as inline content rather than message attachments, preventing signature-only messages from gaining an attachment indicator after the next IMAP metadata sync. - Retain signature images as `multipart/related` CID parts for outgoing mail; regular file attachments remain unchanged. - Kept the application/source release at 0.2.18. ### 0.2.16 - Added opt-in adaptive HTML-mail colors that adjust neutral backgrounds and low-contrast text to the active light/dark theme while preserving layout, images and brand colors. - Added mailbox pagination, enabled by default at 300 messages per page, with optional smooth progressive loading or full-folder loading. - Added runtime schema migration V051 for SQLite, MySQL and PostgreSQL. Existing V050 databases gain `display_preferences.adaptive_email_colors` and `mail_preferences.message_loading_mode` before the new preferences are queried. - Kept V050 as the consolidated compatibility baseline and removed the standalone V050 migration files because those changes are already present in the complete `schema.sql` definitions. - Bumped the application/source release to 0.2.16. ### 0.2.15 - Fixed HTML signature editor container semantics so clicks on whitespace/outside the editable area no longer implicitly activate the Bold toolbar button. - Fixed the HTML signature editor so toolbar actions preserve the active text selection and only run on an actual selection; collapsed-caret clicks no longer enable sticky bold/italic/underline typing modes. - Fixed visible signatures in new messages by creating an editable body entry before the signature instead of placing the signature at the top of an otherwise empty editor. - Refined the visible-signature separator with more breathing room and a subtler solid rule so it reads as a boundary without dominating the composer. - Strips the editor-only body-entry marker from outgoing HTML while retaining the visible-signature marker used for de-duplication. - Consolidated migrations V047-V050 into the V050 compatibility baseline and removed their standalone migration files. Databases older than V050 must be upgraded through a V050-capable release first. - Bumped the application/source release to 0.2.15. ### 0.2.14 - Made pane resizing tolerant of frontend/backend range mismatches: pane widths are normalized server-side, and a rejected width silently disables the in-view resizer for the current session instead of showing a validation error. - Added a default-on compact message-list layout that consolidates sender, subject/snippet, date and status icons into one row when the message pane is wide enough; it can be disabled in Appearance settings. - Added per-identity signature insertion mode: `silent` (default) keeps the composer clean and appends the signature during send, while `visible` inserts an editable/removable signature in the composer. - Visible HTML signatures are structurally marked for de-duplication and keep inline signature images attached through the existing CID flow. - Added runtime schema migration V050 for SQLite, MySQL and PostgreSQL (`mail_preferences.compact_wide_rows`, `identities.signature_insert_mode`). V051 is the next migration number. - Bumped the application/source release to 0.2.14. ### 0.2.13 - Added durable per-tenant blob deletion outbox for every configured blob backend (`filesystem` including shared NFS/POSIX, and S3-compatible storage). Attachment metadata deletion and cleanup enqueue now commit atomically, so a temporary storage failure cannot silently orphan blobs. - Blob cleanup runs immediately when possible and is also retried by a background worker with claim leases and exponential backoff; missing objects are treated as successful idempotent deletes and retries do not dead-letter permanently. - Deleting one attachment, deleting a whole draft, and post-send draft cleanup all use the same durable deletion path. Failed/finalized uploads and failed storage-migration destination objects are queued for cleanup as well. - Storage health probes reuse a fixed probe key so a failed cleanup cannot accumulate unbounded health-check objects. - Added runtime schema migration V049 for SQLite, MySQL and PostgreSQL (`blob_delete_outbox`). V050 is the next migration number. - Bumped the application/source release to 0.2.13. ### 0.2.12 - Saving or updating a draft no longer raises a realtime “new message” toast/system notification for the Drafts folder; the folder/list still refreshes normally, and IMAP snapshots remain `\Draft` + `\Seen`. - Added a composer `Delete draft` action that permanently removes the current IMAP draft and cascades cleanup through the local autosave row, draft attachment rows and uploaded attachment blobs; IMAP deletion is verified before local state is reported as deleted. - IMAP-only drafts opened from other mail clients can be deleted without requiring a pre-existing RustBox draft database row, while UIDVALIDITY and `\\Draft`/Drafts-folder checks prevent deleting a stale or non-draft message by mistake. - Explicit `Save draft` now records the newly appended IMAP UID in the live composer even for a brand-new draft, so later Save/Delete/Send operations consistently target the current server snapshot. - Draft messages in the configured IMAP Drafts folder (or carrying the `\\Draft` flag) now open directly in the normal composer from their server-side RFC822/MIME content; opening does not depend on a RustBox draft database row or `X-RustBox-Draft-ID`, so drafts created by other mail clients can be edited and sent. - Editing an IMAP-sourced draft keeps its original server UID as the replacement source: explicit Save draft appends the new MIME snapshot first and removes the superseded IMAP draft only after the new UID is resolved; sending also removes the source draft after successful SMTP delivery. - Existing IMAP draft attachments stay usable directly from their MIME parts while editing, can be removed/re-embedded without first copying the whole message into the draft database, and are carried into Save/Send together with newly uploaded local attachments. - Inline MIME parts now preserve their original filenames through `Content-Disposition: inline; filename=...` when saving drafts and sending messages, improving Roundcube/Thunderbird interoperability. - IMAP draft reopening restores To/Cc/Bcc, Subject, plain/HTML body, reply threading headers, priority, MDN state, From identity when it matches a configured identity, inline CIDs and attachment filenames directly from the server message. - Fixed manual IMAP draft saving for drafts without To/Cc/Bcc recipients by using a build-only envelope while keeping recipient headers empty; Bcc is retained in saved draft snapshots. - IMAP draft saving now honors the per-account Special folders → Drafts mapping and `default_drafts_folder`, then falls back to the IMAP `\Drafts` attribute and common `Drafts`/`Draft` folder names. - Added an explicit `Save draft` composer action immediately before `Cancel`; it forces the current message and attachments to be persisted as a real IMAP `\Draft` snapshot while leaving background autosave behavior unchanged. - Manual IMAP draft saves now report a failure when RFC822 snapshot creation or IMAP APPEND fails instead of showing a misleading success state. - Fixed draft recovery so attachment metadata is restored from both the browser recovery snapshot and the authoritative server draft. - Composer attachment chips now show every uploaded file, including inline images/links, instead of hiding embedded items from the attachment list. - Adding another attachment now merges by unique attachment ID and refreshes the server-side draft attachment list, preventing an existing chip from appearing to be replaced. - Inline attachment chips expose an attachment-only action that removes the inline occurrence without deleting the uploaded file. - Stretched `Load more` and `Load all` across the full message-list action area with equal-width controls. - Added a composer `Hide fields` / `Show fields` toggle that collapses From/To/Cc/Bcc/Subject/options so the editor can use the freed vertical space; invalid recipient input automatically expands the fields again. - Fixed inline image insertion so adding another image or attachment appends a new element instead of replacing the current selection. - Replaced deprecated `execCommand("insertHTML")` attachment insertion with DOM Range insertion and safe caret placement. - Removed filename/size attachment de-duplication so separate files with identical metadata can still be attached independently. - Moved inline-image resize/alignment controls into a floating contextual panel so selecting an image no longer reduces the composer editing area. - Added mouse/touch resize handles around the selected inline image while preserving its aspect ratio. - Inline images can be dragged to another caret position inside the message body. - Pasting PNG/JPEG/GIF/WebP files with Ctrl/Cmd+V embeds them inline, and dropping files into the editor uploads them at the drop position. - Attachment chips explicitly identify regular/image attachments, while selected embedded images are labelled `Inline image`. - Made attachment re-embed actions compact icon buttons to keep the attachment strip readable. - Collapsed large To/Cc recipient lists in the message reader into a compact summary with on-demand expansion. - Added a `Load all` action beside `Load more` in the message list. - Renamed the reader toolbar action from `Message details` to `Details`. - Images added to HTML messages now embed inline by default; removing an inline image keeps the underlying file available as an attachment that can be embedded again. - Added `Embed` actions for attachments: images are inserted inline and other files can be inserted as CID links in the message body. - Added quick inline-image sizing and alignment controls, plus the existing advanced crop/resize editor and an explicit `Keep as attachment` action. - No schema migration is required; V049 remains the next migration number. ### 0.2.11 - No schema migration is required; V049 remains the next migration number. - RoundCube migration script - Allow to edit all themes. ### 0.2.10 - Message-body links without an explicit `target` now open in a new browser tab/window instead of navigating inside the sandboxed mail preview iframe. - Explicit link targets from the message are preserved; links that open a new tab/window receive `rel="noopener noreferrer"`. - Authentication success toast now appears only after a real login request; restoring an existing session after page refresh stays silent. - Improved iPhone/Safari mail rendering by explicitly declaring UTF-8 in message preview documents and using broader RFC 2047 header decoding (including ISO-8859-2/Windows-1250) for non-ASCII subjects/sender names. - Changed PDF attachment preview to use the authenticated inline attachment URL instead of a `blob:` iframe, improving Safari/iPhone compatibility. - Simplified Composing size-limit information to one compact line with only the attachment and complete-message limits. - No schema migration is required; V049 remains the next migration number. ### 0.2.9 - Made realtime new-mail notifications less aggressive: startup synchronization is silent, notifications are armed only after the application is open, and bulk move/copy/archive/junk/delete destinations suppress self-generated `NewMessages` toasts while the bulk summary toast remains. - Added a success toast after a completed authenticated startup/login. - Restored browser/system modifier shortcuts by limiting RustBox single-key shortcuts to unmodified keys; `Ctrl/Cmd+C`, `Ctrl/Cmd+V`, `Ctrl/Cmd+X`, `Ctrl/Cmd+A`, `Ctrl/Cmd+F`, `Ctrl/Cmd+R` and `Ctrl/Cmd+N` are no longer intercepted by mail actions. - Changed the default messages-per-page setting from 50 to 300 and raised the supported folder-page limit to 300. - No schema migration is required; V049 remains the next migration number. ### 0.2.8 - Scoped default logging so RustBox can run at DEBUG/INFO without enabling verbose dependency logs; Tantivy merge/GC/file-watcher noise and its internal filesystem/source paths are hidden unless explicitly enabled through a custom `RUST_LOG` target filter. Development and deployment examples now use target-scoped filters. - Kept administrator diagnostics process-local and cross-platform: removed host CPU/RAM/load-average and Linux `/proc` sampling; runtime diagnostics now rely on portable RustBox in-memory counters plus the process PID. - Added a disabled-by-default administrator system-diagnostics endpoint with loopback-only CIDR allowlisting, mandatory token or Basic authentication, fail-closed trusted-proxy handling, secret redaction, live SQL/session/storage/search probes, active IMAP IDLE/session/runtime state and bounded security telemetry for failed logins, rate limiting, access denials and server errors. - Moved the synchronization/index activity indicator to the left of Contacts. - Corrected production TOML section ordering so intended top-level database/session/mail/security/special-folder settings are no longer accidentally parsed as search settings. - Refined folder counters so only the unread value is emphasized, hid the sync/index activity icon when idle, and made manual search-index rebuilds drive the same live activity indicator as background indexing. - Made folder totals/unread counts server-authoritative: IMAP `STATUS MESSAGES/UNSEEN` drives badges, selected-mailbox fallback counts `UID SEARCH UNSEEN`, periodic reconciliation repairs counters even when `HIGHESTMODSEQ` is unchanged, and mutation-time compare-and-set/retry prevents out-of-order STATUS responses from rolling counters backwards. - Decoupled counter refresh from folder-tree discovery: a forced counter refresh reuses a fresh cached folder tree and batches STATUS requests instead of forcing another IMAP LIST. - Changed read/unread/flag updates to patch the affected message row and folder badge live; ordinary flag changes no longer rebuild the message list, removing the reader/list jump during mark-read operations. - Added responsive folder counters (`total / unread` when unread is non-zero, total-only at zero, compact mode in narrow sidebars) and surfaced IMAP header-cache plus search-index backlog in the top-right activity indicator/tooltip. - Extended the opt-in security-header middleware with a private `/api` profile (`no-store`, `nosniff`, same-origin CORP, `frame-ancestors 'none'` and `X-Frame-Options: DENY`), while keeping Permissions-Policy/COOP and the full document CSP on HTML and HSTS at the public HTTPS edge; disabling `security_headers_enabled` removes the whole RustBox-added security profile. - Minimized unauthenticated `/api/auth/policy` to `rememberMeEnabled`; optional feature policy moved behind authenticated `/api/auth/session-policy`, and session/remember durations are no longer exposed by policy endpoints. - Added optimistic read/unread updates in the browser with guarded rollback, followed by authoritative server reconciliation; normal read/unread operations no longer replace the message list. - Fixed realtime WebSocket deduplication so unique server `eventId` values always win over payload fingerprints; rapid legitimate counter cycles such as `1 → 0 → 1` are no longer dropped as duplicates. Active-folder total text is patched live together with the sidebar counter. - Hardened IDLE/CONDSTORE reconciliation: counter STATUS no longer advances the flag-sync cursor, non-CONDSTORE servers fall back to a full `UID FLAGS` comparison, partial IMAP fetch streams are discarded instead of being treated as complete, and IDLE header/index writes participate in the same cache-generation transaction. - Fixed UIDVALIDITY reset writes to stay inside the same database transaction on SQLite, MySQL and PostgreSQL instead of mixing transaction/base-connection writes. - Tightened the HTML CSP to `style-src 'self'` plus the narrower `style-src-attr 'unsafe-inline'`; inline JavaScript remains disallowed. - Added regression coverage for authoritative unread refresh, API/document/static header profiles and pre-login policy minimization. - Added database migration V048 on SQLite, MySQL and PostgreSQL: folder counters now carry a monotonic `counter_generation`, so delayed STATUS responses cannot win an ABA race after newer read/unread activity. V049 is the next migration number. - Bumped the application/source release to 0.2.8. ### 0.2.7 - Made opt-in browser security headers content-type-aware instead of attaching document-only policies to every response. - HTML documents keep the full CSP, clickjacking, permissions, COOP, referrer and MIME-sniffing protections. - CSS responses now receive only the applicable `Referrer-Policy` and `X-Content-Type-Options`; JavaScript, JSON, SVG, API and other non-document responses retain `X-Content-Type-Options: nosniff` without redundant document policies. - Added regression coverage for HTML, CSS and API header sets while preserving the disabled-by-default compatibility switch. - No schema migration is required; runtime schema remains V047 and V048 remains the next migration number. ### 0.2.6 - Fixed iPhone/iOS reader toolbar popovers (`Message details` and `Move / copy`) by rendering the open menu outside the horizontally scrolling toolbar and positioning it against the visual viewport. - Made folder counters and message-list refreshes more responsive: realtime sync stores the server-reported unread count, folder hierarchy refresh avoids unnecessary IMAP LIST calls, and manual refresh forces the active-folder status check in parallel with counter refresh. - Improved realtime mail delivery: INBOX remains on IMAP IDLE, the actively viewed folder gets its own IDLE watcher, other folders are reconciled immediately and every 30 seconds, and newly detected headers are cached before realtime events are emitted. - Added an in-app toast for new mail in any folder and deduplicated browser notifications per account/folder/UID to prevent repeated notifications for the same message. - Added an animated indexing indicator beside Contacts while deep message indexing is active, and prevented excluded Trash/Junk/Spam folders from starving the indexing batch. - No schema migration is required; runtime schema remains V047 and V048 remains the next migration number. ### 0.2.5 - After moving or deleting the currently opened message, RustBox now automatically opens the next message in the current list; if the removed message was last, it falls back to the previous message. - The behavior applies to Spam, Not spam, Archive, Move and Delete actions and keeps the reader open while advancing. - No schema migration is required; runtime schema remains V047 and V048 remains the next migration number. ### 0.2.4 - Fixed S3-compatible storage over explicitly allowed plain HTTP endpoints by applying `storage.allow_http` to the final `ClientOptions` object instead of setting it before `with_client_options()` replaces those options. This fixes Garage/MinIO/Ceph-style internal HTTP endpoints without weakening the HTTPS default. - Added an HTTP S3 regression test that verifies a request reaches an explicitly allowed local S3-compatible endpoint. - Updated the pinned Rust toolchain/MSRV to Rust 1.99.1. - Updated RustBox Docker build/runtime images from Debian 12 Bookworm to Debian 13 Trixie (`rust:1.99.1-trixie`, `debian:trixie-slim`) and switched the runtime OpenSSL package to Trixie's `libssl3t64`. - Updated PostgreSQL 17 Compose images from Bookworm to the supported Trixie variant. - No schema migration is required; runtime schema remains V047 and V048 remains the next migration number. ### 0.2.3 - Fixed API fallback routing so administratively disabled or otherwise unknown `/api/*` endpoints return HTTP 404 instead of being caught by the SPA fallback and returning the UI with HTTP 200. - Added administrator feature switches for ManageSieve, diagnostics and About; disabled Sieve/diagnostics routes are not registered, and the matching settings UI is removed while keyboard shortcuts remain available. - Added a protected “Reset all settings” action that restores display, mailbox, notification, folder, special-folder, calendar and OpenPGP preferences without deleting mail, identities, keys, templates or trusted-sender data. - Added opt-in browser security response headers (CSP, frame, referrer, permissions and cross-origin opener policies); the feature is disabled by default for compatibility. - Added a Docker HA example with two RustBox application nodes behind Envoy round-robin load balancing, Valkey-backed sessions, PostgreSQL, OpenSearch and Garage S3 object storage. Envoy adds `X-RustBox-Backend` to responses. - Added current pinned Garage, Valkey and Envoy images to the HA example and an idempotent Garage bucket/key initialization helper. - No schema migration is required; runtime schema remains V047 and V048 remains the next migration number. ### 0.2.2 - Fixed the SQLite isolation regression test to initialize `AppConfig` with a struct literal plus `..Default::default()`, satisfying `cargo clippy --all-targets -- -D warnings` without suppressing `field_reassign_with_default`. - Fixed Rust test isolation for SQLite: test-only `db::pool::configure()` still initializes the requested database schema but no longer pins the first temporary SQLite path in the process-wide `OnceLock`, preventing later tests from reusing a deleted `TempDir` and collapsing into HTTP 500/database-open failures. - Added a regression test that configures SQLite in one temporary directory, removes it, then opens an account database in a second temporary directory. - Strengthened the offline release contract to assert that `scripts/build-release.sh` requires the full locked test suite, warning-free Clippy, and a locked release build before a release is accepted. - Fixed the PostgreSQL startup schema compile failure (`implementation of sqlx::Executor is not general enough`) by executing the multi-statement DDL batch through `sqlx::Executor::execute()` on the transaction connection instead of `sqlx::raw_sql(...).execute(&mut PgConnection)`. - Preserved transactional startup schema initialization and the local `client_min_messages=WARNING` scope used to suppress expected idempotent PostgreSQL NOTICE output. - Added a static regression gate that forbids the lifetime-problematic `raw_sql(&schema).execute(&mut *tx)` PostgreSQL startup path. - No schema migration is required; runtime schema remains V047 and V048 remains the next migration number. - Fixed PostgreSQL header-sync invalidation used by message mutations (including marking/moving messages as spam): the UPSERT now qualifies the existing tenant `user_meta.value`, avoiding `column reference "value" is ambiguous` and HTTP 500 responses. - Added a regression check for tenant SQL rewriting of the qualified `user_meta.value` reference and a static audit covering all PostgreSQL `ON CONFLICT ... DO UPDATE` target-column reads. - Confirmed the equivalent SQLite UPSERT is not ambiguous and MySQL uses `ON DUPLICATE KEY UPDATE`, so no matching backend fix is required. - No schema migration is required; the runtime schema remains V047 and V048 remains the next migration number. - Centralized SQL schema initialization in `db::pool::configure()` so the database session store no longer executes the global schema a second time. - Fixed the duplicate startup DDL for all SQL backends: PostgreSQL no longer repeats `relation ... already exists, skipping` notices, while MySQL and SQLite also avoid the redundant second schema pass. - Scoped PostgreSQL startup schema initialization to a transaction with `client_min_messages=WARNING`, suppressing expected idempotent `CREATE ... IF NOT EXISTS` notices without hiding application/database warnings globally. - Removed the Docker-specific SQLx PostgreSQL notice filter workaround; normal `RUST_LOG=info` is sufficient. - No schema migration is required; the runtime schema remains V047 and V048 remains the next migration number. ### 0.2.1 - Fixed Docker development authentication by documenting and defaulting Compose to `ENVIRONMENT=development`, so session cookies work over direct HTTP while production keeps `Secure` cookies behind HTTPS. - Exposed `pgp_enabled` through the auth policy and hid OpenPGP settings, identity controls, composer controls and reader UI when OpenPGP is disabled by administrator policy. - Prevented disabled OpenPGP from producing repeated UI error toasts or unnecessary API requests. - Added OpenSearch outbox worker backoff for HTTP 429/flood-stage/read-only conditions and stopped the current batch after the first backpressure failure, preventing log storms while preserving durable retries without consuming dead-letter attempts. - Kept OpenSearch disk safety thresholds intact; Docker documentation explains that named volumes consume the Docker host filesystem and how to diagnose/recover flood-stage blocks. - Added a stable `rustbox` Compose project name, documented Docker environment variables, and made application port mapping configurable through `PORT`. - Reduced PostgreSQL startup log noise in the Docker example by suppressing repeated SQLx `NOTICE` messages at the default log filter. ### 0.2.0 - Fixed nested database transactions during message move by reusing the caller-owned transaction for cache removal, header-sync invalidation, folder invalidation, and search outbox writes. - Kept `search_outbox` in sync between tenant and legacy-import table sets so schema/import parity tests pass. - Decoupled SQL, blob storage and search: PostgreSQL/MySQL may still use local `data_dir` backends in single-node mode, while cluster mode no longer requires persistent `data_dir` when cluster-safe backends are configured. - Added `BlobStorage` with filesystem/shared-POSIX and S3-compatible backends, streaming attachment I/O, SHA-256 metadata, S3 retry/timeouts/checksums/SSE options and storage readiness probes. - Added idempotent attachment migration from legacy filesystem paths to logical object keys (`rustbox storage migrate`). - Added OpenSearch as the external full-text backend with an async HTTP client, per-tenant filtering/routing, deterministic document IDs, health checks and rebuild support. - Added a durable SQL search outbox with caller-transaction participation, claim/lease concurrency, retry/backoff, dead-letter state and lag/dead-letter diagnostics. - Hardened clustered indexing against re-enqueue/lease races, sized worker leases from the OpenSearch timeout/failover budget, and made IMAP IDLE deletions enqueue index removals in the same SQL transaction. - Added runtime schema migration V047 for attachment checksums and the search outbox across SQLite, MySQL and PostgreSQL. - Added Docker variants for standalone SQLite, PostgreSQL + SILO (S3-compatible) + OpenSearch, and MySQL + NFS + OpenSearch. - Added tests for cluster capability validation, filesystem blob streaming, OpenSearch tenant scoping and search-outbox transaction rollback/claim/idempotency behavior. ### 0.1.2 - Added `log_level` to TOML configuration (`trace`, `debug`, `info`, `warn`, `error`, `off`); `RUST_LOG` remains an explicit runtime override. - Audited the MySQL and PostgreSQL query catalogs, schema baselines and backend-specific server SQL without adding application tests. - Fixed a MySQL DDL incompatibility by changing indexed `pgp_keys.email` from `TEXT` to `VARCHAR(320)`; MySQL requires a prefix length when indexing `TEXT`/`BLOB` columns. - Fixed destructive MySQL cache upserts: folder refresh and message-header refresh now use `INSERT ... ON DUPLICATE KEY UPDATE` instead of `REPLACE INTO`, preserving child rows, message tags, cached bodies and other columns not part of the refresh. - Documented and enforced cluster storage behavior: mailbox metadata/body cache remains in shared SQL, while draft attachments use either S3-compatible storage or an explicitly shared POSIX filesystem. - Replaced the pre-0.2 storage/search compatibility switches with the final `[storage]` and `[search]` configuration model; cluster mode validates backend capabilities directly and rejects local Tantivy. - Added `pgp_enabled`; cluster mode requires OpenPGP disabled because private-key material is filesystem-backed. - Avoided provisioning per-user Tantivy directories when the search index backend is disabled and made diagnostics/deep indexing honor that setting. - Added a release audit document with SQL compatibility and distributed-storage findings. ### 0.1.1 - Fixed the multi-backend compatibility layer compile blockers reported by the Rust compiler: empty `[]` parameter inference no longer overlaps the generic array implementation, legacy SQLite probing imports `rusqlite::OptionalExtension` without colliding with RustBox's own `OptionalExtension`, and MySQL/PostgreSQL transaction batch execution now uses the typed SQLx transaction helpers instead of the lifetime-problematic `raw_sql` executor path. - Unified the storage model: `database_backend = sqlite | mysql | pgsql` now selects the database engine for the whole application instead of only the SQL session store; `session_backend = database | redis` either keeps sessions in that same database or moves them to Redis/Redis Sentinel. - Added complete SQLite, MySQL and PostgreSQL baselines under `migrations/{sqlite,mysql,pgsql}/schema.sql`. Each schema contains global sessions plus the full account-data schema at baseline version 46; the historical SQLite V001-V046 files are squashed out of the release tree and V047 is reserved for the next schema change across all engines. - Preserved per-account data isolation in a shared physical database with deterministic collision-checked table prefixes, while keeping the session table global. - Added automatic first-use import of legacy 0.1.0 `data_dir/