App Suite Middleware
8.54.350
Behavioral Changes
SCR-2048
Summary: User pseudonyms in exported feedback are keyed by a secret
Effective: 8.54.350 and later
Exported NPS and star rating feedback identifies users by a pseudonym. It was an MD5 hash of context and user ID, which anyone can trace back by hashing all IDs. It is now an HMAC-SHA256 keyed by com.openexchange.userfeedback.pseudonym.secret, so pseudonyms change once with the update and cannot be linked to earlier exports. Set the same secret on all nodes to keep pseudonyms stable across nodes and restarts.
SCR-2047
Summary: Invalid SSL trust level and missing trust stores no longer trust every certificate
Effective: 8.54.350 and later
An invalid value for com.openexchange.net.ssl.trustlevel (for example the typo restriced) now falls back to restricted instead of all, so certificates are validated; the error log names the fallback. If restricted is set but the JVM's default trust store is disabled via com.openexchange.net.ssl.default.truststore.enabled and no custom trust store is configured, Redis, S3, JMAP and Cassandra clients now validate against the JVM's default trust store instead of trusting every certificate. Valid values, including the default all, are not affected. If connections to self-signed endpoints fail after the update, check the configured trust level.
Configuration
SCR-2049
Summary: New configuration options for user feedback pseudonyms
Effective: 8.54.350 and later
A new option keys the pseudonyms of users in exported feedback.
com.openexchange.userfeedback.pseudonym.secretThe secret for the HMAC-SHA256 pseudonyms of users in exported NPS and star rating feedback. Use the same value on all nodes. If empty, a random secret is used per node and start, so pseudonyms differ between nodes and restarts. Default empty. Reloadable, not config-cascade aware. No dedicated properties file.
8.54.346
3rd Party Libraries/License Change
SCR-2044
Summary: Removed the unused libraries eddsa, cose-java and nekohtml and replaced PBKDF2 with the JDK implementation
Effective: 8.54.346 and later
Removed three unused third-party libraries and replaced a fourth with the JDK implementation. No configuration change; no operator action is required.
Removed
cose-java1.1.0 andeddsa0.3.0 from bundlecom.openexchange.multifactor.provider.webauthn, together with thecbor4.5.6,datautilities1.1.0 andnumbers1.8.2 jars embedded alongside them. WebAuthn verification is unaffected; it runs incom.openexchange.webauthn, which still shipscbor.Removed
nekohtml1.9.22 from bundlecom.openexchange.common; the packagesorg.cyberneko.html*are no longer exported. Custom plugins that imported them fromcom.openexchange.commonhave to ship their own copy.Replaced
PBKDF21.1.4 and its transitive dependencypicketbox4.0.21.Final in bundlecom.openexchange.cryptowith the JDK'sPBKDF2WithHmacSHA1; keys derived for legacy encrypted data are unchanged.
8.54.343
3rd Party Libraries/License Change
SCR-2041
Summary: Upgraded the Apache Kafka client from 4.0.0 to 4.0.2 to fix CVE-2026-35554 and CVE-2026-33558
Effective: 8.54.343 and later
Upgraded the Apache Kafka client library from 4.0.0 to 4.0.2 in the target platform. It is used by the Kafka logging appender of com.openexchange.logging. No configuration change; no operator action is required.
- Fixes CVE-2026-35554 (high: concurrent producers could corrupt or misroute records) and CVE-2026-33558 (moderate: secrets in DEBUG logs).
- Apache ServiceMix publishes no bundle beyond 4.0.0, so the platform now ships the upstream
org.apache.kafka:kafka-clientsjar with the former ServiceMix OSGi manifest; the bundle symbolic nameorg.apache.servicemix.bundles.kafka-clientsstays the same, the jar is noworg.apache.servicemix.bundles.kafka-clients-4.0.2-custom.jar.
8.54.338
Database
SCR-2035
Summary: Added index on the targeted shared account
Effective: 8.54.338 and later
Warning
Update Task{{com.openexchange.sharedaccount.storage.rdb.groupware.SharedAccountAddTargetIndexTask}}
Deleting a context or user removes the shared account permissions and user settings that target its shared accounts. Without a matching index this scanned and locked the whole table, so every insert into it waited until the deletion ended and could fail with a socket time-out. A new index sharedaccount_target on (sharedaccount_cid, sharedaccount_user) is added to the tables sharedaccount_permissions and sharedaccount_usersettings. Existing schemas get it through the update task; the ALTER TABLE runs online, but may take a while on large tables.
8.54.337
Database
SCR-2035
Summary: Added index on the targeted shared account
Effective: applies to the 8.54 release line
Warning
Update Task{{com.openexchange.sharedaccount.storage.rdb.groupware.SharedAccountAddTargetIndexTask}}
Deleting a context or user removes the shared account permissions and user settings that target its shared accounts. Without a matching index this scanned and locked the whole table, so every insert into it waited until the deletion ended and could fail with a socket time-out. A new index sharedaccount_target on (sharedaccount_cid, sharedaccount_user) is added to the tables sharedaccount_permissions and sharedaccount_usersettings. Existing schemas get it through the update task; the ALTER TABLE runs online, but may take a while on large tables.
8.54.336
Behavioral Changes
SCR-2030
Summary: Changed renewal of the tokens of external OAuth accounts
Effective: applies to the 8.54 release line
Access tokens of external OAuth accounts (Google, Box, Dropbox, Yahoo) are now renewed before they expire, and requests to the OAuth providers time out after 5 seconds for connecting and 10 seconds for reading. The cluster lock that serializes a renewal is now named after the account alone. During a rolling upgrade, nodes of the previous and the new version do not exclude each other, so an account whose tokens expire in that window may be renewed twice; for Box and Dropbox, which issue single-use refresh tokens, the user may then have to re-authorize the account. No admin action.
SCR-2029
Summary: Requests to the Google APIs honor the SSL configuration
Effective: applies to the 8.54 release line
Requests to the Google APIs for Drive, Calendar and Gmail used to accept any server certificate. They now use the SSL configuration of the middleware: with com.openexchange.net.ssl.trustlevel set to restricted, the certificate of Google is validated against the configured trust store. Deployments that reach Google through a TLS-intercepting proxy need its CA in that trust store. With the default all, nothing changes.
8.54.335
API - HTTP-API
SCR-2028
Summary: Changed status and scope handling of OAuth account requests
Effective: 8.54.335 and later
The status of an OAuth account association is now invalid_grant if the access token of the account is no longer valid, and ok if the OAuth provider or the database cannot be reached for the moment; recreation_needed remains for an account that cannot be used at all. oauth/accounts?action=init leaves out requested scopes that are disabled for the user through com.openexchange.oauth.modules.enabled.<provider> and refuses the request with OAUTH-0043 only if none remains. oauth/accounts?action=create requires the session secret like any other request; the token of the call-back URL no longer replaces it. oauth/proxy refuses provider responses larger than 4 MiB. The App Suite UI needs no adjustment.
SCR-1974
Summary: New capabilities access_tokens and mcp
Effective: 8.54.320 and later; also delivered in 8.54.335
Two new capabilities tell a client what to offer before it calls anything.
access_tokens is awarded to a user who may mint personal access tokens: the user is neither a guest nor anonymous, and the OAuth provider is enabled for the user. These are the checks the accesstoken module applies before minting, so a settings page shown only with this capability never offers what the module would refuse.
mcp is awarded to a user who can use the MCP endpoint: the endpoint is registered on the node, the user is neither a guest nor anonymous, and the OAuth provider is enabled for the user. It is independent of access_tokens, since personal access tokens serve more than the MCP endpoint and an MCP client may also bring a token from an external authorization server. The endpoint checks the capability itself on every request, including an override through com.openexchange.capability.forced.mcp, and refuses a disabled context there as well. Switching the OAuth provider or the capability off for a user or context therefore refuses that user's tokens with 403 at once, including tokens issued before.
No admin action. access_tokens follows com.openexchange.oauth.provider.enabled; mcp also follows com.openexchange.mcp.enabled and whether the open-xchange-mcp package is installed.
Behavioral Changes
SCR-1945
Summary: New MCP server endpoint for read-only access to mail, calendar, contacts, files and tasks
Effective: 8.54.320 and later; also delivered in 8.54.335
The middleware offers a Model Context Protocol server at /mcp (protocol revision 2026-07-28 and, over the same endpoint, the initialize handshake of revisions 2025-03-26, 2025-06-18 and 2025-11-25, which clients such as claude.ai connectors speak: no session identifier is issued, ping is answered, an unknown method is a JSON-RPC error on 200, and the mirroring headers are optional there, stateless HTTP POST) with read-only tools, prompts and resources. The tools are me_get, mail_search, mail_get, mail_attachment_get, calendar_list_events, calendar_get_event, calendar_free_busy, resources_search, contacts_search, contact_get, users_search, files_search, file_get, tasks_search, task_get, vacation_get, reminders_list and a folder tool per module (mail_folders, calendar_folders, contacts_folders, files_folders, tasks_folders); every list result can be paged through an opaque cursor argument and the nextCursor it returns. The prompts daily_briefing, inbox_triage, find_meeting_slot and catch_up_on chain those tools and are offered only where every tool they drive is available to the caller. Resources are read by URI through the templates ox:///files/{id}, ox:///mail/{folder}/{id} and ox:///mail/{folder}/{id}/attachment/{attachment}, as text, marked in _meta when it was cut, or, for anything without a text form, as its bytes up to 5 MiB, refusing a larger one with -32602 rather than cutting it; resources/list enumerates nothing. Callers authenticate with a bearer token that the OAuth provider validates, either a JWT from the configured authorization server or a personal access token; tools and resources are visible and usable only with the scopes read_mail, read_calendar, read_contacts, read_files, read_tasks and read_reminders, while me_get is open to every valid token. /.well-known/oauth-protected-resource/mcp serves the RFC 9728 resource metadata. Tool calls and resource reads are rate-limited per user, bounded in how many of one user run at once on a node, and written as one audit line each at level INFO through the logger com.openexchange.mcp.protocol.McpRequestHandler, without argument values or content, and measured through the meters appsuite.mcp.requests, appsuite.mcp.tool.calls and appsuite.mcp.authentications; the core-mw Grafana dashboard gained an MCP row. A bundle that still waits for a needed service, typically the OAuth provider, logs a warning naming it a minute after start. An OpenAPI document describing both paths, the transport headers, every method, the tools, prompts and resources is published as mcp.openapi.json under components/middleware/mcp/<version>/ next to the SCIM and provisioning documents. The endpoint is off by default and needs the chart feature mcp, com.openexchange.mcp.enabled=true and an enabled OAuth provider (com.openexchange.oauth.provider.enabled). The mail tools reach the primary mail account only; secondary and external accounts, which keep their own credentials, are outside a token session's reach. Documentation: documentation/administration/mcp_server.md.
Arguments that do not match a tool's input schema are answered as a tool result with isError on revisions 2025-11-25 and 2026-07-28, so that the model can correct the call; 2025-03-26 and 2025-06-18 keep the JSON-RPC error -32602. A tool call beyond the per-user rate limit or in-flight bound is a tool result with isError and Retry-After, a resource read beyond it a 429 carrying a JSON-RPC error. A valid token whose user may not use the endpoint (the capability mcp is checked on every request) or cannot sign in, such as a disabled user or context, is answered with 403 without a challenge; a token that cannot be checked right now, with 503 and Retry-After. A JSON-RPC batch is refused with -32600, and the earlier revisions answer a missing resource with -32002. Every tool carries annotations that mark it read-only. calendar_list_events covers every subscribed calendar the user sees across accounts and gives the user's participation status as myStatus; tasks_search gives all-day tasks as dates; vacation_get tells whether the notice replies today and its date range; reminders_list names the occurrence of a series; mail_search without a query lists the newest messages; users_search no longer fills in an e-mail address the address book withholds. A JWT can be tied to the endpoint through com.openexchange.mcp.audience (SCR-2027).
A request body has to hold exactly one JSON object; anything else is refused with 400 and -32700, a charset no runtime knows with 415. A nextCursor continues only the list it was handed out for: the same tool with the same arguments apart from cursor and limit. calendar_folders and contacts_folders list calendars and address books by the identifiers the calendar and contact tools report and take, such as cal://0/31 and con://0/32. calendar_list_events tells through calendar whether an event was read through the user's own, a shared or a public calendar. List results carry partial and warnings where a calendar, address book or folder could not be read, instead of failing or looking complete. mail_get joins every inline text part of a message and no longer lists inline images as attachments; a text file declared as application/octet-stream is served as text. For a caller without read access to the global address book, users_search matches names only and reports time zone and language for the caller alone. task_get, like the get action of the tasks module, refuses someone else's task in a folder where the caller may read only their own objects. Resource reads are measured through appsuite.mcp.resource.reads.
Configuration
SCR-2027
Summary: New configuration option for the audience of JWTs accepted by the MCP server
Effective: 8.54.335 and later
The MCP server endpoint /mcp can require that a JWT was issued for it.
com.openexchange.mcp.audienceComma-separated audiences of which a JWT has to name at least one in itsaudclaim to be accepted by/mcp; any other JWT is answered with401andinvalid_token. Set it where the authorization server also issues tokens for other clients, such as the mobile app, sincecom.openexchange.oauth.provider.jwt.audienceapplies to every resource server of the installation alike. Empty accepts every JWT the OAuth provider accepts; the endpoint then logs a warning at start-up ifcom.openexchange.oauth.provider.jwt.jwksUriis set or the OAuth provider runs in modetoken_introspection. Personal access tokens are not affected. Default empty. Reloadable, not config-cascade aware. No dedicated properties file.
SCR-1944
Summary: New configuration options for the MCP server
Effective: 8.54.335 and later
The MCP server endpoint /mcp is configured through these properties, read when the bundle com.openexchange.mcp starts and again on a configuration reload. A reload takes the endpoint down and up again only when com.openexchange.mcp.enabled changes; every other property takes effect while the endpoint keeps answering.
com.openexchange.mcp.enabledWhether the MCP endpoint/mcpand its resource metadata document/.well-known/oauth-protected-resource/mcpare registered. Defaultfalse. Reloadable, not config-cascade aware. No dedicated properties file.com.openexchange.mcp.allowedOriginsComma-separatedOriginheader values a browser-based client may send. A request with anOriginthat is not listed is answered with403. Default empty, which refuses every request carrying anOriginheader. The endpoint sends no CORS headers and answersOPTIONSwith405, so a browser client on another origin needs CORS at the ingress. Reloadable, not config-cascade aware. No dedicated properties file.com.openexchange.mcp.authorizationServersComma-separated issuer URLs advertised asauthorization_serversin the RFC 9728 resource metadata. Default empty;com.openexchange.oauth.provider.allowedIssueris advertised then, if set. Reloadable, not config-cascade aware. No dedicated properties file.com.openexchange.mcp.serverNameThe name reported asserverInfo.name. Defaultopen-xchange-mcp. Reloadable, not config-cascade aware. No dedicated properties file.com.openexchange.mcp.instructionsThe natural-language guidance a client receives fromserver/discover. Default: a sentence describing the read-only access to mail, calendar, contacts, files, tasks, reminders and the vacation notice. Reloadable, not config-cascade aware. No dedicated properties file.com.openexchange.mcp.rateLimit.callsHow many tool calls and resource reads a user may make per window, counted across all nodes through the rate limiter service. Beyond that a tool call is answered as a tool result withisErrorand aRetry-Afterheader, a resource read with429andRetry-After. A value less than or equal to0(zero) disables the limit. Default60. Reloadable, not config-cascade aware. No dedicated properties file.com.openexchange.mcp.rateLimit.windowSecondsThe window of the tool call limit in seconds. A value less than1is treated as1. Default60. Reloadable, not config-cascade aware. No dedicated properties file.com.openexchange.mcp.maxConcurrentCallsHow many tool calls and resource reads of one user may run at the same time on a node. A call holds its slot until its answer is written, so a client that reads slowly cannot pile up answers in memory. A further call is refused like one beyond the rate limit, withRetry-After: 1, takes no permit of the rate limit and is audited with outcomebusy. Per node, since a call in flight lives on the node that runs it. A value less than or equal to0(zero) switches the bound off. Default4. Reloadable, not config-cascade aware; a changed value applies to calls that start after the reload. No dedicated properties file.
8.54.333
API - HTTP-API
SCR-2026
Summary: New Mobile API endpoint for folder changes since a state
Effective: applies to the 8.54 release line
The Mobile API gains GET /mobile/v1/mail/folder-changes, with which native mail apps fetch the folders created, updated and destroyed since the state of an earlier folder list, instead of reloading the whole list. GET /mobile/v1/mail/folders now returns that state at the top level. The delta embeds the created and updated folders in folders and maps former to new identifiers in renamed for accounts whose folder identifiers change on a rename. A call returns at most limit identifiers (default and maximum 500) and sets hasMore when more follow. The state covers the accounts, subscribedOnly and, if the list asked for them, the counts of the list it came from. A state that is unknown, expired or whose account is gone is answered with 410 and code cannot_calculate_changes; the client reloads the list. The middleware keeps a snapshot per state in Redis, at most 50 and 1.5 MiB per user for 7 days after the last use; a list of more than about 5000 folders gets no state. IMAP and JMAP accounts are supported.
Behavioral Changes
SCR-2022
Summary: New metrics, dashboard panels and alerts for the Mobile API and push deliveries
Effective: applies to the 8.54 release line
The new counter appsuite_pns_deliveries_total counts the notifications handed to APNs and FCM by transport (apns_http2, fcm) and by the gateway's answer (result: accepted, invalid_token, invalid_request, rejected, failed). appsuite_mobile_requests_seconds gains histogram buckets from 50 ms to 30 s, so percentiles can be computed across pods. The Mobile API row of the core-mw Grafana dashboard adds the 95th percentile duration, the share of 503 answers, token sign-ins, push deliveries and open event streams. With extras.monitoring.alerts.enabled, the chart's PrometheusRule gains five alerts of severity warning: AppSuiteMobileApiUnavailable (more than 5% of a pod's requests answered with 503), AppSuiteMobileTokenValidationFailing (tokens cannot be validated), AppSuiteMobileApiSlow (95th percentile above one second), AppSuiteAccessTokenSignInsRateLimited (more than 20 rate-limited sign-ins in 15 minutes) and AppSuitePushDeliveriesFailing (more than 10% of a transport's pushes failed or refused). Shipped with chart version 6.25.23. No new configuration.
Configuration
SCR-2021
Summary: New configuration option for the header cache of Jetty connections
Effective: applies to the 8.54 release line
A new option sizes the header field cache each connection of the Jetty HTTP engine keeps. Its default is smaller than Jetty's own.
com.openexchange.http.jetty.headerCacheSizeThe size of the header field cache of each connection, in entries. The cache saves parsing repeated request headers on a kept-alive connection and lives as long as the connection. Jetty's own default of1024costs about 100 KB of heap per connection, which adds up for long-lived connections such as event streams.0switches the cache off. Default256. Not reloadable, not config-cascade aware. File:jetty.properties.
8.54.332
API - HTTP-API
SCR-2017
Summary: New Mobile API endpoint to change many messages at once
Effective: 8.54.328 and later; also delivered in 8.54.331, 8.54.332
The Mobile API gains POST /mobile/v1/mail/message-bulk, with which native mail apps change any number of messages in one call: set seen, flagged, answered, color, categoryId and keywords, and move, copy, archive, spam, notSpam, trash or deletePermanently. Up to 50 operations run in order, each with its own result: 200, 207 with the failed identifiers and their problem, or an error status; a failing operation does not stop the others. Where message identifiers do not survive moves, the new ones are listed in moved; copies are listed in copies. The call needs the scope write_mail and requires Idempotency-Key; a retry after a lost response gets the kept result, which is kept up to 1 MB even if com.openexchange.ajax.idempotency.maxResultSize is lower. All operations together may name at most 1000 identifiers (Me.limits.maxBulkIds); more are answered with 413.
The response also carries states: for each folder the operations changed, the state right before and after them, as oldState and newState for GET /mobile/v1/mail/folders/{folderId}/message-changes. A folder is listed only if every change in between was one of the request's own; after a change by another client, or without CONDSTORE, it is left out and the app syncs as usual.
8.54.331
API - HTTP-API
SCR-2017
Summary: New Mobile API endpoint to change many messages at once
Effective: 8.54.328 and later; also delivered in 8.54.331
The Mobile API gains POST /mobile/v1/mail/message-bulk, with which native mail apps change any number of messages in one call: set seen, flagged, answered, color, categoryId and keywords, and move, copy, archive, spam, notSpam, trash or deletePermanently. Up to 50 operations run in order, each with its own result: 200, 207 with the failed identifiers and their problem, or an error status; a failing operation does not stop the others. Where message identifiers do not survive moves, the new ones are listed in moved; copies are listed in copies. The call needs the scope write_mail and requires Idempotency-Key; a retry after a lost response gets the kept result, which is kept up to 1 MB even if com.openexchange.ajax.idempotency.maxResultSize is lower. All operations together may name at most 1000 identifiers (Me.limits.maxBulkIds); more are answered with 413.
The response also carries states: for each folder the operations changed, the state right before and after them, as oldState and newState for GET /mobile/v1/mail/folders/{folderId}/message-changes. A folder is listed only if every change in between was one of the request's own; after a change by another client, or without CONDSTORE, it is left out and the app syncs as usual.
8.54.330
API - HTTP-API
SCR-2020
Summary: New Mobile API endpoints for message lists, conversations and search
Effective: 8.54.329 and later; also delivered in 8.54.330
The Mobile API gains GET /mobile/v1/mail/folders/{folderId}/messages, GET /mobile/v1/mail/folders/{folderId}/threads and POST /mobile/v1/mail/search. Native mail apps use them to list and search the messages and conversations of a folder, newest first and in pages. The cursor stays correct while mail arrives or is removed. Lists filter by unseen, flagged, categoryId and a receivedAfter window, and return the state for message-changes. A search field, operator or scope the mail server cannot search is answered with 400, never with all messages. hasAttachments follows the keywords $HasAttachment and $HasNoAttachment (Dovecot mail_attachment_detection_options = add-flags), else a multipart/mixed Content-Type, the same in lists and in search. Conversations need threading on the mail server (501 threading_unsupported otherwise). The endpoints need the scope read_mail.
SCR-2018
Summary: New Mobile API endpoints to read a message, its body, source, attachments and attachment previews
Effective: 8.54.329 and later; also delivered in 8.54.330
The Mobile API gains five operations for reading one message, all with scope read_mail and none of them marking the message seen: GET /mobile/v1/mail/messages/{messageId} returns headers, flags and, on request through fields, the attachment list; .../body returns the body sanitized against the server's allow-list, as HTML or text, with externalContent (block, proxy, allow) and maxBytes; .../raw and .../attachments/{attachmentId} return the source and an attachment with Range support (206, 416); .../attachments/{attachmentId}/preview returns a scaled image of a picture or, with the document converter, of a document's first page, 202 while it renders and 404 with code preview_unavailable without a converter. Where virus scanning is enforced, source, attachments and previews are scanned first.
8.54.329
API - HTTP-API
SCR-2020
Summary: New Mobile API endpoints for message lists, conversations and search
Effective: 8.54.329 and later
The Mobile API gains GET /mobile/v1/mail/folders/{folderId}/messages, GET /mobile/v1/mail/folders/{folderId}/threads and POST /mobile/v1/mail/search. Native mail apps use them to list and search the messages and conversations of a folder, newest first and in pages. The cursor stays correct while mail arrives or is removed. Lists filter by unseen, flagged, categoryId and a receivedAfter window, and return the state for message-changes. A search field, operator or scope the mail server cannot search is answered with 400, never with all messages. hasAttachments follows the keywords $HasAttachment and $HasNoAttachment (Dovecot mail_attachment_detection_options = add-flags), else a multipart/mixed Content-Type, the same in lists and in search. Conversations need threading on the mail server (501 threading_unsupported otherwise). The endpoints need the scope read_mail.
SCR-2018
Summary: New Mobile API endpoints to read a message, its body, source, attachments and attachment previews
Effective: 8.54.329 and later
The Mobile API gains five operations for reading one message, all with scope read_mail and none of them marking the message seen: GET /mobile/v1/mail/messages/{messageId} returns headers, flags and, on request through fields, the attachment list; .../body returns the body sanitized against the server's allow-list, as HTML or text, with externalContent (block, proxy, allow) and maxBytes; .../raw and .../attachments/{attachmentId} return the source and an attachment with Range support (206, 416); .../attachments/{attachmentId}/preview returns a scaled image of a picture or, with the document converter, of a document's first page, 202 while it renders and 404 with code preview_unavailable without a converter. Where virus scanning is enforced, source, attachments and previews are scanned first.
8.54.328
API - HTTP-API
SCR-2017
Summary: New Mobile API endpoint to change many messages at once
Effective: 8.54.328 and later
The Mobile API gains POST /mobile/v1/mail/message-bulk, with which native mail apps change any number of messages in one call: set seen, flagged, answered, color, categoryId and keywords, and move, copy, archive, spam, notSpam, trash or deletePermanently. Up to 50 operations run in order, each with its own result: 200, 207 with the failed identifiers and their problem, or an error status; a failing operation does not stop the others. Where message identifiers do not survive moves, the new ones are listed in moved; copies are listed in copies. The call needs the scope write_mail and requires Idempotency-Key; a retry after a lost response gets the kept result, which is kept up to 1 MB even if com.openexchange.ajax.idempotency.maxResultSize is lower. All operations together may name at most 1000 identifiers (Me.limits.maxBulkIds); more are answered with 413.
8.54.326
API - HTTP-API
SCR-2017
Summary: New Mobile API endpoint to change many messages at once
Effective: applies to the 8.54 release line
The Mobile API gains POST /mobile/v1/mail/message-bulk, with which native mail apps change any number of messages in one call: set seen, flagged, answered, color, categoryId and keywords, and move, copy, archive, spam, notSpam, trash or deletePermanently. Up to 50 operations run in order, each with its own result: 200, 207 with the failed identifiers and their problem, or an error status; a failing operation does not stop the others. Where message identifiers do not survive moves, the new ones are listed in moved; copies are listed in copies. The call needs the scope write_mail and requires Idempotency-Key; a retry after a lost response gets the kept result, which is kept up to 1 MB even if com.openexchange.ajax.idempotency.maxResultSize is lower. All operations together may name at most 1000 identifiers (Me.limits.maxBulkIds); more are answered with 413.
SCR-2010
Summary: New Mobile API endpoints for sign-in, account data and recipient suggestions
Effective: 8.54.320 and later
The Mobile API gains the endpoints a native mail app needs to sign in and load the account. In mode token, POST /mobile/v1/auth/token exchanges login, password and device name for a personal access token. A user with a second factor gets 401 with second_factor_required and a challenge, answered through POST /mobile/v1/auth/token/second-factor with a TOTP, backup or SMS code; a first call without code sends the SMS and returns 202. GET /mobile/v1/auth/token reads the presented token, DELETE /mobile/v1/auth/token revokes it. In mode idp all four answer 404. In both modes, GET /mobile/v1/me returns user, scopes and settings with an ETag; GET /mobile/v1/mail/accounts lists mail accounts with stableMessageIds and stableFolderIds; GET /mobile/v1/mail/signatures, GET /mobile/v1/mail/categories (with unread counts) and GET /mobile/v1/contacts/autocomplete return signatures, categories and recipient suggestions. Sign-in shares lockout and rate limits with the access token sign-in.
SCR-2007
Summary: New Mobile API endpoints for push subscriptions
Effective: 8.54.320 and later
The Mobile API gains /mobile/v1/push/subscriptions, with which native mail apps register a device for push notifications through APNs or FCM. POST registers the device, optionally with Idempotency-Key; GET lists the subscriptions of the token or OAuth client or reads one by subscriptionId; PATCH (application/merge-patch+json) changes one; DELETE removes it. All need the scope read_mail. Registering the same device token again replaces the subscription and keeps its identifier. Invalid input is answered with 400 or 422 and the field in errors[].pointer, a body over 64 KiB with 413. A subscription made with an access token ends with the token; one made in mode idp ends after 30 days without renewal.
SCR-1958
Summary: New Attribute sentFolderAccess for Deputy Permissions to Access the Granting User's Sent Folder
Effective: applies to the 8.54 release line
The DeputyPermission and GrantedDeputyPermission objects of the deputy module carry the new optional string attribute sentFolderAccess, granting the deputy access to the granting user's standard Sent folder of the primary mail account. It is independent of sendOnBehalfOf; with both granted, the copy of a mail sent on behalf is filed into the granting user's Sent folder as well, where it appears unread, and as before into the deputy's own.
none(default): no access.write: copies are filed, but the deputy cannot read the folder and it is not offered in the deputy's folder tree: it is reported withsubscribed=false, and the granting user's INBOX as mounted in the deputy's tree reportssubscr_subflds=falsewhen that folder is its only subscribed child.readWrite: the deputy can read the folder and file into it, and it is subscribed.PUT /appsuite/api/deputy?action=newand?action=updateaccept the attribute; on update an absent attribute keeps the current value.GET ?action=all,?action=getand?action=reversereturn it.- The new capability
deputy_sent_foldertells a client whether the deployment offers the option (see SCR-1956). Newly granting or raising the access without it is rejected withDEPUTY-0017, while repeating the stored value or lowering it is accepted. - The access requires a
mailmodule permission in the same deputy permission, otherwiseDEPUTY-0015. Unknown values are rejected withSVL-0010, an unresolvable Sent folder withIMAP_DEPUTY-0016orIMAP_DEPUTY-0017, and folder modespecificwith the Sent folder but without the INBOX among the selected folders withIMAP_DEPUTY-0019, since the deputy's view of a covered Sent folder is derived from the INBOX (a selection without the Sent folder is not affected; an update that only changessentFolderAccessis checked against the folders the permission currently covers). - If the copy cannot be filed, the send still succeeds and the response of the compose send request (
PUT /appsuite/api/mail/compose/{id}/send) carries the new warningMSG-0134in itswarnings, so the deputy is not left believing the copy exists.
Purely additive - existing clients and payloads are unaffected. See the API documentation and the feature documentation for further details.
API - Java
SCR-1832
Summary: Relocated configuration and user-configuration Java packages
Effective: applies to the 8.54 release line
Custom bundles and plugins that use the configuration API must be adapted:
com.openexchange.config.ConfigurationService,Reloadable,Interests,PropertyFilterand related types moved to thecom.openexchange.config.commonpackage and bundle; import statements and bundle manifests must be repointed.- The
UserConfigurationAPI moved tocom.openexchange.config.universal;ServerSession.getUserConfiguration()now returnsUserConfigurationImpl.
Code compiles against the old manifest imports but fails at OSGi resolution, so the manifest change is mandatory. The OX-maintained plugin repositories are adapted in the same release train.
API - RMI
SCR-1960
Summary: New Field sentFolderAccess in the Deputy Permission Data Objects of the Administrative RMI and gRPC Interfaces
Effective: applies to the 8.54 release line
The RMI data objects com.openexchange.admin.rmi.dataobjects.DeputyPermission (and thereby ActiveDeputyPermission) and DeputyPermissionDescription carry the new optional string field sentFolderAccess with the values none, write and readWrite, granting the deputy access to the granting user's standard Sent folder. The description object tracks it with the companion isSentFolderAccessSet flag, so an absent field keeps the current value on update.
The gRPC provisioning API mirrors it in deputy.proto: DeputyPermission.sent_folder_access (field 8) and the description pair sentFolderAccess (15) with SentFolderAccessSet (16). Semantics and validation match the SOAP interface, see SCR-1959. The rollback of a failed revoke-all on the shared account interface restores previously existing deputy permissions regardless of the deployment switch, so it cannot leave permissions half revoked.
Purely additive - existing RMI and gRPC clients are unaffected. See the feature documentation for further details.
API - SOAP
SCR-1959
Summary: New Element sentFolderAccess in the Deputy Permissions of the OXDeputyPermissionsService
Effective: applies to the 8.54 release line
The data objects DeputyPermission and ActiveDeputyPermission of the OXDeputyPermissionsService carry the new optional, nillable element sentFolderAccess with the values none (default), write and readWrite, granting the deputy access to the granting user's standard Sent folder (see SCR-1958 for the semantics). It is appended as the last element of both complex types, so the element sequence of the existing WSDL stays unchanged. The grant, grantMultiple and update operations accept it; get, list and listGivenTo return it. On update an absent element keeps the current value.
The access requires a mail module permission in the same deputy permission, otherwise the request fails with DEPUTY-0015; newly granting or raising it while the deployment has the option switched off fails with DEPUTY-0017 (see SCR-1956). Because this path has no session of the granting user, the standard Sent folder is resolved from the provisioning data and the mail settings; if it cannot be resolved the request fails with IMAP_DEPUTY-0016 or IMAP_DEPUTY-0017; a deputy from another context needs a DoveAdm connection with the namespace prefixes configured (IMAP_DEPUTY-0018 otherwise), and folder mode specific with the Sent folder but without the INBOX among the selected folders fails with IMAP_DEPUTY-0019. All of these fail before anything is stored. Existing SOAP clients are unaffected.
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap="http://soap.admin.openexchange.com" xmlns:xsd="http://dataobjects.soap.admin.openexchange.com/xsd" xmlns:xsd1="http://dataobjects.rmi.admin.openexchange.com/xsd">
<soapenv:Body>
<soap:grant>
<soap:deputyPermission>
<xsd:userId>7</xsd:userId>
<xsd:sendOnBehalfOf>true</xsd:sendOnBehalfOf>
<xsd:modulePermissions>
<xsd:moduleId>mail</xsd:moduleId>
<xsd:folderPermission>2</xsd:folderPermission>
<xsd:readPermission>4</xsd:readPermission>
<xsd:writePermission>0</xsd:writePermission>
<xsd:deletePermission>0</xsd:deletePermission>
<xsd:admin>false</xsd:admin>
</xsd:modulePermissions>
<xsd:folderMode>default</xsd:folderMode>
<xsd:sentFolderAccess>readWrite</xsd:sentFolderAccess>
</soap:deputyPermission>
<soap:context><xsd:id>1</xsd:id></soap:context>
<soap:user><xsd:id>4</xsd:id></soap:user>
<soap:auth>
<xsd1:login>oxadmin</xsd1:login>
<xsd1:password>secret</xsd1:password>
</soap:auth>
</soap:grant>
</soapenv:Body>
</soapenv:Envelope>
See the feature documentation for further details.
Behavioral Changes
SCR-2016
Summary: Changed revoke of deputy permissions without a stored mail ACL baseline
Effective: applies to the 8.54 release line
Revoking a deputy permission restores the rights the deputy held on the granting user's mailboxes before the grant, taken from the mail ACL baseline in the table deputy_mail_acl_baseline (SCR-1853). Deputy permissions granted before that table existed have no row there. For those, the baseline is no longer read from its legacy location, the granting user's INBOX metadata entry /shared/vendor/vendor.open-xchange/deputydir-<deputyId>: every sharee of the INBOX, the deputy included, may write that entry, and a revoke would restore whatever rights it claims.
Such a permission now counts as having an empty baseline. Its revoke takes back all rights of the deputy on the granting user's mailboxes, including rights the deputy held before the grant and rights granted through another deputy permission for the same deputy. The next update of such a permission records the empty baseline in the table, so its eventual revoke behaves the same. Deputy permissions with a recorded baseline are not affected.
There is no switch for the previous behavior. No configuration change is required; after revoking an older deputy permission, operators or users may have to share affected folders with the deputy again.
SCR-2011
Summary: Signatures are sanitized when saved
Effective: 8.54.320 and later
HTML signatures created, updated or imported through the snippet module (new, update, import) are now sanitized. Before, the sanitized result was discarded and the given HTML stored. Saving a signature therefore removes markup outside the sanitizer's whitelist, such as sip: and callto: links or inline SVG. Plain-text signatures without HTML tags, such as Anna <anna@example.com>, stay unchanged; content that cannot be sanitized is stored as escaped text. Stored signatures change only when saved again. No admin action.
SCR-1990
Summary: Changed how Guard looks up recipient keys via DNS SRV records and remote key servers
Effective: applies to the 8.54 release line
Before asking the configured key servers, Guard looks for HKP servers announced in the _hkps._tcp and _hkp._tcp DNS SRV records of the recipient's domain. This lookup changes as follows:
- All announced servers are tried in priority order (lower priority first, higher weight first within a priority) until one returns a key. Before, records with priority 0 were never queried, and only the first announced server was asked, even if it was unreachable. Guard may therefore now contact external key servers for domains whose SRV records it ignored so far.
- The SRV part of a lookup, DNS queries included, uses at most half of
com.openexchange.guard.remoteKeyLookupTimeout; the configured key servers always keep the rest. A whole lookup no longer exceeds that timeout. Before, a slow or unresponsive DNS server could use up the entire time, so the configured key servers were never asked and the recipient was treated as a guest. - The DNS lookup queries type
SRVinstead ofANYand no longer walks the resolver's search path.
The SRV lookup cannot be switched off. With com.openexchange.guard.dns.allowUnsignedSRVRecords=false, only DNSSEC-signed SRV records are used.
SCR-1969
Summary: MCP lists report 'returned' instead of 'totalMatches' and advertise only the scopes a node offers
Effective: applies to the 8.54 release line
Two corrections to what the MCP endpoint tells a client about itself.
A list result names the size of the page as returned instead of totalMatches. The field never held the number of matches there are: a tool asks its backend for one page plus a single entry, so the whole count is never known. A model reading totalMatches had every reason to believe it had seen everything. Whether more entries follow is what truncated and nextCursor say, and that is unchanged.
scopes_supported in the RFC 9728 resource metadata document, and the scope parameter of a WWW-Authenticate challenge, name the scopes the tools registered on that node require, rather than a fixed list. A deployment that leaves out a tool package no longer advertises that package's scope, so a client is not sent after access it gains nothing from.
Both are visible to a client. No admin action, no configuration, no migration.
SCR-1968
Summary: New error codes MCP-0001 to MCP-0003 for the MCP endpoint
Effective: applies to the 8.54 release line
The MCP endpoint and its tools raise error codes of their own instead of a general error. Until now every failure below the endpoint came back as a general OXException, so a log could only be filtered by matching the message text, and a node whose calendar stack was gone looked exactly like a caller sending nonsense.
MCP-0001 - The %1$s service is not available. A service a tool needs is not there: the node is missing the package behind that module, or it is still starting. Category SERVICE_DOWN.
MCP-0002 - Could not read the %1$s. A message, file or attachment could not be read; the cause is logged. Category ERROR.
MCP-0003 - Not a valid %1$s: %2$s. The caller sent a value the tool cannot use, so nothing is wrong with the node. Category USER_INPUT.
Callers see no new failure mode: the first two still reach a client as a tool error (200 with isError: true), the third as it did before. What changes is that an operator can alert on one kind of failure without matching free text. No admin action, no configuration.
SCR-1948
Summary: OAuth provider consults every token validator and answers 401 for a JWT of an unknown user
Effective: applies to the 8.54 release line
Three changes in com.openexchange.oauth.provider.impl: the resource-server side asks every registered OAuthAuthorizationService in ranking order and takes the first that recognizes a token, so that additional token kinds such as personal access tokens plug in next to the JWT validator; the provider starts in mode expect_jwt without com.openexchange.oauth.provider.jwt.jwksUri, then without a JWT validator, instead of failing to start; and a JWT whose subject resolves to no user is answered with 401 (invalid_token) instead of 500, logged at INFO.
A validator may hand parameters to the session the provider establishes (ValidationResponse.getSessionParameters()), and a validator that cannot check a token at all, e.g. because its storage is unavailable, reports that as TokenValidationUnavailableException; the HTTP API and the DAV interfaces then answer 503 (temporarily_unavailable, Retry-After) without a challenge, so that the client keeps its credential. No admin action.
Configuration
SCR-2003
Summary: New configuration options for Mobile API push notifications
Effective: 8.54.320 and later
The following options control push notifications for Mobile API apps through APNs and FCM. All are reloadable, config-cascade aware and have no dedicated properties file.
com.openexchange.mobile.push.notificationContentWhat a visible new-message push reveals to Apple or Google when a subscription asks for nothing:none(a neutral text),senderorsenderAndSubject. Defaultnone. An unknown value counts asnone.com.openexchange.mobile.push.maxNotificationContentThe most a subscription may ask a new-message push to reveal; a higher request is lowered to it, also for existing subscriptions. DefaultsenderAndSubject. An unknown value counts asnone.com.openexchange.mobile.push.maxSubscriptionsPerUserThe most push subscriptions a user may hold across all Mobile API apps; a further registration is refused with422. Registering a device again does not count.0means no limit. Default20.
SCR-2002
Summary: New Mobile API endpoints for sending, drafts and uploads, and configuration options for uploads
Effective: applies to the 8.54 release line
The Mobile API gains endpoints to send mails, keep drafts and upload attachments:
POST /mobile/v1/mail/sendsends a mail without a draft. It requires anIdempotency-Key; a retry with the same key orclientMessageIdis answered withduplicateinstead of sending the mail again.GETandPOST /mobile/v1/mail/drafts,GET,PUTandDELETE /mobile/v1/mail/drafts/{draftId} andPOST /mobile/v1/mail/drafts/{draftId}/sendmanage drafts.PUTrequiresIf-Match: without it the answer is428, and if the draft changed meanwhile it is412with the current draft.POST /mobile/v1/uploadsstores a file to attach,DELETE /mobile/v1/uploads/{uploadId} removes it. An upload needsUpload-Lengthand cannot be part of a batch.
Reading drafts needs the scope read_mail, all other endpoints write_mail. Request bodies of sends and drafts are limited to 4 MiB (413).
The following options limit the files an app stores through the Mobile API at /mobile/v1 to attach them to mails. Uploads are kept for 24 hours and do not count against the user's quota. Both limits hold exactly only with Redis Lua scripting (com.openexchange.redis.lua.enabled, the default); without it, simultaneous uploads may exceed them slightly.
com.openexchange.mobile.api.uploads.maxCountHow many uploads a user may keep at a time; a further upload is refused with507and codequota_exceeded. At most10000; a value less than or equal to0(zero), or a higher one, means10000. Default100. Reloadable, config-cascade aware. No dedicated properties file.com.openexchange.mobile.api.uploads.maxSizeHow many bytes the uploads of a user may take up in total; beyond that an upload is refused with507and codequota_exceeded. A single upload is limited like a mail attachment (MAX_UPLOAD_SIZEand the user's upload quotas). A value less than or equal to0(zero) sets no limit. Default1073741824(1 GiB). Reloadable, config-cascade aware. No dedicated properties file.
SCR-1962
Summary: New configuration options for personal access tokens
Effective: applies to the 8.54 release line
Two limits for personal access tokens, the bearer credentials a user mints through the HTTP API module accesstoken.
com.openexchange.accesstoken.maxLifetimeDaysThe longest lifetime a user may give a token, in days from the moment it is minted; a request with a laterexpiresis refused withACCESSTOKEN-0007. A value of 0 (zero) removes the limit. Default 365. Reloadable, config-cascade aware. No dedicated properties file.com.openexchange.accesstoken.maxTokensPerUserHow many tokens a user may hold at once; expired tokens count until the clean-up drops them a week after their expiry. Minting beyond the limit is refused withACCESSTOKEN-0006. A value of 0 (zero) removes the limit. Default 50. Reloadable, config-cascade aware. No dedicated properties file.
SCR-1956
Summary: New Properties and Capability for Deputy Sent Folder Access
Effective: applies to the 8.54 release line
In order to let a granting user receive a copy of every mail a deputy sends in their name, the Sent folder access option is introduced, controlled by two new lean configuration properties and announced through a new capability.
com.openexchange.deputy.sentFolderAccessEnabledmakes the option available. It defaults tofalse, so operators enable the option deliberately; it is reloadable and config-cascade aware, and is evaluated in the granting user's scope. While it isfalse, newly granting or raising the access is rejected withDEPUTY-0017and no copy is filed any more - including for permissions that already carry it, so the copies can be withdrawn deployment-wide without revoking permissions one by one. Repeating the stored level and lowering it stay possible.com.openexchange.deputy.provider.imap.fallbackSentFolderNameis an operator override for the administrative provisioning path, which resolves the granting user's Sent folder without a session of that user. It is empty by default, relative to the personal namespace prefix, reloadable and config-cascade aware.
Clients detect availability through the new capability deputy_sent_folder, granted exactly while the property is true and, like deputy, never granted to guests.
New error codes, all raised before anything is stored: DEPUTY-0015 (access without a mail module permission), DEPUTY-0017 (access raised while the option is off), IMAP_DEPUTY-0016 and IMAP_DEPUTY-0017 (Sent folder not existent respectively not determinable), IMAP_DEPUTY-0018 (cross-context access without a DoveAdm connection or its namespace prefixes) and IMAP_DEPUTY-0019 (folder mode specific covering the Sent folder but not the INBOX while access is granted; checked on grants and on updates against the folders the permission currently covers).
Operator note: on the upgrade to 8.54, and only that one, a node still running the previous version can widen a deputy's rights on the granting user's Sent folder; deployments rule this out by enabling the property only once the rolling upgrade has completed on every node. With acl_defaults_from_inbox = yes, enabling the option replaces the inherited INBOX rights on that folder with the granted level.
See the feature documentation for the rolling-upgrade details, the required Dovecot settings and the folder resolution order.
SCR-1944
Summary: New configuration options for the MCP server
Effective: applies to the 8.54 release line
The MCP server endpoint /mcp is configured through these properties, read when the bundle com.openexchange.mcp starts and again on a configuration reload.
com.openexchange.mcp.enabledWhether the MCP endpoint/mcpand its resource metadata document/.well-known/oauth-protected-resource/mcpare registered. Defaultfalse. Reloadable, not config-cascade aware. No dedicated properties file.com.openexchange.mcp.allowedOriginsComma-separatedOriginheader values a browser-based client may send. A request with anOriginthat is not listed is answered with403. Default empty, which refuses every request carrying anOriginheader. Reloadable, not config-cascade aware. No dedicated properties file.com.openexchange.mcp.authorizationServersComma-separated issuer URLs advertised asauthorization_serversin the RFC 9728 resource metadata. Default empty;com.openexchange.oauth.provider.allowedIssueris advertised then, if set. Reloadable, not config-cascade aware. No dedicated properties file.com.openexchange.mcp.serverNameThe name reported asserverInfo.name. Defaultopen-xchange-mcp. Reloadable, not config-cascade aware. No dedicated properties file.com.openexchange.mcp.instructionsThe natural-language guidance a client receives fromserver/discover. Default: a sentence describing the read-only access to mail, calendar and contacts. Reloadable, not config-cascade aware. No dedicated properties file.com.openexchange.mcp.rateLimit.callsHow many tool calls a user may make per window, counted across all nodes through the rate limiter service; beyond that a call is answered with429and aRetry-Afterheader. A value less than or equal to0(zero) disables the limit. Default60. Reloadable, not config-cascade aware. No dedicated properties file.com.openexchange.mcp.rateLimit.windowSecondsThe window of the tool call limit in seconds. A value less than1is treated as1. Default60. Reloadable, not config-cascade aware. No dedicated properties file.com.openexchange.mcp.maxConcurrentCallsHow many tool calls and resource reads of one user may run at the same time on a node; a further one is answered with429andRetry-After: 1, takes no permit of the rate limit and is audited with outcomebusy. Per node, since a call in flight lives on the node that runs it. A value less than or equal to0(zero) switches the bound off. Default4. Reloadable, not config-cascade aware. No dedicated properties file.
SCR-1830
Summary: Renamed configuration properties keep resolving under their previous names
Effective: applies to the 8.54 release line
A number of configuration properties were renamed to consistent, fully-qualified names, for example com.openexchange.hazelcast.network.join to com.openexchange.hazelcast.network.join.mode and the bare legacy key JMXServerPort to com.openexchange.jmx.serverPort. Deployments that still configure an old name are not affected: when a renamed property is not set under its new name, the server automatically falls back to the value configured under the previous name. If both names are set, the new name wins. The fallback is a transitional measure and will be removed in a future release, so configurations should be migrated to the new names; every value served through the fallback is reported in the server log, typically at startup, as Deprecated key <previous name> detected. See the property changes documentation for the complete list of renamed properties with previous name, new name and, where a bare previous name only applies within one file, that file.
SCR-1829
Summary: Migrated middleware configuration to typed properties with built-in defaults and removed 55 shipped .properties files
Effective: applies to the 8.54 release line
The middleware configuration is migrated to typed property definitions whose default values live inside the server. As a consequence, 55 .properties files that only carried default values are no longer shipped to /opt/open-xchange/etc. The built-in defaults are identical to the values those files used to ship, so effective configuration is unchanged and no operator action is required. Overriding a default works as before: set the property in any .properties file in the configuration directory (the server reads them all, file names do not matter) or through the config cascade. Files that are read by name (configdb.properties, system.properties, whitelist.properties, the OAuth provider files, AdminUser.properties, Group.properties, Resource.properties, permissions.properties, caldav.properties and others) are still shipped. See the property documentation for the authoritative defaults. The removed files are listed in the property changes documentation.
Database
SCR-1957
Summary: New Columns sentFolderAccess and sentFolderCovered in Table deputy for the Deputy Sent Folder Access
Effective: applies to the 8.54 release line
Warning
Update Task{{com.openexchange.deputy.impl.groupware.DeputyStorageAddSentFolderAccessColumnTask}}
{{com.openexchange.deputy.impl.groupware.DeputyStorageAddSentFolderCoveredColumnTask}}
The deputy table gains two columns for the deputy Sent folder access.
sentFolderAccess stores the level of access a deputy is granted to the granting user's standard Sent folder (0 none, 1 write, 2 readWrite). Behavior-neutral for existing rows: the default 0 keeps existing deputy permissions without Sent folder access.
sentFolderCovered records, after every mail grant or update, whether the module permission's folder set covers the granting user's standard Sent folder (1) or not (0). It tells a Sent folder covered by the folder mode apart from the Sent-specific grant of the option; the deputy metadata on the folder cannot serve that purpose, as the deputy may rewrite it. NULL means not recorded yet: existing rows get the value on their next mail grant or update and fall back to the stored folder mode until then.
ALTER TABLE deputy ADD COLUMN sentFolderAccess TINYINT UNSIGNED NOT NULL DEFAULT 0;
ALTER TABLE deputy ADD COLUMN sentFolderCovered TINYINT(1) DEFAULT NULL;
Fresh schemas: com.openexchange.deputy.impl.groupware.DeputyStorageCreateTableService. Existing schemas: com.openexchange.deputy.impl.groupware.DeputyStorageAddSentFolderAccessColumnTask and com.openexchange.deputy.impl.groupware.DeputyStorageAddSentFolderCoveredColumnTask (both idempotent, depending on DeputyStorageCreateTableTask). The deputy table is small on typical deployments, so no noticeable ALTER cost is expected.
Packaging/Bundles
SCR-2005
Summary: Package open-xchange-mobile-api ships push subscriptions and requires open-xchange-pns-impl
Effective: 8.54.320 and later
The package open-xchange-mobile-api now also ships the bundle com.openexchange.mobile.push, which manages the push subscriptions of Mobile API apps, and requires open-xchange-pns-impl. Installations that deploy the package need open-xchange-pns-impl as well; push credentials for the apps are configured as described in the Mobile API documentation.
SCR-1831
Summary: Consolidated configuration bundles into com.openexchange.config.common
Effective: applies to the 8.54 release line
The shared configuration types are reorganized into dedicated bundles. Newly introduced:
com.openexchange.config.common- contains the classicConfigurationService, the reload types and the lean configuration API; thecom.openexchange.configpackage moves here fromcom.openexchange.configread, which remains as the provider implementation.com.openexchange.config.universal- the relocated user-configuration API.com.openexchange.config.mapping- resolves renamed property keys to their previous names.com.openexchange.admin.common,com.openexchange.sessiond.configandcom.openexchange.timer- split out of their host bundles to break dependency cycles.
The bundle com.openexchange.config.lean is renamed to com.openexchange.config.lean.impl; the API package name com.openexchange.config.lean is unchanged. All affected packages are shipped as before; install lists that pin individual bundles must be updated accordingly.
8.54.325
API - HTTP-API
SCR-2014
Summary: New Mobile API operations for live events and batch requests
Effective: 8.54.322 and later; also delivered in 8.54.323, 8.54.324, 8.54.325
The Mobile API gets two operations:
GET /mobile/v1/events: live changes as Server-Sent Events.stateevents carry anEventPayloadwith the API's folder identifiers and the topicsmail.message.*andmail.folder.changed. The stream needs scoperead_mail, skips changes made by its own device and answers404wherecom.openexchange.pns.transport.sse.enabledisfalsefor the user.POST /mobile/v1/batch: up to 20 operations in one request, run in order. An entry can refer to results of earlier entries with$ref:<id>#<JSON pointer>. Each entry carries its own status; scopes andIdempotency-Keyapply per entry.
At <dispatcher prefix>events, HEAD now answers 405 instead of opening a stream, and errors use the problem format of the Mobile API: each carries a {
}, e.g. {{invalid_token}}, and a {{type}} of {{https://documentation.open-xchange.com/mobile/v1/problems/<code>}}. Status codes and headers are unchanged.
8.54.324
API - HTTP-API
SCR-2014
Summary: New Mobile API operations for live events and batch requests
Effective: 8.54.322 and later; also delivered in 8.54.323, 8.54.324
The Mobile API gets two operations:
GET /mobile/v1/events: live changes as Server-Sent Events.stateevents carry anEventPayloadwith the API's folder identifiers and the topicsmail.message.*andmail.folder.changed. The stream needs scoperead_mail, skips changes made by its own device and answers404wherecom.openexchange.pns.transport.sse.enabledisfalsefor the user.POST /mobile/v1/batch: up to 20 operations in one request, run in order. An entry can refer to results of earlier entries with$ref:<id>#<JSON pointer>. Each entry carries its own status; scopes andIdempotency-Keyapply per entry.
At <dispatcher prefix>events, HEAD now answers 405 instead of opening a stream, and errors use the problem format of the Mobile API: each carries a {
}, e.g. {{invalid_token}}, and a {{type}} of {{https://documentation.open-xchange.com/mobile/v1/problems/<code>}}. Status codes and headers are unchanged.
SCR-2013
Summary: New Mobile API endpoint for the message changes of a mail folder
Effective: 8.54.324 and later
The Mobile API gains GET /mobile/v1/mail/folders/{folderId}/message-changes, with which native mail apps fetch the messages created, updated and destroyed in a folder since a state taken from the folder list or from an earlier call. A call returns at most limit created and updated messages (default and maximum 500) and hasMore; include embeds fields of those messages, never their body. On IMAP the server needs CONDSTORE and QRESYNC; JMAP accounts are supported as well. A state that is too old, belongs to another folder or cannot be computed is answered with 410 and code cannot_calculate_changes. The endpoint needs the scope read_mail.
8.54.323
API - HTTP-API
SCR-2014
Summary: New Mobile API operations for live events and batch requests
Effective: 8.54.322 and later; also delivered in 8.54.323
The Mobile API gets two operations:
GET /mobile/v1/events: live changes as Server-Sent Events.stateevents carry anEventPayloadwith the API's folder identifiers and the topicsmail.message.*andmail.folder.changed. The stream needs scoperead_mail, skips changes made by its own device and answers404wherecom.openexchange.pns.transport.sse.enabledisfalsefor the user.POST /mobile/v1/batch: up to 20 operations in one request, run in order. An entry can refer to results of earlier entries with$ref:<id>#<JSON pointer>. Each entry carries its own status; scopes andIdempotency-Keyapply per entry.
HEAD on <dispatcher prefix>events now answers 405 instead of opening a stream; the endpoint is otherwise unchanged.
Configuration
SCR-2009
Summary: New Configuration Property to Use PKCE in the OpenID Connect Authorization Code Flow
Effective: 8.54.323 and later
The new lean configuration property com.openexchange.oidc.pkceMethod enables Proof Key for Code Exchange (PKCE, RFC 7636) for the OpenID Connect authorization code flow. PKCE binds an authorization code to the authentication request, so that an intercepted code cannot be redeemed elsewhere. It defaults to an empty value, is reloadable and not config-cascade aware.
If set to a code challenge method, typically S256, a code challenge is sent with every authentication request, and the matching code verifier is presented when the authorization code is exchanged. The OpenID Provider can then be configured to require PKCE for the client. Method plain is discouraged, and an unsupported method leads to a failed login and a warning in the log. Set the property only once all middleware nodes are updated. No behavior change for existing deployments.
See the property documentation for further details.
SCR-2008
Summary: New Configuration Property to Bind OpenID Connect Logins to the Initiating Browser
Effective: 8.54.323 and later
The new lean configuration property com.openexchange.oidc.bindStateToBrowser binds an OpenID Connect authentication request to the browser that initiated it, which protects against login CSRF. It defaults to false, is reloadable and not config-cascade aware.
If enabled, the init request sets the short-lived cookie open-xchange-oidc-state-<state>, and the authentication callback is only accepted from a browser that presents it. The cookie is scoped to the host and path of com.openexchange.oidc.rpRedirectURIAuth, and is sent with SameSite=None; Secure for an https callback, so that the silent relogin inside an iframe still works. The init request therefore has to reach the same host as the callback. No behavior change for existing deployments.
See the property documentation for further details.
SCR-1978
Summary: New configuration options for live change events
Effective: 8.54.320 and later; also delivered in 8.54.322, 8.54.323
The new push notification transport sse delivers notifications to live change streams (Server-Sent Events). The following options control it.
com.openexchange.pns.transport.sse.enabledWhether the transport is enabled. It can be refined per client and topic by appending.<client>and.<client>.<topic>. Defaultfalse. Reloadable, config-cascade aware. No dedicated properties file.com.openexchange.pns.transport.sse.pingIntervalThe interval in milliseconds in which an open stream refreshes its presence and sends apingevent to the client; a presence that is not refreshed expires after twice this interval. Default30000. Reloadable, config-cascade aware. No dedicated properties file.com.openexchange.pns.transport.sse.maxConnectionsPerUserThe maximum number of streams a user may have open across the cluster, counted per endpoint. Streams at<dispatcher prefix>eventsand at/mobile/v1/eventsdo not replace each other; at/mobile/v1/eventsthe limit applies per device (access token, orPush-Subscription-Idin modeidp). Opening one more closes the oldest stream of the same endpoint and device with reasonreplaced. A value less than or equal to 0 (zero) disables the limit. Default5. Reloadable, config-cascade aware. No dedicated properties file.com.openexchange.pns.transport.sse.maxConnectionsPerNodeThe maximum number of streams open on one node. Further requests are rejected with HTTP status503and aRetry-Afterheader, so the client can reconnect to another node. A value less than or equal to 0 (zero) disables the limit. Default5000. Reloadable, not config-cascade aware. No dedicated properties file.com.openexchange.pns.transport.sse.maxLifetimeThe maximum lifetime of a stream in milliseconds. Once reached, the stream ends with reasonlifetimeand the client reconnects. A value less than or equal to 0 (zero) is ignored and the default is used. Default1800000(30 minutes). Reloadable, config-cascade aware. No dedicated properties file.com.openexchange.pns.transport.sse.tokenCheckIntervalThe interval in milliseconds in which the bearer token of a stream is validated again; a stream whose token was revoked or expired then ends with reasontoken_invalid. Keep it below 5 minutes, the time an idle OAuth session lives. A value less than or equal to 0 (zero) is ignored and the default is used. Default120000. Reloadable, config-cascade aware. No dedicated properties file.com.openexchange.imap.liveChanges.modeHow changes made outside the middleware, e.g. by another mail client, are noticed for live change streams of users whose primary account is IMAP.polllooks at the mailbox of a user with an open stream once perpollInterval, in oneLIST "" "*" RETURN (STATUS (MESSAGES UIDNEXT UIDVALIDITY HIGHESTMODSEQ))command on a pooled connection; it needs the IMAP extensions LIST-EXTENDED and LIST-STATUS, and CONDSTORE for changes that leave the message counts alone.offleaves such changes unnoticed. Changes made through the middleware are reported either way. Defaultpoll. Reloadable, config-cascade aware. No dedicated properties file.com.openexchange.imap.liveChanges.pollIntervalThe interval in milliseconds in which such a mailbox is looked at. Streams are handled every 5 seconds, so shorter values take effect as 5 seconds. Default10000. Reloadable, config-cascade aware. No dedicated properties file.com.openexchange.imap.liveChanges.maxWatchesThe maximum number of users a node watches this way. Streams of further users still report the changes made through the middleware. A value less than or equal to 0 (zero) disables the limit. Default5000. Reloadable, not config-cascade aware. No dedicated properties file.com.openexchange.mail.liveChanges.echoWindowThe time in milliseconds within which the mail server’s report of a change the middleware made itself is left out, so that a live change stream sees such a change once. It has to exceed the time the mail server needs to report a change, e.g.com.openexchange.imap.liveChanges.pollInterval. A value less than or equal to 0 (zero) switches the suppression off and reports the change twice. Default15000. Reloadable, config-cascade aware. No dedicated properties file.
8.54.322
API - HTTP-API
SCR-2014
Summary: New Mobile API operations for live events and batch requests
Effective: 8.54.322 and later
The Mobile API gets two operations:
GET /mobile/v1/events: live changes as Server-Sent Events.stateevents carry anEventPayloadwith the API's folder identifiers and the topicsmail.message.*andmail.folder.changed. The stream needs scoperead_mail, skips changes made by its own device and answers404wherecom.openexchange.pns.transport.sse.enabledisfalsefor the user.POST /mobile/v1/batch: up to 20 operations in one request, run in order. An entry can refer to results of earlier entries with$ref:<id>#<JSON pointer>. Each entry carries its own status; scopes andIdempotency-Keyapply per entry.
HEAD on <dispatcher prefix>events now answers 405 instead of opening a stream; the endpoint is otherwise unchanged.
SCR-2012
Summary: New Mobile API endpoints for mail folders
Effective: 8.54.321 and later; also delivered in 8.54.322
The Mobile API gains /mobile/v1/mail/folders, with which native mail apps read and manage the mail folders of all accounts. GET /mobile/v1/mail/folders returns a flat list with parentId, counts and, per folder, a state for message changes; on IMAP servers with LIST-STATUS and CONDSTORE the states of all folders of an account come from one command, and If-None-Match answers 304 while nothing changed. POST creates a folder and requires Idempotency-Key; GET, PATCH (application/merge-patch+json: rename, move, subscribe) and DELETE (to the trash unless permanent=true) work on /mobile/v1/mail/folders/{folderId}. Writes need If-Match (428 without it, 412 when stale); a name clash is answered with 409 and code folder_exists. Reads need the scope read_mail, writes write_mail. On Dovecot and JMAP a folder keeps its identifier when it is renamed or moved.
Configuration
SCR-1978
Summary: New configuration options for live change events
Effective: 8.54.320 and later; also delivered in 8.54.322
The new push notification transport sse delivers notifications to live change streams (Server-Sent Events). The following options control it.
com.openexchange.pns.transport.sse.enabledWhether the transport is enabled. It can be refined per client and topic by appending.<client>and.<client>.<topic>. Defaultfalse. Reloadable, config-cascade aware. No dedicated properties file.com.openexchange.pns.transport.sse.pingIntervalThe interval in milliseconds in which an open stream refreshes its presence and sends apingevent to the client; a presence that is not refreshed expires after twice this interval. Default30000. Reloadable, config-cascade aware. No dedicated properties file.com.openexchange.pns.transport.sse.maxConnectionsPerUserThe maximum number of streams a user may have open across the cluster, counted per endpoint. Streams at<dispatcher prefix>eventsand at/mobile/v1/eventsdo not replace each other; at/mobile/v1/eventsthe limit applies per device (access token, orPush-Subscription-Idin modeidp). Opening one more closes the oldest stream of the same endpoint and device with reasonreplaced. A value less than or equal to 0 (zero) disables the limit. Default5. Reloadable, config-cascade aware. No dedicated properties file.com.openexchange.pns.transport.sse.maxConnectionsPerNodeThe maximum number of streams open on one node. Further requests are rejected with HTTP status503and aRetry-Afterheader, so the client can reconnect to another node. A value less than or equal to 0 (zero) disables the limit. Default5000. Reloadable, not config-cascade aware. No dedicated properties file.com.openexchange.pns.transport.sse.maxLifetimeThe maximum lifetime of a stream in milliseconds. Once reached, the stream ends with reasonlifetimeand the client reconnects. A value less than or equal to 0 (zero) is ignored and the default is used. Default1800000(30 minutes). Reloadable, config-cascade aware. No dedicated properties file.com.openexchange.pns.transport.sse.tokenCheckIntervalThe interval in milliseconds in which the bearer token of a stream is validated again; a stream whose token was revoked or expired then ends with reasontoken_invalid. Keep it below 5 minutes, the time an idle OAuth session lives. A value less than or equal to 0 (zero) is ignored and the default is used. Default120000. Reloadable, config-cascade aware. No dedicated properties file.com.openexchange.imap.liveChanges.modeHow changes made outside the middleware, e.g. by another mail client, are noticed for live change streams of users whose primary account is IMAP.polllooks at the mailbox of a user with an open stream once perpollInterval, in oneLIST "" "*" RETURN (STATUS (MESSAGES UIDNEXT UIDVALIDITY HIGHESTMODSEQ))command on a pooled connection; it needs the IMAP extensions LIST-EXTENDED and LIST-STATUS, and CONDSTORE for changes that leave the message counts alone.offleaves such changes unnoticed. Changes made through the middleware are reported either way. Defaultpoll. Reloadable, config-cascade aware. No dedicated properties file.com.openexchange.imap.liveChanges.pollIntervalThe interval in milliseconds in which such a mailbox is looked at. Streams are handled every 5 seconds, so shorter values take effect as 5 seconds. Default10000. Reloadable, config-cascade aware. No dedicated properties file.com.openexchange.imap.liveChanges.maxWatchesThe maximum number of users a node watches this way. Streams of further users still report the changes made through the middleware. A value less than or equal to 0 (zero) disables the limit. Default5000. Reloadable, not config-cascade aware. No dedicated properties file.com.openexchange.mail.liveChanges.echoWindowThe time in milliseconds within which the mail server’s report of a change the middleware made itself is left out, so that a live change stream sees such a change once. It has to exceed the time the mail server needs to report a change, e.g.com.openexchange.imap.liveChanges.pollInterval. A value less than or equal to 0 (zero) switches the suppression off and reports the change twice. Default15000. Reloadable, config-cascade aware. No dedicated properties file.
8.54.321
API - HTTP-API
SCR-2012
Summary: New Mobile API endpoints for mail folders
Effective: 8.54.321 and later
The Mobile API gains /mobile/v1/mail/folders, with which native mail apps read and manage the mail folders of all accounts. GET /mobile/v1/mail/folders returns a flat list with parentId, counts and, per folder, a state for message changes; on IMAP servers with LIST-STATUS and CONDSTORE the states of all folders of an account come from one command, and If-None-Match answers 304 while nothing changed. POST creates a folder and requires Idempotency-Key; GET, PATCH (application/merge-patch+json: rename, move, subscribe) and DELETE (to the trash unless permanent=true) work on /mobile/v1/mail/folders/{folderId}. Writes need If-Match (428 without it, 412 when stale); a name clash is answered with 409 and code folder_exists. Reads need the scope read_mail, writes write_mail. On Dovecot and JMAP a folder keeps its identifier when it is renamed or moved.
8.54.320
3rd Party Libraries/License Change
SCR-1920
Summary: Upgraded the Equinox OSGi framework from 3.24.200 to 3.24.300
Effective: 8.54.320 and later
Upgraded the Equinox OSGi framework in the target platform from 3.24.200 to 3.24.300. The bundle symbolic name and all exported packages are unchanged; the jar is the upstream artifact, byte-identical to the one published on Maven Central.
The file name carries the bundle version, so every place that names it explicitly moves with it:
oxscripts/sbin/open-xchange.inandopen-xchange-cloud.in— theOSGI_JARthe service scripts start,docker/go/open-xchange/core/entrypoint/core/start.go— the same path in the container entry point,openexchange-test/.classpathandcom.openexchange.bundles/3rdPartyLibs.properties.
The obsolete org.osgi.annotation 6.0.0 bundle is removed alongside. It dates from 2014 and contains only the four annotations of org.osgi.annotation.versioning, which the platform already ships properly in org.osgi.annotation.versioning 1.1.2. No operator action is required.
SCR-1913
Summary: Upgraded the Bean Validation API from 1.1.0.Final to 2.0.1.Final
Effective: 8.54.320 and later
Upgraded the Bean Validation API in the target platform from 1.1.0.Final to 2.0.1.Final. The bundle symbolic name stays javax.validation.api, but the eight exported packages now carry version 2.0.1.Final, and javax.validation.valueextraction is exported in addition.
The upgrade is additive: no class and no member was removed between the two versions, 37 classes were added, and ConstraintValidator.initialize changed from abstract to a default method. Every consumer that imports these packages resolves against 2.0.1 as well — the ranges in use are [1.1,3), [2.0,3) and [2.0.0,3.0.0), plus imports without a range.
It also closes an existing gap. App Suite Office compiles against Bean Validation 2.0.2, which it ships itself, and uses types that 1.1.0 does not contain at all (for example ClockProvider and NotBlank), while its bundles were wired to the platform's 1.1.0 export at runtime.
No configuration change and no operator action are required.
SCR-1905
Summary: Upgraded Apache Tika from 3.3.1 to 4.0.0
Effective: 8.54.320 and later
Upgraded tika-core from 3.3.1 to 4.0.0 in bundle com.openexchange.tika.util. Tika stays fully encapsulated — the bundle exports only com.openexchange.tika.util — so the major upgrade is invisible to consumers and MIME type detection is unchanged.
The 4.0 API required three adjustments inside the wrapper:
TikaConfigwas replaced by a JSON-based configuration. The parameterlessTika()constructor yields the same defaults the removedTika(TikaConfig)call produced.Detector.detectnow takes aTikaInputStreamand aParseContextinstead of a plainInputStream.- The HTTP header constants moved from
MetadatatoHttpHeadersand changed fromStringtoProperty.
Tika 4 declares commonmark as a dependency for its Markdown output handler, which this bundle never uses, so those three jars are not embedded. No operator action is required.
SCR-1899
Summary: Upgraded eight third-party libraries to their latest patch or minor release
Effective: 8.54.320 and later
Upgraded eight third-party libraries to their latest patch or minor release. No API or configuration change; no operator action is required.
- SLF4J and its JCL, JUL, log4j and OSGi bridges from 2.0.18 to 2.0.19 in the target platform.
- Micrometer from 1.17.0 to 1.17.1 in
com.openexchange.metrics.micrometer(also raises the embeddedmicrometer-commonsandmicrometer-observation). - Spring Beans and Spring Core from 7.0.8 to 7.0.9 in
com.openexchange.xml. httpclient5-cachefrom 5.6.2 to 5.6.4 incom.openexchange.saml, matching the HttpClient 5 version the target platform ships.- CBOR from 4.5.2 to 4.5.6 in
com.openexchange.webauthn, matchingcom.openexchange.multifactor.provider.webauthn. The release moved its string utilities intodatautilities, which is now embedded alongside it. - JavaCC from 7.0.12 to 7.0.13, Mockito from 3.12.4 to 5.23.0 and Jackson Datatype Joda from 2.11.0 to 2.22.2 — build- and test-time only, not shipped.
SCR-1896
Summary: Upgraded the embedded S3 encryption client from 3.6.1 to 4.0.2
Effective: 8.54.320 and later
Upgraded amazon-s3-encryption-client-java from 3.6.1 to 4.0.2 in bundle software.amazon.awssdk.
amazon-s3-encryption-client-java-3.6.1.jaris replaced byamazon-s3-encryption-client-java-4.0.2.jar; no exported package changes.- Version 4.0 removes
S3EncryptionClient.builder(). The client is now created viabuilderV4()with the commitment policy pinned toFORBID_ENCRYPT_ALLOW_DECRYPTand the algorithm suite toALG_AES_256_GCM_IV12_TAG16_NO_KDF.
The stored object format is unchanged: objects written before the upgrade stay readable, newly written objects stay readable by earlier App Suite versions, and no data migration is required. Without the pinned settings the 4.0 default of key commitment with REQUIRE_ENCRYPT_REQUIRE_DECRYPT would reject every previously written encrypted object.
SCR-1895
Summary: Upgraded the embedded SAAJ implementation from 2.0.1 to 3.0.6
Effective: 8.54.320 and later
Upgraded the embedded SAAJ implementation in software.amazon.awssdk from 2.0.1 to 3.0.6, the version that com.openexchange.soap.common already ships. Both bundles now embed the same version.
The jar is embedded because the S3 encryption client uses com.sun.xml.messaging.saaj.packaging.mime.internet.MimeUtility to decode content metadata; nothing else in the bundle touches SAAJ. That class has an identical public API in both versions and depends only on jakarta.activation, which the platform provides. No operator action is required.
SCR-1894
Summary: Upgraded the Kotlin OSGi bundle from 2.4.10 to 2.4.20
Effective: 8.54.320 and later
Upgraded the Kotlin OSGi bundle in the target platform from 2.4.10 to 2.4.20, replacing kotlin-osgi-bundle-2.4.10.jar with kotlin-osgi-bundle-2.4.20.jar.
The bundle keeps its symbolic name org.jetbrains.kotlin.osgi-bundle; the exported packages move from version 2.4.10 to 2.4.20 and gain kotlin.coroutines.debug. No package was removed. The two consuming bundles import the Kotlin packages without a version range, so no operator action is required.
SCR-1893
Summary: Upgraded embedded third-party libraries in 20 bundles
Effective: 8.54.320 and later
Upgraded embedded third-party libraries in 20 bundles. All are minor or patch releases within the same major version.
AWS SDK for Java 2.46.21 to 2.55.1 in
software.amazon.awssdkKubernetes Client 7.8.0 to 7.9.0 in
io.fabric8.kubernetesGuava 33.6.0-jre to 33.7.1-jre and Caffeine 3.2.4 to 3.3.0 in
com.google.guavaNimbus OAuth 2.0 SDK 11.37.2 to 11.38.2 in
com.nimbusSnakeYAML 2.6 to 2.7 in
org.yaml.snakeyamllibphonenumber 9.0.34 to 9.0.39 in
com.openexchange.smsLiquibase 5.0.3 to 5.0.4 in
liquibase.coreGeoIP2 5.1.0 to 5.2.0 and MaxMind DB 4.1.0 to 4.2.0 in
com.openexchange.geolocation.maxmind.binary; MaxMind DB 4.2.0 rejects crafted databases beyond new decoder limits, regular lookups stay far below themBox Java SDK 10.15.1 to 10.17.0 in
com.openexchange.file.storage.boxcom;commons-codec, now required by the SDK, is imported from the target platformJolokia 2.6.0 to 2.6.3 in
com.openexchange.jolokiaprometheus-metrics 1.7.0 to 1.9.0 in
com.openexchange.metrics.micrometer, now at one version across all embeddedprometheus-metrics-*jarsand further minor and patch updates to metadata-extractor, Dropbox SDK, Woodstox, Dropwizard Metrics, jTNEF, Xalan serializer, brownies-collections and jackson-datatype-joda
The jar file names listed in each bundle's Bundle-ClassPath and 3rdPartyLibs.properties change accordingly. No artifact was added to or removed from any embed set, and no operator action is required.
SCR-1891
Summary: Upgraded the embedded TwelveMonkeys, Apache CXF, Google API Client, Jetty and OkHttp libraries
Effective: 8.54.320 and later
Upgraded the third-party libraries embedded in five bundles.
- TwelveMonkeys ImageIO 3.13.1 to 3.15.2 in
com.openexchange.imagetransformation.java; 3.15.1 and 3.15.2 harden the IFF, PICT and PSD decoders against crafted files - Apache CXF 4.2.2 to 4.2.3 in
com.openexchange.soap.common - Google API Client in
com.google.api.client: google-http-client 2.1.1 to 2.2.0, google-api-client 2.9.0 to 2.9.1, google-auth-library-oauth2-http 1.47.0 to 1.52.0, api-common 2.65.0 to 2.68.0, and the Calendar, Drive and Gmail service APIs - Jetty 12.1.12 to 12.1.13 in
com.openexchange.http.jetty - OkHttp 5.4.0 to 5.5.0 in
com.squareup.okhttp3, which also updates the embeddedokio-jvmfrom 3.17.0 to 3.18.1
The jar file names listed in each bundle's Bundle-ClassPath and 3rdPartyLibs.properties change accordingly. The newer Google libraries add jspecify-1.0.0.jar as a further embedded artifact. No operator action is required.
SCR-1870
Summary: Upgraded Jackrabbit WebDAV from 2.21.19 to 2.22.4 in target platform
Effective: 8.54.320 and later
The Jackrabbit WebDAV client library (jackrabbit-webdav) is upgraded from 2.21.19 to 2.22.4 in the target platform. Our copy stays re-wrapped: the bundle's Import-Package range for javax.servlet and javax.servlet.http is widened from the upstream [3.1,4) to [4,5), because the platform exports javax.servlet 4.0.0, which the upstream range excludes. Exported packages and their versions are unchanged, and the library is used internally by the WebDAV file storage and DAV subscription bundles only. No admin action.
SCR-1869
Summary: Upgraded Logback from 1.5.38 to 1.6.3 in target platform
Effective: 8.54.320 and later
Logback (logback-classic, logback-core) is upgraded from 1.5.38 to 1.6.3 in the target platform. The only configuration element that disappears is the long deprecated ch.qos.logback.classic.turbo.ReconfigureOnChangeFilter. The configurations shipped with the middleware do not use it, but a logback.xml that still declares it as a <turboFilter> has to drop that element and rely on the scan attribute instead. No other Joran action, model or appender was removed. No admin action otherwise.
SCR-1867
Summary: Upgraded MySQL Connector/J from 9.7.0 to 26.7.0 in target platform
Effective: 8.54.320 and later
Upgraded the MySQL JDBC driver in the target platform (com.openexchange.bundles): mysql-connector-j 9.7.0 to 26.7.0.
The jump in the version number is a change of the upstream versioning scheme, not a rewrite: 26.7.0 is the release that directly follows 9.7.0, with no version in between. Bundle symbolic name (com.mysql.cj), the 27 exported packages and the JDBC driver class (com.mysql.cj.jdbc.Driver) are unchanged, and every consumer imports the driver packages without a version range, so the exported package versions moving from 9.7.0 to 26.7.0 affects nobody. No source change was required.
Compatibility with MariaDB — the only supported database — was verified against MariaDB 10.11.19: reading (folders, mail, calendar, contacts, capabilities, quota) and a full write cycle (insert, select, update, delete of a task) both work, with no SQL exception of any kind in the log.
SCR-1866
Summary: Upgraded Kotlin OSGi bundle and Equinox Configuration Admin in target platform, added the OSGi Coordinator API
Effective: 8.54.320 and later
Upgraded two libraries in the target platform (com.openexchange.bundles):
kotlin-osgi-bundle2.2.21 to 2.4.10org.eclipse.equinox.cm1.4.100 to 1.6.400
Equinox Configuration Admin 1.6.x adds a mandatory import of org.osgi.service.coordinator;version="[1.0.0,2.0.0)", which no bundle in the target platform provided. The OSGi Coordinator API bundle org.osgi.service.coordinator 1.0.2 is therefore added alongside it; without it the org.eclipse.equinox.cm bundle would not resolve.
The Kotlin upgrade moves the exported package versions from 2.2.21 to 2.4.10 for all 116 packages. That is safe here because every consumer -- com.squareup.okhttp3 and com.openexchange.jmap -- imports the Kotlin packages without a version range.
SCR-1865
Summary: Upgraded commons-collections4, commons-validator, javassist, jctools, joda-time, jakarta.validation-api, xmlunit, protobuf-java, HK2, Logback, SPI Fly and Apache mime4j in target platform
Effective: 8.54.320 and later
Routine upgrades of third-party libraries in the target platform (com.openexchange.bundles). No source change was required.
commons-collections44.5.0 to 4.6.0commons-validator1.10.1 to 1.11.0javassist3.32.0-GA to 3.33.0-GAjctools-core4.0.6 to 4.0.7joda-time2.14.2 to 2.14.4jakarta.validation-api3.1.0 to 3.1.1xmlunit-core2.10.0 to 2.14.0protobuf-java4.35.1 to 4.36.2hk2-api,hk2-locator,hk2-utilsandaopalliance-repackaged4.0.1 to 4.0.2glassfish-corba-omgapi5.0.0 to 5.0.2logback-classicandlogback-core1.5.37 to 1.5.38; both keep importingorg.slf4j;version="[2.0,3)", so SLF4J stays at 2.0.18org.apache.aries.spifly.dynamic.bundle1.3.7 to 1.3.8; it now requires ASM 9.10 and OSGi Core R8, both provided by the target platformapache-mime4j-core,apache-mime4j-domandapache-mime4j-storage0.8.14 to 0.8.15
As before, glassfish-corba-omgapi is re-wrapped so that the specification packages javax.rmi and javax.rmi.CORBA are exported at the specification version 1.0.0 rather than at the bundle version. Without that, jdo-api cannot satisfy its javax.rmi;version="[1.0,2)" import and the javax.jdo bundle stops resolving.
Apache mime4j 0.8.15 adds default parsing limits: at most 512 MIME parts, a nesting depth of 64, 16384 header fields and 1 MiB of header data per message. Mail import (mail?action=import) parses strictly by default, so a message beyond these limits is now rejected with MSG-0101; with strictParsing=false it is still imported.
SCR-1864
Summary: Upgraded BouncyCastle, Jackson, PDFBox, jsoup, HttpClient5, FreeMarker, Commons Codec and snappy-java in target platform
Effective: 8.54.320 and later
Upgraded third-party libraries in the target platform (com.openexchange.bundles):
bcmail-jdk18on,bcpg-jdk18on,bcpkix-jdk18onandbcutil-jdk18onupgraded from 1.84 to 1.86,bcprov-jdk18onfrom 1.84 to 1.86jackson-core,jackson-databind, thejackson-dataformat-*,jackson-datatype-*,jackson-jakarta-rs-*andjackson-module-*jars upgraded from 2.22.0 to 2.22.2 (jackson-annotationsstays at 2.22, no patch release exists)pdfbox,pdfbox-io,fontboxandxmpboxupgraded from 3.0.7 to 3.0.8jsoupupgraded from 1.22.2 to 1.23.2httpclient5upgraded from 5.6.2 to 5.6.4;httpcore5stays at 5.4.3, the version HttpClient 5.6.4 is built againstfreemarkerupgraded from 2.3.34 to 2.3.35commons-codecupgraded from 1.22.0 to 1.22.1snappy-javaupgraded from 1.1.10.7 to 1.1.10.8
BouncyCastle 1.85 removed the sample-code packages org.bouncycastle.crypto.examples, org.bouncycastle.mail.smime.examples and org.bouncycastle.openpgp.examples, and removed org.bouncycastle.iana; org.bouncycastle.asn1.iana moved from bcutil to bcprov. The stale (unused) imports of org.bouncycastle.crypto.examples and org.bouncycastle.iana were removed from the com.openexchange.saml bundle manifest. FreeMarker 2.3.35 dropped freemarker.debug and freemarker.debug.impl, which no bundle imports. No source change was required.
BouncyCastle 1.86 fixes 13 CVEs, among them OpenPGP subkey certification and SEIPDv1 truncation, CMS AuthenticatedData and the NameConstraints check on end-entity certificates in PKIXCertPathReviewer. It introduces default limits: S/MIME nesting depth 64 (org.bouncycastle.mime.max_depth), PBKDF2 iteration count 10,000,000 (org.bouncycastle.pbe.max_iteration_count) and 262,144 nodes when building certificate paths (org.bouncycastle.x509.max_cert_path_build_nodes). It also removes legacy post-quantum packages from bcprov such as org.bouncycastle.pqc.crypto.mlkem and org.bouncycastle.pqc.jcajce.provider.kyber; no bundle imports them.
SCR-1863
Summary: Updated Netty libraries from v4.2.16 to v4.2.18 in bundle io.netty and Lettuce from v7.6.0 to v7.7.0 in bundle io.lettuce
Effective: 8.54.320 and later
Upgraded Netty from v4.2.16.Final to v4.2.18.Final and netty-tcnative-classes from v2.0.80.Final to v2.0.84.Final in bundle io.netty, and Lettuce from v7.6.0.RELEASE to v7.7.0.RELEASE together with reactor-core from v3.8.6 to v3.8.7 in bundle io.lettuce. The Netty update fixes CVE-2026-59903, a cache poisoning and information disclosure issue in netty-codec-http, as well as several further security advisories affecting netty-codec-http and netty-codec-http2 (resource exhaustion, header validation, request smuggling). Bundle io.lettuce now also exports the new packages io.lettuce.core.probabilistic and io.lettuce.core.probabilistic.arguments; no packages were removed. No admin action.
API - HTTP-API
SCR-1995
Summary: New Mobile API for native mail apps at /mobile/v1
Effective: 8.54.320 and later
The middleware serves a resource-oriented HTTP API for native mail apps at /mobile/v1, described by its own OpenAPI document. This release brings the foundation every operation builds on; the mail operations follow.
- Every request carries a bearer token. A deployment signs in either with personal access tokens (
oxa_…) or with the operator's identity provider, never with both; a token of the other kind is answered with401. GET /mobile/v1/auth/configneeds no token and tells a client which of the two modes applies and what it needs for it.- Errors are
application/problem+json(RFC 9457) with a stable {
} clients switch on, the middleware's own error code as {{oxCode}} and the request's tracking identifier as {{instance}}.
* Every {{GET}} also answers {{HEAD}}. An unknown path is {{404}}, a known path with another method {{405}} with {{Allow}}.
* {{If-None-Match}} answers {{304}}. The write operations that follow will answer {{428}} without {{If-Match}} and {{412}} with the current representation when it is stale.
The API answers only where {{com.openexchange.mobile.api.enabled}} is set, and it needs the OAuth provider to validate tokens. Existing endpoints are unaffected.
SCR-1993
Summary: New header Idempotency-Key and parameter clientMessageId to send a mail at most once
Effective: 8.54.320 and later
The requests POST /mail?action=new, POST /mail/compose/{id}/send and PUT /mail?action=transport accept the header Idempotency-Key: a retry with the same key is not performed again, but answered with the kept result of the first request and the response header Idempotent-Replayed: true. A client can also pick a UUID per mail and pass it as clientMessageId when opening the composition space (POST /mail/compose), when sending, or as field of mail?action=new. The Message-ID header is built from it, and a later send of the same mail returns the first result with the response field duplicate set to true. New error codes IDEM-0001 to IDEM-0007; the REST API answers them with status 400, 409 or 422. Purely additive; requests without the header or the parameter behave as before.
SCR-1991
Summary: New login actions accessToken and accessTokenSecondFactor to sign in for a personal access token
Effective: 8.54.320 and later
POST <dispatcher prefix>login?action=accessToken lets a client without a browser sign in with user name and password (JSON body with login, password and the optional label, scopes and expires) and answers like accesstoken?action=new: with the token's metadata and, once, its secret. The sign-in runs the regular login with a transient session; no cookie is set and no session remains. App passwords and guest credentials are refused. A user with a second factor gets the error ACCESSTOKEN-0012 with a challenge (challengeId, expiresAt, factors) as data and confirms it with POST <dispatcher prefix>login?action=accessTokenSecondFactor (challengeId, provider, deviceId, secret_code) without sending the password again; only authenticator apps, backup strings and SMS are offered. Attempts are limited per login name, client address and user (LGI-0028, ACCESSTOKEN-0017). Both actions are off unless com.openexchange.accesstoken.signin.enabled is set; other bundles get the same sign-in through the OSGi service AccessTokenSignInService. Purely additive.
SCR-1977
Summary: New parameter pushToken for changing mail requests
Effective: 8.54.320 and later
Changes to mail and mail folders made through the middleware are now published as push notifications with the new topics ox:mail:changed, ox:mail:deleted and ox:mail:folder. Existing subscriptions to * or ox:mail:* receive them, too. To keep a client from being notified about its own changes, the following requests accept the new optional query parameter pushToken, the client's push token; the subscription with this token is skipped.
PUT /mail?action=updatePUT /mail?action=flagsPUT /mail?action=color_labelPUT /mail?action=copyPUT /mail?action=copy_multiplePUT /mail?action=movePUT /mail?action=move_allPUT /mail?action=deletePUT /mail?action=expungePUT /mail?action=clearPOST /mail?action=newandPUT /mail?action=newPUT /mail?action=autosave
The folders requests new, update, delete and clear already accept pushToken; it now applies to mail folder notifications as well. Requests without the parameter behave as before.
See the HTTP API documentation for further details.
SCR-1974
Summary: New capabilities access_tokens and mcp
Effective: 8.54.320 and later
Two new capabilities tell a client what to offer before it calls anything.
access_tokens is awarded to a user who may mint personal access tokens: the user is neither a guest nor anonymous, and the OAuth provider is enabled for the user. These are the checks the accesstoken module applies before minting, so a settings page shown only with this capability never offers what the module would refuse.
mcp is awarded to a user who can use the MCP endpoint: the endpoint is registered on the node, the user is neither a guest nor anonymous, and the OAuth provider is enabled for the user. It is independent of access_tokens, since personal access tokens serve more than the MCP endpoint and an MCP client may also bring a token from an external authorization server.
No admin action. access_tokens follows com.openexchange.oauth.provider.enabled; mcp also follows com.openexchange.mcp.enabled and whether the open-xchange-mcp package is installed.
SCR-1946
Summary: New HTTP API module accesstoken for personal access tokens
Effective: 8.54.320 and later
The new module accesstoken lets a user mint bearer tokens for clients that cannot run an OAuth flow, such as a script or an MCP client with a fixed Authorization header: new (parameters label, scopes, expires) returns the token metadata and, once, the secret oxa_<context-id>_<random>; all lists the user's tokens together with the scopes the user may grant (grantable_scopes, each as scope and description); delete revokes one. The actions require a full session of a regular user: a session made from a token cannot mint tokens, and guest users cannot mint tokens at all (ACCESSTOKEN-0004). The module is registered only while the OAuth provider is enabled, and minting is refused where the provider is disabled for the user (ACCESSTOKEN-0005). The bundles com.openexchange.accesstoken (API), com.openexchange.accesstoken.impl (storage, validator plug-in, clean-up, password-change handler, mail guard) and com.openexchange.accesstoken.json (the module) ship in the package open-xchange-core, since the tokens are a bearer credential for every OAuth-protected endpoint.
A token is bound to its user and limited to the scopes chosen at minting. The grantable scopes are the ones the installed modules register with the OAuth provider (OAuthScopeProvider, e.g. read_contacts, write_contacts, read_mail, read_calendar), offered only where the user's capabilities admit the module. Two limits apply, both configurable (com.openexchange.accesstoken.maxLifetimeDays, default 365, and com.openexchange.accesstoken.maxTokensPerUser, default 50). The token is accepted as bearer token by every endpoint that validates tokens through the OAuth provider; the HTTP API modules with restricted actions take it in place of the session parameter. A request outside the token's scopes is 403 with insufficient_scope; an unknown or revoked token is 401 with invalid_token, an expired one 401 with a description naming the expiry; when the token cannot be checked because the database is unavailable, the answer is 503 with temporarily_unavailable and the client keeps its token.
For mail access the token stores no credential of its own: minting asks the mail credential vault (feature access-token) to keep what the session provides, sealed with the token's secret, and the listing reports whether it did as mail_access. Under master authentication (com.openexchange.mail.passwordSource=global) the vault records that nothing is needed, so mail_access is true; a session without password or OAuth tokens mints a token with mail_access false, and such a token never reaches the mail server. A password change the user makes through App Suite revokes every token of the user; a password set through provisioning does not, since that path posts no password-change event. A token minted without mail credential is refused at the mail server with ACCESSTOKEN-0008 instead of presenting its secret there. Expired tokens are dropped by a clean-up job a week after their expiry.
SCR-1936
Summary: Soft-Deleted Attendees Marked with a Read-Only Extended Parameter in the Calendar HTTP API
Effective: 8.54.320 and later
The calendar HTTP API now marks a soft-deleted user (pending deletion) that appears as an attendee of an existing appointment, so a client can tell a leaver apart from an ordinary external participant.
When another participant loads an event a soft-deleted user is invited to, the user is resolved to an external attendee: the display name and address are kept, but the internal entity reference is dropped (the appointment data itself is not modified). Such an attendee now carries the read-only extended parameter X-OX-SOFT-DELETED in its extendedParameters object, whose value is the time of the soft-deletion in milliseconds since the epoch (UTC).
The marker is applied on read only: it is never written to the storage, and it is left out of exported iCal data, so it does not reach CalDAV clients. It is surfaced through the existing extendedParameters field, so no new attribute is introduced in the schema.
The organizer of an event keeps its internal reference and is therefore not marked - this preserves the ability to hand an appointment to another user through the change-organizer operation; the organizer's soft-deleted state stays resolvable through the user's soft_deleted attribute (SCR-1933).
See the Soft-deleted users documentation, as well as the HTTP API documentation for further details.
SCR-1933
Summary: Soft-deleted user state visible in the HTTP API
Effective: 8.54.320 and later
The user module renders the new attribute soft_deleted (column 628), the time a user has been soft-deleted in milliseconds since the epoch (UTC, not shifted by the user's time zone); absent for every other user. Soft-deleted users stay absent from users?action=all and users?action=search, a users?action=get with the identifier answers with the attribute set. A login of a soft-deleted user is refused with AUTHORIZATION-0004 ("The account is disabled and pending deletion") instead of AUTHORIZATION-0001. Lets a client tell a leaver apart from a disabled account.
SCR-1887
Summary: New Attribute classifiedAccess for Deputy Permissions to See Confidential Appointments
Effective: 8.54.320 and later
A granting user can now allow a deputy to see appointments marked as confidential in the shared calendar folders with all details, independently of the deputy attending them. The deputy permission gained the optional attribute classifiedAccess next to sendOnBehalfOf, accepted by PUT /ajax/deputy?action=new and PUT /ajax/deputy?action=update and reported back by action=get, action=all and action=reverse. Like sendOnBehalfOf it is an attribute of the whole deputy permission, but it exclusively concerns the calendar:
none(default) - classified appointments are anonymized (confidential) or hidden (private), as for any other user the calendar is shared withconfidential- appointments marked as confidential are shown with all details
Appointments marked as private always stay hidden from deputies.
Example request:
{
"userId": 4,
"folderMode": "default",
"classifiedAccess": "confidential",
"modulePermissions": {
"calendar": {
"permission": 257
}
}
}
The right applies to the calendar folders covered by the deputy permission. Together with the granted folder permission, the deputy may also edit and delete confidential appointments, classify appointments as confidential or back as public, and create confidential appointments in those folders; attachments as well as the details of confidential appointments in free/busy results and conflict checks follow the same rule, and the appointments keep their private / confidential event flags. On update, the attribute is only changed when present in the request, so the right is revoked by sending none explicitly. A level that cannot be granted is rejected with DEPUTY-0014, an unknown value with SVL-0010. The attribute is omitted in responses when not granted, so existing grants serialize as before.
Purely additive - existing requests and grants are unaffected. Known limitation: granting or revoking the right modifies no appointment, so clients synchronizing the shared calendar incrementally (action=updates, CalDAV, EAS) only pick up the changed visibility with their next full synchronization.
See the feature documentation and the HTTP API documentation for further details.
SCR-1878
Summary: New vacation rule fields "vacationMode", "subjectExt" and "textExt" to treat internal and external senders differently
Effective: 8.54.320 and later
The vacation action command of the mail filter v2 HTTP API (mailfilter/v2?action=new and action=update) gains fields that let the vacation notice answer internal and external senders differently:
vacationMode— the explicit mode:all(every sender receives the same notice — the classic rule),split(external senders receive the separate external text/subject) orinternalOnly(only internal senders receive a notice)textExt/subjectExt— the text and subject for external senders in modesplit;textExtis required by that mode, and withoutsubjectExtexternal senders get the auto-generated subject of RFC 5230, never the internal one
{"actioncmds":[{"id":"vacation","days":"7","vacationMode":"split","subject":"Out of office","text":"Back in 10 days.","subjectExt":"Out of office","textExt":"Your message will be forwarded.","from":["anton@example.com"]}]}
The client contract on vacationMode: clients supporting the new fields MUST send it on every write of a vacation action — including when the feature is not announced. all is always accepted, and an existing extended rule stays editable with its modes: while the feature is unavailable, clients echo the mode they read and offer the downgrade to all only as an explicit user choice with a warning — availability can also flip temporarily on a configuration mistake, and a routine edit must not degrade the rule. A vacation action WITHOUT vacationMode is treated as written by a client unaware of the fields: an existing internal/external structure of the rule is preserved across the otherwise complete replace instead of being silently stripped. The contract only holds if "always" means always — a client that stops sending the field once vacationInternalAvailable turns false leaves extended rules of its users in place instead of sunsetting them. On read, every vacation rule reports its vacationMode (all on classic notices too, pre-existing rules included) — sole exception are multi-branch rules the middleware does not recognize as extended vacation rules: they report no mode and carry an errormsg instead, see below; textExt/subjectExt are present exactly when set. Invalid combinations and values are rejected with MAIL_FILTER-0044: split without textExt, all/internalOnly combined with the external fields, an unknown mode value, external fields without vacationMode, and a subjectExt containing line breaks. A rule carrying more than one vacation action is rejected with the new MAIL_FILTER-0045.
Whether a sender is internal or external is decided by the classifier header the mail platform stamps on every delivered message (see the accompanying Configuration SCR). The feature is an operator opt-in; its availability is announced in the mail filter config action as vacationInternalAvailable, and requests introducing the extended modes while it is unavailable are rejected with MAIL_FILTER-0043. Updates of multi-branch rules the middleware does not recognize as extended vacation rules are rejected on both interfaces: the v2 interface refuses with MAIL_FILTER-0042 when the rule carries a vacation action and MAIL_FILTER-0041 otherwise (the legacy v1 interface makes a similar split, keyed on the rule's vacation flag), and the v2 list action announces the refusal up front by reporting such rules with an errormsg in the matching wording (the rule and its leading branch stay reported), so clients can render them read-only instead of failing on save. The v1 interface additionally rejects every extended vacation rule with MAIL_FILTER-0042, pointing the user to an up-to-date client (they stay fully editable through v2). See the accompanying Java-API SCR for the underlying parser change.
The pre-existing subject field now rejects line breaks the same way the new subjectExt does (MAIL_FILTER-0044): a line break used to be written verbatim into the Subject: header of the generated reply — saves that used to pass with a multi-line subject now fail.
Known limitation: Exchange ActiveSync devices read and set the out-of-office state through the legacy v1 interface (USM), whose vacation model cannot represent the extended two-branch structure — for a split/internalOnly notice such a device shows OOF as switched off, and setting OOF from the device fails. Editing the notice through the v2 interface is unaffected.
Existing clients are otherwise unaffected: without the new fields the produced Sieve rule is unchanged, and the additions to the JSON returned on read are the vacationMode field (all) on every vacation rule and the errormsg on unrecognized multi-branch rules. See the feature documentation for further details.
SCR-1875
Summary: New optional parameter applyDefaultAlarms for the iCal import request
Effective: 8.54.320 and later
The iCalendar import request import?action=ICAL accepts the new optional parameter applyDefaultAlarms. When set to true, alarms contained in the imported iCalendar data are skipped for appointments, and the calendar user's configured default alarms (defaultAlarmDate and defaultAlarmDateTime) are applied instead. It has no effect on tasks.
The appointment imported by the following request ends up with the user's default alarms, although the iCalendar data carries no VALARM component:
POST /ajax/import?action=ICAL&folder=cal%3A%2F%2F0%2F31&applyDefaultAlarms=true
Content-Type: multipart/form-data; boundary=--boundary
--boundary
Content-Disposition: form-data; name="file"; filename="appointment.ics"
Content-Type: text/calendar
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Example Corp//Booking//EN
BEGIN:VEVENT
UID:5a1c0f6e-3d21-4a77-9d1f-1e0b7c2a9f44
DTSTAMP:20260901T100000Z
DTSTART:20270301T100000Z
DTEND:20270301T110000Z
SUMMARY:Flight to Berlin
END:VEVENT
END:VCALENDAR
--boundary--
This addresses appointments that do not originate from the user's own calendar, e.g. one added from a mail attachment: such data usually carries either no alarm at all or an alarm chosen by whoever created the file, so the user ends up without the reminders they configured. Since the server cannot tell where the data came from, the client decides. The change is purely additive - without the parameter, alarms are imported as before, which keeps export/import round trips within the calendar module intact.
See the import request documentation for further details.
SCR-1874
Summary: Moved the user field guest_created_by from column 616 to column 627
Effective: 8.54.320 and later
The user module exposed the field guest_created_by as column 616, the very same identifier the contacts module uses for yomiFirstName. The user field has been moved to the previously unused column 627, so column 616 now denotes yomiFirstName in every module.
This is not backward compatible. Clients that request column 616 from user?action=all, user?action=list or user?action=search no longer receive guest_created_by but the contact's yomiFirstName; they have to request column 627 instead. The JSON field name guest_created_by is unchanged, hence user?action=get is not affected.
See the detailed user data for further details.
SCR-1860
Summary: New setting stating the trash folders of the OpenCloud/Nextcloud accounts of a user
Effective: 8.54.320 and later
The trash folder of every OpenCloud or Nextcloud account of the user is stated by the setting modules/files/opencloud/folder/trash or modules/files/nextcloud/folder/trash of the config tree, so io.ox/files//opencloud/folder/trash or io.ox/files//nextcloud/folder/trash as a JSLob path. For OpenCloud that folder is a virtual one holding the trash bins of the spaces, so it cannot be derived from the identifier of an account by a client. For Nextcloud it is the user’s trash bin the instance states.
The value is an object holding one fully qualified folder identifier per account, by the identifier of that account. It is empty for a user without an OpenCloud or Nextcloud account, and the setting is not available at all for a user without the infostore module.
{"1":"opencloud://1/L3RyYXNo"}
{"2":"nextcloud://2/L3JlbW90ZS5waHAvZGF2L3RyYXNoYmluL2FudG9uQGNvbnRleHQxLm94LnRlc3QvdHJhc2gv"}
SCR-1858
Summary: New actions and columns for the shares of items within external file storages
Effective: 8.54.320 and later
The shares an item has within an external file storage that supports being shared that way are stated and managed through the infostore module.
PUT /infostore?action=createSharecreates a share,GET /infostore?action=listShareslists them,PUT /infostore?action=updateShareupdates one, andPUT /infostore?action=removeShareremoves them. The parameteridis optional for all of them: stated, it denotes the file to share, omitted, the folder the parameterfolderstates is shared.- The column
7050of the detailed infoitem data and the column3250of the detailed folder data state the shares of an item, each of them as an object holding the properties of one share.
Purely additive — existing requests are unaffected.
See the API documentation for further details.
API - Java
SCR-1880
Summary: Changed behavior of Sieve script parsing: "elsif"/"else" control blocks become branches of a single rule instead of unsupported rules
Effective: 8.54.320 and later
The mail filter middleware now parses Sieve rules containing elsif/else control blocks into a single rule holding all branches; previously every such block was reported as a separate unsupported rule. The change applies to every parsed script — hand-written rules included — regardless of the feature toggle of the accompanying Configuration SCR.
Implementations of the com.openexchange.mailfilter.MailFilterInterceptor Java interface see these rules on every read and write and must not assume single-branch rules any more: a Rule may now carry several branches (IfCommand followed by ElsifCommand/ElseCommand entries), more than one vacation action, and an else branch without a test command. The new Rule.getBranches() method enumerates a rule's branches; Rule.hasMultipleBranches() names the multi-branch check. Both are documented on Rule and MailFilterInterceptor. Deployments without own interceptor implementations are unaffected.
Consequences for scripts containing such blocks:
their commands now participate in the Sieve capability validation and contribute to the regenerated
requireline on every write; a command requiring a capability the Sieve server does not announce makes the write fail (previously such blocks were preserved verbatim and ignored by the validation)a script write-back regenerates the blocks in canonical formatting instead of copying them verbatim — comments and custom formatting inside hand-written
elsif/elseblocks are lost on the next write through the middleware; note that saving any rule regenerates the whole script, so an edit of an unrelated rule already triggers thisrule listings shift: every such block used to occupy a list position of its own as an "unsupported" error rule; it now merges into its preceding rule, so affected scripts list fewer rules and following rules move up (the merged error rules were not editable anyway)
Script parsing additionally normalizes lone CR characters (classic-Mac line breaks) to CRLF, inside string literals such as vacation texts too; a script containing lone CRs — e.g. written through ManageSieve — is persisted in the normalized form on its next write through the middleware.
API - REST
SCR-1986
Summary: New administrative REST endpoints for inspecting the cross-context grant index
Effective: 8.54.320 and later
Two preliminary endpoints under HTTP basic authentication report which foreign contexts hold cross-context access on a resource, as recorded in the xctx_grants index:
GET /preliminary/crosscontext/v1/grants/{context}/{entity} - for one resource owner, e.g. a shared accountGET /preliminary/crosscontext/v1/grants/{context} - for every resource owner of a context that has one
{"context": 1337, "entity": 14, "grants": {"SHARED_ACCOUNT": [42, 58]}}
The grants are keyed by type rather than by groupware module, because a grant can be module-less. Both endpoints report the index verbatim: no backing store is consulted, there is no fallback when the index holds nothing, and the trust-zone authority is not applied - so an empty result means nothing is recorded, not that nothing exists. Purely additive.
See the REST API documentation for further details.
SCR-1939
Summary: SCIM: query parameter deleteMode on DELETE /Users/{id} for an immediate hard delete
Effective: 8.54.320 and later
The SCIM endpoint accepts the query parameter deleteMode on DELETE /scim/v2/contexts/{cid}/Users/{id} with the values deactivate, softDelete and delete, overriding the mode configured through com.openexchange.scim.deleteMode for that call. With delete the account is removed at once even in a context configured for softDelete, and an account that is soft-deleted already (and therefore hidden from SCIM) is removed as well, bypassing the retention period: the way to honor a request under GDPR Art. 17 without waiting. An unknown value is answered with 400 invalidValue. Without the parameter nothing changes. OpenAPI document and documentation (administration/scim.md, section "Delete mode") updated.
SCR-1925
Summary: Soft-deleting and restoring users over the provisioning API
Effective: 8.54.320 and later
UserService.DeleteUsers takes the enum field mode (HARD, the default, or SOFT), the new RestoreUsers (POST /prov/v1/contexts/{context_id}/users:restore) lifts the soft-deleted state, and ListUserData lists the soft-deleted users of a context with the filter soft_deleted=true. The User message carries the read-only soft_deleted (milliseconds since the epoch), absent for every other user. Soft-deleting the context administrator, a guest or an already soft-deleted user, and restoring a user that is not soft-deleted, yield 400.
DeleteUsers with mode: SOFT now honors dest_user, which it accepted and ignored before: it names the user the shared data is reassigned to once the retention period has passed. Absent leaves it to the context administrator, 0 or less drops the data. A destination that does not exist, is a guest, is soft-deleted itself or is one of the users being soft-deleted yields 400.
SCR-1918
Summary: Added the /SharedAccounts, /Deputies and /SecondaryAccounts types to the SCIM 2.0 service provider
Effective: 8.54.320 and later
The SCIM 2.0 service provider ([SCR-1901], [SCR-1907], [SCR-1914]) now covers the shared accounts of a context: mailboxes and calendars several users work in, provisioned through OXSharedAccountInterface. It also covers deputies, provisioned through OXDeputyPermissionsInterface, and the secondary mail accounts of the users, provisioned through OXSecondaryAccountInterface.
- New resource type
SharedAccountunder/scim/v2/contexts/{context_id}/SharedAccountswith the schemaurn:ietf:params:scim:schemas:extension:openxchange:2.0:SharedAccount: the profile attributes of a user (userName,displayName,name,emails,preferredLanguage,timezone, phone numbers, addresses, the enterprise extension) withoutactiveandgroups;password(write only, the mailbox password, random when omitted);aliases(the mailbox address is always part of it);permissions, one entry per user or group of the context withvalue,$ref,type(UserorGroup),mailandcalendaras permission levelnone,viewer,editor,authororadmin(absent for no access),grantedCapabilitiesanddeniedCapabilitiessuch assendAs.GETwithfilter(eqonuserName,displayName,emails.value,externalId, combinable withand),startIndexandcount;POST,GET,PUT,PATCH,DELETEwith the versioning of [SCR-1909].userNameanddisplayNameare unique within the context (409uniqueness). A listing leavespermissionsout, since they cost a provisioning call per account; a single read carries them. - A
PUTapplies what the document carries and keeps the rest: the provisioning API converts a shared account to a user on its way to the storage and skips unset fields, so its attributes cannot be cleared.permissionsomitted onPUTstay as they are; an empty list, or aPATCHremoving the attribute, removes every permission granted within the context. Permissions are set per distinct access in one call; a permission naming a user or group that does not exist, or an unknown capability, is refused with400invalidValue, and onPOSTthe just created account is removed again in that case. Permissions granted to users of other contexts are neither shown nor touched. DELETEremoves the account and its mailbox; a shared account cannot log in, so there is nothing to deactivate.externalIdis stored with resource type4and removed with the account; shared accounts stay invisible under/Users.- Discovery:
/ResourceTypeslists theSharedAccount,DeputyandSecondaryAccounttypes,/Schemastheir schemas. - The photo, the full set of phone and fax types and the OX-specific contact fields of the user extension ([SCR-1908]) apply to the shared account as well.
- New resource type
Deputyunder/scim/v2/contexts/{context_id}/Deputieswith the schemaurn:ietf:params:scim:schemas:extension:openxchange:2.0:Deputy:grantor(the user delegating, required and immutable; a different one is refused with400mutability),deputy(value,$ref,typeUserorGroup; immutable as well),sendOnBehalfOf,folderMode(default,all,specific),classifiedAccess(none,confidential,private) andmodules, one entry per module withpermissionas levelnone,viewer,editor,authororadminand, forspecific, thefolders. Addressed by the identifier the deputy service assigns; anexternalIdof the client's own system is stored and searchable.GETwithfilter(eqongrantor.value,deputy.value,deputy.type,externalId),POST,PUT,PATCH,DELETE; aPUTkeeps what it does not carry, and"externalId": nullremoves the external identifier. A grant made outside the simple permission mode reads ascustomand cannot be written back; granting a module to the same deputy twice is refused with409uniqueness. The modulemailneeds an administrative way into the grantor's mailbox - a token Dovecot accepts, the mail master account, or DoveAdm. No notification mail is sent. - New resource type
SecondaryAccountunder/scim/v2/contexts/{context_id}/SecondaryAccountswith the schemaurn:ietf:params:scim:schemas:extension:openxchange:2.0:SecondaryAccount: one mail account of one user, addressed as{user}-{account}, which stays the same when the address changes.user(required and immutable),primaryAddress(required, unique among the accounts of its user),name(the address when omitted),personal,replyTo,login(required),password(write only),mailandtransportwithserver,port,protocol,secure,startTlsand, fortransport, its ownloginandpassword; onPOSTasource(none,primary,localhost) for an end-point the document leaves out; the standardfolders, thespamHandlerand anexternalIdof the client's own system.GETwithfilter(eqonuser.value,primaryAddress,externalId),POST,PUT,PATCH,DELETE; aPUTkeeps what it does not carry, and"externalId": nullremoves the external identifier. A second account with the same address is refused with409uniqueness. Passwords are never returned.
Purely additive. /SharedAccounts requires com.openexchange.sharedaccount.enabled=true, as the provisioning API does.
SCR-1917
Summary: Completed the App Suite user extension of the SCIM 2.0 service provider: capabilities, clearing by null, driveUserFolderMode, filestoreId
Effective: 8.54.320 and later
The App Suite user extension urn:ietf:params:scim:schemas:extension:openxchange:2.0:User of the SCIM 2.0 service provider (SCR-1901) now covers the remaining per-user settings of the provisioning API.
- New multi-valued string attribute
capabilities: the per-user capability overrides aschangecapabilitiesstores them, a name grants a capability, a leading minus denies one. Rendered on single reads only, never in listings. A list sent withPUTorPATCHbecomes the stored set: names it adds are granted or denied, names it no longer carries are dropped so the configuration applies again; an omitted attribute keeps the stored overrides, an explicitnullor aPATCHremovedrops them all. A name the permission configuration forbids is refused with400invalidValue; onPOSTthe just created account is removed again in that case. Both a name and its denial in one list are refused. - A
nullalias list, or aPATCHremovingaliases, reduces the aliases to the mailbox and sender addresses. Anullfor, or aPATCHremoving,imapLogin,imapServer,smtpServer,defaultSenderAddress,maxQuota,passwordExpiredoraccessCombinationName, which the provisioning API cannot clear, is refused with400and a message naming the value to send instead; previously it was ignored. - The version of a single read (
meta.version,ETag) now covers the access combination and the capability overrides, so a conditionalGETwithIf-None-Matchnotices their change instead of answering304;If-MatchonPUT,PATCHandDELETEis checked against that version. The version of a user in a listing still covers only what the listing shows.accessCombinationNameandcapabilitiesare declaredreturned: defaultin/Schemas. - New attribute
driveUserFolderMode(default,normal,none, case-insensitive), thedriveUserFolderModeof the provisioning create: honored onPOSTonly, declaredimmutableandreturned: neversince the provisioning API does not store it; aPUTorPATCHcarrying a value is refused with400mutability. - New integer attribute
filestoreId, declaredimmutable: onPOSTthe user gets an own file storage there, as with the provisioning create; on reads it is rendered while the user has an own storage. APUTorPATCHmay echo the stored value, any other value, and anullwhile a storage is set, is refused with400mutability, since moving a user's files is a job of the provisioning API.
Provisioning error messages that wrap an internal exception are reduced to the message; a missing file store and a quota the provisioning API may not assign are answered with 400 instead of 500. Users with an own file storage gain filestoreId in their representation, and every single read carries the access combination in its version, so versions change once. Everything else is additive; /Schemas lists the new attributes.
SCR-1914
Summary: Added the /Resources type and group memberships on users to the SCIM 2.0 service provider
Effective: 8.54.320 and later
The SCIM 2.0 service provider (SCR-1901, SCR-1907) now covers the resources of a context and shows memberships from both sides.
- New resource type
Resourceunder/scim/v2/contexts/{context_id}/Resourceswith the schemaurn:ietf:params:scim:schemas:extension:openxchange:2.0:Resource:displayNameandemailare required,nameis derived from the display name unless sent (lower-cased, blanks turned into dashes, accented letters reduced, a counter appended where taken),description,available(trueby default) andpermissions, a list of{value, type, privilege} wherevalueis a user or group identifier,typeisUserorGroupandprivilegeone ofnone,ask_to_book,book_directly,delegate.GETwithfilter(eqonname,displayName,email,externalId, combinable withand),startIndexandcount;POST,GET,PUT,PATCH,DELETEwith the versioning of SCR-1909.nameandemailare unique within the context (409uniqueness), anamesent explicitly is checked againstCHECK_RES_UID_REGEXP, permissions followcom.openexchange.resource.simplePermissionMode. Permissions omitted onPUTstay as they are; an empty list restores the default.externalIdis stored with resource type3and removed with the resource. - Users carry the read-only
groupsattribute on single reads (value,$ref,displayof every group the user is a member of; declaredreturned: request), groups carrymembers[].displayon single reads. Listings leave both out, someta.versionagrees between a listing and a single read. - Discovery:
/ResourceTypeslists theResourcetype,/Schemasits schema.
Purely additive. Attributes declared returned: request are rendered only when the attributes parameter of a single read names them.
SCR-1910
Summary: Added JSON Web Tokens as bearer credential to the SCIM 2.0 service provider
Effective: 8.54.320 and later
The SCIM 2.0 service provider (SCR-1901) now accepts a JSON Web Token issued by the identity provider as Bearer credential next to provisioning tokens, once com.openexchange.scim.allowJwt is enabled for the context. A bearer value containing a dot is treated as JWT; provisioning tokens never contain one.
- The token is validated by the OAuth provider in mode
expect_jwt(signature against the configured JWK set, issuer, audience, expiry) and resolved to a context and a user through the provider's claim lookup (contextLookupClaim,userLookupClaim). It has to resolve to the context in the URL, its subject has to be the context administrator, and its scope has to carryscim(mapped throughcom.openexchange.oauth.provider.scope.[EXTERNAL_SCOPE]). Requests made with it act as the context administrator, like provisioning tokens. - A valid token for another user or without the scope is answered with
403; an invalid, expired or foreign token with401;503while the provider cannot reach its key source or no OAuth authorization service is available. Refusals are logged with the client (azp) and the reason, and never counted against the lock for failed basic authentications. /ServiceProviderConfiglists the schemeJSON Web Tokenonce it is enabled.
Purely additive; nothing changes while the property stays at its default.
SCR-1909
Summary: Added resource versions with If-Match and read-only checks to the SCIM 2.0 service provider
Effective: 8.54.320 and later
The SCIM 2.0 service provider (SCR-1901, SCR-1907) now versions its resources (RFC 7644, section 3.14) and refuses PATCH operations on read-only attributes.
Every user and group carries
meta.version, a weak entity tag derived from the attributes of its representation, and is returned with theETagheader on single reads,POST,PUTandPATCH;/ServiceProviderConfigadvertisesetag.supported=true. The version is the same on every node and does not depend on the request's host.PUT,PATCHandDELETE /Users/{id} and/Groups/{id} honorIf-Match(entity tags or*, weak comparison): a resource that changed since the client read it is answered with412and nothing is written.If-None-Matchon a single read yields304. Without these headers the behavior is unchanged, the last writer wins.A
PATCHoperation whose path targets a read-only attribute (id,meta,groups) is refused with400mutabilityinstead of being applied and silently dropped. Read-only attributes inside a path-lessvalue, as echoed back by some providers, are ignored the wayPUTignores them.
Purely additive for clients that do not send the headers.
SCR-1908
Summary: Added the App Suite user extension to the SCIM 2.0 service provider
Effective: 8.54.320 and later
Settings a directory does not model, but an operator wants to drive per user, are now available on /scim/v2/contexts/{context_id}/Users in the extension urn:ietf:params:scim:schemas:extension:openxchange:2.0:User, declared in /Schemas and listed for the User resource type. Unlike the core attributes, an extension attribute the document omits on PUT keeps its value; only what is sent changes.
accessCombinationName: the named module access combination fromModuleAccessDefinitions.properties, applied on create and changed through the provisioning API on update. An unknown name is refused with400. Declaredreturned: requestand returned on single reads only.aliases: the alias set. The mailbox address and the default sender address are always part of it, as the provisioning API insists on; when the mailbox address changes, the old one is dropped.defaultSenderAddress: added to the aliases where missing; a sender equal to the mailbox address follows it when that changes.maxQuotain megabytes (-1for unlimited),imapLogin,imapServer,smtpServerandpasswordExpired.- The OX-specific personal and company contact fields that have no home in the core or enterprise schema:
birthdayandanniversaryas ISO dates,maritalStatus,numberOfChildren,spouseName,profession,roomNumber,assistantName,note,info,categories,businessCategory,commercialRegister,taxId,salesVolume, and the 20 free-form fields as the positional arrayuserFields. Like the other extension attributes they are cleared by an explicitnull.
For the context administrator, mail servers and password state cannot be changed over SCIM (403), in addition to password, deactivation and deletion: pointing the administrator's mail account at another host would hand that password over. Purely additive.
SCR-1907
Summary: Added the /Groups endpoint to the SCIM 2.0 service provider
Effective: 8.54.320 and later
The SCIM 2.0 service provider (SCR-1901) now serves the groups of a context under /scim/v2/contexts/{context_id}/Groups, so an identity provider can provision group memberships next to the users. Authentication, media types, body limits and error model are those of the users endpoint.
GET /Groupswithfilter(eqondisplayName,externalIdand the App Suitename, combinable withand),startIndexandcount;GET /Groups/{id}. Members are rendered withvalue,$refandtype.POST /Groups:displayNameis required and unique within the context (409uniquenessotherwise). The identifier the provisioning API needs besides lives in the extensionurn:ietf:params:scim:schemas:extension:openxchange:2.0:Groupasname; when a client does not send it, it is derived from the display name (lower-cased, blanks turned into dashes, accented letters reduced to their base letter, a counter appended where the name is taken). A name sent explicitly is checked againstCHECK_GROUP_UID_REGEXP.PUT /Groups/{id} replaces display name and members; a document withoutmembersempties the group.PATCH /Groups/{id} appliesaddwith a list of members,removewithmembers[value eq "..."]or with the members named invalue, andreplaceofdisplayName; adding a member twice is harmless.DELETE /Groups/{id} removes the group.- Only regular users can be members; a member referring to a guest or an unknown user is refused with
400invalidValue. The context's standard group is readable but refuses changes and deletion with403. externalIdis stored inscim_external_idwith resource type2and searchable./ResourceTypeslists theGrouptype and/Schemasthe core group schema and the extension.
Purely additive. See the administration article "SCIM provisioning" for the mapping and the identity provider setup.
SCR-1901
Summary: Added a SCIM 2.0 service provider for provisioning the users of a context from an identity provider
Effective: 8.54.320 and later
In order to let an identity provider such as Entra ID, Okta, authentik or the Univention Nubus provisioning client drive the users of a context with the SCIM connector it already ships, the middleware now acts as a SCIM 2.0 service provider (RFC 7643, RFC 7644). The context is the SCIM tenant and sits in the base URL /scim/v2/contexts/{context_id}; the endpoint is served by the new package open-xchange-scim on the admin role and is meant to be routed to a public hostname for exactly the /scim/ prefix.
GET /ServiceProviderConfig,GET /ResourceTypesandGET /Schemas: discovery, declaring the supported subset of the core user schema and the enterprise extension.GET /Userswithfilter(eqonuserName,externalId,emails.valueanddisplayName, combinable withand),startIndexandcount(at most 200); other filters are refused withinvalidFilter.POST /Users,GET,PUT,PATCHandDELETE /Users/{id}:PATCHfollows RFC 7644 including paths such asemails[type eq "work"].valueand the string booleans Entra ID sends foractive; attribute names are matched case-insensitively and unknown ones are refused withinvalidPath.externalIdis stored and searchable.activemaps tomailenabled. A user created withoutpasswordgets a random, unusable one and logs in through single sign-on.DELETEdeactivates by default; see SCR-1902 forcom.openexchange.scim.deleteMode.- The mapped core attributes cover the full contact record:
photoscarried as a base64data:URI, andphoneNumbersfor every phone and fax slot the user has (work, work2, home, home2, mobile, mobile2, fax, fax_home, fax_other, pager, other, assistant, callback, car, company, ip, isdn, primary, radio, telex, ttytdd), alongside the name, e-mail, instant-messaging and postal-address attributes and the enterprise extension.
Callers authenticate with a Bearer provisioning token of scope SCIM bound to the context (SCR-1897), which acts as the context administrator, or with HTTP basic credentials checked like every provisioning operation. The endpoint never grants access without a credential, refuses basic credentials while administrator authentication is disabled in the deployment, and locks a login for fifteen minutes after ten failed basic attempts, answered with 429. Only regular accounts are visible; the context administrator cannot be deactivated, deleted or given a password over SCIM. Request bodies are limited to one megabyte. Responses use application/scim+json; application/json is accepted in Accept and Content-Type. Purely additive. See the administration article "SCIM provisioning" for the attribute mapping and the identity provider setup.
SCR-1897
Summary: Added provisioning tokens as bearer credentials for scoped provisioning interfaces
Effective: 8.54.320 and later
In order to let automated clients such as an identity provider or a provisioning script authenticate without an administrator's password, the provisioning API gains provisioning tokens: bearer secrets bound to one scope and to one context or, as cross-context tokens, to several contexts at once, managed by the new gRPC service ProvisioningTokenService, which the HTTP gateway exposes by default.
POST /prov/v1/contexts/{context_id}/tokenscreates a token fromlabel,scope(SCIMorPROVISIONING) and an optionalexpiresin milliseconds since the epoch (0or absent: the token does not expire). The response carries the metadata and, exactly once, the secret of the formox_<context-id>_<64 hex characters>.GET /prov/v1/contexts/{context_id}/tokenslists the tokens of a context without secrets, includinglastUsedand, incontextIds, the contexts a token opens.DELETE /prov/v1/contexts/{context_id}/tokens/{token_id} revokes a token.POST /prov/v1/tokenscreates a cross-context token fromcontextIds(at least two distinct contexts),label,scopeandexpires; its secret has the formox_x_<64 hex characters>.GET /prov/v1/tokenslists the cross-context tokens the caller stands above,DELETE /prov/v1/tokens/{token_id} revokes one.GET /prov/v1/contexts/{context_id}/cross-context-tokenslists the cross-context tokens that open a context, for the administrator of that context, each naming that context and no other;DELETE /prov/v1/contexts/{context_id}/cross-context-tokens/{token_id} ends that one context's exposure without touching the others.
The tokens of a context are managed like every other operation inside a context: by the context administrator, a reseller administrator owning the context or, where MASTER_ACCOUNT_OVERRIDE permits it, the master administrator. Cross-context tokens are managed only by the master administrator or a reseller administrator owning every one of the contexts; creating one additionally needs MASTER_ACCOUNT_OVERRIDE: with the override off no administrator reaches into a context, so none may issue a token that acts inside one, while listing and revoking work regardless, so what was issued before can still be seen and taken back. A cross-context token is issued above the contexts, so the context it opens has no part in it; its administrator can however see which cross-context tokens reach into the context and detach the context from one, which ends that exposure without revoking the token and is no lasting veto. They are stored in the configuration database, and a context that is deleted is dropped from them; a token left without a context is deleted with it. Only the SHA-256 hash of a secret is stored.
A token of scope SCIM opens the SCIM service provider of each context it is bound to. A token of scope PROVISIONING is presented on /prov as Authorization: Bearer <secret> and acts as an administrator of exactly the contexts it is bound to: any other context, every master operation such as creating a context, and the token services themselves are refused with 401. A cross-context token of that scope is what lets one credential grant shared account permissions across contexts.
The new bundles com.openexchange.provisioning.token and com.openexchange.provisioning.token.impl ship with open-xchange-core; the service com.openexchange.grpc.provisioning.ProvisioningTokenService is part of the gateway's default provisioningGateway.services list. Purely additive, existing operations are unaffected.
SCR-1861
Summary: New REST end-point POST /authentication/v1/resolve to resolve a user to its identifiers
Effective: 8.54.320 and later
In order to let an external authentication service look up a user before a session exists, the new end-point POST /authentication/v1/resolve resolves a user to its user identifier, context identifier and the database schema its context lives in. It is protected by Basic authentication like the other end-points of that bundle.
The request body denotes the user in exactly one of two shapes:
loginInfowithcontextNameanduserName- the caller has already mapped its identity provider's response onto the login names, which are looked up as-is.identitywithtypeandvalue- an opaque identifier the caller cannot map itself, left to a deployment-specific resolver.A body that sets both or neither is answered with
400, a user no resolver knows with404.The resolution itself is provided by implementations of the new OSGi service
com.openexchange.external.authentication.common.api.identity.UserResolver, exported by bundlecom.openexchange.external.authentication.common. They are consulted in the order of their service ranking; the built-in resolver has the lowest possible ranking and handles theloginInfoshape only, so a deployment can contribute its own resolver for opaque identifiers without displacing it.
API - RMI
SCR-1943
Summary: New RMI interface OXProvisioningTokenInterface
Effective: 8.54.320 and later
The new RMI interface com.openexchange.admin.rmi.OXProvisioningTokenInterface, bound as OXProvisioningToken, manages provisioning tokens, as the gRPC service ProvisioningTokenService does. For the tokens of a context, create(Context, String label, String scope, long expires, Credentials) returns the new token together with its secret, list(Context, Credentials) the tokens of the context without secrets, and revoke(Context, String tokenId, Credentials) whether a token was revoked. For cross-context tokens, which open several contexts at once, createCrossContext(Context[] ctxs, String label, String scope, long expires, Credentials) takes at least two distinct contexts and the credentials of the master administrator or a reseller administrator owning every one of the contexts, and only where MASTER_ACCOUNT_OVERRIDE lets an administrator reach into a context at all, listCrossContext(Credentials) returns the cross-context tokens the caller stands above, which needs no override for the master administrator, and revokeCrossContext(String tokenId, Credentials) whether one was revoked; a context administrator is refused on all three. The context reached into is not left without a say: listCrossContextReachingInto(Context, Credentials) returns the cross-context tokens that open the given context, each naming that context and no other, and detachCrossContext(Context, String tokenId, Credentials) ends that one context's exposure - the token keeps working for every other context it opens and is deleted only if this was the last one. Both are authenticated against the context reached into, so its own administrator may call them; neither is a revocation nor a lasting veto. Tokens travel as the serializable data object com.openexchange.admin.rmi.dataobjects.ProvisioningToken, whose secret is set only on the result of a create; getContextIds() names the contexts a token opens, and a cross-context token has getContextId() 0 and isCrossContext() true. The context's schema is brought up to date first, and invalid input such as an unknown scope, an expiration time in the past or a single context for a cross-context token is refused with InvalidDataException. The interface is registered by com.openexchange.admin and is not site-aware. Purely additive.
SCR-1923
Summary: Soft-deleting and restoring users over OXUserInterface
Effective: 8.54.320 and later
OXUserInterface offers softDelete(Context, User[], Credentials), restore(Context, User[], Credentials), both also for a single user, and listSoftDeleted(Context, Credentials). A soft-deleted user is a leaver: hidden from the address book, not invitable, unable to log in, deleted for good after the retention period, while the data is kept and the login name stays reserved (see the behavioral change). The context administrator, guests, shared accounts and users that are already soft-deleted are rejected with InvalidDataException, as is restoring a user that is not soft-deleted. The data object User carries the read-only softDeleted (java.util.Date), null for every other user; the listing returns identifier and softDeleted only.
softDelete is also offered as softDelete(Context, User[], Integer, Credentials) and for a single user, where the Integer names the user the shared data is reassigned to once the retention period has passed, with the same meaning as destUser on delete: null leaves it to the context administrator, 0 or less drops the data. The destination is checked when it is named - it must exist and may be neither a guest, nor the user being soft-deleted, nor a soft-deleted user itself (InvalidDataException) - and checked again before it is used. restore forgets it.
SCR-1889
Summary: New Field classifiedAccess in the Deputy Permission Data Objects of the Administrative RMI and gRPC Interfaces
Effective: 8.54.320 and later
The data objects com.openexchange.admin.rmi.dataobjects.DeputyPermission (and thereby ActiveDeputyPermission) and DeputyPermissionDescription used by OXDeputyPermissionsInterface gained the field classifiedAccess next to sendOnBehalfOf, with the accessors getClassifiedAccess() and setClassifiedAccess(String) (plus isClassifiedAccessSet() / removeClassifiedAccess() on the description). It carries the level of the granting user's classified appointments a deputy may see with all details in the covered calendar folders: none or confidential, null when not granted; appointments marked as private always stay hidden from deputies, and the attribute has no meaning for other modules. The gRPC provisioning interface exposes the same as field classified_access (number 7) of the DeputyPermission message and as classifiedAccess / ClassifiedAccessSet (numbers 13 and 14) of the DeputyPermissionDescription message in deputy.proto. A value other than none or confidential is rejected with an InvalidDataException; null still means none when granting, and an attribute not set on the description leaves the level unchanged when updating.
Purely additive - the serialVersionUIDs of the data objects are unchanged, so RMI clients built against the previous version keep working, and the proto fields are wire compatible. See the feature documentation for further details.
API - SOAP
SCR-1950
Summary: New SOAP service OXProvisioningTokenService
Effective: 8.54.320 and later
The new SOAP service OXProvisioningTokenService manages provisioning tokens, the bearer secrets an automated client such as an identity provider driving the SCIM service provider or a script driving the provisioning API authenticates with, and offers what the command line tools createprovisioningtoken, listprovisioningtokens and revokeprovisioningtoken do. create takes the context, a label of at most 128 characters, the scope (scim or provisioning) and an optional expires in milliseconds since the epoch (0 or absent: no expiry) and returns the token with its secret, the only time the secret is shown; list returns the tokens of the context with their lastUsed time but without secrets; revoke takes the tokenId and returns true if the token existed. For cross-context tokens, which open several contexts at once, createCrossContext takes the contexts as ctxs (at least two distinct ones) with label, scope and expires, listCrossContext returns the cross-context tokens the caller stands above, and revokeCrossContext takes the tokenId; creating needs the master administrator or a reseller administrator owning every one of the contexts, and only where MASTER_ACCOUNT_OVERRIDE lets an administrator reach into a context at all, listing and revoking the master administrator or, where MASTER_ACCOUNT_OVERRIDE permits it, a reseller administrator owning every one of the contexts. For the context reached into, listCrossContextReachingInto takes the context and returns the cross-context tokens that open it, each naming that context and no other, and detachCrossContext takes the context and a tokenId and ends that one context's exposure; both are authenticated against that context, so its own administrator may call them. The ProvisioningToken element carries the contexts a token opens in contextIds; a cross-context token has contextId 0. Authorization for a context follows every other operation inside a context: the context administrator, a reseller administrator owning the context or, where MASTER_ACCOUNT_OVERRIDE permits it, the master administrator. The service ships with open-xchange-admin-soap; its WSDL is at /webservices/OXProvisioningTokenService?wsdl. Example request and response, and a cross-context request:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap="http://soap.admin.openexchange.com" xmlns:xsd="http://dataobjects.soap.admin.openexchange.com/xsd" xmlns:xsd1="http://dataobjects.rmi.admin.openexchange.com/xsd">
<soapenv:Header/>
<soapenv:Body>
<soap:create>
<soap:ctx>
<xsd:id>1</xsd:id>
</soap:ctx>
<soap:label>Entra ID</soap:label>
<soap:scope>scim</soap:scope>
<soap:auth>
<xsd1:login>oxadmin</xsd1:login>
<xsd1:password>secret</xsd1:password>
</soap:auth>
</soap:create>
</soapenv:Body>
</soapenv:Envelope>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ns2:createResponse xmlns="http://dataobjects.soap.admin.openexchange.com/xsd" xmlns:ns2="http://soap.admin.openexchange.com">
<ns2:return>
<id>5b0e7c2a9d4f4e1b8c3a6d9f2e1b4c7a</id>
<contextId>1</contextId>
<contextIds>1</contextIds>
<label>Entra ID</label>
<scope>scim</scope>
<createdBy>oxadmin</createdBy>
<created>1789112149008</created>
<expires>0</expires>
<lastUsed>0</lastUsed>
<secret>ox_1_9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08</secret>
</ns2:return>
</ns2:createResponse>
</soap:Body>
</soap:Envelope>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap="http://soap.admin.openexchange.com" xmlns:xsd="http://dataobjects.soap.admin.openexchange.com/xsd" xmlns:xsd1="http://dataobjects.rmi.admin.openexchange.com/xsd">
<soapenv:Header/>
<soapenv:Body>
<soap:createCrossContext>
<soap:ctxs>
<xsd:id>1</xsd:id>
</soap:ctxs>
<soap:ctxs>
<xsd:id>2</xsd:id>
</soap:ctxs>
<soap:label>Shared account automation</soap:label>
<soap:scope>provisioning</soap:scope>
<soap:auth>
<xsd1:login>oxadminmaster</xsd1:login>
<xsd1:password>secret</xsd1:password>
</soap:auth>
</soap:createCrossContext>
</soapenv:Body>
</soapenv:Envelope>
SCR-1924
Summary: Soft-deleting and restoring users over the OXUserService SOAP interface
Effective: 8.54.320 and later
The OXUserService offers the operations softDelete, softDeleteMultiple, restore, restoreMultiple and listSoftDeleted, the SOAP counterparts of the new OXUserInterface methods. The User element carries the read-only soft_deleted (xs:dateTime), absent for a user that is not soft-deleted. Clients that generate their stubs from the WSDL regenerate them to use the operations; existing operations are unchanged.
softDelete and softDeleteMultiple take the optional element reassign, the user the shared data is reassigned to once the retention period has passed, with the same meaning as on delete.
SCR-1921
Summary: Administrative SOAP operations accept a plain contextId, and moveContextFilestore reports the correct fault
Effective: 8.54.320 and later
Several administrative SOAP operations now accept the context identifier directly, as an alternative to sending a whole ctx element. The new contextId element is optional and appended after the existing ones, so requests that do not use it are unaffected. When both are given, contextId wins.
Affected operations:
OXTaskMgmtService:flush,deleteJob,getJobList,getTaskResultsOXGroupService:listAll
A request may now be sent as:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:s="http://soap.admin.openexchange.com"
xmlns:r="http://dataobjects.rmi.admin.openexchange.com/xsd">
<soapenv:Body>
<s:listAll>
<s:auth>
<r:login>oxadmin</r:login>
<r:password>secret</r:password>
</s:auth>
<s:contextId>1</s:contextId>
</s:listAll>
</soapenv:Body>
</soapenv:Envelope>
Existing clients are unaffected on the wire: the element is minOccurs="0" and the previous element order is unchanged. Clients that compile against the generated Java stubs of getJobList, getTaskResults or listAll see one additional parameter after regenerating them.
Independently of that, OXContextService.moveContextFilestore reported a NoSuchReasonException fault when the destination filestore did not exist, although the operation declares NoSuchFilestoreException for exactly that case. It now reports the declared fault. The WSDL is unchanged; only which of the two declared faults is returned differs.
SCR-1888
Summary: New Element classifiedAccess in the Deputy Permissions of the OXDeputyPermissionsService
Effective: 8.54.320 and later
The DeputyPermission and ActiveDeputyPermission types of the OXDeputyPermissionsService SOAP interface gained the optional element classifiedAccess, next to sendOnBehalfOf. It lets the granting user allow the deputy to see appointments marked as confidential in the covered calendar folders with all details, independently of the deputy attending them; like sendOnBehalfOf it is an attribute of the whole deputy permission, but it exclusively concerns the calendar. Accepted values are none (default) and confidential (confidential appointments are shown with details); appointments marked as private always stay hidden from deputies. The element is honored by grant and update (where it is only applied when present, so none revokes the right), and returned by list and getDeputyPermission; it is absent when the right was not granted. A level that cannot be granted fails with DEPUTY-0014. A value other than none or confidential is rejected as invalid data; an absent value still means none when granting and leaves the level unchanged when updating.
Example grant request appointing user 7 as calendar deputy of user 4 with access to confidential appointments:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap="http://soap.admin.openexchange.com" xmlns:xsd="http://dataobjects.soap.admin.openexchange.com/xsd" xmlns:xsd1="http://dataobjects.rmi.admin.openexchange.com/xsd">
<soapenv:Header/>
<soapenv:Body>
<soap:grant>
<soap:deputyPermission>
<xsd:userId>7</xsd:userId>
<xsd:sendOnBehalfOf>false</xsd:sendOnBehalfOf>
<xsd:classifiedAccess>confidential</xsd:classifiedAccess>
<xsd:modulePermissions>
<xsd:moduleId>calendar</xsd:moduleId>
<xsd:folderPermission>2</xsd:folderPermission>
<xsd:readPermission>4</xsd:readPermission>
<xsd:writePermission>0</xsd:writePermission>
<xsd:deletePermission>0</xsd:deletePermission>
<xsd:admin>false</xsd:admin>
</xsd:modulePermissions>
</soap:deputyPermission>
<soap:context><xsd:id>1</xsd:id></soap:context>
<soap:user><xsd:id>4</xsd:id></soap:user>
<soap:auth><xsd1:login>oxadmin</xsd1:login><xsd1:password>secret</xsd1:password></soap:auth>
</soap:grant>
</soapenv:Body>
</soapenv:Envelope>
A list response carries the element next to sendOnBehalfOf the same way:
<ActiveDeputyPermission>
<userId>7</userId>
<sendOnBehalfOf>false</sendOnBehalfOf>
<classifiedAccess>confidential</classifiedAccess>
<modulePermissions>
<moduleId>calendar</moduleId>
<folderIds>cal://0/31</folderIds>
...
</modulePermissions>
<deputyId>...</deputyId>
<grantorId>4</grantorId>
</ActiveDeputyPermission>
Purely additive - a request without the element grants a permission exactly as before, and clients generated from the previous WSDL keep working. See the feature documentation for further details.
Behavioral Changes
SCR-2006
Summary: Reject Own-Object Rights in Cross-Context Folder Permissions
Effective: 8.54.320 and later
A cross-context grant on a calendar or contacts folder may no longer carry an own-objects level for reading, writing or deleting. Creating or updating a folder permission like that is refused with FLD-1039 (INVALID_PERMISSIONS).
A principal from another context never owns objects in the folder's context, so such a level granted nothing at object level. The grantee saw an empty folder, or could create appointments they could then neither change nor delete. All-objects levels, and own-objects levels for users of the folder's own context, are unaffected. The rule applies to cross-context deputy permissions as well.
Grants stored before this change remain in place, and a folder carrying one stays editable: only new or changed entries are checked. No operator action is required.
See the feature documentation for further details.
SCR-2001
Summary: Stricter anti-virus verdicts, more scanned downloads and a scan log
Effective: 8.54.320 and later
Anti-virus scanning fails closed in more cases: an ICAP answer carrying X-Infection-Found, X-Virus-ID or X-Violations-Found counts as infected, and an answer that is neither a finding nor an unmodified response, e.g. a redirect, counts as not scanned instead of clean. Whole mails (.eml, zip_messages, attach_src, get_structure) and zip downloads of PIM and appointment attachments are now scanned as well, and so are files downloaded via share links, which were not scanned before. An encrypted item downloaded via the drive module without decryption is logged as not scanned instead of being reported as clean. Users for whom scanning is enforced get the capability antivirus_enforced. Every scan outcome is logged by com.openexchange.antivirus.scan (findings and refused items at WARN) and counted in appsuite.antivirus.scans.outcome. No admin action; route that logger to your monitoring to review findings.
SCR-1985
Summary: Changed behavior of listing the permission holders of a shared account
Effective: 8.54.320 and later
listSharedAccountPermissionsForSharedAccount (SOAP and RMI, and the listsharedaccountpermissionsbysharedaccount command-line tool) now reports the permissions held by users and groups of every context, not only those that happen to reside in the same database schema as the shared account.
A cross-context permission is stored in the context it was granted to, so querying the account's own schema was always a partial answer whose completeness depended on how contexts were distributed across schemas. The contexts to read are now taken from the xctx_grants index.
No client change is required. On a system upgraded from an earlier version the index is backfilled by a daily job that skips schemas with a pending update task, so until those updates have run the previous, co-located-only result may still be returned.
SCR-1984
Summary: A Nextcloud account whose credentials the instance rejects states an error
Effective: 8.54.320 and later
An account whose credentials a Nextcloud instance rejects is put into an error state rather than having every request of it fail against the instance anew. The rejection is stored with the account, its folders state it towards the client, and it is logged once rather than per request. Throughout the period com.openexchange.file.storage.nextcloud.retryAfterErrorInterval states, see https://jira.atlassian.open-xchange.com/browse/SCR-1971 , the instance is not asked again; afterwards, the next request asks it once more and the error is forgotten if the credentials are accepted. Reconfiguring the account, e.g. by storing a new password or by linking another OAuth account, drops the error immediately, so a corrected account is usable without waiting. Only rejected credentials are remembered this way, any other failure being one of a single request.
SCR-1983
Summary: The Nextcloud file storage supports OAuth, versions, trash, sharing and search
Effective: 8.54.320 and later
The file storage service nextcloud was limited to reading and writing the files of an instance. It supports the following from now on, as far as the instance states that it does:
An account may be linked to an OAuth account rather than storing a username and an app token, see https://jira.atlassian.open-xchange.com/browse/SCR-1981 .
The versions of a file are listed, read, restored and removed.
A deleted item is listed within the trash bin of the account, restored to the location it was deleted from, and purged. It is restored by the instance, so it keeps its versions, and the location it was deleted from is fallen back to the folder a request states if it no longer exists.
An item is shared through a link, and the shares granted to the users and the groups of the instance are stated as the permissions of that item and applied from the ones a client states, see SCR-1971. The shared items are listed within virtual folders, see https://jira.atlassian.open-xchange.com/browse/SCR-1982
.
A search is performed by the instance, which evaluates the name and the media type of an item, while a criterion it does not evaluate is applied by the middleware to the items it states.
An item states the link that opens it within the web interface of the instance, and a preview of it is served as its thumbnail, which the instance serves under an endpoint of its own.
See the feature documentation for further details.
SCR-1973
Summary: Custom trust store is now honoured alongside the JVM's default trust store
Effective: 8.54.320 and later
A certificate that only the custom trust store knew was rejected whenever the JVM's default trust store was enabled as well, which is the default. Both stores were served by one trust manager each, and SSLContext only ever uses the first trust manager it is given, so the custom one was never asked. Setting com.openexchange.net.ssl.custom.truststore.enabled to true therefore had no effect unless com.openexchange.net.ssl.default.truststore.enabled was set to false, which in turn dropped every publicly known certificate authority.
Both trust stores are now served by a single trust manager that accepts a certificate as soon as either of them validates it. A certificate issued by an internal certificate authority is trusted while the public ones keep working.
This affects every outbound TLS connection of the middleware that runs with com.openexchange.net.ssl.trustlevel set to restricted; with the default all no certificate is validated in the first place and nothing changes. No configuration change is required. Deployments that worked around the defect by disabling the default trust store can enable it again.
SCR-1972
Summary: An over-sized MCP request body is refused with a JSON-RPC error instead of an empty 413
Effective: 8.54.320 and later
A request body above the MCP endpoint's limit of 1 MiB is answered with a JSON-RPC error object carrying -32700 and a message naming the limit, instead of a bare 413 with no content. A client that speaks only the protocol now learns why it was refused.
The answer is the same whichever way the size becomes known. A body whose Content-Length already exceeds the limit is refused before anything is read; one that never announced its length is refused while it is read. Until now only the second case could answer at all, and it did so differently.
Behind it, the body is parsed straight from a bounded reader rather than collected into a string first, so a request no longer holds its own body twice in memory while it is parsed. No admin action, no configuration.
SCR-1970
Summary: New folder type for the virtual folders of a file storage
Effective: 8.54.320 and later
A file storage may state folders that have no counterpart within it, but list the items of other folders under a certain aspect, such as the ones a user shares. Those folders are meant to be navigated to by a client, while a traversal of the folder tree is supposed to pass them over.
FileStorageFolderType states a value VIRTUAL_FOLDER for them now, which a storage sets on such a folder, and the ZIP archive of a folder passes them over along with the trash folder of the storage. The value is added to an existing enumeration. No storage stated folders of that kind before, so the archives of the existing storages are unchanged except for the Nextcloud one, whose virtual folders are passed over from now on rather than being archived along with the folders their items are located in.
SCR-1965
Summary: Drive offers the OAuth scope read_files
Effective: 8.54.320 and later
The Drive module now offers the OAuth scope read_files, so a user may grant an application read access to their files alone. Its read actions have carried @RestrictedAction(module = "files", type = READ) all along, but no bundle registered the scope with the OAuth provider, so it appeared on no consent screen and could not be granted - the module was out of reach for every token and every OAuth client. It is registered now the way every other module registers its scopes, by com.openexchange.file.storage.json, and requires the capability infostore. Like every module scope it opens the module, not a single feature: a token carrying read_files reads Drive through the HTTP API as well as through the read-only tools of the MCP endpoint (SCR-1945), which is what makes those tools usable in the first place. There is no write_files: writing to Drive stays outside what a scope can grant. No admin action.
SCR-1964
Summary: Cross-Context Shared Account Permissions Are Subject to the Cross-Context Trust Zones
Effective: 8.54.320 and later
A shared account permission for users or groups of another context is now subject to the cross-context trust zones: the shared account and the target context have to share at least one tag of com.openexchange.crosscontext.trustZones, otherwise the grant is refused with SAC-0008, reported as 403 over the HTTP provisioning gateway. There is no separate switch.
A caller that holds authority over the target context anyway is exempt. The policy is skipped whenever the credential in effect for that context - the one supplied for the entry if there is one, the credential of the call otherwise - authenticates against it. That covers an administrator credential valid in both contexts, as well as a scoped provisioning token that covers the target context.
The policy is evaluated when access is newly granted, and only then. Existing permissions keep working whatever the zones say afterwards, revoking is never restricted, updating an existing grant is not affected, and re-sending an unchanged permission set grants nothing new and is therefore not refused.
Nothing that worked before is refused. Granting across contexts previously required credentials for both of them, so every existing caller is covered by the exemption, and the zones only govern the single-credential grant that SCR-1963 newly permits. A deployment without an installed cross-context authority provider is unaffected.
See the general documentation for further details.
SCR-1963
Summary: Shared Account Permissions Across Contexts Require Only the Shared Account's Context Administrator
Effective: 8.54.320 and later
Provisioning a shared account permission for users or groups of another context no longer requires administrator credentials for both contexts. A grant is authenticated against the context of the granting entity, a revocation is accepted from either side - the rule deputy permissions already follow.
createSharedAccountPermissions: the shared account's context.deleteSharedAccountPermissions: the shared account's context or the entities' context, whichever accepts the credentials.setSharedAccountPermissions,getSharedAccountPermissions: the shared account's context. The per-contextauthsections stay accepted and are still verified when supplied, but are no longer required.
Each call accepts strictly more than before, so no client has to change. The operations also become usable over interfaces that carry a single credential, in particular the HTTP provisioning gateway and SCIM.
See the general documentation for further details.
SCR-1961
Summary: Permanent push keeps its credential in the mail credential vault, persisted regardless of com.openexchange.push.credstorage.rdb
Effective: 8.54.320 and later
Permanent push keeps the credential it needs to run without a session in the mail credential vault (table mail_credential, encrypted with com.openexchange.sessiond.encryptionKey), the way snoozed mail, scheduled mail and the GDPR mail export keep theirs. It does so wherever com.openexchange.push.credstorage.enabled is set to true - persisted in the database, whatever com.openexchange.push.credstorage.rdb says.
Before, com.openexchange.push.credstorage.rdb decided whether the credential was kept in cluster memory (the default) or persisted to the database. It no longer decides that. It only decides where rows of the former storage still live until they are gone: with true, in the credentials table, from where a clean-up job moves them into the vault and deletes them; with false, in cluster memory, from where they are never migrated - they go with the next login of the user, which captures the credential into the vault.
A deployment that enabled the credential storage in order to keep credentials out of the database has to be aware of this: from this version on, the only way to keep them out is to leave com.openexchange.push.credstorage.enabled switched off, at the price of no permanent push for users without a session.
Along with it, deputy permissions managed without DoveAdm report failures of the mailbox access with the codes of the administrative mailbox service (MBADM-0003, MBADM-0004) instead of the former IMAP_DEPUTY- codes for DoveAdm failures, which are gone.
SCR-1945
Summary: New MCP server endpoint for read-only access to mail, calendar, contacts, files and tasks
Effective: 8.54.320 and later
The middleware offers a Model Context Protocol server at /mcp (protocol revision 2026-07-28 and, over the same endpoint, the initialize handshake of revisions 2025-03-26, 2025-06-18 and 2025-11-25, which clients such as claude.ai connectors speak: no session identifier is issued, ping is answered, an unknown method is a JSON-RPC error on 200, and the mirroring headers are optional there, stateless HTTP POST) with read-only tools, prompts and resources. The tools are me_get, mail_search, mail_get, mail_attachment_get, calendar_list_events, calendar_get_event, calendar_free_busy, resources_search, contacts_search, contact_get, users_search, files_search, file_get, tasks_search, task_get, vacation_get, reminders_list and a folder tool per module (mail_folders, calendar_folders, contacts_folders, files_folders, tasks_folders); every list result can be paged through an opaque cursor argument and the nextCursor it returns. The prompts daily_briefing, inbox_triage, find_meeting_slot and catch_up_on chain those tools and are offered only where every tool they drive is available to the caller. Resources are read by URI through the templates ox:///files/{id}, ox:///mail/{folder}/{id} and ox:///mail/{folder}/{id}/attachment/{attachment}, as text or, for anything without a text form, as the first 5 MiB of its bytes; resources/list enumerates nothing. Callers authenticate with a bearer token that the OAuth provider validates, either a JWT from the configured authorization server or a personal access token; tools and resources are visible and usable only with the scopes read_mail, read_calendar, read_contacts, read_files, read_tasks and read_reminders, while me_get is open to every valid token. /.well-known/oauth-protected-resource/mcp serves the RFC 9728 resource metadata. Tool calls and resource reads are rate-limited per user, bounded in how many of one user run at once on a node, and written as one audit line each at level INFO through the logger com.openexchange.mcp.protocol.McpRequestHandler, without argument values or content, and measured through the meters appsuite.mcp.requests, appsuite.mcp.tool.calls and appsuite.mcp.authentications; the core-mw Grafana dashboard gained an MCP row. A bundle that still waits for a needed service, typically the OAuth provider, logs a warning naming it a minute after start. An OpenAPI document describing both paths, the transport headers, every method, the tools, prompts and resources is published as mcp.openapi.json under components/middleware/mcp/<version>/ next to the SCIM and provisioning documents. The endpoint is off by default and needs the chart feature mcp, com.openexchange.mcp.enabled=true and an enabled OAuth provider (com.openexchange.oauth.provider.enabled). The mail tools reach the primary mail account only; secondary and external accounts, which keep their own credentials, are outside a token session's reach. Documentation: documentation/administration/mcp_server.md.
SCR-1938
Summary: Alerting for a failed retention purge of a soft-deleted user
Effective: 8.54.320 and later
The retention job com.openexchange.admin.softdelete.SoftDeleteRetentionExecution counts every purge attempt in the Micrometer counter appsuite.provisioning.softdelete.purges with the tag result=success|failure (Prometheus: appsuite_provisioning_softdelete_purges_total), so an alert can be raised on failures. A failed purge is recorded at the user as the attribute purgeFailed in the namespace softDelete, holding the time and the error, readable over every provisioning API (userAttributes over RMI, user_attributes over gRPC) until the purge succeeds or the user is restored; the restore drops the attribute. The failure is written to the audit trail com.openexchange.provisioning.userLifecycleAuditTrail as well. Before this change a failed purge was only logged at ERROR and retried silently with the next run. Documentation: administration/user_soft_delete.md, administration/cleanup_jobs.md.
SCR-1937
Summary: Audit trail of the user lifecycle: soft-delete, restore, delete and failed purge
Effective: 8.54.320 and later
The provisioning layer writes one line per user and event to the logger com.openexchange.provisioning.userLifecycleAuditTrail at INFO: the soft-deletion, the restore, the deletion for good (by an administrator or by the retention job, then naming the time the user had been soft-deleted), and a failed retention purge. Each line names the acting administrator and, where the request came in over SCIM, the origin the SCIM endpoint records (client, scheme, context). Lines look like:
soft-deleted user 7 in context 1 by oxadmin via scim basic oxadmin in context 1
deleted user 7 in context 1 by soft-delete-retention, soft-deleted since 2026-09-09T11:00:00Z
failed to purge user 7 in context 1 by soft-delete-retention, soft-deleted since 2026-09-09T11:00:00Z: <error>
The logger is meant to be routed to an appender of its own (append-only where the trail is to be kept) with a pattern carrying %lmdc, so the line keeps the tracking identifier and the client address; the appender's timestamp is the time of the event. Without a dedicated appender the lines go to the ordinary log. Documentation: administration/user_soft_delete.md, section "Audit trail".
SCR-1935
Summary: Shares of a soft-deleted user are frozen for every other user
Effective: 8.54.320 and later
While a user is soft-deleted, everything the user has shared is withheld from every other user, without exception: the shared calendars, address books and task folders, the personal files and the files shared one by one, and the shared mail folders (a deputy's access included) are hidden from every other user and cannot be opened, no matter what the stored permissions say. The stored permissions are not touched; the freeze is applied when the effective permission is calculated (folder storage, legacy folder permission, infostore object permissions, the "Shared files" listing, the mail folder tree and the opening of a shared IMAP folder). A restore brings the shares back as they were.
Public folders the user created are no shares and stay accessible. The owner keeps access to the own data, so the data export and the final deletion are unaffected. Before this change, internal shares of a soft-deleted user stayed accessible to the colleagues.
A deputy of a soft-deleted user can neither read the mailbox nor send mail in that user's name: the "send on behalf of" check answers as if the privilege were withheld (MSG-0129), and the grants of a soft-deleted user are absent from the deputy's reverse listings (deputy?action=reverse, reverseIds) until the restore. The stored deputy permissions are not touched.
Documentation: administration/user_soft_delete.md.
SCR-1931
Summary: The SCIM endpoint names its origin in the provisioning log entries and counts its authentications
Effective: 8.54.320 and later
Every SCIM request acts as the context administrator, so the log entries a change produced were indistinguishable from a command line call, and the requests of an identity provider could not be told apart from each other. The endpoint now names itself and the credential it acted with.
- For the duration of a request the log property
com.openexchange.provisioning.originnames the endpoint, the authentication scheme and the credential, for examplescim token 7 as oxadmin in context 1; a provisioning token appears by its identifier, a JSON Web Token by its client, basic credentials by their login. The entries of that request carry it, the ones the provisioning services write included. The machine-readable records of extended logging are the exception: they drop the log properties by design. - One entry per request goes to the logger
com.openexchange.scim.auditTrail, atINFOfor the methods that change something and atDEBUGfor reads, with method, path, status, duration, caller and, for a create, the location of the new resource. It is a trail of its own, meant to be routed to an appender withadditivity="false"; give that appender a pattern with%lmdc, or client address and tracking identifier are lost. - New counter
appsuite.scim.authenticationswith the tagsscheme(token,basic,jwt,none) andresult(accepted,missing,invalid,disabled,forbidden,throttled,unavailable). It tells apart what the status of a request cannot: a wrong password, a token for another context and a scheme switched off by configuration are all answered with401. Duration, path and status of the requests themselves were already recorded by the REST layer asappsuite.restapi.requests, so no timer was added. - A login locked after repeated failed basic authentications is logged with
WARNonce, when the lock snaps; before, the lock was silent.
Purely additive: no interface changes, no new configuration, and nothing is logged that was not logged before, apart from the new trail and the lock entry.
SCR-1927
Summary: Soft-deleted user state for leavers
Effective: 8.54.320 and later
A user can be soft-deleted, a state between disabled and deleted for the leaver case: the user cannot log in and existing sessions end, is hidden from the global address book, auto-completion, the user listing and group member lists (the memberships are kept), cannot be invited to appointments and answers no free/busy, and the share links and guests the user invited no longer resolve. Mail, files, calendar and contacts are kept and the login name, mail address and aliases stay reserved. mailenabled is left untouched, so a user that was disabled before stays disabled after a restore; the reports count soft-deleted users as disabled. After com.openexchange.user.softDelete.retentionDays (default 30) the clean-up job com.openexchange.admin.softdelete.SoftDeleteRetentionExecution deletes the user for good, hourly on one node of the cluster. The state is set and lifted through the provisioning APIs (RMI, SOAP, gRPC/HTTP) and through SCIM with com.openexchange.scim.deleteMode=softDelete; soft-deleted users are not exposed over SCIM. See administration/user_soft_delete.md.
The deletion for good hands the shared data to the user named when the user was soft-deleted, remembered as the user attribute softDelete/reassignTo; without one it goes to the context administrator as before. The destination is checked again before it is used: one that has been deleted or soft-deleted itself in the meantime falls back to the context administrator rather than failing the purge. Restoring a user forgets the destination; there is no separate call to change it, soft-delete the user again.
SCR-1919
Summary: Removing the calendar access of a shared account permission is persisted when the permission has no mail access
Effective: 8.54.320 and later
OXSharedAccountInterface.setSharedAccountPermissions (RMI, SOAP and gRPC alike) decides per stored permission whether an update has to be written. That decision compared the new calendar configuration against the stored mail configuration instead of the stored calendar configuration. For a permission without mail access, dropping its calendar access therefore compared null with null, counted as unchanged and was never written: the entity kept its calendar access, and the call returned successfully.
The comparison now uses the stored calendar configuration. Permissions with mail access were not affected, since their mail and calendar account identifiers never match. No interface changes.
SCR-1915
Summary: Resource permissions in the provisioning API: returned by getData, validated in simple mode, compared by value
Effective: 8.54.320 and later
Three defects in the handling of resource permissions by the provisioning API (RMI, SOAP and gRPC alike) are fixed; they surfaced while attaching resources to the SCIM service provider.
OXResourceInterface.getDatafor a single resource, and the array form and the gRPCListResourcesby identifiers built on it, returned resources without their permissions; the storage applied them to the input object instead of the returned copy. They are now returned, aslistandlistAllalways did.- Creating or changing a resource with permissions failed with an internal error whenever
com.openexchange.resource.simplePermissionModewas not set explicitly in a properties file: the flag was read without its documented default. It is now read through the lean configuration service, so the defaulttrueapplies. ResourcePermissioncompared by identity. The simple-mode validation therefore refused the two configurations it documents,book_directlyfor group0alone andask_to_bookfor group0together with delegates, and did not recognize the delete marker that resets the permissions. Permissions now compare by entity, kind and privilege, the privilege case-insensitively; the delete marker is recognized by identity only. With the validation working, simple mode now also refuses any list without delegates that is not exactly the default, which it was meant to.
Clients that worked around the failures by not sending permissions are unaffected; clients that sent permissions in simple mode see the documented validation for the first time.
SCR-1886
Summary: Readable 403 page for OpenID Connect logins declined because the user or context is disabled
Effective: 8.54.320 and later
When an OpenID Connect login is declined because the user or the user's context is disabled, the middleware now answers with a readable 403 page telling the user that the account is not available, instead of a bare error page that named the internal user and context identifiers. The identifiers are logged at INFO level instead. The page deliberately offers no link back to the sign-in page: where the sign-in page starts the OpenID Connect flow on its own, such a link only leads back to the identity provider, which authenticates the disabled account again and returns the user to the same page.
The decision is routed through the backend's com.openexchange.oidc.OIDCExceptionHandler, which gains the methods handleContextDisabled(request, response, contextId) and handleUserDisabled(request, response, userId, contextId), mirroring the SAML ExceptionHandler. Both have default implementations rendering the page above, so existing OpenID Connect backends inherit the behavior without changes; a backend can override them to render its own page. The JSON response path is unchanged and keeps answering with LGI-0006.
SCR-1885
Summary: Cache invalidations cross remote Redis sites
Effective: 8.54.320 and later
With remote Redis sites enabled (com.openexchange.redis.sites.enabled), cache invalidations are now repeated on the remote sites' Redis storages, and the messages that invalidate node-local caches of contexts, users, resellers and cache events are published there as well. Two middleware clusters attached to one config database therefore no longer serve stale contexts, users or database assignments after a provisioning change on the other cluster until the cache entries expire. Remote sites are best-effort: an unreachable site is logged, counted in appsuite.redis.remote.failures.total and skipped with a back-off; it never fails the provisioning call. Deployments that only share the databases should set com.openexchange.redis.sites.scope to invalidation to keep sessions on their site. No admin action for deployments without remote sites.
SCR-1883
Summary: Changed creation of the "Collected addresses" folder to the first collected contact instead of login
Effective: 8.54.320 and later
In order to work without a login hook, the contact collector no longer creates a user's "Collected addresses" folder through a login handler at every login. The folder is created the first time the collector has a contact to store, i.e. with the first collected email address that is not yet known as a contact; collector runs that only increment use counts of known contacts create nothing.
- Until then the user has no "Collected addresses" folder, and
modules/mail/contactCollectFolder(io.ox/mail//contactCollectFolderin the JSlob) reports no folder. - With the default configuration the switches
com.openexchange.user.contactCollectOnMailAccessandcom.openexchange.user.contactCollectOnMailTransportare off, so many users only get the folder through an appointment or task with an external participant or through a share notification, and some never do. - Deleting the folder works as before; it is recreated with the next collected address instead of at the next login.
- Existing folders and settings remain valid, no database change or update task is involved, and rolling upgrades are safe. No operator action is required.
Clients that identify the folder through the setting read at login should recognize it through the folder's meta marker __ccf# instead. See the feature documentation for further details.
SCR-1882
Summary: Provisioning read operations now require credentials
Effective: 8.54.320 and later
Provisioning operations that previously answered without checking credentials now require them. This affects the gRPC services DBMigrationService (migration and lock status), LoginCounterService, ShareService (share listings), ConsistencyService and FileChecksumsService (file listings), ExternalAccountService (account listing), DataExportService (task listings) and the access combination lookup of UserService. Registry-wide operations take the master administrator, operations inside a context take that context's administrator; the consistency listings take the master administrator, matching the checkconsistency command-line tool. A caller that sends no or wrong credentials now receives 401 instead of a result. Scripts and monitoring that read these endpoints anonymously have to send credentials from now on. Cross-site forwarding of these read operations is not possible while the underlying RMI methods carry no credentials.
SCR-1881
Summary: Database password no longer part of provisioning responses (HTTP API, SOAP, listdatabase --csv)
Effective: 8.54.320 and later
Provisioning responses no longer carry the password of a registered database. This affects the HTTP API (GET /prov/v1/databases and the register, createSchema and schema-listing responses), the SOAP services (listDatabase, registerDatabase, createSchema, the schema-listing calls and the read and write database inside every returned context) and the command-line tool listdatabase, whose --csv output drops the password column. Registering or changing a database still sends the password, and getDbAccessInfoForSchema of the schema-move interface still returns the access data on purpose. Scripts that read the password from these responses or from the CSV column have to take it from the configdb or from the deployment's configuration instead.
SCR-1859
Summary: Folders shared within an external file storage are stated by the shares action of the folders module
Effective: 8.54.320 and later
The shares action of the folders module states the folders a user shares within an external file storage as well, rather than only the ones of the database. Previously only the storage registered for the requested content type was asked, so the folders of a file storage were not stated at all.
Only a storage that supports being shared that way is affected, so the listing is unchanged for a deployment without such a storage.
SCR-1856
Summary: New file storage service for OpenCloud
Effective: 8.54.320 and later
In order to integrate the files of an OpenCloud instance, the new file storage service opencloud is introduced. It accesses the files through WebDAV, and the spaces, shares and permissions of the instance through the libre graph API, authenticating either with a username and an app token, or through OAuth.
The service is available for the users the capability com.openexchange.capability.filestorage_opencloud is enabled for. An account states the personal space of the user, the project spaces below a virtual folder Spaces, the shared items below a virtual folder Shared, and the trash bins of the spaces below a virtual folder Trash. As OpenCloud limits the storage per space, the quota of a folder is the one of the space it belongs to. As it grants rights through roles, a share states the role that matches the requested permissions best, so a file that is shared for writing is shared with a role that allows deleting it as well, there being no role that allows the one without the other. A share that is meant to be read-only is never granted a role that allows to change the item. An item states the URL that opens it within the web interface of the instance, and a folder states whether it supports permissions, a quota and sorting.
See the feature documentation for further details.
SCR-1851
Summary: Bounded the init container's configdb and middleware readiness waits
Effective: 8.54.320 and later
In order to let an unreachable database fail visibly instead of hanging a pod indefinitely, the core-mw init container now bounds its readiness waits for the configdb and, during initial bootstrapping, for the middleware itself. Previously both the legacy bash and the Go init variant retried forever without sleeping, so a misconfigured MYSQL_HOST left the pod in Init:0/1 with no non-zero exit and no Kubernetes event, while the retry loop consumed a full CPU core. Once a timeout elapses, the init container names the unreachable target and exits non-zero. The reason is also written to the pod's termination log, so it stays readable in the pod status after the restart that follows.
While a wait is running, progress is reported every 30 seconds, for example Still waiting for the MySQL Server at db:3306 after 31s of 300s, so a slow start is visible instead of silent.
A configdb host name that does not resolve is reported after a separate, shorter grace period of 30 seconds, because an unresolvable name is a configuration error rather than a database that is slow to start. The grace period exists because on a fresh install the name legitimately stays unresolvable for a while, for instance while cluster DNS is still starting or a headless service has no endpoints yet. Temporary resolver failures do not count towards it.
Two new Helm chart values control the budgets, both in seconds:
initWait.dbTimeout, default300, passed to the init container asINIT_DB_WAIT_TIMEOUTinitWait.middlewareTimeout, default300, passed asINIT_MW_WAIT_TIMEOUT
The poll intervals and the DNS grace are not exposed as chart values; they are fixed at 2 seconds, 5 seconds for the middleware wait, and 30 seconds respectively. The Go init binary additionally honors INIT_DB_WAIT_INTERVAL, INIT_MW_WAIT_INTERVAL and INIT_DB_DNS_TIMEOUT if they are set by hand, where 0 disables the early exit on an unresolvable name; these accept a Go duration string such as 30s or 2m, and a plain number is read as seconds.
No operator action is required, the values are additive and defaulted. Deployments that legitimately need to wait longer than five minutes for their database have to raise initWait.dbTimeout; a sufficiently high value restores the previous, effectively unbounded behavior.
CLT
SCR-1980
Summary: Added command-line tool jfrrecord for on-demand flight recordings
Effective: 8.54.320 and later
The new command-line tool jfrrecord records a Java Flight Recorder profile on a running middleware node for a fixed duration and writes the file on the host running the tool. It needs no restart and no agent, leaves nothing on the middleware's host, and requires the master administrator's credentials.
-f/--fileand--overwrite: the target file-d/--duration: 60 seconds by default, at most 10 minutes--settings:defaultorprofile--method-timing: times the given methods (eventjdk.MethodTiming)--cpu-time: samples CPU time, Linux only
Each node runs at most one recording. It stops after its duration, is capped at 256 MB, and is discarded when the tool terminates or has not polled for 2 minutes. System properties, environment variables, JVM arguments and process command lines are not recorded.
Method timing requires org.osgi.framework.bootdelegation=jdk.jfr.tracing. The shipped config.ini template now sets it; deployments with their own config.ini have to add it.
Usage:
$ jfrrecord --help
usage: jfrrecord
-A,--adminuser <adminUser> Admin username
--cpu-time Additionally samples CPU time (event jdk.CPUTimeSample, experimental). Linux only
-d,--duration <duration> How long to record, in seconds ("90" or "90s") or minutes ("5m"). Default is 60
seconds, maximum is 10 minutes
-f,--file <file> The path name of the file to write the recording to; e.g. "/tmp/recording.jfr". The
file is written where this tool runs
-h,--help Prints this help text
-H,--host <jmxHost> The optional JMX host (default:localhost)
-l,--login <jmxLogin> The optional JMX login (if JMX authentication is enabled)
--method-timing <filters> Times invocations of the given methods (event jdk.MethodTiming). Semicolon-separated
fully qualified class names, each optionally followed by "::" and a method name; e.g.
"com.openexchange.drive.impl.DriveServiceImpl::syncFolders". At most 10 filters.
Requires "org.osgi.framework.bootdelegation=jdk.jfr.tracing" on the middleware
--overwrite Overwrite the file if it already exists
-p,--port <jmxPort> The optional JMX port (default:9999)
-P,--adminpass <adminPassword> Admin password
--responsetimeout <timeout> The optional response timeout in seconds when reading data from server (default: 0s;
infinite)
-s,--password <jmxPassword> The optional JMX password (if JMX authentication is enabled)
--settings <settings> The JFR configuration: "default" (default, low overhead) or "profile" (finer sampling,
more overhead)
The Open-Xchange flight recording tool
SCR-1966
Summary: New command line tool detachprovisioningtoken
Effective: 8.54.320 and later
The new command line tool detachprovisioningtoken ends one context's exposure to a cross-context provisioning token: the token stops opening the context given with -c, keeps working for every other context it opens, and is deleted only if this was the last one. A cross-context token is issued by an administrator standing above the contexts, so the context reached into has no part in it; listprovisioningtokens with --reaching-into shows which tokens open a context, and this tool ends such an access. It is not a revocation - revokeprovisioningtoken ends a token everywhere at once - and no lasting veto, since an administrator above the contexts may issue a token covering the context again. Detaching a context the token does not open is reported on the error stream and ends with exit code 0. It ships with open-xchange-admin next to the other administration tools and calls the RMI interface OXProvisioningTokenInterface, so it needs neither the HTTP gateway nor gRPC access; authorization follows every other operation inside a context: the context administrator, a reseller administrator owning the context or, where MASTER_ACCOUNT_OVERRIDE permits it, the master administrator.
Usage: detachprovisioningtoken
-h,--help Prints a help text
--environment Show info about commandline environment
--nonl Remove all newlines (\n) from output
--responsetimeout <responsetimeout> response timeout in seconds for reading response from the backend (default 0s; infinite)
-A,--adminuser <adminuser> ? Admin username
-P,--adminpass <adminpass> ? Admin password
-c,--contextid <contextid> * The context to detach
--token-id <id> * The identifier of the token, as listprovisioningtokens --reaching-into shows it
Entries marked with an asterisk (*) are mandatory.
Entries marked with an question mark (?) are mandatory depending on your
configuration.
Entries marked with a pipe (|) are mandatory for one another which means that
at least one of them must be set.
SCR-1942
Summary: New command line tool revokeprovisioningtoken
Effective: 8.54.320 and later
The new command line tool revokeprovisioningtoken revokes a provisioning token given by --token-id, the identifier listprovisioningtokens shows; its secret is refused from then on. Given a context with -c, a token bound to that context is revoked; without a context, a cross-context token, which only the master administrator or, where MASTER_ACCOUNT_OVERRIDE permits it, a reseller administrator owning every one of the contexts may revoke. A token that does not exist, or lies out of the caller's reach, is reported on the error stream, so a revocation that did not happen is not mistaken for one that did, and ends with exit code 0, like deletesecondaryaccount does for an unknown account. It ships with open-xchange-admin next to the other administration tools and calls the RMI interface OXProvisioningTokenInterface, so it needs neither the HTTP gateway nor gRPC access; authorization for a context follows every other operation inside a context: the context administrator, a reseller administrator owning the context or, where MASTER_ACCOUNT_OVERRIDE permits it, the master administrator.
Usage: revokeprovisioningtoken
-h,--help Prints a help text
--environment Show info about commandline environment
--nonl Remove all newlines (\n) from output
--responsetimeout <responsetimeout> response timeout in seconds for reading response from the backend (default 0s; infinite)
-A,--adminuser <adminuser> ? Admin username
-P,--adminpass <adminpass> ? Admin password
-c,--contextid <contextid> The id of the context
--token-id <id> * The identifier of the token, as listprovisioningtokens shows it
Entries marked with an asterisk (*) are mandatory.
Entries marked with an question mark (?) are mandatory depending on your
configuration.
Entries marked with a pipe (|) are mandatory for one another which means that
at least one of them must be set.
SCR-1941
Summary: New command line tool listprovisioningtokens
Effective: 8.54.320 and later
The new command line tool listprovisioningtokens lists provisioning tokens, expired ones included: identifier, the contexts a token opens, label, scope, creator, creation time, expiration time and last successful use, as ISO-8601 in UTC. Given a context with -c, the tokens bound to that context are listed; without a context, the cross-context tokens the caller stands above: all of them for the master administrator, which needs no MASTER_ACCOUNT_OVERRIDE, and for a reseller administrator those opening only contexts it owns, which needs it as every reseller access to a context does. Secrets are never shown.--csv gives CSV output, with empty fields where a token does not expire or was never used. Given a context together with --reaching-into, the listing runs the other way round: the cross-context tokens that open that context, which its administrator has no other way to see; each is shown naming that context and no other. It ships with open-xchange-admin next to the other administration tools and calls the RMI interface OXProvisioningTokenInterface, so it needs neither the HTTP gateway nor gRPC access; authorization for a context follows every other operation inside a context: the context administrator, a reseller administrator owning the context or, where MASTER_ACCOUNT_OVERRIDE permits it, the master administrator.
Usage: listprovisioningtokens
-h,--help Prints a help text
--environment Show info about commandline environment
--nonl Remove all newlines (\n) from output
--responsetimeout <responsetimeout> response timeout in seconds for reading response from the backend (default 0s; infinite)
-A,--adminuser <adminuser> ? Admin username
-P,--adminpass <adminpass> ? Admin password
-c,--contextid <contextid> The id of the context
--csv Format output to csv
Entries marked with an asterisk (*) are mandatory.
Entries marked with an question mark (?) are mandatory depending on your
configuration.
Entries marked with a pipe (|) are mandatory for one another which means that
at least one of them must be set.
SCR-1940
Summary: New command line tool createprovisioningtoken
Effective: 8.54.320 and later
The new command line tool createprovisioningtoken creates a provisioning token, the bearer secret an automated client such as an identity provider driving the SCIM service provider or a script driving the provisioning API authenticates with. Given a context with -c, the token is bound to that context; given --contexts with at least two context identifiers instead, a cross-context token is created that opens every one of them, which only the master administrator or a reseller administrator owning every one of the contexts, and only where MASTER_ACCOUNT_OVERRIDE lets an administrator reach into a context at all may do. --label says what the token is for, --scope names the interface it opens, scim or provisioning (defaults to scim for a context and is mandatory together with --contexts, so that moving a client from one context to several cannot silently change what its token opens), and --expires takes a date as yyyy-MM-dd (midnight UTC) or a date and time with offset such as 2027-01-01T12:00:00Z; without it the token does not expire. The tool prints the identifier and, this once, the secret of the form ox_<context-id>_<64 hex characters>, or ox_x_<64 hex characters> for a cross-context token. It ships with open-xchange-admin next to the other administration tools and calls the RMI interface OXProvisioningTokenInterface, so it needs neither the HTTP gateway nor gRPC access; authorization for a context follows every other operation inside a context: the context administrator, a reseller administrator owning the context or, where MASTER_ACCOUNT_OVERRIDE permits it, the master administrator.
Usage: createprovisioningtoken
-h,--help Prints a help text
--environment Show info about commandline environment
--nonl Remove all newlines (\n) from output
--responsetimeout <responsetimeout> response timeout in seconds for reading response from the backend (default 0s; infinite)
-A,--adminuser <adminuser> ? Admin username
-P,--adminpass <adminpass> ? Admin password
-c,--contextid <contextid> | The id of the context
--contexts <contexts> | The contexts a cross-context token opens, comma-separated, at least two; instead of a single context. Needs the master administrator or a reseller administrator owning every one of them
--label <label> * What the token is for, for example the identity provider using it; at most 128 characters
--scope <scope> The interface the token opens: scim or provisioning. Defaults to scim for a context; mandatory together with --contexts
--expires <date> When the token expires: yyyy-MM-dd (midnight UTC) or a date and time with offset such as 2027-01-01T12:00:00Z. Without, it does not expire
Entries marked with an asterisk (*) are mandatory.
Entries marked with an question mark (?) are mandatory depending on your
configuration.
Entries marked with a pipe (|) are mandatory for one another which means that
at least one of them must be set.
SCR-1930
Summary: New option --soft-deleted for listuser
Effective: 8.54.320 and later
listuser gains the option --soft-deleted that lists the soft-deleted users of the context only; the search pattern and the guest options do not apply to it. The ordinary listing keeps including soft-deleted users. The CSV output of the user listings (--csv) carries the new column SoftDeleted, the date of the soft-deletion, empty for every other user.
Usage: listuser
-h,--help Prints a help text
--environment Show info about commandline environment
--nonl Remove all newlines (\n) from output
--responsetimeout <responsetimeout> response timeout in seconds for reading response from the backend (default 0s; infinite)
-c,--contextid <contextid> * The id of the context
-A,--adminuser <adminuser> ? Admin username
-P,--adminpass <adminpass> ? Admin password
--csv Format output to csv
--includeguests Include guest users
--excludeusers Exclude users, only show guests
--soft-deleted List the soft-deleted users of the context only; the search pattern and the guest options do not apply.
-s,--searchpattern <searchpattern> The search pattern which is used for listing. This applies to name.
-i,--ignorecase Whether to perform look-up case-insensitive
--length <length> Limit result size
--offset <offset> Set offset for limited result size
Entries marked with an asterisk (*) are mandatory.
Entries marked with an question mark (?) are mandatory depending on your
configuration.
Entries marked with a pipe (|) are mandatory for one another which means that
at least one of them must be set.
SCR-1929
Summary: New command line tool restoreuser
Effective: 8.54.320 and later
The new command line tool restoreuser lifts the soft-deleted state set by softdeleteuser: the user appears in the address book and in group member lists again, can be invited again and, unless disabled through mailenabled, can log in again. A user that is not soft-deleted is rejected. The user is addressed by -i or -u in the context given by -c.
Usage: restoreuser
-h,--help Prints a help text
--environment Show info about commandline environment
--nonl Remove all newlines (\n) from output
--responsetimeout <responsetimeout> response timeout in seconds for reading response from the backend (default 0s; infinite)
-c,--contextid <contextid> * The id of the context
-A,--adminuser <adminuser> ? Admin username
-P,--adminpass <adminpass> ? Admin password
-i,--userid <userid> | Id of the user
-u,--username <username> | Username of the user
Entries marked with an asterisk (*) are mandatory.
Entries marked with an question mark (?) are mandatory depending on your
configuration.
Entries marked with a pipe (|) are mandatory for one another which means that
at least one of them must be set.
SCR-1928
Summary: New command line tool softdeleteuser
Effective: 8.54.320 and later
The new command line tool softdeleteuser marks a user as soft-deleted, the leaver state between disabled and deleted: the user cannot log in, is hidden from the address book and from group member lists, cannot be invited, and the share links and guests the user invited stop working, while mail, files, calendar and contacts are kept and the login name stays reserved. After com.openexchange.user.softDelete.retentionDays the user is deleted for good; until then restoreuser brings the user back. The context administrator, guests, shared accounts and users that are already soft-deleted are rejected. The user is addressed like with deleteuser, by -i or -u in the context given by -c.
Usage: softdeleteuser
-h,--help Prints a help text
--environment Show info about commandline environment
--nonl Remove all newlines (\n) from output
--responsetimeout <responsetimeout> response timeout in seconds for reading response from the backend (default 0s; infinite)
-c,--contextid <contextid> * The id of the context
-A,--adminuser <adminuser> ? Admin username
-P,--adminpass <adminpass> ? Admin password
-i,--userid <userid> | Id of the user
-u,--username <username> | Username of the user
-r,--reassign <reassign> The user id shared data will be assigned to once the retention period has passed. If omitted the context admin will be used instead.
--no-reassign If set all shared data will be deleted instead of being assigned once the retention period has passed.
Entries marked with an asterisk (*) are mandatory.
Entries marked with an question mark (?) are mandatory depending on your
configuration.
Entries marked with a pipe (|) are mandatory for one another which means that
at least one of them must be set.
The options -r/--reassign <id> and --no-reassign name the user the shared data is reassigned to once the retention period has passed, exactly as on deleteuser: --reassign hands it to that user, --no-reassign drops it, neither leaves it to the context administrator. The destination is checked when it is named and again before it is used.
Changed defaults
SCR-1998
Summary: More carrier threads for virtual threads by default
Effective: 8.54.320 and later
The middleware now starts with -Djdk.virtualThreadScheduler.parallelism=256, the number of carrier threads its virtual threads run on, unless a JAVA_OPTS_* value already sets it. A virtual thread that loads a class is pinned to its carrier: with no more carriers than CPU cores, requests loading the same class at once could pin them all while the holder of a class loader lock waited for a carrier, and the middleware stopped answering. Carrier threads are ordinary platform threads, and idle ones cost little. Set the option in JAVA_OPTS_OTHER or in ox-scriptconf.sh to choose another value.
Configuration
SCR-2000
Summary: New configuration options for anti-virus scanning
Effective: 8.54.320 and later
New options to enforce anti-virus scans and to secure and bound the connection to the ICAP service.
com.openexchange.antivirus.enforceScans every download of a user who has the anti-virus feature enabled, regardless of the client'sscanparameter. Items that cannot be scanned are refused, as is every download while the anti-virus service is unavailable. Defaultfalse. Reloadable, config-cascade aware. No dedicated properties file.com.openexchange.icap.client.connectTimeoutTime-out in milliseconds for establishing the connection to the ICAP server. A value less than or equal to0leaves it to the operating system. Default5000. Reloadable, not config-cascade aware. No dedicated properties file.com.openexchange.icap.client.requestTimeoutMaximum time in milliseconds a single ICAP request may take once connected, sending the item included. A value less than or equal to0imposes no limit. Default120000. Reloadable, not config-cascade aware. No dedicated properties file.com.openexchange.icap.client.tlsContacts the ICAP server via TLS. Certificate and host name are always verified, againstcom.openexchange.icap.client.tls.truststoreor the JDK's default trust store, independent ofcom.openexchange.net.ssl.trustlevel. Defaultfalse. Reloadable, not config-cascade aware. No dedicated properties file.com.openexchange.icap.client.tls.truststorePath of a PKCS#12 or JKS trust store for TLS connections to the ICAP server, e.g. holding a custom CA. If empty, the JDK's default trust store is used. Default empty. Reloadable, not config-cascade aware. No dedicated properties file.com.openexchange.icap.client.tls.truststorePasswordPassword of the trust store given bycom.openexchange.icap.client.tls.truststore. Default empty. Reloadable, not config-cascade aware. No dedicated properties file.
SCR-1996
Summary: New configuration options for the Mobile API
Effective: 8.54.320 and later
The following options control the Mobile API at /mobile/v1. All are reloadable and have no dedicated properties file.
com.openexchange.mobile.api.enabled
Whether the API answers. Default false. Config-cascade aware: the server-level value decides whether /mobile/v1 answers at all, a lower level whether a signed-in user may use it.
com.openexchange.mobile.auth.mode
How apps sign in: token for personal access tokens, idp for the operator's identity provider. Default token.
com.openexchange.mobile.auth.idp.issuer
Mode idp: the OpenID Connect issuer published by auth/config. Empty by default. Without it and com.openexchange.mobile.auth.idp.clientId, auth/config answers 500.
com.openexchange.mobile.auth.idp.clientId
Mode idp: the public client identifier of the app. Empty by default.
com.openexchange.mobile.auth.idp.scopes
Mode idp: the scopes, comma-separated, the app requests. Empty by default.
com.openexchange.mobile.auth.idp.audience
Mode idp: the resource indicator (RFC 8707) the app passes, if the identity provider needs one. Empty by default. It is published only; a token's audience is checked when com.openexchange.oauth.provider.jwt.audience is set.
com.openexchange.mobile.auth.idp.redirectUris.ios,com.openexchange.mobile.auth.idp.redirectUris.android
Mode idp: the redirect URIs of the two apps. Empty by default.
SCR-1994
Summary: New configuration options for sending a mail at most once
Effective: 8.54.320 and later
The following options control the Idempotency-Key header and the client message identifier of mail send requests.
com.openexchange.ajax.idempotency.enabledWhether actions honor theIdempotency-Keyheader. Defaulttrue. Reloadable, not config-cascade aware. No dedicated properties file.com.openexchange.ajax.idempotency.retentionHow long the result of a request other than a send is kept for a retry. Default24h. Reloadable, not config-cascade aware. No dedicated properties file.com.openexchange.ajax.idempotency.sendRetentionHow long the result of sending a mail is kept for a retry. Default7d. Reloadable, not config-cascade aware. No dedicated properties file.com.openexchange.ajax.idempotency.maxResultSizeThe largest result kept for a retry;0keeps none. Default64KB. Reloadable, not config-cascade aware. No dedicated properties file.com.openexchange.ajax.idempotency.claimTimeoutHow long the key of a running request lives without being renewed; at least30s. Default5m. Reloadable, not config-cascade aware. No dedicated properties file.com.openexchange.mail.sentMessageIds.enabledWhether a mail with a client message identifier is sent at most once. Defaulttrue. Reloadable, not config-cascade aware. No dedicated properties file.com.openexchange.mail.sentMessageIds.retentionHow long the client message identifier of a sent mail is remembered; each takes about 0.3 KB in Redis. Default30d. Reloadable, not config-cascade aware. No dedicated properties file.
SCR-1992
Summary: New configuration options for signing in for a personal access token
Effective: 8.54.320 and later
The following options control the sign-in with user name and password for a personal access token through login?action=accessToken.
com.openexchange.accesstoken.signin.enabledWhether a client may sign in for a token. Defaultfalse. Reloadable, config-cascade aware. No dedicated properties file.com.openexchange.accesstoken.signin.defaultScopesThe scopes, comma-separated, a token gets when the client names none; scopes the user cannot grant are left out. Defaultread_mail,write_mail,read_contacts. Reloadable, config-cascade aware. No dedicated properties file.com.openexchange.accesstoken.signin.defaultLifetimeDaysThe lifetime in days of a token when the client names no expiry, capped bycom.openexchange.accesstoken.maxLifetimeDays. A value less than or equal to 0 (zero) is ignored and the default is used. Default90. Reloadable, config-cascade aware. No dedicated properties file.com.openexchange.accesstoken.signin.allowedClientsTheclientidentifiers, comma-separated, that may sign in; empty allows every client. Default empty. Reloadable, config-cascade aware. No dedicated properties file.com.openexchange.accesstoken.signin.challengeLifetimeHow long in milliseconds a user with a second factor has to confirm the sign-in; the password is kept sealed for that time. A value less than or equal to 0 (zero) or above one hour is ignored and the default is used. Default300000. Reloadable, config-cascade aware. No dedicated properties file.com.openexchange.accesstoken.signin.rateLimit.perLoginHow many sign-in attempts a login name may make in the time window, counted cluster-wide before the password is checked; the same number bounds the second-factor codes and SMS per user. A sign-in with the full password or a confirmed second factor clears the count. 0 or less disables this limit. Default5. Reloadable, not config-cascade aware. No dedicated properties file.com.openexchange.accesstoken.signin.rateLimit.perIpHow many failed sign-ins a client address may have in the time window, counted cluster-wide; the address is the transport address, IPv6 per /64 network. 0 or less disables this limit. Default20. Reloadable, not config-cascade aware. No dedicated properties file.com.openexchange.accesstoken.signin.rateLimit.timeWindowThe time window in milliseconds of the limits, between 10 seconds and 12 hours; values outside are clamped. 0 or less disables the limits per login name and address. Default900000. Reloadable, not config-cascade aware. No dedicated properties file.
SCR-1989
Summary: New plural existing*Secrets list values in the core-mw Helm chart
Effective: 8.54.320 and later
In order to take sensitive configuration from several Secrets, for example one ExternalSecret per credential, without merging them into one Secret first, every additive existing*Secret value of the core-mw Helm chart gets a plural twin that accepts a list of further Secret names in the release namespace:
existingPropertiesSecretsexistingUISettingsSecretsexistingMetaSecretsexistingContextSetsSecretsexistingETCFilesSecretsexistingETCBinariesSecretsexistingYAMLFilesSecretsexistingEnvSecrets
The singular value stays supported and is mounted first; a name given twice is used once. The file-based lists are projected into the same directory as the singular Secret, so the keys of all listed Secrets have to be distinct, and a duplicate property is still decided by the numeric file-name prefix, not by list order. Entries of existingEnvSecrets become further envFrom sources after existingEnvSecret; a later entry wins on a duplicate key, and extraEnv wins over all of them. The lists can be set per role or per node type under roles.<role>.values and scaling.nodes.<type>.values, where they replace the global list rather than extend it. Adding, removing or editing a listed Secret changes the checksum/existingSecrets pod annotation and rolls the pods. All lists default to empty; existing deployments are unaffected and do not roll on upgrade. existingASConfigSecret, redis.existingSecret and mysql.existingSecret stay singular.
SCR-1982
Summary: The folders of a Nextcloud account are arranged like the ones of an OpenCloud account
Effective: 8.54.320 and later
The root folder of a Nextcloud account no longer holds the files of the user directly. It holds the virtual folders Personal, Shares and Team folders, along with Trash, where Shares holds Shared with me, Shared with others and Shared via link. The items of the user are listed within Personal, Shared with me or Team folders, depending on the way the instance mounts them, so the identifier of an item is unchanged - only the folder it states as its parent is.
Within the meta of a folder, the object nextcloud states:
folderType- the kind of the folder, one ofpersonal,shared,sharedWithMe,sharedWithOthers,sharedViaLink,teamFolders,teamFolderandtrash, absent for a folder of the user.quota- the quota of the storage the folder denotes, stated for Personal and for each team folder, holdingtotalandusedin bytes, where a storage the instance does not limit states-1as its total.backwardLink- the link that opens the folder within the web interface of the instance, which a file states within itsmetaas well.
The virtual folders are of the type VIRTUAL_FOLDER, so a traversal of the folder tree passes them over, see https://jira.atlassian.open-xchange.com/browse/SCR-1970 .
SCR-1981
Summary: New properties of the OAuth provider of a Nextcloud instance
Effective: 8.54.320 and later
An account of the Nextcloud file storage may be linked to an OAuth account from now on, see https://jira.atlassian.open-xchange.com/browse/SCR-1981 , which the new OAuth service with the service identifier nextcloud is introduced for. It is configured through the following properties, all of which are reloadable and evaluated through the config cascade, so a deployment may integrate a different instance per context or user:
com.openexchange.oauth.nextcloud.enabled, defaultfalse, states whether the service is offered at all.com.openexchange.oauth.nextcloud.hostname, empty by default, states the host of the instance the deployment integrates with. It is required if the service is enabled.com.openexchange.oauth.nextcloud.apiKeyandcom.openexchange.oauth.nextcloud.apiSecret, both empty by default, state the client the deployment authenticates with. Both are required: as opposed to the OpenCloud provider, the Nextcloud one authenticates as a confidential client only, so a client without a secret is not supported and the service is not offered until both hold a value.com.openexchange.oauth.nextcloud.redirectUrlandcom.openexchange.oauth.nextcloud.productName, the ordinary properties of an OAuth service.com.openexchange.oauth.nextcloud.authorizationUrl,com.openexchange.oauth.nextcloud.tokenUrlandcom.openexchange.oauth.nextcloud.userInfoUrl, empty by default, state the endpoints of an identity provider in front of the instance, e.g. Keycloak. Left empty, the endpoints of the instance itself are used.com.openexchange.oauth.nextcloud.scope, defaultopenid profile email offline_access, states the scopes that are requested when the deployment is authorized, separated by spaces.
An account that authenticates with a username and an app token needs none of these, the OAuth service being optional for the storage. The storage is registered regardless, as long as the package open-xchange-oauth is enabled, see SCR-1855.
SCR-1978
Summary: New configuration options for live change events
Effective: 8.54.320 and later
The new push notification transport sse delivers notifications to live change streams (Server-Sent Events). The following options control it.
com.openexchange.pns.transport.sse.enabledWhether the transport is enabled. It can be refined per client and topic by appending.<client>and.<client>.<topic>. Defaultfalse. Reloadable, config-cascade aware. No dedicated properties file.com.openexchange.pns.transport.sse.pingIntervalThe interval in milliseconds in which an open stream refreshes its presence and sends apingevent to the client; a presence that is not refreshed expires after twice this interval. Default30000. Reloadable, config-cascade aware. No dedicated properties file.com.openexchange.pns.transport.sse.maxConnectionsPerUserThe maximum number of streams a user may have open across the cluster. Opening one more closes the user's oldest stream with reasonreplaced. A value less than or equal to 0 (zero) disables the limit. Default5. Reloadable, config-cascade aware. No dedicated properties file.com.openexchange.pns.transport.sse.maxConnectionsPerNodeThe maximum number of streams open on one node. Further requests are rejected with HTTP status503and aRetry-Afterheader, so the client can reconnect to another node. A value less than or equal to 0 (zero) disables the limit. Default5000. Reloadable, not config-cascade aware. No dedicated properties file.com.openexchange.pns.transport.sse.maxLifetimeThe maximum lifetime of a stream in milliseconds. Once reached, the stream ends with reasonlifetimeand the client reconnects. A value less than or equal to 0 (zero) is ignored and the default is used. Default1800000(30 minutes). Reloadable, config-cascade aware. No dedicated properties file.com.openexchange.pns.transport.sse.tokenCheckIntervalThe interval in milliseconds in which the bearer token of a stream is validated again; a stream whose token was revoked or expired then ends with reasontoken_invalid. Keep it below 5 minutes, the time an idle OAuth session lives. A value less than or equal to 0 (zero) is ignored and the default is used. Default120000. Reloadable, config-cascade aware. No dedicated properties file.com.openexchange.imap.liveChanges.modeHow changes made outside the middleware, e.g. by another mail client, are noticed for live change streams of users whose primary account is IMAP.polllooks at the mailbox of a user with an open stream once perpollInterval, in oneLIST "" "*" RETURN (STATUS (MESSAGES UIDNEXT UIDVALIDITY HIGHESTMODSEQ))command on a pooled connection; it needs the IMAP extensions LIST-EXTENDED and LIST-STATUS, and CONDSTORE for changes that leave the message counts alone.offleaves such changes unnoticed. Changes made through the middleware are reported either way. Defaultpoll. Reloadable, config-cascade aware. No dedicated properties file.com.openexchange.imap.liveChanges.pollIntervalThe interval in milliseconds in which such a mailbox is looked at. Streams are handled every 5 seconds, so shorter values take effect as 5 seconds. Default10000. Reloadable, config-cascade aware. No dedicated properties file.com.openexchange.imap.liveChanges.maxWatchesThe maximum number of users a node watches this way. Streams of further users still report the changes made through the middleware. A value less than or equal to 0 (zero) disables the limit. Default5000. Reloadable, not config-cascade aware. No dedicated properties file.com.openexchange.mail.liveChanges.echoWindowThe time in milliseconds within which the mail server’s report of a change the middleware made itself is left out, so that a live change stream sees such a change once. It has to exceed the time the mail server needs to report a change, e.g.com.openexchange.imap.liveChanges.pollInterval. A value less than or equal to 0 (zero) switches the suppression off and reports the change twice. Default15000. Reloadable, config-cascade aware. No dedicated properties file.
SCR-1975
Summary: New Properties to Configure Default Alarms for Newly Provisioned Calendar Accounts
Effective: 8.54.320 and later
Two new lean configuration properties define a default alarm that is taken over into a user's calendar settings when the internal calendar account is provisioned:
com.openexchange.calendar.defaultAlarmDatefor appointments whose start date is of typedate, i.e. all-day appointmentscom.openexchange.calendar.defaultAlarmDateTimefor appointments whose start date is of typedate-time
Each value is a trigger duration relative to the start of the appointment as defined in RFC 5545, e.g. -PT15M or PT9H, and yields a single alarm with action DISPLAY. Both default to an empty value, are reloadable and config-cascade aware down to scope user. Existing accounts are not touched, and a legacy defaultReminder setting already stored for the user takes precedence. No operator action is required; without a value there is no default reminder, as before.
See the property documentation for further details.
SCR-1971
Summary: New properties matching the users and groups of a Nextcloud instance with the ones of the server
Effective: 8.54.320 and later
The Nextcloud file storage states the shares of an item as its permissions, and applies the permissions a client states as shares, which requires the users and groups of the instance to be matched with the ones of the server. Four properties are introduced for that, all of them reloadable and evaluated through the config cascade:
com.openexchange.file.storage.nextcloud.entityResolver.attribute, defaultmail, states whether a user is matched by its mail address or by the name it logs in with, the respective other one being used as a fallback.com.openexchange.file.storage.nextcloud.entityResolver.mappingFile, empty by default, states the path to a file that maps the entities of both systems statically, which takes precedence over matching by attribute. It states one entry per line, ':'-separated:contextId:nextCloudUserId:oxUserIdfor a user,group:contextId:nextCloudGroupId:oxGroupIdfor a group.com.openexchange.file.storage.nextcloud.entityResolver.folderPermissions, defaultfalse, expresses the shares of a listed folder as its permissions, which is a request per shared folder.com.openexchange.file.storage.nextcloud.entityResolver.objectPermissions, defaultfalse, expresses the shares of a listed file as its object permissions, which is a request per shared file.com.openexchange.file.storage.nextcloud.retryAfterErrorInterval, default300, the period, in seconds, an account whose credentials the Nextcloud instance rejected is not asked again for.
Disabled, a shared item states that it is shared, but not with whom. Neither property applies to the permissions a client states for an item, which are applied to its shares in any case.
A deployment that registers a NextCloudEntityResolver of its own supersedes the default implementation, which is registered with the default service ranking.
SCR-1967
Summary: New properties to configure authentication and TLS for the Cassandra connection
Effective: 8.54.320 and later
In order to connect to Cassandra clusters that require authentication or encryption in transit, the connection opened by bundle com.openexchange.nosql.cassandra is now configurable through new lean configuration properties:
com.openexchange.nosql.cassandra.username- the user name to authenticate with; empty by default, in which case no authentication is performed at allcom.openexchange.nosql.cassandra.password- the accompanying passwordcom.openexchange.nosql.cassandra.authProviderClass- the driver's authentication provider,PlainTextAuthProviderby defaultcom.openexchange.nosql.cassandra.ssl- encrypts the connections using TLS,falseby defaultcom.openexchange.nosql.cassandra.sslKeystorePathandcom.openexchange.nosql.cassandra.sslKeystorePassword- the key store holding the client certificate, for clusters that require client certificate authentication
Encryption follows the server's central SSL configuration: the trust material, the enabled protocols and the cipher suites are taken from the properties with prefix com.openexchange.net.ssl., and the node's host name is matched against its certificate unless com.openexchange.net.ssl.hostname.verification.enabled is turned off. A certificate authority that is unknown to the JVM therefore belongs into the central custom trust store; there is no Cassandra-specific trust store.
None of the properties is reloadable or config-cascade aware; they are evaluated once when the session is built, so a changed credential requires a restart. The change is purely additive - with an empty user name and TLS disabled the resulting driver configuration is unchanged, so existing installations are unaffected.
Note that -Ddatastax-java-driver.advanced.auth-provider.* JVM arguments continue to take precedence over the authentication properties. A deployment still passing credentials that way keeps working, but a leftover argument silently overrides the configured value.
A rejected credential is now reported as an authentication error instead of unreachable contact points, and is retried on the same timer, so that a secret which has not been rolled out yet degrades the node instead of failing bundle start.
SCR-1955
Summary: New configuration options for the mail credential vault's token exchange
Effective: 8.54.320 and later
The mail credential vault keeps what lets a feature reach a mailbox without a live session - snoozed mail, scheduled mail, the GDPR mail export and permanent push. These options say that what it keeps is obtained through an OAuth token exchange (RFC 8693) rather than taken from the session as it is. Each of them also exists with a feature identifier appended - snoozed-mail, scheduled-mail, gdpr-export or push - and that value wins for that feature, so a deployment configures the exchange once and can still say something different about a single one. A feature added later needs no option of its own.
com.openexchange.mail.credential.oauth.tokenExchangeWhether the credentials kept for a feature come from a token exchange. Set it tofalsebehind a feature identifier to leave that feature out of what all others do; left empty there, the feature inherits. Where a deployment configuredcom.openexchange.mail.snoozed.oauth.tokenExchangeorcom.openexchange.mail.scheduled.oauth.tokenExchangebefore these options existed, those values are still read and take precedence over the shared one, so nothing has to be migrated. Default false. Reloadable, config-cascade aware. No dedicated properties file.com.openexchange.mail.credential.oauth.tokenExchange.backendPathThe path of the OpenID Connect back-end to exchange with. Empty means the back-end the session itself came from. Default empty. Reloadable, config-cascade aware. No dedicated properties file.com.openexchange.mail.credential.oauth.tokenExchange.scopeThe OAuth scope to request. Empty means none is requested. Worth setting per feature, since reaching a mailbox needs less than sending through it. Default empty. Reloadable, config-cascade aware. No dedicated properties file.com.openexchange.mail.credential.oauth.tokenExchange.additionalParametersFurther parameters for the exchange request, as a singlekey=valuepair; multiple pairs are separated by&. Typically selects the exchange policy at the OAuth server, e.g.usecase=mail-snooze, which is per feature by nature. Empty means none are sent. Default empty. Reloadable, config-cascade aware. No dedicated properties file.
SCR-1954
Summary: New configuration options for the Web Push transport and WebDAV-Push
Effective: 8.54.320 and later
New configuration for the Web Push transport of the push notification service (bundle com.openexchange.pns.transport.webpush, package open-xchange-pns-impl) and for the WebDAV-Push extension for CalDAV and CardDAV (bundle com.openexchange.dav.push, package open-xchange-dav). All properties are lean properties with built-in defaults and no properties file; the feature is off by default.
com.openexchange.pns.transport.webpush.*: enabling the transport (with the usual per-client and per-topic suffixes), the VAPID key - preferably as a Kubernetes secret through the key store service, alternatively as properties -, the egress policy for the client-supplied push resources (HTTPS only, host allowlist, local endpoints, "trust all" handling), theTTLandUrgencyheaders, the maximum lifetime of a subscription and the number of subscriptions a user may hold.com.openexchange.httpclient.webpush.*: the managed HTTP client the transport delivers with, tuned through the generic HTTP client keys; the transport ships with its own defaults (short timeouts, larger pool, hard timeouts enabled).com.openexchange.dav.push.*: the config-cascade aware switch offering WebDAV-Push to CalDAV/CardDAV clients (defaultfalse), the maximum lifetime of WebDAV-Push subscriptions, and the secret the opaque collection topics are derived with.
Rotating the VAPID key invalidates the existing subscriptions at the push services until the clients re-register with the new key; all nodes of a cluster must use the same key.
See the general documentation, as well as the configuration documentation for the Web Push transport and for WebDAV-Push for further details.
SCR-1934
Summary: Removed the property com.openexchange.antivirus.mode along with the experimental streaming operation mode
Effective: 8.54.320 and later
The property com.openexchange.antivirus.mode (default double-fetch) is no longer evaluated and has been removed; deployments still setting it can simply drop it. Its experimental streaming value never returned the scanned content to the requesting client and left the ICAP connection open after every scan, so the mode has been removed along with the property. Anti-Virus scanning now always behaves as it did with the previous default.
The Java method AntiVirusService.canStream(), which only reported that mode, has been removed as well.
SCR-1926
Summary: New configuration options for soft-deleted users
Effective: 8.54.320 and later
Options for the soft-deleted user state, the leaver state between disabled and deleted.
com.openexchange.user.softDelete.retentionDaysThe number of days a soft-deleted user is kept before the clean-up jobcom.openexchange.admin.softdelete.SoftDeleteRetentionExecutiondeletes the user for good, the same way an explicit delete through the provisioning API would.0keeps soft-deleted users until an administrator deletes or restores them. Default30. Reloadable, config-cascade aware. No dedicated properties file.com.openexchange.scim.deleteModeAccepts the additional valuesoftDelete: a SCIMDELETEof a user soft-deletes the user instead of deactivating or deleting it. Defaultdeactivate, unchanged. Reloadable, config-cascade aware. No dedicated properties file.
SCR-1912
Summary: New property com.openexchange.oauth.provider.jwt.audience; EC and RSA-PSS signatures accepted
Effective: 8.54.320 and later
Two changes to the OAuth provider's validation of JSON Web Tokens in mode expect_jwt, closing gaps found while attaching the SCIM service provider to it.
- New property
com.openexchange.oauth.provider.jwt.audience: a comma separated list of audiences (claimaud) tokens are accepted for; a token has to carry one of them. Empty, the default, keeps the audience unchecked, as before. Reloadable and config-cascade aware. Operators are advised to set it, so that only tokens minted for the deployment pass. - Tokens signed with the RSA-PSS (PS256/384/512) and EC (ES256/384/512) algorithms are accepted next to RS256/384/512, according to the key type in the JWK set. Symmetric algorithms and
noneremain refused. - An empty
com.openexchange.oauth.provider.allowedIssuerstill accepts every issuer whose key the JWK set holds, but is now logged as a warning on start and reload.
SCR-1911
Summary: New property com.openexchange.scim.allowJwt
Effective: 8.54.320 and later
com.openexchange.scim.allowJwt defines whether the SCIM endpoint accepts JSON Web Tokens issued by the identity provider next to provisioning tokens. The token is validated by the OAuth provider (com.openexchange.oauth.provider.mode=expect_jwt), has to belong to the context, carry the scope scim and name the context administrator as subject. Reloadable and config-cascade aware down to the context. Defaults to false, so an enabled OAuth provider does not open the endpoint unnoticed. No operator action is required by default.
SCR-1902
Summary: New properties com.openexchange.scim.deleteMode, com.openexchange.scim.allowBasicAuth and com.openexchange.scim.allowUnauthenticatedDiscovery
Effective: 8.54.320 and later
Three lean configuration properties control the new SCIM service provider. All three are reloadable and config-cascade aware down to the context.
com.openexchange.scim.deleteModedefines whatDELETE /scim/v2/contexts/{context_id}/Users/{id} does:deactivatekeeps the account and its data and only refuses login,deleteremoves the account and its data the way the provisioning API does. It defaults todeactivate, so a misconfigured identity provider cannot wipe a context in one sync cycle.com.openexchange.scim.allowBasicAuthdefines whether the SCIM endpoint accepts HTTP basic credentials of administrators next to provisioning tokens. It defaults totrue;falsemakes the endpoint token-only, which exposes no password to online guessing.com.openexchange.scim.allowUnauthenticatedDiscoverydefines whether the discovery endpoints/ServiceProviderConfig,/ResourceTypesand/Schemasanswer a caller that presents no credential. They describe the protocol, not the context, and identity providers read them before they authenticate. It defaults totrue; a credential that is presented is checked either way, andfalserequires one for discovery as well.
No operator action is required by default.
SCR-1900
Summary: New lean configuration property com.openexchange.push.dovecot.unregisterOnMissingSession
Effective: 8.54.320 and later
In order to keep Dovecot Push registrations from piling up for users whose session has ended, the new lean configuration property com.openexchange.push.dovecot.unregisterOnMissingSession is introduced. When Dovecot notifies about a new message for a user for which no session can be resolved, the middleware drops that user's push registration through DoveAdm. It defaults to true, is reloadable and not config-cascade aware.
The registration is kept if it may still be wanted, that is if a permanent push listener or push notification subscriptions exist for that user. A configured DoveAdm end-point (com.openexchange.dovecot.doveadm.endpoints) is required; without one no registration is dropped. Set the property to false to restore the previous behavior.
See the property documentation for further details.
SCR-1892
Summary: New configuration option for caching the master administrator's password verification on provisioning calls
Effective: 8.54.320 and later
Provisioning calls are session-less and verify the master administrator's password against mpasswd on every call; with the default BCRYPT hash that costs about 100 ms of CPU per call and caps the master-level throughput. A successful verification is now remembered per node for a configurable time, so that further calls with the same credentials skip the password hash. Only a keyed hash of the password under a random per-node key is kept in memory; a wrong password always pays the full check, and reloading mpasswd with a changed hash invalidates the remembered verification. In addition, the duration of administrator authentication is exposed as Micrometer timer appsuite.provisioning.auth.duration with tags mode (master or context) and status.
com.openexchange.admin.masterPasswordVerificationTtlSecondsSpecifies how long (in seconds) a successful verification of the master administrator's password is remembered on a node. A value less than or equal to0(zero) disables the cache and verifies the password hash on every call. Default300. Reloadable, not config-cascade aware. No dedicated properties file.
SCR-1884
Summary: New configuration options for remote Redis sites
Effective: 8.54.320 and later
Options for using remote Redis sites for cache invalidation, e.g. between two middleware clusters attached to one config database.
com.openexchange.redis.sites.scopeWhat the configured remote sites are used for.all: sessions are replicated to the remote sites and looked up there, and cache invalidations are repeated there.invalidation: cache invalidations only; the sessiond sees no remote sites, so sessions stay on their site. Useinvalidationfor clusters that merely share the databases. Defaultall. Not reloadable, not config-cascade aware. File:redis.properties.com.openexchange.redis.[site].cache.enabledWhether the remote site denoted by[site](one of the identifiers listed incom.openexchange.redis.sites) runs a dedicated Redis instance for cache data. If enabled, that instance is configured through the[site].cacheinfix, e.g.com.openexchange.redis.[site].cache.hosts, and cache invalidations are repeated there; otherwise they are repeated on the remote site's regular Redis instance. Defaultfalse. Not reloadable, not config-cascade aware. File:redis.properties.
SCR-1879
Summary: New properties "vacation.internal.enabled" and "vacation.internal.externalTagHeader" for vacation notices treating internal and external senders differently
Effective: 8.54.320 and later
Two new lean configuration properties control the extended (internal/external) vacation rules of the accompanying HTTP-API SCR. Both are reloadable and config-cascade aware; the feature ships dark and requires an explicit operator opt-in:
com.openexchange.mail.filter.options.vacation.internal.enabled— the feature toggle, defaults tofalse.com.openexchange.mail.filter.options.vacation.internal.externalTagHeader— the name of the classifier header the mail platform stamps on every delivered message (e.g.Vacation-External), defaults to empty; configuring it is a prerequisite. The values are fixed convention:truemarks the sender external,falseinternal. A message carrying neither value — or both — receives no vacation notice at all: broken or missing stamping fails closed instead of leaking the internal text to external senders.
The mail platform MUST delete inbound instances of the classifier header before stamping, on every delivery path without exception. Before enabling, verify the stamping end to end following the recipe in the feature documentation: a message injected from an external sender with a forged header instance must arrive carrying the genuine external stamp instead — proving both deleting and stamping — on every delivery path (MX, internal delivery, forwards, list expansion). Do not enable the feature before every node in the cluster runs 8.54 or later, and only once a rollback below 8.54 is no longer expected: an older middleware keeps delivery working but edits extended rules unsafely, and any script write from a pre-8.54 node — saving or deleting any rule, not only vacation notices — temporarily splits an extended rule's branches in the middleware's rule model (delivery is unaffected; an 8.54 middleware reattaches the branches on the next read). The security contract, a stamping recipe, and the operator runbook including the downgrade behavior are described in the feature documentation; see the property documentation for the full property descriptions.
SCR-1877
Summary: New configuration option for contact auto-complete result limiting
Effective: 8.54.320 and later
In order to bound the size of a contact auto-complete response, a new lean configuration property is introduced for the contacts module.
com.openexchange.contact.autocomplete.maxResultsDefines the maximum number of contacts returned by anaddressbooks?action=autocompleterequest that does not supply aright_hand_limitof its own. Such a request previously returned every matching contact, so a query of one or two characters could read and transfer a large part of the address book. A value of 0 (zero) or less disables the limit. Default 100. Reloadable, config-cascade aware. File: contact.properties.
Related change in the same area: a client-supplied right_hand_limit is now applied to auto-complete requests for every sort order. It was previously discarded whenever sort was omitted or set to one of the special sort orders, which are the ones App Suite uses for contacts.
SCR-1876
Summary: New configuration option for the virtual CalDAV collection "All my public appointments"
Effective: 8.54.320 and later
A new configuration option enables the virtual CalDAV collection All my public appointments, which exposes all appointments the user attends in public calendars as an additional calendar, without having to synchronize whole public calendars. Appointments can neither be created nor deleted through the collection, but the own participation status and alarms of existing appointments can be changed. The collection is only offered to users with access to public folders.
com.openexchange.caldav.allPublicEvents.enabledEnables the virtualAll my public appointmentscollection via CalDAV. Defaultfalse. Reloadable, config-cascade aware. File:caldav.properties.
SCR-1868
Summary: Changed the custom fields syntax in logback.xml
Effective: 8.54.320 and later
Custom fields for the JSON and Logstash encoders are now declared with nested <name> and <value> elements inside <customField>. The previous attribute form, together with the <newRule> declaration for com.openexchange.logback.extensions.encoders.CustomFieldAction, silently stopped working when logback removed support for <newRule> in version 1.3, so no custom field has been written since. Administrators using custom fields have to rewrite those entries in logback.xml and can drop the <newRule> line; an incomplete field is now reported as a warning on the appender.
<appender name="LOGSTASH" class="com.openexchange.logback.extensions.appenders.logstash.LogstashAppender">
<encoder class="com.openexchange.logback.extensions.encoders.JSONEncoder">
<customField><name>deployment</name><value>production</value></customField>
<customField><name>cluster</name><value>eu-1</value></customField>
</encoder>
</appender>
SCR-1862
Summary: Renamed misspelled property com.openexchange.mail.prependReplyPrefx to com.openexchange.mail.prependReplyPrefix
Effective: 8.54.320 and later
The property com.openexchange.mail.prependReplyPrefx - which controls whether the reply prefix (e.g. Re: ) is prepended to the unquoted reply text instead of being included in the quoted text - was misspelled. It is now named com.openexchange.mail.prependReplyPrefix. It defaults to false and is neither reloadable nor config-cascade aware.
No operator action is required: the misspelled name is still evaluated as a fallback, so an existing configuration keeps working. It is deprecated and logs a warning once when it takes effect; deployments that set it should move to the corrected name.
See the property documentation for further details.
SCR-1857
Summary: New properties for the OpenCloud file storage
Effective: 8.54.320 and later
The following properties are introduced for the file storage service opencloud. All of them are reloadable, and all but com.openexchange.file.storage.opencloud.entityResolver.attribute and com.openexchange.file.storage.opencloud.entityResolver.mappingFile are config-cascade aware, so a deployment may integrate a different OpenCloud instance per context or user.
com.openexchange.capability.filestorage_opencloudenables the storage for a user. It defaults tofalse.com.openexchange.file.storage.opencloud.entityResolver.attributestates the attribute the users of an instance are matched with the ones of the server by, eithermailorusername. It defaults tomail.com.openexchange.file.storage.opencloud.entityResolver.mappingFilestates the path to a file mapping the users and groups of an instance to the ones of the server statically, which takes precedence over matching them by an attribute. It is empty by default.com.openexchange.file.storage.opencloud.entityResolver.folderPermissionsstates whether the shares of a folder are expressed as its permissions, which requires a request per folder. It defaults tofalse.com.openexchange.file.storage.opencloud.entityResolver.objectPermissionsstates whether the shares of a file are expressed as its object permissions. It defaults tofalse.com.openexchange.file.storage.opencloud.entityResolver.createdModifiedBystates whether the users that created and modified an item are looked up within the instance. It defaults totrue, and states the session's user otherwise.com.openexchange.oauth.opencloud.hostnamestates the host of the instance the deployment integrates with. It is empty by default and required ifcom.openexchange.oauth.opencloud.enabledis set totrue.com.openexchange.oauth.opencloud.authorizationUrlstates the URL the user is directed to in order to authorize the deployment. It is empty by default, which uses the endpoint of the identity provider built into OpenCloud.com.openexchange.oauth.opencloud.tokenUrlstates the URL an authorization code and a refresh token are redeemed at. It is empty by default, which uses the endpoint of the identity provider built into OpenCloud.com.openexchange.oauth.opencloud.userInfoUrlstates the URL the identity of the user is read from. It is empty by default, which uses the endpoint of the identity provider built into OpenCloud.com.openexchange.oauth.opencloud.scopestates the scopes that are requested when the deployment is authorized, separated by spaces. It defaults toopenid profile email offline_access.
OAuth is configured through the properties of an OAuth service with the service identifier opencloud, so com.openexchange.oauth.opencloud.enabled, apiKey, apiSecret, redirectUrl and productName. An instance that is fronted by an identity provider of its own, e.g. Keycloak, states its endpoints through the properties above. A public client that authenticates with PKCE states no apiSecret. Such an instance associates a token with one of its users by the claims the token states, e.g. the roles or the groups of that user, so the client the deployment is authorized with needs to state the same claims as the client of the instance itself, which may require a further scope.
See the property documentation for further details.
SCR-1854
Summary: New property com.openexchange.calendar.useNoReplyAddressForNotifications
Effective: 8.54.320 and later
In order to let deployments whose no-reply relay is not authorized for the users' mail domains pass SPF and DMARC checks, the new lean configuration property com.openexchange.calendar.useNoReplyAddressForNotifications is introduced. If enabled, the configured no-reply address replaces the From header of calendar notification mails to internal recipients that are transported via the no-reply account, and the Sender and Reply-To headers are dropped. It defaults to false, is reloadable and config-cascade aware.
External iMIP messages are never affected, as their From header has to stay aligned with the ORGANIZER property. Note that the property keys on the recipient being internal, not on the message kind: with com.openexchange.calendar.useIMipForInternalUsers enabled, internal users receive full iMIP messages and those are rewritten as well, which RFC-conformant calendar clients may reject. Enable both only if internal recipients read their invitations in App Suite.
It applies wherever the no-reply account is used, which is not limited to com.openexchange.calendar.preferNoReplyForNotifications: guests, users without webmail permission, restricted sessions and impersonation sessions take that account on their own, so mails triggered by them change as well. The value is evaluated for the acting user, not for the organizer. No operator action is required by default. See the property documentation for further details.
Database
SCR-1987
Summary: Added the "xctx_grants" Cross-Context Grant Index Table and Create-Table Update Task
Effective: 8.54.320 and later
Warning
Update Task{{com.openexchange.crosscontext.impl.storage.rdb.groupware.CrossContextGrantsCreateTableTask}}
New per-context table xctx_grants in the resource owner's context user schema - the mirror of xctx_liaisons, one small row per foreign context holding access on a resource of this context.
CREATE TABLE xctx_grants (
`cid` INT4 UNSIGNED NOT NULL,
`entity` INT4 UNSIGNED NOT NULL,
`module` INT4 UNSIGNED NOT NULL DEFAULT 0,
`grantee_cid` INT4 UNSIGNED NOT NULL,
`type` INT4 UNSIGNED NOT NULL DEFAULT 0,
PRIMARY KEY (`cid`, `entity`, `module`, `grantee_cid`, `type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
cid/entity = owner context + resource owner; grantee_cid = the foreign context holding access; type = LiaisonType (3 = SHARED_ACCOUNT). A shared account permission is stored in the grantee's context, so the account's own context has no record of it - this index is what makes it answerable.
- Fresh schemas:
com.openexchange.crosscontext.impl.storage.rdb.groupware.CrossContextGrantsCreateTableService - Existing schemas:
com.openexchange.crosscontext.impl.storage.rdb.groupware.CrossContextGrantsCreateTableTask(UpdateTaskAdapter, no dependencies, idempotent) - Cleanup and backfill:
CrossContextGrantsDeleteListenerplus theDatabaseCleanUpServicejobsSharedAccountGrantsSourceCleanUpExecutionandGrantsCleanUpExecution(1/day each)
The source-side job backfills grants issued before the index existed. It skips any schema with a pending update task, so on an upgraded system the index completes once those updates have run.
See the feature documentation for further details.
SCR-1952
Summary: New tables mail_credential and mail_credential_migration
Effective: 8.54.320 and later
Warning
Update Task{{com.openexchange.mail.credential.impl.groupware.MailCredentialCreateTableTask}}
The two new tables mail_credential and mail_credential_migration in every context schema belong to the mail credential vault: the first keeps the encrypted mail credentials, the second how far the clean-up job that moves credentials of the old format into the vault has come. Both are added by the update task com.openexchange.mail.credential.impl.groupware.MailCredentialCreateTableTask. Expired rows of mail_credential are dropped hourly by a database clean-up job; rows of deleted users and contexts are dropped with them. A table whose credentials have all been moved carries a non-zero finished in mail_credential_migration. No admin action.
Table layout:
CREATE TABLE mail_credential (
`cid` INT4 UNSIGNED NOT NULL,
`id` BINARY(16) NOT NULL,
`user` INT4 UNSIGNED NOT NULL,
`feature` VARCHAR(64) NOT NULL,
`kind` VARCHAR(16) NOT NULL,
`caller_key` TINYINT(1) NOT NULL DEFAULT 0,
`login` VARCHAR(255) DEFAULT NULL,
`ciphertext` TEXT DEFAULT NULL,
`created` BIGINT(20) NOT NULL,
`expires` BIGINT(20) NOT NULL DEFAULT 0,
`version` INT4 NOT NULL DEFAULT 1,
`lease` BIGINT(20) NOT NULL DEFAULT 0,
PRIMARY KEY (`cid`,`id`),
KEY `user` (`cid`,`user`,`feature`,`created`),
KEY `expires` (`expires`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE mail_credential_migration (
`table_name` VARCHAR(64) NOT NULL,
`resume_at` BINARY(16) DEFAULT NULL,
`finished` BIGINT(20) NOT NULL DEFAULT 0,
PRIMARY KEY (`table_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
SCR-1947
Summary: New table access_token in the context database schema
Effective: 8.54.320 and later
Warning
Update Task{{com.openexchange.accesstoken.impl.groupware.AccessTokenCreateTableTask}}
Personal access tokens are stored in the new table access_token of the context database schema, created for new schemas by the create-table service and for existing ones by the update task com.openexchange.accesstoken.impl.groupware.AccessTokenCreateTableTask: columns cid, id (BINARY(16)), user, hash (the SHA-256 hash of the secret; the secret itself is never stored), label, scopes, created, expires, last_used and credential (BINARY(16), the identifier of the row in mail_credential the mail credential vault keeps for the token, sealed with the token's secret; NULL when nothing is kept), with primary key (cid, id), unique key (cid, hash) and key (cid, user). Rows are removed together with their user or context; the vault row goes with the token when it is revoked or expires. No admin action beyond the usual update task run.
SCR-1922
Summary: New column softDeleted in the user tables
Effective: 8.54.320 and later
Warning
Update Task{{com.openexchange.groupware.update.tasks.UserAddSoftDeletedColumnTask}}
The tables user and del_user of every context schema receive the nullable column softDeleted (BIGINT(64)), the time a user has been soft-deleted in milliseconds since the epoch, together with the index softDeletedIndex on user. New schemas receive the column with the table, existing schemas through the update task. Existing rows keep NULL, no data is migrated.
ALTER TABLE user ADD COLUMN softDeleted BIGINT(64) DEFAULT NULL;
ALTER TABLE user ADD INDEX softDeletedIndex (softDeleted);
ALTER TABLE del_user ADD COLUMN softDeleted BIGINT(64) DEFAULT NULL;
SCR-1903
Summary: New table scim_external_id in the context schema
Effective: 8.54.320 and later
Warning
Update Task{{com.openexchange.scim.storage.internal.groupware.ScimStorageCreateTableTask}}
{{com.openexchange.scim.storage.internal.groupware.ScimExternalIdTextKeyTask}}
The SCIM service provider stores the externalId an identity provider assigns to a resource - a user, group, resource, shared account, deputy or secondary account - in the new table scim_external_id of every context schema, unique per context and resource type and compared case-sensitively. The identifier of the resource is kept as text, since deputies and secondary accounts have none that is a number. New schemas receive the table from ScimStorageCreateTableService, existing schemas through the update task ScimStorageCreateTableTask, and a schema that received the table with a numeric id before is converted by ScimExternalIdTextKeyTask; the bundle com.openexchange.scim.storage ships with open-xchange-core so that every node knows the table. It starts empty, no data is migrated, and rows are removed with their user or context.
CREATE TABLE scim_external_id (
cid INT4 UNSIGNED NOT NULL,
type TINYINT UNSIGNED NOT NULL,
id VARCHAR(191) NOT NULL,
external_id VARCHAR(255) NOT NULL,
PRIMARY KEY (cid, type, id),
UNIQUE KEY external_id (cid, type, external_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
SCR-1898
Summary: New tables provisioning_token in the context schema and crosscontext_provisioning_token in the configdb
Effective: 8.54.320 and later
Warning
Update Task{{com.openexchange.provisioning.token.impl.groupware.ProvisioningTokenCreateTableTask}}
Provisioning tokens bound to a context are stored in the new table provisioning_token of every context schema. New schemas receive it from ProvisioningTokenCreateTableService, existing schemas through the update task. The table holds the SHA-256 hash of a secret together with label, scope, creator and the creation, expiration and last-use times. It starts empty, no data is migrated.
CREATE TABLE provisioning_token (
cid INT4 UNSIGNED NOT NULL,
id BINARY(16) NOT NULL,
hash VARCHAR(64) NOT NULL,
label VARCHAR(128) NOT NULL,
scope VARCHAR(32) NOT NULL,
created_by VARCHAR(191) NOT NULL,
created BIGINT(20) NOT NULL,
expires BIGINT(20) NOT NULL DEFAULT 0,
last_used BIGINT(20) NOT NULL DEFAULT 0,
PRIMARY KEY (cid, id),
UNIQUE KEY hash (cid, hash)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
Cross-context tokens, which open several contexts at once, live in the configuration database: the token in the new table crosscontext_provisioning_token, one row per context it opens in crosscontext_provisioning_token_context. Both are created at start-up, each by its own change set of configdbChangeLog.xml - 8:crosscontext_provisioning_token:create and 8:crosscontext_provisioning_token_context:create - so no manual step is needed; they start empty as well. Deleting a context removes its rows from crosscontext_provisioning_token_context, and a token left with no context is deleted along with it.
CREATE TABLE crosscontext_provisioning_token (
id BINARY(16) NOT NULL,
hash VARCHAR(64) NOT NULL,
label VARCHAR(128) NOT NULL,
scope VARCHAR(32) NOT NULL,
created_by VARCHAR(191) NOT NULL,
created BIGINT(20) NOT NULL,
expires BIGINT(20) NOT NULL DEFAULT 0,
last_used BIGINT(20) NOT NULL DEFAULT 0,
PRIMARY KEY (id),
UNIQUE KEY hash (hash)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE crosscontext_provisioning_token_context (
token BINARY(16) NOT NULL,
cid INT4 UNSIGNED NOT NULL,
PRIMARY KEY (token, cid),
KEY cid (cid)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
SCR-1890
Summary: New Column classifiedAccess in Table deputy for the Calendar Deputy Right to See Confidential Appointments
Effective: 8.54.320 and later
Warning
Update Task{{com.openexchange.deputy.impl.groupware.DeputyStorageAddClassifiedAccessColumnTask}}
In order to persist the new right of a calendar deputy to see the granting user's confidential appointments with the deputy permission itself (rather than in folder metadata a folder administrator could tamper with), the table deputy gained the column classifiedAccess next to sendOnBehalfOf, holding the granted level as numerical identifier (0 for none, 1 for confidential).
ALTER TABLE deputy ADD COLUMN classifiedAccess TINYINT UNSIGNED NOT NULL DEFAULT 0;
The update task adds the column to existing schemas, fresh schemas get it through the create table service. Existing rows keep the default 0, so no deputy gains the right implicitly and no operator action is required. The column is read by the calendar only when a deputy encounters a classified appointment in a shared calendar folder carrying deputy permissions, through the deputy service's storage-only listing of the grants the folder owner made to the deputy.
SCR-1853
Summary: New table deputy_mail_acl_baseline holding the mail ACL baseline of a deputy permission
Effective: 8.54.320 and later
Warning
Update Task com.openexchange.deputy.provider.imap.groupware.DeputyMailAclBaselineCreateTableTask
In order to record a deputy permission's mail ACL baseline reliably, the mailboxes on which the deputy already held an ACL before the permission was granted are now kept in the new table deputy_mail_acl_baseline instead of in the granting user's INBOX metadata entry /shared/vendor/vendor.open-xchange/deputydir-<deputyId>.
The baseline is now recorded once, at the first grant. It was previously re-captured on every grant, so a mailbox outside the permission's folder list that had only received its ACL through the grant itself, typically a subfolder Dovecot inherits from INBOX, counted as pre-existing and kept the deputy's ACL when the permission was revoked.
CREATE TABLE deputy_mail_acl_baseline (
cid INT4 UNSIGNED NOT NULL,
uuid BINARY(16) NOT NULL,
user INT4 UNSIGNED NOT NULL,
account INT4 UNSIGNED NOT NULL DEFAULT 0,
fullname VARCHAR(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
rights VARCHAR(32) CHARACTER SET latin1 NOT NULL DEFAULT '',
PRIMARY KEY (cid, uuid, account, fullname),
KEY userId (cid, user)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
Deputy permissions granted before this change are not migrated; their baseline is still read from the metadata entry, and both locations are cleared when the permission is revoked. The update task only creates the table and does not touch existing rows. No operator action is required.
Packaging/Bundles
SCR-1999
Summary: New bundle com.openexchange.mail.messageid.redis for the folder rename history of the Mobile API
Effective: 8.54.320 and later
The package open-xchange-core ships the new bundle com.openexchange.mail.messageid.redis. It keeps a 30-day history of mail folders renamed or moved through App Suite in Redis, one hash ox-mail-folder-renames:<context>:<user>:<account> per mail account, so that Mobile API clients can recognize renamed folders on mail servers whose folder identifiers depend on the name (not Dovecot). Entries are written only for users with com.openexchange.mobile.api.enabled; updates use a Lua script unless com.openexchange.redis.lua.enabled is false. No admin action.
SCR-1997
Summary: New package open-xchange-mobile-api and Helm feature mobile-api
Effective: 8.54.320 and later
The new package open-xchange-mobile-api ships the bundle com.openexchange.mobile.api, which serves the Mobile API at /mobile/v1. It requires open-xchange-core and open-xchange-oauth-provider.
The core-mw Helm chart keeps the package switched off through the new feature mobile-api, like mcp. Both switches are needed: the feature starts the bundle, com.openexchange.mobile.api.enabled lets it answer.
SCR-1979
Summary: New bundle com.openexchange.pns.transport.sse
Effective: 8.54.320 and later
Added the new bundle com.openexchange.pns.transport.sse to the open-xchange-pns-impl package. It provides the push notification transport sse, which delivers notifications to live change streams (Server-Sent Events) and shares the presence of open streams among the nodes via Redis. The transport is disabled by default, see com.openexchange.pns.transport.sse.enabled.
SCR-1953
Summary: New bundles for WebDAV-Push: com.openexchange.dav.push and com.openexchange.pns.transport.webpush
Effective: 8.54.320 and later
The package open-xchange-pns-impl gains the bundle com.openexchange.pns.transport.webpush: a push notification service transport with id webpush that delivers RFC 8291 encrypted Web Push messages with RFC 8292 VAPID authorization to arbitrary push services (UnifiedPush distributors, FCM Web Push endpoints). It exports com.openexchange.pns.transport.webpush.WebPushService and the packages ...webpush.crypto and ...webpush.vapid, registers the managed HTTP client webpush, and imports com.openexchange.keystore from open-xchange-core to read the VAPID key from a Kubernetes secret. Public key derivation uses the BouncyCastle bundles already on the platform; no new third-party library.
The package open-xchange-dav gains the bundle com.openexchange.dav.push: WebDAV-Push (draft-bitfire-webdav-push-00, as used by DAVx5) for CalDAV and CardDAV. It contributes the transports, topic and supported-triggers property mixins, the push-register handling, the registration servlet below /push, the calendar, contact and task handlers that turn changes into notifications for the client webdav-push, and the push-message generator. It plugs into the DAV stack via the new SPI com.openexchange.dav.DAVPushService exported by com.openexchange.dav, which additionally gained the webdav-push DAV header option, the POST hook and the Push-Dont-Notify header handling. Because the bundle uses the transport's service, open-xchange-dav now depends on open-xchange-pns-impl.
Both bundles start with their packages and stay inert until configured: no VAPID key means no transport, and com.openexchange.dav.push.enabled defaults to false. No Helm chart change (the secret uses the existing OX_KEYSTORE mechanism), no database change (regular push notification service subscriptions), no admin action unless push for DAVx5 is wanted.
See the general documentation for further details.
SCR-1949
Summary: New package open-xchange-mcp with the bundles of the MCP server
Effective: 8.54.320 and later
The new package open-xchange-mcp contains the bundles com.openexchange.mcp (MCP endpoint, prompts, me_get), com.openexchange.mcp.contacts (contacts_search, contact_get, users_search), com.openexchange.mcp.mail (mail_search, mail_get, mail_attachment_get, mail resources), com.openexchange.mcp.calendar (calendar_list_events, calendar_get_event, calendar_free_busy, resources_search), com.openexchange.mcp.files (files_search, file_get, file resources), com.openexchange.mcp.tasks (tasks_search, task_get), com.openexchange.mcp.vacation (vacation_get) and com.openexchange.mcp.reminders (reminders_list); each module bundle also registers its folder tool. com.openexchange.mcp exports the service interfaces com.openexchange.mcp.tools.McpTool and com.openexchange.mcp.resources.McpResource, so further bundles can contribute tools and resources. The package depends on open-xchange-core, which ships the personal access tokens the endpoint accepts (SCR-1946). The Helm chart core-mw knows it as the feature mcp (features.definitions.mcp), which is disabled by default in features.status, so the bundles are not started unless an operator enables the feature; the endpoint additionally needs com.openexchange.mcp.enabled=true. No admin action unless the MCP server is wanted.
SCR-1916
Summary: New package open-xchange-admin-soap-common required by the administrative SOAP packages
Effective: 8.54.320 and later
The administrative SOAP bundles each carried their own copy of the same XML types. Those shared types now live in the new bundle com.openexchange.admin.soap.common, shipped in the new package open-xchange-admin-soap-common.
The following packages now depend on it, so it is pulled in automatically on upgrade and no operator action is required:
open-xchange-admin-soapopen-xchange-admin-soap-reselleropen-xchange-admin-soap-usercopy
No published WSDL changes: every copy already used the same XML namespace and the same content model, so the consolidation is not visible on the wire. Existing SOAP clients are unaffected.
SCR-1904
Summary: Added package open-xchange-scim and the chart feature scim
Effective: 8.54.320 and later
The SCIM service provider is packaged as follows.
- New package
open-xchange-scimwith the bundlecom.openexchange.scim; it depends onopen-xchange-coreandopen-xchange-admin. - New bundle
com.openexchange.scim.storageinopen-xchange-core, carrying the table, its update task and the delete listener. - New chart feature
sciminhelm/core-mw(features.definitions.scim=open-xchange-scim), disabled globally and enabled on theadminrole by default throughroles.admin.values.features.status.scim, so the endpoint runs on the admin pods only. Set it todisabledthere to switch the endpoint off.
SCR-1855
Summary: New bundles for OpenCloud and Nextcloud and new dependency of open-xchange-file-storage-webdav on open-xchange-oauth
Effective: 8.54.320 and later
The file storage service opencloud and refactoring of existing file storage service nextcloud ships with two new bundles.
com.openexchange.opencloud, the generated client of the libre graph API, within the packageopen-xchange-file-storage-webdavcom.openexchange.oauth.opencloud, the OAuth provider of an OpenCloud instance, within the packageopen-xchange-oauthcom.openexchange.oauth.nextcloud, the OAuth provider of an Nextcloud instance, within the packageopen-xchange-oauth
The OpenCloud and Nextcloud storages require the package open-xchange-oauth to be enabled, no matter whether an account authenticates with a username and an app token or through OAuth, as they are not registered at all otherwise.