RustBox 0.2.28 release manifest

0.2.28 release changes:
- replies to own sent messages target the other participant(s), with account aliases/identities treated as own addresses
- reader toolbar toggles between threaded and single-message presentation using the persisted mailbox `thread_mode` preference
- legacy Polish text repair covers common UTF-8/Windows-1252 and ISO-8859-2/Western-Latin mojibake in headers, bodies and cached summaries/details; exact ISO-8859-2 folded-subject coverage added

0.2.27 release changes:
- predefined styles promote to a custom copy when monochrome mode is changed
- wide message rows allocate more space to subjects and expose the complete subject as a tooltip
- message quick actions overlay to the left on hover instead of reserving hidden width
- Shift-click message popups use a reader-only embedded layout
- adaptive HTML mail colors derive light/dark behavior from the actual reader background for custom palettes
- mailbox storage opens a graphical per-folder IMAP size view; folder properties show measured size in MB when available

0.2.26 release changes:
- licensing: GNU AGPL v3 (`LICENSE`) with an optional paid commercial license (`LICENSING.md`); `NOTICE.md` keeps the MIT notice for parts originating from Oxi; Cargo metadata declares `AGPL-3.0-only`
- removes the Oxi synchronisation tooling (`scripts/*upstream*.sh`, `scripts/oxi-*.sh`, `UPSTREAM_BASE`, `docs/UPSTREAM.md`); RustBox is developed independently
- redesigned Settings → About (author, repository, license, origin)
- documentation: `docs/FEATURES.md` with screenshots in `docs/screenshots/`, rewritten README and user-facing CHANGELOG
- Polish settings notes are rendered after translations load; verbose code comments trimmed

0.2.25 release changes:
- filters: "Apply now" runs a visual Sieve rule on messages already in a folder; `POST /api/filters/match` finds matches with IMAP `UID SEARCH` (exact lower bound, superset upper bound, the gap decided from the local cache; undecidable messages are reported and skipped), and the client applies flags/labels, copies and a final move; per-rule "Apply now" in the rule list and an "apply to existing messages" option in the quick filter dialog
- filters: exceptions ("except if"), new conditions (To only, Cc only, size larger/smaller, has attachment, high priority), new actions (add RustBox label, move to Trash), rule templates, creating a missing target folder, "Always move messages from this sender…" in the message context menu, and translated rule summaries
- the message context menu groups forwarding, labels, snooze, filters, sender actions and technical tools into submenus and never exceeds the window height (scrolls instead), so Delete stays reachable
- fixes editing a predefined style mixing two styles: "Allow editing predefined style" seeds the layout/typography from the style and keeps the style; only a colour change creates a confirmed RustBox custom copy taken from the screen, with the theme pinned to the current light/dark mode; switching style locks editing again
- fixes "Soft" contrast removing all borders (self-referencing line colour)

0.2.24 release changes:
- fixes the "Style gradients" switch having no visible effect at the default "standard" depth: gradients moved out of the immersive-only tokens into a dedicated gradient layer (`59-skin-gradients.css`) that applies at every style depth
- every predefined style except the RustBox custom style now has gradients behind the switch: translucent defaults (app-bar gloss, toolbar tint, sidebar fade, selection sweep, glossy primary/compose buttons, accent backdrop glow) plus signature gradients for styles such as Roundcube Classic/Larry, GitHub, Discord, Reddit, Yahoo, Dracula, Tokyo Night, Synthwave, Aqua, Terminal and Retro 95
- adds 6 retro predefined styles: Windows XP inspired (Luna title bar, green Start-style compose button, task-pane sidebar), Mac OS X Aqua inspired (pinstripes, gel buttons, Aqua selection), iOS 6 inspired (glossy navigation bar, linen, glossy buttons), Windows 7 Aero inspired (Harmony backdrop, glass app bar), Frutiger Aero and BeOS inspired (yellow tab app bar)
- adds a time-of-day theme (`theme = schedule`, light/dark switch times) and an optional per-account colour (app-bar stripe and address dot), persisted by migration V054
- adds an optional second calendar reminder per event (e.g. 1 day before; choices up to 1 week) plus default reminders for new events in Settings → Notifications; all-day events are reminded at the start of the working day; clearing a reminder in the event editor now really clears it (V054)
- groups the interface-style selector into categories (RustBox & classic, Roundcube, mail apps, apps, editor themes, atmospheric, retro) with short, consistent names in English and Polish
- adds a live low-contrast warning for custom colours (WCAG thresholds), skeleton rows while a folder loads and a short cross-fade when the theme or style changes
- fixes the RustBox default selection colour being dark in the light theme, subjects being squeezed by snippets in message rows, and the flagged-message marker (now a side bar instead of a bottom line); reader-toolbar labels collapse below 820px instead of overflowing

0.2.23 release changes:
- adds a "Style depth" appearance setting (`skin_depth`, migration V053): `colors` applies only a predefined style's palette, `standard` (default, previous behavior) adds its fixed layout profile, `immersive` additionally applies its structural design tokens — typography, control/button/avatar shapes, floating or flush panes, app-bar and search treatment, selection markers, row cards and backdrop
- adds a separate "Style gradients" switch (`skin_gradients`, default off, same V053 migration): every gradient used by a style — backdrops, app bars, primary buttons, Roundcube Classic bevels — is applied only when it is on; otherwise each style uses its flat fallback colors
- introduces the interface-style standard: every predefined style now ships light + dark palettes, a fixed layout profile and a structural token block (`57-skin-tokens.css`, consumed by `58-skin-immersive.css`); all 23 existing styles were adapted to it
- adds 14 predefined styles: Dracula, Catppuccin, Solarized, Gruvbox, Tokyo Night, Rosé Pine, Everforest, Synthwave, Paper, Terminal, Aqua Glass, Linear inspired, Material You inspired and Retro 95
- the interface-style catalog is defined once in Rust (`INTERFACE_SKINS`) and shared by preference and `default_interface_skin` validation; the release validator enforces catalog/selector/profile/palette/token parity
- consolidates V051 and V052 into the fresh schemas; the compatibility baseline is now V052
- fixes the fresh MySQL schema: `mail_preferences.message_loading_mode` now uses an expression default (MySQL rejects literal defaults on TEXT)

0.2.22 release changes:
- adds message snooze: snoozing a message hides it client-side from its folder's current view until the chosen wake time, without touching server-side folder pagination/count SQL; `snoozed_messages` is a local-only cache table with no IMAP side effects, so a snoozed message is unaffected on any other client
- adds an "Undo Send" window: when `undo_send_window_secs > 0` (default 8s, max 120s), a submitted message is held in memory and only dispatched to SMTP after the window elapses unless cancelled; nothing is persisted, so a server restart during the window loses the staged send the same way an in-flight SMTP connection would be lost
- adds `GET/DELETE /messages/send/{pending_send_id}` to poll or cancel a staged send, and `POST/DELETE /messages/{folder}/{uid}/snooze`, `GET /messages/snoozed`, `GET /folders/{name}/snoozed-uids` for snooze

0.2.21 release changes and retained feature set:
- adds configurable login brute-force protection (`[login_rate_limit]`): per-IP token bucket plus per-mailbox and per-IP failed-attempt lockouts with exponential back-off; locked attempts return 429 + Retry-After before any IMAP connection
- the public `/api/auth/policy` GET no longer consumes the per-IP login budget
- adds `cookie_secure = auto | always | never`; `auto` sets the cookie `Secure` attribute only for requests that really arrived over HTTPS, so plain-HTTP installs keep working
- adds attachment memory guards: attachment sets that cannot fit `max_message_size_mb` once MIME-encoded are rejected with 413 from stored sizes before any file is loaded (send, IMAP draft snapshot, attach-message, streaming upload), and `max_concurrent_message_builds` / `message_build_wait_secs` bound how many requests hold attachments in RAM at once
- adds gmail-style message row opening: Ctrl/Cmd+click opens a message in a new tab, Shift+click opens it in a popup window, both deep-linked (outside selection mode; existing shift/ctrl selection behavior while selecting is unchanged)
- fixes the Axum route visibility mismatch for `AttachMessageSourceRequest`, allowing the forward-as-attachment handler to compile from `routes::mod`
- falls back to IMAP `INTERNALDATE` for messages with a missing/invalid message `Date` and repairs cached missing dates when opening a folder
- forwards a message as a real server-created `message/rfc822` `.eml` draft attachment
- expands sandboxed HTML message previews to the real remaining reader height and keeps that height synchronized across layout/fullscreen changes
- adds equal-height text/HTML signature fields and a dedicated raw-HTML + visual signature editor with image insertion
- keeps synchronization activity visible during page/progressive/all mailbox loads and individual message retrieval
- adds Shift-click contiguous message selection and direct first/last pagination controls
- fixes HTML signature formatting so toolbar actions preserve the selected text and reliably toggle formatting
- gives new messages with visible signatures a real editable body entry above the signature and softens/spaces the composer-only separator
- strips the editor-only body-entry marker from serialized outgoing HTML while retaining signature de-duplication metadata
- defaults wide message panes to compact single-line rows, with an Appearance setting to disable the behavior
- adds per-identity signature insertion mode: silent append-on-send (default) or visible/editable in the composer
- adds a durable SQL blob-deletion outbox used by filesystem/shared POSIX/NFS and S3 backends; attachment/draft cleanup is transactionally queued and retried until successful
- failed finalized uploads and failed destination objects from storage migration are also queued for cleanup; storage health probes reuse a fixed key to avoid unbounded probe-object buildup
- suppresses realtime “new message” toast/system notifications for Drafts saves while preserving normal folder/list refresh and `\Draft` + `\Seen` IMAP flags
- adds `Delete draft` in the composer; IMAP deletion is UID/UIDVALIDITY-checked and local draft attachment rows/blobs are cascaded after the server draft is removed
- adds an explicit `Save draft` action before `Cancel`; it persists the current message and attachments as a real IMAP Drafts snapshot without changing background autosave behavior
- explicit IMAP draft saves surface snapshot/APPEND failures instead of reporting a false success
- draft recovery restores attachment metadata from browser snapshots plus the authoritative server draft
- attachment chips show all uploaded files, including inline images/links, and new uploads merge by unique attachment ID instead of replacing visible entries
- fixes multi-attachment/inline-image insertion so new items append instead of replacing the selected image/content
- uses DOM Range insertion with stable caret placement for embedded images and CID file links
- floats image sizing/alignment actions over the editor instead of consuming composer height
- supports Ctrl/Cmd+V image embedding, file drop at the caret, dragging embedded images within the body, and four resize handles
- labels embedded images separately from regular/image attachment chips
- keeps attachment re-embed actions compact and allows separate same-name/same-size files
- compact recipient address summaries with expandable full recipient lists
- adds default pagination at 300 messages per page, plus optional smooth progressive loading and full-folder loading
- adds a composer `Hide fields` / `Show fields` control so From/To/Cc/Bcc/Subject/options can collapse and the editor can use the freed vertical space
- inline-by-default image attachments, re-embedding, CID file links, and inline image layout controls
- opens message-body links without an explicit target in a new tab/window instead of navigating inside the sandboxed mail-preview iframe; explicit targets remain intact
- shows the login success toast only after a real POST login; session restoration/page refresh is silent
- improves non-ASCII/Polish mail display with explicit UTF-8 preview documents and broader RFC 2047 header decoding (including ISO-8859-2/Windows-1250)
- previews PDFs from the authenticated inline attachment endpoint instead of a blob iframe for Safari/iPhone compatibility
- presents compose attachment/message size limits as compact raw values without implementation-detail descriptions
- retains the 0.2.9 realtime notification, shortcut and 300-message mailbox behavior
- adds opt-in adaptive HTML-mail colors in Appearance plus mailbox loading-mode selection, persisted by runtime migration V051
Architecture:
- database_backend = sqlite | mysql | pgsql selects authoritative SQL only
- blob storage and search are independent from the SQL engine
- PostgreSQL/MySQL single-node deployments may continue to use data_dir-backed filesystem/Tantivy
- cluster mode clears an implicit/default data_dir; explicit data_dir is not forbidden, but local-only capabilities are rejected

Blob storage:
- filesystem: local single-node or shared POSIX with storage.shared=true
- shared POSIX examples: NFS, CephFS, GlusterFS
- s3: AWS S3 and S3-compatible endpoints such as MinIO/Ceph RGW
- attachment rows use logical storage keys and SHA-256 metadata
- storage migration command: rustbox storage migrate --from filesystem:/path --to configured [--dry-run] [--verify]

Search:
- local_tantivy: single-node only
- opensearch: cluster-capable external index
- disabled: no full-text index
- OpenSearch operations are tenant-scoped by user_hash
- durable SQL outbox provides claim/lease, retry/backoff and dead-letter handling
- rebuild command: rustbox search reindex

Database/schema:
- migrations/{sqlite,mysql,pgsql}/schema.sql contain the current fresh schema
- compatibility baseline is V052; former V047-V052 changes are consolidated into `schema.sql` and their standalone migrations are removed
- databases older than V052 must be upgraded with a V052-capable RustBox release (0.2.22) before using this build
- V053 adds `display_preferences.skin_depth` (default `standard`) and `display_preferences.skin_gradients` (default off) to existing V052 tenants
- V054 adds `theme_light_from`, `theme_dark_from`, `account_color`, `calendar_events.reminder_extra_minutes` and the calendar default reminders to existing V053 tenants
- next migration number: V055

Docker examples:
- docker/compose-single-sqlite.yml
- docker/compose-ha-garage.yml (2x RustBox + Envoy + Valkey + PostgreSQL + OpenSearch + Garage S3)
- docker/compose-postgres-s3-opensearch.yml (PostgreSQL + SILO/S3 + OpenSearch)
- docker/compose-mysql-nfs-opensearch.yml (MySQL + real NFS volume + OpenSearch)

Operations/docs:
- standalone `scripts/migrate_roundcube.py` imports Roundcube contacts/identities using RustBox config.toml and supports SQLite/MySQL/PostgreSQL targets
- README.md documents 0.2.28 configuration
- docker/README.md documents Compose variants
- docs/STORAGE_SEARCH_OPERATIONS.md documents failure modes, shared-storage requirements and migration behavior
- TODO.md leaves live SILO/MinIO/AWS/Ceph and real multi-node acceptance validation explicitly pending

Build/release metadata:
- `scripts/package-release.sh` runs the full Rust release gate before creating ZIP/SHA256 artifacts
- Cargo.toml version = 0.2.28
- rust-toolchain.toml pins Rust 1.99.1
- Cargo.lock is retained for reproducible application builds; the release gate generates it only when missing
- CI and scripts/build-release.sh use the retained Cargo.lock (generating it only if missing), then run locked tests/Clippy/release build
