45 KiB
RustBox 0.2.31 - implementation status
- Draft saves are realtime-silent: Drafts folder/list updates continue, but saving a draft does not surface as a new-mail notification.
This file tracks the
0.2.31source snapshot. Storage/search scope and outstanding acceptance work are tracked inTODO.md. A feature is only listed as implemented when working code exists in this source tree; partial items are called out explicitly.
0.2.31 touch action sheet
- On touch screens (
hover: none) a long press (450 ms) on a message row opens#context-menuas a bottom sheet: sender/subject header, quick actions (read/unread and flag via the flags endpoint, archive/delete viaperformBulkon that message with undo), then the desktop context-menu items with drill-down submenus. - The sheet uses the theme tokens (
--ui-size,--ui-scale-factor,--density-factor,--icon-size,--radius-*), is capped at 560 px on tablets, owns one history entry (Android back closes it) and closes on backdrop tap, drag-down, Escape or any action. Rows are not draggable on touch screens; the hover quick-action overlay is hidden there. - Context-menu "Mark read/unread" and "Flag/Unflag" update flags without opening the message.
0.2.30 CalDAV / CardDAV server
- Optional (
[dav] enabled = true): one calendar (VEVENT) and one address book per account under<base_path>/dav/, RFC 6764.well-knownredirects, HTTP Basic auth against IMAP with a login cache and the login brute-force guard. - PROPFIND, REPORT (calendar-query with time-range, calendar-multiget, addressbook-query, addressbook-multiget), GET/HEAD, PUT, DELETE, strong ETags with If-Match/If-None-Match, CTag. Not implemented: sync-collection, VTODO, MKCALENDAR/MKCOL, server-side recurrence expansion.
- Uploaded objects are stored verbatim (
dav_data, migration V055) and webmail edits are patched into them per property. Details:docs/CALDAV_CARDDAV.md.
0.2.29 realtime open-thread refresh
- When realtime reports
NewMessages, the currently open message detail is re-fetched and its thread is re-rendered if the user is still viewing the same message.
0.2.28 thread replies and legacy Polish mail decoding
- Reply composition detects all configured account/identity addresses; replying to an own sent message selects the first non-own recipient and Reply all keeps the remaining external participants.
- The mailbox toolbar toggles the existing
thread_modepreference between a grouped thread list and a flat message list. Thread groups use Message-ID/In-Reply-To/References metadata, can be expanded/collapsed, and the reader follows the same preference. - Header/body rendering repairs common Polish mojibake while retaining RFC 2047 charset decoding; cached summaries/details are normalized on output so old cached rows benefit without a schema migration.
0.2.27 mail-list and appearance polish
- Monochrome changes on a predefined style promote it to a custom copy before applying the change.
- Wide message rows give the subject more horizontal space and expose its full value in a tooltip.
- Read/unread, flag and open actions appear to the left on hover without reserving hidden row width.
- Shift-click popups use a reader-only embedded layout from initial render.
0.2.26 licensing and documentation
- Licensed under AGPL-3.0-only with an optional commercial license (
LICENSE,LICENSING.md,NOTICE.md). The Oxi synchronisation scripts andUPSTREAM_BASEwere removed; the About page no longer embeds an Oxi revision. docs/FEATURES.mdlists all features with screenshots (docs/screenshots/, generated against a mocked API).- Translations are applied to dynamic settings notes after they load.
0.2.25 filters
- Apply now (
POST /api/filters/match,src/routes/filters.rs): the client sends the rule as a JSON tree ({op: allof|anyof, args},{not},{test: {kind, match, value, header}}, max depth 12 / 64 tests). The server translates it into two IMAPUID SEARCHcriteria — an upper bound (superset) and a lower bound (certainly matching) — and runs them via the newImapClient::search_uids. Header/subject:containstests and sizes are exact; address tests (IMAPFROMalso matches display names),:is/:matches, non-ASCII values and has-attachment are narrowed from themessagescache with three-valued evaluation; messages that still cannot be decided are counted asuncertainand not touched. At most 20 000 UIDs are returned. - The client (
91-sieve-apply.js) applies, per message: flags (\Seen, IMAP keywords), labels (bulk tag endpoint), copies, then one move/Trash — via the existing message endpoints, 4 requests at a time with progress. Redirect, discard and set-flags are not applied to existing messages and are listed as skipped in the confirmation. - Entry points: Check matches / Apply now in the rule editor (re-check required after the rule changes), Apply now per rule in the rule list (rules using syntax the editor cannot rewrite are refused), the quick filter dialog option, and Always move messages from this sender… in the message context menu.
- Editor additions: exceptions (
allof (<conditions>, not anyof (<exceptions>)), parsed back), conditionsto-only,cc-only,size-over/size-under,has-attachment(requiresmime),priority-high(X-Priority1/2 orImportance: high), actionslabel(addflag "RustBox-<tag id>") andtrash, templates, missing-folder creation on save. No schema change. - Not done: drag-and-drop reordering of rules (the ↑/↓ buttons remain).
0.2.25 interface fixes
- Message context menu: grouped into submenus (Forward, Labels, Snooze, Filters, Sender, More); menus are capped at the window height and scroll; scrolling inside a menu does not close it; submenus open on hover, click or →, close with ← / Esc.
- Predefined-style editing: enabling it copies the style's layout profile into the user's display preferences and the layout then follows those values while the style's palette, tokens and gradients stay active. A colour change asks to create a RustBox custom copy built from the computed palette on screen plus the current layout, and pins
themeto the resolved light/dark mode. Changinginterface_skinclearspreset_scale_unlocked; changingskin_depthwhile editing re-seeds the layout. data-contrast="soft"no longer defines--linefrom itself (a CSS custom-property cycle that left it empty).
0.2.24 style gradients and retro styles
- "Style gradients" (
skin_gradients) now works at every style depth. Gradients live only in59-skin-gradients.css, generated from one table:--grad-backdrop,--grad-appbar,--grad-toolbar,--grad-sidebar,--grad-selectedand--grad-primaryare painted asbackground-imagelayers over the colours chosen by the palette and depth, so translucent defaults work for every style; signature styles override them with opaque gradients and declare matching app-bar / button text colours. The RustBox custom style is excluded.57-skin-tokens.cssno longer contains gradients. - Adds Windows XP, Mac OS X Aqua, iOS 6, Windows 7 Aero, Frutiger Aero and BeOS inspired styles, each with palettes, a layout profile, structural tokens and signature gradients.
- Theme "By time of day" (
theme = schedule,theme_light_from/theme_dark_from, migration V054): evaluated in the browser's local time, re-checked every minute. - Optional per-account colour (
account_color, V054): 3px stripe on the app bar and a dot before the account address. Display preferences are stored per account, so each account keeps its own colour. - Custom colours show a live WCAG contrast warning (text/panel, text/background, text/selection 4.5:1; secondary text and accent 3:1; button text on accent 4.5:1). Hidden for predefined palettes.
- Message list shows skeleton rows while a folder loads; theme / style changes cross-fade for 0.3s (disabled with reduced motion).
- Fixes: the default RustBox selection colour (
#28352e) is treated as automatic so the light theme keeps a light selection; subjects take priority over snippets in message rows; flagged messages use a red side bar; reader-toolbar button labels collapse below 820px. - Message rows already show label chips (
message.tags). - Calendar: optional second reminder per event (
reminder_extra_minutes, V054; choices from "at start" to "1 week before"), default reminders for new events in Settings → Notifications (calendar_settings.default_reminder_minutes/default_extra_reminder_minutes), both exported as VALARMs. All-day events are reminded relative to the working-day start (default 09:00).UpdateEventnow distinguishes an absent reminder fromnull, so clearing a reminder works. - The interface-style selector is grouped with
<optgroup>s (labels translated through the scoped_textmap) and uses short names;INTERFACE_SKINSfollows the same order.
0.2.23 interface styles
- Adds Appearance → Style depth (
display_preferences.skin_depth, migration V053):colorsapplies only a predefined style's palette and leaves layout controls editable;standard(default) keeps the previous palette + fixed layout behavior;immersiveadditionally applies the style's structural tokens. - Adds Appearance → Style gradients (
display_preferences.skin_gradients, same V053 migration, default off). Every gradient a style uses (immersive backdrops, app bars, primary buttons, the Synthwave logo, Roundcube Classic bevels in any depth) is scoped tohtml[data-skin-gradients="true"]; with the switch off the style uses flat fallback colors. The release validator rejects ungated gradients. - Interface-style standard: each predefined style has light and dark palettes (
55-roundcube-skins.css,56-interface-styles.css), a fixed layout profile (interfaceStyleProfiles) and a structural token block in57-skin-tokens.css(fonts, heading treatment, control/button/compose/avatar radii, floating vs. flush panes, pane borders/shadows/glass, app bar and search field, sidebar colors, selection background/marker, row cards, primary/compose buttons, focus ring, bevels).58-skin-immersive.cssconsumes the tokens only at the immersive depth; floating-pane geometry is limited to the desktop three-pane layout (>820px). - All 23 existing styles were adapted to the standard; 14 styles were added: Dracula, Catppuccin, Solarized, Gruvbox, Tokyo Night, Rosé Pine, Everforest, Synthwave, Paper, Terminal, Aqua Glass, Linear inspired, Material You inspired, Retro 95.
- The style catalog lives in
display_preferences::INTERFACE_SKINSand is shared by preference anddefault_interface_skinvalidation;scripts/validate-release-static.pychecks catalog/selector/profile/palette/token parity. - V051 and V052 are consolidated into the fresh schemas (baseline V052). The fresh MySQL schema now uses an expression default for
mail_preferences.message_loading_mode.
0.2.22 snooze and undo send
- Adds message snooze:
snoozed_messages(migration V052) is a local-only cache table — snoozing a message records a wake time and the client hides that message from its current folder view until then, viaGET /folders/{name}/snoozed-uids; it has no IMAP side effects and does not touch server-side folder pagination/count SQL inroutes/messages/mod.rs.POST/DELETE /messages/{folder}/{uid}/snoozeset/clear a snooze, andGET /messages/snoozedlists everything currently sleeping across folders for the "View snoozed messages…" dialog. UI: a "Snooze…" context-menu entry and thebshortcut (message open) prompt for a duration;Shift+Bopens the snoozed-messages list. - Adds an "Undo Send" window: a new
undo_send_window_secsconfig knob (default 8, max 120 seconds; 0 disables it and restores immediate synchronous send). When enabled, a submitted message is staged in an in-memoryUndoSendRegistry(src/undo_send.rs) instead of being dispatched immediately; the HTTP response returns right away withstatus: "pending", and a delayed task actually sends the message once the window elapses unless cancelled first.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: a server restart during the window loses the staged send, the same as an in-flight SMTP connection would be lost. The composer shows a toast with an "Undo" action and live countdown for the window. - Known limitation: a brand-new, never-autosaved compose that is sent and then left un-undone relies on the existing autosave/draft-recovery mechanism for recovery if the dispatch itself fails; this is not a new guarantee beyond what autosave already provides.
0.2.21 login hardening
- 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/policyGET no longer consumes the per-IP login budget - Adds
cookie_secure = auto | always | never;autosets the cookieSecureattribute 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_mbonce MIME-encoded are rejected with 413 from stored sizes before any file is loaded (send, IMAP draft snapshot, attach-message, streaming upload), andmax_concurrent_message_builds/message_build_wait_secsbound 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)
0.2.20 browser mail UX
-
The forward-as-attachment route request extractor is crate-visible, matching the handler visibility required by the parent router and avoiding Rust
private_interfaces/private-type build errors. -
Mailbox message dates fall back to IMAP
INTERNALDATEwhen the RFC 5322Dateheader is missing/invalid; stale cached missing dates are repaired on folder access. -
Forward-as-attachment creates a server-side
message/rfc822.emlattachment from the original IMAP RFC822 source. -
HTML message previews use the full remaining vertical reader area and recompute their iframe height as reader chrome/layout changes.
-
Signature management uses equal-height text and HTML fields and includes a raw-HTML modal with a sanitized visual editor and inline image import.
-
Mail loading activity is represented by the app-bar spinner for pagination, progressive/full-folder retrieval and individual message/draft loading.
-
Shift-click selects a contiguous range in the visible message list, and pagination includes direct first/last page navigation.
-
Signature HTML formatting now preserves the selected text when toolbar controls are used, so bold/italic/underline and remove-format can be toggled reliably.
-
New messages with a visible signature always get an editable body line before the signature; the composer separator is spaced farther from the body and rendered as a subtle solid rule.
-
Wide message panes use a default-on compact single-line row layout (sender, subject/snippet, date and icons); users can disable it in Appearance settings.
-
Each identity can choose signature insertion:
silentappends only during send and keeps the editor clean, whilevisibleinserts an editable/removable signature into the composer. -
Large To/Cc lists collapse to a compact recipient summary and expand on demand.
-
Mailbox loading defaults to pagination at 300 messages per page. Users can switch to smooth progressive loading or explicit full-folder loading in Mailbox settings.
-
The composer can collapse From/To/Cc/Bcc/Subject/options from its header so the editor receives the freed vertical space; invalid recipient input automatically reopens the fields.
-
The composer exposes an explicit
Save draftaction beforeCancel; it forces a real IMAP Drafts snapshot with the current MIME body and attachments while background autosave remains local/server-working-state only. -
The composer exposes
Delete draftfor IMAP and recovered drafts; deletion verifies the current IMAP UID/UIDVALIDITY, expunges the server draft first, then removes the local autosave row, cascaded attachment rows and uploaded attachment blobs. -
HTML composing embeds newly added PNG/JPEG/GIF/WebP images inline by default while preserving deleted inline content as reusable attachments.
-
Attachment chips can re-embed images or insert CID links for other files, and selected inline images expose quick size/alignment controls plus the advanced crop editor.
-
Clipboard image paste and file drag/drop insert at the current/drop caret; already embedded images can be dragged to a new body position.
-
Selected inline images expose four pointer resize handles that preserve aspect ratio, and attachment chips distinguish image attachments from ordinary files.
-
Message-body links without an explicit
targetdefault totarget="_blank"so external navigation opens outside the sandboxed mail-preview iframe; explicit targets are preserved and_blanklinks receivenoopener noreferrer. -
Login success is shown only after a real login request; refreshing/restoring an existing authenticated session is silent.
-
Message/HTML preview documents explicitly declare UTF-8, and encoded-word header decoding delegates first to
mail-parserfor broader charset coverage, including UTF-8, ISO-8859-2 and Windows-1250 subjects and display names. -
PDF attachments are previewed from the authenticated inline attachment URL instead of a
blob:iframe, improving Safari/iPhone compatibility. -
Composing displays attachment/message size limits as one compact line without configuration/MIME implementation details.
-
Initial synchronization remains silent for new-mail notifications; bulk operations retain one summary toast; browser/system modifier shortcuts remain preserved.
-
Mailbox pagination is enabled by default at 300 messages per page; the API/preferences accept page sizes up to 300, with progressive and load-all alternatives.
-
Blob deletion uses a durable SQL outbox for filesystem/shared POSIX/NFS and S3 backends; attachment/draft metadata deletion is transactionally paired with cleanup enqueue and failed deletes retry in the background.
-
V047-V052 are consolidated into the V052 compatibility baseline (0.2.23). V053 adds
skin_depthandskin_gradientsto existing V052 tenants; V054 is next.
0.2.8 browser security headers
security_headers_enabledremains opt-in and disabled by default for compatibility; optional browser-security headers are centralized in this middleware, so disabling it removes the RustBox-added security profile while normal response semantics such asContent-Typeand route-specific caching remain.- HTML/XHTML documents receive the full document profile: CSP,
X-Frame-Options: DENY,Permissions-Policy,Cross-Origin-Opener-Policy: same-origin,Referrer-Policy: no-referrerandX-Content-Type-Options: nosniff. - The HTML CSP allows only same-origin scripts (
script-src 'self') and disables inline script attributes; the theme bootstrap is an external fingerprinted asset. Stylesheets are same-origin only (style-src 'self'), whilestyle-src-attr 'unsafe-inline'is narrowly retained because the current UI still sets per-element style attributes dynamically. - Private
/apiresponses receiveCache-Control: no-store,X-Content-Type-Options: nosniffandCross-Origin-Resource-Policy: same-origin. API responses are normally non-frameable (frame-ancestors 'none'/X-Frame-Options: DENY);application/pdfis the narrow exception and allows same-origin framing for the attachment preview (frame-ancestors 'self'/SAMEORIGIN). API JSON does not receive the full document CSP,Permissions-Policy, COOP orReferrer-Policy. - CSS receives
Referrer-Policy: no-referrerandX-Content-Type-Options: nosniff; application assets also receive same-origin CORP without redundant HTML document policies. - HSTS is deliberately not synthesized by RustBox because the application may sit behind a TLS-terminating reverse proxy and cannot reliably determine the public HTTPS boundary; HSTS belongs at that HTTPS edge.
- The public
/api/auth/policyis data-minimized to the single login-form capabilityrememberMeEnabled. Optional feature availability (OpenPGP, Sieve, diagnostics, About, multi-account sessions) is returned only from authenticated/api/auth/session-policy; session/remember durations are not exposed to the browser policy endpoints.
0.2.8 storage/search architecture
- SQL backend selection (
sqlite/mysql/pgsql) is independent from blob storage and search backend selection. - Cluster mode clears an implicit/default
data_dir; PostgreSQL/MySQL single-node deployments may still intentionally usedata_dir-backed filesystem storage and local Tantivy. - Blob storage supports local/shared POSIX filesystems and S3-compatible object storage. Shared POSIX is an explicit capability (
storage.shared=true) suitable for operator-provided NFS/CephFS/GlusterFS mounts. - Draft attachment upload/read/delete/snapshot/send paths use the
BlobStorageabstraction and logical object keys instead of assuming node-local paths. - OpenSearch is available as the cluster-capable indexer. Local Tantivy remains single-node only; search may also be disabled.
- Search mutations use a durable SQL outbox with claim/lease, retry/backoff and dead-letter state; the index remains rebuildable derived state.
- The V050 compatibility baseline includes the former V047 attachment/search-outbox changes, V048 folder counter generation, V049 blob deletion outbox and compact-row/signature preferences. V051 adds adaptive HTML-mail colors and mailbox loading mode; V052 is reserved as the next migration number.
- SQL global-schema initialization is centralized in
db::pool::configure(); database-backed sessions do not repeat startup DDL for SQLite, MySQL or PostgreSQL. - Docker Compose examples cover standalone SQLite, PostgreSQL + SILO (S3-compatible) + OpenSearch, and MySQL + NFS + OpenSearch.
- Live SILO/MinIO/AWS S3/Ceph RGW, live NFS multi-host, live OpenSearch and full multi-node failover acceptance remain release-environment validation items and are not represented as completed here.
Implemented / wired in this working snapshot
Runtime
- Single Rust runtime with embedded HTML/CSS/vanilla-JavaScript UI.
- No TypeScript, React, Next.js, Node.js or Bun product build/runtime.
- EN/PL language assets embedded in the binary.
Mail and IMAP workflow
- Folder totals and unread counters use authoritative IMAP
STATUS (... MESSAGES UNSEEN ...)data. If STATUS is unavailable for a selected mailbox, RustBox falls back toUID SEARCH UNSEEN; it never treats SELECT/EXAMINEUNSEEN(the first unseen sequence number) as an unread count. - Folder counter reconciliation runs independently from content synchronization and repairs
MESSAGES/UNSEENeven whenHIGHESTMODSEQis unchanged. A manual counter refresh reuses the cached folder tree while it is fresh and batches STATUS calls on one IMAP session instead of forcing another LIST. - Read/unread/flag mutations update the affected row/badge optimistically, then reconcile against authoritative IMAP state and publish targeted flag/
FolderStatusevents without replacing the whole message list. The active folder is watched through IMAP IDLE; other folders are reconciled every 30 seconds. - Large folders may hydrate message headers incrementally, but the sidebar total/unread badge remains server-authoritative. IDLE/periodic reconciliation keeps the CONDSTORE cursor separate from counter refresh, falls back to a full
UID FLAGScomparison when CONDSTORE is unavailable, and discards partial IMAP fetch streams rather than applying incomplete state. The top-right activity indicator distinguishes active synchronization/indexing from an incomplete local IMAP header cache and exposes backlog details in its tooltip. - Wide folder badges display
total / unreadwhile unread is non-zero and total alone at zero; narrow sidebars switch to the compact existing counter presentation. - IMAP folder list, hierarchy, create, create subfolder, rename, delete, subscribe/unsubscribe, plus collapsible branch navigation with per-account remembered expansion state.
- Special folders stored per account: Sent, Drafts, Trash, Junk and Archive. Spam/Junk additionally supports a browser-wide default destination with a per-account override and IMAP special-use/name fallback.
- Special-folder references follow folder renames and are cleared when a referenced folder is deleted.
- Administrators can define server-wide default Sent/Drafts/Trash/Junk/Archive folder names; per-account settings still override them when present.
- Sent-copy, delete-to-Trash, Archive and Junk actions use the configured special folders.
- Mark-all-read and empty-folder operations.
- Message move and copy operations.
- Multi-select and bulk read/unread, flag/unflag, archive, move, copy, label and delete actions, including explicit select-all across the complete cached search result set.
- Drag-and-drop a message onto a folder to move it.
- Previous/next message navigation plus keyboard actions for common reader operations, compose, selection mode and selecting the current message.
- After moving/deleting the currently opened message (including Spam, Not spam, Archive and Move), the reader automatically opens the next list item and falls back to the previous item when the removed message was last.
- Message reading, collapsible thread navigation, attachments, raw headers, full RFC822
.emldownload viaBODY.PEEK[]and.emlimport into a selected IMAP folder via APPEND. - Message/folder context menus with common Roundcube-style actions, including folder properties, full message source, Message-ID/address copy and mailing-list unsubscribe; the reader sender line also exposes compose/contact/search/filter actions.
- Mailbox quota display where the IMAP server exposes quota, including warning/danger states at 80%/95%. The storage summary opens a per-folder usage view backed by IMAP message sizes, and folder properties expose the measured size when the server permits the read.
- Per-account mailbox behavior: 20/50/100/200 messages per page, date/sender/subject/size sorting in both directions, configurable mark-read delay, threaded/flat reader mode, HTML/plain preference, remote-image policy including Contacts-only loading, quote limiting, optional list snippets and a default “flagged on top” mode. Each folder can inherit, force-enable or force-disable flagged-first ordering; sorting is applied before pagination.
- IMAP capability diagnostics for IDLE, MOVE, UIDPLUS, CONDSTORE, QRESYNC, SORT, THREAD, NAMESPACE, SPECIAL-USE, QUOTA, ACL, LIST-EXTENDED and UTF8=ACCEPT.
- Forward-as-attachment using
message/rfc822. - Mailing-list unsubscribe action from
List-Unsubscribe; RFC 8058 one-click HTTPS requests are sent by the backend only whenList-Unsubscribe-Post: List-Unsubscribe=One-Clickis advertised, with redirect-free public-address SSRF checks. Ordinary HTTPS/mailto links remain user-confirmed UI actions. - Explicit upload-body limits for attachments, contact imports/photos, ICS imports and
.emlimport so Axum does not reject allowed uploads at its smaller default limit. - Attachment context menu supports in-app preview/download for safe image/PDF/text/HTML/video classes; HTML is sandboxed and video uses native browser controls. Multiple attachments can be downloaded as a client-built UTF-8 ZIP without external runtime dependencies.
- Message rows expose compact hover/focus quick actions for read/unread and flag/unflag; flagged rows also receive a subtle red lower-edge marker.
Composer / identities
- HTML, plain-text and Markdown compose modes. Markdown keeps its source text for the plain alternative and generates escaped/sanitized HTML for preview and delivery without accepting raw Markdown HTML.
- Expanded HTML toolbar: bold, italic, underline, strike, lists, quote, alignment, links, font, size, color, clear formatting, undo/redo.
- Draft autosave, explicit recovery of saved/browser-emergency drafts, and attachment upload/delete. IMAP Drafts messages are also first-class editable drafts: RustBox opens their RFC822/MIME content directly from IMAP (including external-client drafts), stages only the normal autosave/recovery state locally while editing, and replaces the source UID only after the new snapshot is appended and resolved safely.
X-RustBox-Draft-IDremains an internal UID-resolution aid for RustBox-created snapshots, not a prerequisite for opening or editing a draft. - Drag-and-drop attachments with visible upload progress. Attachment uploads stream multipart chunks through
BlobStoragewith an enforced size limit instead of buffering the whole attachment in memory; filesystem and S3-compatible backends share the same logical-object flow. - Inline image upload using CID attachments, including paste and drag/drop of PNG/JPEG/GIF/WebP images into the composer. During editing, CID images use authenticated same-origin draft-attachment preview URLs and are serialized back to
cid:references for saved/sent MIME content. Inline images can be resized, aligned left/center/right/inline and cropped in-app (GIF cropping is intentionally disabled to preserve animation); display-only resizing does not rewrite the source file. Unused CID attachments are cleaned when their image is removed from the editor. - Attachment reminder for common English/Polish wording.
- Multiple identities.
- Identity Reply-To, automatic Bcc, Organization metadata, text/HTML signatures and per-action signature rules/position; replies/forwards auto-select the matching identity from To/Cc/Delivered-To/Envelope-To when possible.
- Composer From identity selection and signature insertion.
- Outgoing priority (normal/high/low) and MDN/read-receipt requests are persisted in drafts and emitted consistently in SMTP delivery and the IMAP Sent copy; all envelope/header mailboxes are parsed before SMTP delivery. Administrators can also configure validated additional outgoing headers; structural/MIME headers and CR/LF-injected values are rejected.
Search, labels and contacts
- Tantivy/search backend retained.
- Search operators including
from:,to:,subject:,folder:,has:attachment,is:read,is:unread,is:flagged,is:unflaggedand date filters. Active search terms are highlighted in sender, subject and snippet text using DOM text nodes rather than result HTML. - Advanced-search dialog and saved searches.
- User labels/tags with validated HEX colors, message chips, context actions and bulk assignment. Optional native IMAP-keyword synchronization writes stable RustBox keywords only on folders advertising persistent custom-keyword support, imports observed RustBox keywords and provides an authoritative reconcile action that also applies remote removals only on confirmed keyword-capable folders.
- Contacts CRUD/search with phone, job title, postal address, website and birthday fields.
- Contact photo upload/remove/display (PNG/JPEG/WebP, 2 MB limit, MIME signature validation).
- Contact groups, membership editing and group filtering.
- vCard import/export includes the expanded profile fields and multiple e-mail/phone/address values.
- Duplicate-contact detection checks primary and alternate e-mail addresses; merge keeps target data, alternate values, group memberships and a source photo when the target has none.
- vCard attachments can be imported directly from a message attachment.
- Composer To/Cc/Bcc fields use the existing contact/known-address autocomplete API with keyboard navigation.
Sieve
- ManageSieve raw script list/get/put/delete/rename/activate/deactivate/check.
- STARTTLS, implicit TLS and explicit plaintext opt-in.
- Quick filters generated from sender/domain/subject use an in-app wizard with condition/action preview, capability-aware actions, optional mark-as-read chaining, target-script selection, server validation and direct save/activation; an Advanced editor handoff remains available. The wizard explicitly states that Sieve changes apply to future incoming messages.
- Visual rule builder with From/domain/To/Subject/List-ID/custom-header conditions, three-condition nested AND/OR grouping and per-condition negation.
- Visual actions: move, copy, redirect, discard, mark read, add IMAP flag and keep, with an optional second action.
- RustBox-generated visual rules carry stable markers and can be reordered by drag/drop or keyboard-accessible up/down controls without rewriting unrelated raw-script text.
- Builder merges
requireextensions, disables actions unsupported by advertised server capabilities and validates generated scripts with the server. - Autoresponder/vacation builder supports subject, body, reply interval, mailing-list/automated-mail suppression and optional inclusive start/end dates using
currentdatewhen the server advertisesdate+relational. - Sieve scripts can be backed up/restored as a validated RustBox JSON bundle; restore validates all scripts before writing, and saving over an active script requires an explicit application confirmation.
Calendar and realtime
- Calendar event CRUD UI with Month, Week, Day and Agenda views.
- iCalendar/ICS import UI over the existing parser/backend, plus direct import from message attachments and full local ICS export.
- Calendar event editor includes all-day mode, RRULE storage, attendees, organizer, status, reminder, meeting URL and color metadata.
- Browser WebSocket client connected to the existing IMAP IDLE/event backend.
- Realtime folder/message refresh for NewMessages/FlagsChanged/FolderUpdated events.
- Desktop notifications, optional sound, per-folder selection and persisted Do Not Disturb hours. Aggregate unread count updates the browser-tab title and the application badge where the Badging API is available.
- Account switch/remove/add workflow for multiple authenticated accounts in one browser session.
- Active-session list across browsers while the RustBox process is running, with creation/last-active timestamps, friendly browser/device labels, full User-Agent hover detail, individual revoke and revoke-all-others.
- Diagnostics panel exposing IMAP capabilities (including persistent custom-keyword support), authenticated SMTP connectivity, ManageSieve status, browser WebSocket state, local synchronization-cache timestamps/counts and live IMAP IDLE task/subscriber counts without returning server host names or credentials. Public liveness (
/api/health) and local-storage readiness (/api/ready) endpoints are available for service supervision. - Optional administrator system diagnostics endpoint (disabled by default) supports explicit IP/CIDR allowlisting plus token/Basic authentication, redacts configured secrets, probes SQL/session/storage/search backends, reports active sessions and IMAP IDLE state, and surfaces bounded node-local security signals for failed logins, 403/429 responses and 5xx failures.
- Search-index diagnostics expose cached/indexed counts, with a rebuild operation from the configured account-data backend.
- IMAP reconnect control drops reusable request-path sessions and verifies a fresh authenticated connection.
- Graceful SIGTERM/Ctrl+C shutdown stops accepting new requests and aborts dedicated IMAP IDLE tasks before process exit.
Personalization
- Persistent per-user display preferences in the configured application database.
- Light/dark/system theme.
- Presets: sage, blue, teal, cyan, indigo, slate, graphite, amber, orange, violet, rose, red and custom.
- Custom accent, background, panel and text colors.
- Font family and size, message row height, folder/message pane widths, border radius and contrast. Advanced controls include icon size, toolbar/sidebar density, reader/composer widths, selection color, unread font/badge styling, compact headers, border visibility, scrollbar density, monochrome mode and per-pane background overrides.
- Date-format (locale/ISO/EU/US) and time-format (locale/24-hour/12-hour) preferences.
- Compact/comfortable density, preview right/bottom/off, UI scale and reduced motion.
- Avatar/attachment/flag visibility toggles.
- Folder and message-list pane widths can also be adjusted directly with draggable splitters and persist through the existing per-user display preferences. Keyboard shortcuts are documented in an in-app reference dialog.
Build/installation fixes in this snapshot
- Fixed the
E0499mutable-borrow failure inmark_all_read: theuid_store()stream is dropped beforesession.logout(). - Fixed a duplicate
elseintroduced in the calendar delete handler during the calendar expansion. - All executable Rust SQL is centralized under
src/queries/{sqlite,mysql,pgsql}/queries.rs, including session queries.
Partially implemented - keep visible as incomplete
- Sieve visual builder now supports a three-condition nested AND/OR group, negation, two actions, capability-aware disabling, managed-rule ordering and scheduled vacation dates. An arbitrary-depth condition tree, arbitrary-length action stack and full capability-aware condition/action matrix remain unfinished.
- Composer autosave/recovery state is stored locally, while deliberately saved drafts are standard RFC822/MIME messages in the configured IMAP Drafts folder. IMAP drafts from RustBox or another client open directly in the composer; replacement is conservative (append/resolve first, then remove the prior UID after UIDVALIDITY verification). Wider real-server compatibility still needs the release gate.
- Calendar is local/ICS-capable and stores recurrence/attendee/reminder metadata, but recurrence expansion/edit semantics, invitation responses and CalDAV synchronization are not finished.
- Notifications are wired through IMAP IDLE/WebSocket, include permission status/test controls, and local calendar reminders can show browser notifications and play the configured sound while RustBox is open. Service-worker/background delivery is still not finished.
- Identities emit Organization metadata; composer priority, MDN request headers and validated administrator-defined outbound headers are wired. SMTP DSN negotiation remains to be completed.
Not yet complete - do not claim Roundcube parity
- MOVE-based Undo is available for move/delete-to-Trash/archive/spam when the destination UID can be resolved unambiguously from pre-MOVE UIDNEXT plus exact Message-ID. A persistent multi-step undo history and servers/messages without a safely resolvable destination UID remain unfinished.
- Contact model supports multiple e-mails/phones/addresses and duplicate merge UI; CardDAV and LDAP synchronization/UI remain.
- OpenPGP roadmap scope is implemented end-to-end: per-user keyring, generate/import/export/delete, passphrase test/change, identity binding, sign/encrypt/sign+encrypt, PGP/MIME + inline PGP, verify/decrypt, trust/fingerprint UI, WKD and optional HTTPS HKP. Real
cargo testand interoperability regression coverage are still release-gate work. - TNEF/
winmail.datdecoding and full SMTP DSN negotiation are still not implemented. SMTPUTF8/IDN behavior still requires the real-server compatibility matrix from the release plan. - IMAP ACL/shared-folder management and full capabilities settings UI remain the next major protocol block.
- Spam backend adapters for rspamd/SpamAssassin/custom command.
- Calendar Month/Week/Day/Agenda views are present; recurrence expansion/exceptions, invitation response workflow and CalDAV remain.
- Full CardDAV synchronization.
- Password-change adapters remain unfinished. Session inventory/device metadata is now persistent across restarts and shared-session backends support cross-node listing/revocation.
- Diagnostics now includes IMAP reconnect, SMTP connectivity, index rebuild and last local folder/message synchronization metadata; deeper background task/queue metrics and repair workflows remain.
- Full live security, acceptance and performance matrix remains release-environment work tracked in
TODO.md.
UI reliability / workflow fixes in the current snapshot
- Contact editor, calendar, raw-header and message-source dialogs use viewport-bounded layouts with independent scrolling and non-overlapping actions. Generic dialogs also fall back to whole-card scrolling when their content exceeds the browser workspace. Settings uses an explicit header + flexible-content grid so short panels no longer push the navigation/content block toward the bottom of the modal; settings navigation/content scroll independently when needed.
- Month calendar grid and toolbar layout are responsive and remain usable under browser/UI scaling.
- UI scale now affects font size, row height, pane widths, icons, field/button padding and control/header dimensions without being overwritten by the separate font-size setting.
- Responsive layout keeps user-selected folder/message pane widths within safe viewport bounds instead of reverting to fixed widths.
- The mobile composer keeps address labels and controls aligned, and the mobile folder drawer can be closed by its toggle, backdrop, Escape or folder selection while hierarchy disclosure controls keep larger touch targets.
- Application actions have reusable success/error/warning/info toast feedback, including from within native modal dialogs.
- Primary and bulk message toolbars include Spam / Not spam, driven by the configured Junk folder.
- Reader includes Print, Source viewer and quick Create-filter-from-sender actions.
- Reader addresses expose one-click contact actions; trusted senders can be added from a message so remote images load automatically unless the global policy is Never.
- Header viewer shows a structured common-header summary and the raw header block.
- vCard/iCalendar attachment SVG action icons no longer leak their markup as visible text; reader cleanup also strips only RustBox-owned icon-sprite fragments from malformed MIME content.
- Message HTML receives an additional client-side sanitization pass before sandboxed iframe rendering; plain text is preserved exactly.
- Embedded CSS/JS use content-fingerprinted URLs with
Cache-Control: public, max-age=31536000, immutable; HTML and unversioned compatibility aliases remainno-store. - Native browser confirmation/text prompts are no longer used by the main UI workflows; destructive actions and folder/label/text choices use RustBox application dialogs.
- Message selection has an explicit mode: per-row checkboxes only appear while selecting, with select-all and bulk actions grouped in the selection toolbar.
- Search selection can materialize and select every cached matching result, not only the currently rendered page.
- Folder/message list state now preserves the last folder, loaded-page depth and scroll position; active searches also retain their query and scroll position during normal realtime/refresh flows.
- Network failures surface an offline/reconnecting banner. Only idempotent GET/HEAD requests receive bounded automatic retries; send/move/delete and other mutating requests are never auto-replayed.
- Toasts with actions keep Undo and the close button adjacent in a dedicated action group so closing a toast cannot trigger Undo.
- MOVE-based actions expose a toast Undo only when the backend can identify the server-assigned destination UID without guessing; bulk Undo reverses resolved moves in reverse order.
- Thread lookup now validates complete References Message-ID tokens after the SQL candidate query, preventing partial-ID false matches.
- Safe attachment previews stay inside a RustBox modal instead of opening a browser popup.
- Composer uploads expose progress, clipboard images become CID inline attachments, and browser-local recovery preserves edits when a draft save fails.
- Compact/comfortable density now visibly affects controls/toolbars/dialog headers without overwriting the separate row-height and pane-width preferences.
- Toasts show the remaining auto-dismiss time and pause their countdown while hovered or keyboard-focused.
- Appearance uses persisted
interface_skinmodes for RustBox (custom), Roundcube Classic, Roundcube Larry, Roundcube Elastic, Clean, Graphite and High contrast. The old Compact/Balanced/Comfortable/Reading preset cards were removed. Fixed interface styles apply their own visual geometry and palette without overwriting the user's stored RustBox custom values; selecting RustBox re-enables manual color, spacing, pane and typography controls. Classic Dark remains an adaptation because upstream Classic has no official dark skin. - Composing settings now persist the default format, autosave interval, default priority, default read-receipt request, attachment-reminder behavior, default font, reply placement, quote style, forward mode and browser spellcheck through display preferences. Saved/canned responses can be managed as text or sanitized HTML and inserted from the composer toolbar.
- Message Print uses an in-page print frame instead of a popup and prints sanitized HTML or plain text with From/To/Date metadata.