App Suite Releases
  • 8.47
  • 8.35
  • 7.10.6
Imprint
  • 8.47
  • 8.35
  • 7.10.6
Imprint
  • Release 8.55Upcoming
    • Noteworthy Changes
      • Important Changes
      • App Suite Middleware
    • Changelogs
      • App Suite UI
      • App Suite Middleware
      • Additional Components
        • AI Service
        • Booking Service
        • OX Guard UI
        • OX Documents
        • Switchboard
        • UI Service
    • Helm Charts
      • Helm chart values
      • App Suite Stack Chart
      • Booking
      • Helm Chart core-cacheservice
      • Helm Chart core-documentconverter
      • Helm Chart core-documents-collaboration
      • Helm Chart core-imageconverter
      • core-mw
      • UI Service
      • Switchboard
  • Release 8.54
  • Release 8.53
  • Release 8.52
  • Release 8.51
  • Release 8.50
Maintained. Older releases are best effort.
Upcoming
Not released yet
LTS
Long-term support branch
LTS discontinued
No longer supported, upgrade recommended

App Suite Middleware

8.55.511

Configuration

SCR-2238

Summary: Reloadable Redis command timeout and connection pool settings, and new degraded mode configuration options

Effective: 8.55.511 and later

The Redis command timeout and the connection pool settings now take effect on a configuration reload, without restarting the middleware. This also applies to the instance-specific variants (com.openexchange.redis.<instance>.…).

  • com.openexchange.redis.commandTimeoutMillis: The timeout for a Redis command in milliseconds; a reload applies it to established connections as well. Default 5000. Reloadable, not config-cascade aware. File: redis.properties.

  • com.openexchange.redis.connection.pool.maxTotal: The maximum number of connections in the pool. Only applies to pool mode dedicated. Default 100. Reloadable, not config-cascade aware. File: redis.properties.

  • com.openexchange.redis.connection.pool.maxIdle: The maximum number of idle connections in the pool. Only applies to pool mode dedicated. Default 100. Reloadable, not config-cascade aware. File: redis.properties.

  • com.openexchange.redis.connection.pool.minIdle: The minimum number of idle connections kept in the pool. Only applies to pool mode dedicated. Default 0. Reloadable, not config-cascade aware. File: redis.properties.

  • com.openexchange.redis.connection.pool.maxWaitSeconds: How long to wait for a free pooled connection, in seconds. Only applies to pool mode dedicated. Default 2. Reloadable, not config-cascade aware. File: redis.properties.

  • com.openexchange.redis.connection.pool.minIdleSeconds: The idle time in seconds after which a pooled connection may be evicted. Default 60. Reloadable, not config-cascade aware. File: redis.properties.

  • com.openexchange.redis.connection.pool.cleanerRunSeconds: The interval in seconds between two runs of the pool cleaner. Default 10. Reloadable, not config-cascade aware. File: redis.properties.

  • com.openexchange.redis.connection.pool.invalidateOnCommandTimeout: Whether a pooled connection that ran into a command timeout is closed instead of returned to the pool. Only applies to pool mode dedicated. Default false. Reloadable, not config-cascade aware. File: redis.properties.

  • com.openexchange.redis.connection.pool.newConnectionIfWaitExceeded: Whether to open a new connection once waiting for a free pooled connection exceeds com.openexchange.redis.connection.pool.maxWaitSeconds. Only applies to pool mode dedicated. Default true. Reloadable, not config-cascade aware. File: redis.properties.

In addition, a degraded mode for the Redis connector was introduced: in the degraded state the failure thresholds of both circuit breakers are multiplied by a factor and their open delays are shortened, so that sporadic failures - typically packet loss towards the Redis end-point - do not open them over and over again. The instance-specific variants (com.openexchange.redis.<instance>.degraded.…) exist as well.

  • com.openexchange.redis.degraded.mode: The degraded mode: off, monitor (transitions are only logged and counted), auto (the connector enters and leaves the degraded state by itself) or forced (permanently degraded). Default "off". Reloadable, not config-cascade aware. File: redis.properties.

  • com.openexchange.redis.degraded.failureThreshold: The number of command timeouts or connect failures within the observation window that lets mode auto enter the degraded state. Default 5. Reloadable, not config-cascade aware. File: redis.properties.

  • com.openexchange.redis.degraded.windowSeconds: The observation window in seconds the failures are counted in. Default 60. Reloadable, not config-cascade aware. File: redis.properties.

  • com.openexchange.redis.degraded.minDurationSeconds: The minimum time in seconds the connector stays degraded before a clean observation window lets it return to normal. Default 120. Reloadable, not config-cascade aware. File: redis.properties.

  • com.openexchange.redis.degraded.maxDurationSeconds: The maximum time in seconds the connector stays degraded in one stretch; once exceeded it returns to normal and re-evaluates. Default 900. Reloadable, not config-cascade aware. File: redis.properties.

  • com.openexchange.redis.degraded.breaker.failureThresholdFactor: The factor applied to the failure thresholds and failure executions of both circuit breakers while degraded. Default 4. Reloadable, not config-cascade aware. File: redis.properties.

  • com.openexchange.redis.degraded.breaker.delayMillis: The delay in milliseconds both circuit breakers wait in open state while degraded, replacing their configured delays. Default 2000. Reloadable, not config-cascade aware. File: redis.properties.

8.55.498

Changed defaults

SCR-2216

Summary: Up to 512 carrier threads for virtual threads by default

Effective: 8.55.495 and later

Along with -Djdk.virtualThreadScheduler.parallelism=256 (SCR-1998), the middleware now sets -Djdk.virtualThreadScheduler.maxPoolSize=512, the most carrier threads the JVM may run, including those it adds while virtual threads wait in a host name lookup, an fsync or a file lock. The JVM default of max(parallelism, 256) left none to add at 256, so slow DNS lookups could block every carrier.

The option is not added if a JAVA_OPTS_* value sets the parallelism or the maximum pool size. With a parallelism above 256, also set -Djdk.virtualThreadScheduler.maxPoolSize above it.

8.55.497

3rd Party Libraries/License Change

SCR-2213

Summary: Upgraded third-party libraries to their latest patch or minor release

Effective: 8.55.497 and later

Upgraded third-party libraries to their latest patch or minor release. No API or configuration change; no operator action is required.

  • AWS SDK for Java from 2.57.6 to 2.57.8, embedded in software.amazon.awssdk.
  • Jackson 3 (core, databind, dataformat-yaml) from 3.2.3 to 3.2.4, embedded in io.fabric8.kubernetes.

8.55.496

Changed defaults

SCR-2217

Summary: Changed the default carrier threads for virtual threads to scale with the CPUs

Effective: 8.55.496 and later

The container entrypoint and the oxscripts now default -Djdk.virtualThreadScheduler.parallelism to four carrier threads per CPU, at least 64, instead of a static 256. A static 256 raised the load average and CPU time of small nodes during bursts of work, while the protection against the class-loading hang of SCR-1998 does not need more. CPUs are counted as the JVM counts them: those the process may run on, capped by the CPU quota of its cgroup.

-Djdk.virtualThreadScheduler.maxPoolSize now defaults to the parallelism plus 256 instead of a static 512, keeping the headroom of SCR-2216 at any parallelism. It is now also derived from a parallelism that a JAVA_OPTS_* value or JAVA_XTRAOPTS sets. No operator action is required; both options yield to configured values.

8.55.495

Changed defaults

SCR-1998

Summary: More carrier threads for virtual threads by default

Effective: 8.55.495 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.

Along with it, the middleware sets -Djdk.virtualThreadScheduler.maxPoolSize=512, the most carrier threads the JVM may run, including those it adds while virtual threads wait in a host name lookup, an fsync or a file lock. The JVM default of max(parallelism, 256) left none to add at 256, so slow DNS lookups could block every carrier. The option is not added if a JAVA_OPTS_* value sets the parallelism or the maximum pool size; with a parallelism above 256, also set -Djdk.virtualThreadScheduler.maxPoolSize above it.

8.55.484

API - HTTP-API

SCR-2171

Summary: Address Books of Shared Accounts in the HTTP API

Effective: applies to the 8.55 release line

Users holding a contacts permission for a shared account get its address books through the addressbooks module and folders of content type addressdata; the legacy contacts module does not support them.

  • Each shared account is a contacts account con://<n>; its folders are the account's private address books and its contacts Trash folder (con://<n>/<id>). They keep their type (1, Trash 16), are listed below the private root folder and carry the new folder field com.openexchange.contacts.provider (column 3303) with value sharedaccount.
  • The account's default address book is announced read-only in the io.ox/core JSlob under sharedaccount/<account id>/folder/addressbooks.
  • sharedaccount?action=all|get report moduleConfigurations.contacts; subscribed, usedForSync and usedInPicker of these folders are kept per user under userSettings.contacts.
  • Permission levels map to folder permissions as for calendars; contacts and their attachments are written as the shared account.

Purely additive. See the HTTP API documentation for further details.

SCR-1958

Summary: New Attribute sentCopyToGranter for Deputy Permissions to Put a Copy of Mails a Deputy Sends into the Granting User's Sent Folder

Effective: applies to the 8.55 release line

The DeputyPermission and GrantedDeputyPermission objects of the deputy module carry the new optional boolean attribute sentCopyToGranter. If set, the granting user gets a copy of the mails the deputy sends for them, on their behalf or as them, in their own standard Sent folder, marked as read and only once; whether a mail gets a copy is decided each time the deputy sends, and SCR-1956 lists the mails that get no copy. The middleware puts the copy there through administrative mail access; the copy gives the deputy no additional permission.

  • PUT /appsuite/api/deputy?action=new and ?action=update accept the attribute; on update an absent attribute keeps the current value, unless the update withdraws both sendOnBehalfOf and sendAs. GET ?action=all, ?action=get and ?action=reverse return it as true where granted and omit it otherwise; ?action=reverse also omits it while com.openexchange.deputy.sentCopyToGranterEnabled is false for the granting user.
  • It requires sendOnBehalfOf or sendAs (see SCR-2144), otherwise the request fails with DEPUTY-0017, unless it is an update that passes the stored value back unchanged: withdrawing both then withdraws it as well, as it does for SCIM, which always passes the stored value back. Switching between them keeps it, and so does a grant of sendAs that acts as sendOnBehalfOf while com.openexchange.deputy.sendAsEnabled is disabled (see SCR-2143).
  • Granting it fails with DEPUTY-0018 if none of the strategies in com.openexchange.mail.admin.strategies applies to the granting user (doveadm only where com.openexchange.deputy.sentCopyToGranterDoveAdmEnabled is set), and, through the HTTP API, with DEPUTY-0019 while com.openexchange.deputy.sentCopyToGranterEnabled is false for the granting user (passing back an already stored value unchanged still works), which SCIM, as administrative provisioning, is not subject to; clients should offer it only where the capability deputy_sent_copy is granted (see SCR-1956).
  • A value that is no boolean is rejected with SVL-0010, as for sendOnBehalfOf and sendAs.
  • The SCIM resource Deputies carries the same boolean attribute sentCopyToGranter; a PUT that leaves it out keeps the stored value, unless the update withdraws both sendOnBehalfOf and sendAs, a PATCH that removes it sets it to false. Asking for it where DEPUTY-0018 applies is answered with HTTP status 400 (invalidValue). Since every deputy resource now carries it, its meta.version (ETag) changes with this update, as described for sendAs in SCR-2147.

Apart from the SCIM ETag, the change only adds the attribute: requests without it behave as before. See the API documentation and the feature documentation for further details.

API - RMI

SCR-2173

Summary: Contacts Permission Level in the RMI, gRPC, CLT and SCIM Provisioning of Shared Accounts

Effective: applies to the 8.55 release line

The provisioning of shared accounts takes and reports the permission level for the address books of a shared account, next to the calendar and mail levels. As for these (see SCR-2167), an omitted level, or one without permission level, means the configured default com.openexchange.sharedaccount.contacts.defaultPermissionSet applies.

  • RMI: OXSharedAccountInterface.createSharedAccountPermissions(..., calendarConfiguration, contactsConfiguration, mailConfiguration, ...); SharedAccountPermissions and SharedAccountPermissionsOnSet gain contactsConfig. The previous signature remains.
  • gRPC: contacts_config in SharedAccountPermissions, CreateSharedAccountPermissionsRequest and SharedAccountPermissionsOnSet.
  • CLT: createsharedaccountpermissions -K / --contactspermissionlevel; the list tools print a ContactsConfig column.
  • SCIM: attribute contacts in the permissions of the SharedAccount resource.
  • REST: moduleConfigurations.contacts of a shared account, with moduleAccountId con://<n>.

Existing calls are unaffected.

SCR-1960

Summary: New Field sentCopyToGranter in the Deputy Permission Data Objects of the Administrative RMI and gRPC Interfaces

Effective: applies to the 8.55 release line

The RMI data objects com.openexchange.admin.rmi.dataobjects.DeputyPermission (and thereby ActiveDeputyPermission) and DeputyPermissionDescription carry the new boolean field sentCopyToGranter (isSentCopyToGranter and setSentCopyToGranter): if set, the granting user gets a copy of the mails the deputy sends for them, on their behalf or as them, in their standard Sent folder. The description object tracks it with the companion flag isSentCopyToGranterSet, so an absent field keeps the current value on update, unless the update withdraws both sendOnBehalfOf and sendAs. The shared account interface carries the field along wherever it converts deputy permissions, e.g. when it restores the deputy permissions it revoked because converting a user into a shared account failed; a deputy permission whose copy cannot be granted at that moment (DEPUTY-0018) is restored without it, as is one whose copy was left stored without a right to send (see SCR-1957).

The gRPC provisioning API mirrors it in deputy.proto: DeputyPermission.sent_copy_to_granter (field 9) and the description pair sentCopyToGranter (17) with SentCopyToGranterSet (18). It goes through the same RMI service as the SOAP interface, so semantics and validation are the same, see SCR-1959.

The change only adds fields: the existing gRPC field numbers stay as they are, and an RMI or gRPC client that does not set the field keeps the stored value on update, unless the update withdraws both sendOnBehalfOf and sendAs. See the feature documentation for further details.

API - SOAP

SCR-2172

Summary: New Element contactsConfig in the SOAP Service for Shared Accounts

Effective: applies to the 8.55 release line

The SOAP service http://soap.admin.openexchange.com/OXSharedAccountService takes and reports the permission level for the address books of a shared account, next to calendarConfig and mailConfig. As for these (see SCR-2167), an omitted contactsConfig, or one without permissionLevel, means the configured default com.openexchange.sharedaccount.contacts.defaultPermissionSet applies.

  • New type ContactsConfiguration with the element permissionLevel (none, viewer, editor, author or admin).
  • New optional element contactsConfig in createSharedAccountPermissions, as the last element after auth.
  • New optional element contactsConfig in SharedAccountPermissions, as returned by listSharedAccountPermissions, listSharedAccountPermissionsForSharedAccount and getSharedAccountPermissions.
  • New optional element contactsConfig in SharedAccountPermissionsForContext, as taken by setSharedAccountPermissions.
<soap:createSharedAccountPermissions>
  <soap:ctx><xsd:id>2</xsd:id></soap:ctx>
  <soap:users><xsd:id>4</xsd:id></soap:users>
  <soap:sharedAccountCtx><xsd:id>1</xsd:id></soap:sharedAccountCtx>
  <soap:sharedAccount><xsd:name>support</xsd:name></soap:sharedAccount>
  <soap:auth><xsd:login>oxadmin</xsd:login><xsd:password>secret</xsd:password></soap:auth>
  <soap:contactsConfig><xsd:permissionLevel>viewer</xsd:permissionLevel></soap:contactsConfig>
</soap:createSharedAccountPermissions>

Existing requests are unaffected. See the feature documentation for further examples.

SCR-1959

Summary: New Element sentCopyToGranter in the Deputy Permissions of the OXDeputyPermissionsService

Effective: applies to the 8.55 release line

The data objects DeputyPermission and ActiveDeputyPermission of the OXDeputyPermissionsService carry the new optional, nillable boolean element sentCopyToGranter: if set, the granting user gets a copy of the mails the deputy sends for them, on their behalf or as them, in their standard Sent folder (see SCR-1958 for the semantics). In both complex types the element is appended at the end of the sequence, after sendAs (see SCR-2145). The grant and update operations accept it; get and list return it. On update an absent element keeps the current value, unless the update withdraws both sendOnBehalfOf and sendAs.

It requires sendOnBehalfOf or sendAs, otherwise the request fails with DEPUTY-0017, unless it is an update that passes the stored value back unchanged (see SCR-1958); granting it fails with DEPUTY-0018 if none of the strategies in com.openexchange.mail.admin.strategies applies to the granting user, doveadm only where com.openexchange.deputy.sentCopyToGranterDoveAdmEnabled is set (see SCR-1956). Requests without the element behave as before; responses carry the new element, which clients that validate strictly against an older WSDL do not expect.

<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:admin>false</xsd:admin>
          <xsd:folderPermission>2</xsd:folderPermission>
          <xsd:readPermission>4</xsd:readPermission>
          <xsd:writePermission>0</xsd:writePermission>
          <xsd:deletePermission>0</xsd:deletePermission>
        </xsd:modulePermissions>
        <xsd:sentCopyToGranter>true</xsd:sentCopyToGranter>
      </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-2032

Summary: Distributed files require a token per file

Effective: applies to the 8.55 release line

The servlet at /dfm/distributedFiles, through which nodes fetch files from each other, requires the token of the requested file: in the header X-OX-DFM-Token, or for GET in the query parameter token. A request without it is answered with status 404. Nodes pass the token on their own; no admin action. During a rolling upgrade a node not yet upgraded cannot fetch files from an upgraded node, which affects preview downloads, inline images of the PDF export and job results until all nodes are upgraded. A node announces itself with the IP address from the environment variable POD_IP, if set, instead of its host name.

Changed defaults

SCR-2140

Summary: Changed default of com.openexchange.groupware.update.background.idleMillis

Effective: applies to the 8.55 release line

The schema lock of background update tasks now expires like the lock of blocking update tasks.

  • com.openexchange.groupware.update.background.idleMillis A background update lock that has not been refreshed for this many milliseconds counts as left behind, and another node may run the remaining background update tasks of that schema. Before, a node that died while running them kept the schema locked until an admin removed the lock. Keep the value above com.openexchange.groupware.update.refreshIntervalMillis (20000); 0 restores the old behavior. Default 0 → 60000. Reloadable, not config-cascade aware. No dedicated properties file.

SCR-2057

Summary: No write scope in the default scopes of a signed-in access token

Effective: applies to the 8.55 release line

The default of com.openexchange.accesstoken.signin.defaultScopes changes from read_mail,write_mail,read_contacts to read_mail,read_contacts, so a client that signs in through login?action=accessToken without naming scopes gets no write scope. A client that needs one names it in scopes. The Mobile API keeps its defaults through com.openexchange.mobile.auth.token.scopes. Deployments that rely on the old default set the property to it.

Configuration

SCR-2169

Summary: New Property com.openexchange.sharedaccount.contacts.defaultPermissionSet

Effective: applies to the 8.55 release line

Shared accounts now give access to their address books. The new lean property com.openexchange.sharedaccount.contacts.defaultPermissionSet is the contacts permission set of every user holding a permission for a shared account, also of permissions without a contacts level of their own, the counterpart of com.openexchange.sharedaccount.calendar.defaultPermissionSet. It defaults to none, is reloadable and config-cascade aware. Explicitly granted levels can only raise it.

Permissions granted before the update get no contacts level of their own (see SCR-2170). With the default none, nobody sees the address books of an existing shared account until this property is raised or a contacts level is provisioned.

See the property documentation and the feature documentation for further details.

SCR-2080

Summary: New configuration option to switch off single MCP tools

Effective: applies to the 8.55 release line

This option lets an administrator switch off single tools of the MCP server /mcp.

  • com.openexchange.mcp.tools.disabled Comma-separated names of MCP tools that are switched off. A tool switched off is neither listed by tools/list nor callable; a call is answered as for a tool that does not exist. Its resource is not served either: mail_get covers ox:///mail/{folder}/{id}, mail_attachment_get the attachments, file_get ox:///files/{id}. Prompts that need it are no longer listed. briefing_get reads its sections on its own; switch it off too to keep a module out of the briefing. Default empty. Reloadable, config-cascade aware. No dedicated properties file.

SCR-2058

Summary: New configuration option for the default scopes of the Mobile API

Effective: applies to the 8.55 release line

Tokens from POST /mobile/v1/auth/token get their default scopes from this option instead of com.openexchange.accesstoken.signin.defaultScopes.

  • com.openexchange.mobile.auth.token.scopes Comma-separated scopes a token from POST /mobile/v1/auth/token gets when the app names none; scopes the user cannot grant are left out. Default read_mail,write_mail,read_contacts. Reloadable, not config-cascade aware. No dedicated properties file.

SCR-2031

Summary: New configuration options for the AJAX job queue

Effective: applies to the 8.55 release line

The AJAX job queue keeps the state and the result of a job in Redis, so that every node answers jobs requests for it and long-running requests no longer need sticky routing. If the node executing a job is gone, the job is answered with error JOB-0005.

  • com.openexchange.jobqueue.maxResultSize The maximum size in bytes of a job result that is kept in Redis. A bigger result is kept in an encrypted file on the node that executed the job, from where the other nodes fetch it. A value less than or equal to 0 (zero) means unlimited. Default 1048576. Reloadable, not config-cascade aware. No dedicated properties file.

  • com.openexchange.jobqueue.maxDistributedResultSize The maximum size in bytes of a job result that nodes other than the executing one serve. A bigger result is served by the executing node only; other nodes answer with error JOB-0006. A value less than or equal to 0 (zero) means unlimited. Default 67108864. Reloadable, not config-cascade aware. No dedicated properties file.

SCR-1956

Summary: New Capability deputy_sent_copy and Properties for the Copy of Mails a Deputy Sends for the Granting User

Effective: applies to the 8.55 release line

In order to let a granting user find a copy of the mails a deputy sends for them, on their behalf or as them, in their own standard Sent folder, deputy permissions get the new option sentCopyToGranter (see SCR-1958). The middleware puts the copy there through administrative mail access; the copy gives the deputy no additional permission.

  • The new capability deputy_sent_copy tells clients whether a user may grant the option. It is granted while deputy permissions are available to the user (capability deputy) and one of the strategies in com.openexchange.mail.admin.strategies (see SCR-2023) applies to the user; a token that a provisioning request brings along does not count. With the strategy token, the capability only checks that a token is kept for the user, not that it can still be used. It is never granted to guests. Whether a mail gets a copy is decided each time the deputy sends.
  • The new property com.openexchange.deputy.sentCopyToGranterEnabled (default true, reloadable, config-cascade-aware) switches the copy on or off for the granting user. While it is false, the capability is not offered, granting the option is rejected with DEPUTY-0019 except through administrative provisioning (passing back an already stored value unchanged still works), and no copy is made; stored grants are kept and take effect again once it is true.
  • The new property com.openexchange.deputy.sentCopyToGranterDoveAdmEnabled (default false, reloadable, config-cascade-aware) decides for the granting user whether the strategy doveadm counts for the copy. DoveAdm copies the mail from the deputy's Sent folder, which needs both mailboxes on the same Dovecot instance. While the property is false, the capability, granting the option and the copy only consider the other strategies; if none of them applies, granting the option is rejected with DEPUTY-0018.
  • The existing property com.openexchange.deputy.provider.imap.doveadm.personalNamespace is now also read for the copy, whatever strategy applies, in the scope of the granting user, if no Sent folder full name is stored with the granting user's primary mail account. com.openexchange.deputy.provider.imap.doveadm.sharedNamespace and com.openexchange.deputy.provider.imap.doveadm.publicNamespace are read for every copy, where set: no copy goes into a folder below them, even if the granting user stored such a folder as their Sent folder; this is logged with level WARN.
  • The copy contains the Bcc recipients of the mail, so the granting user sees whom the deputy sent a blind copy to. A copy the mail server refuses is not made.
  • If com.openexchange.mail.noCopyIntoStandardSentFolder is set to true for the deputy, no copy is made; granting the option is not rejected in that case.
  • No copy is made for mails sent as raw MIME (mail?action=new with PUT), resent (mail?action=resend) or bounced (mail?action=bounce), for mails the calendar sends for the granting user, such as invitations, changes and cancellations a deputy sends on their behalf, and for mails sent through an account other than the deputy's primary mail account. Nor is one made if the granting user found for the From address belongs to another context and another user who appointed the deputy has the same address; this is logged with level WARN. The mail itself is sent in all these cases.
  • With the strategy doveadm, where enabled, the copy is taken from the deputy's Sent folder: if the deputy chose not to keep copies of sent mails, the mail is put there for the copy and removed once it is copied; it stays there if no copy is made, if the copy or the removal fails for good, or if the deputy's session has ended by then, as for a scheduled mail.

The copy is made in the background after the send. See the feature documentation for the strategies, the retries, the logging and all cases without a copy.

Database

SCR-2170

Summary: Existing Shared Account Permissions Get a Contacts Account

Effective: applies to the 8.55 release line

Warning

Update Taskcom.openexchange.sharedaccount.storage.rdb.groupware.SharedAccountAddContactsAccountTask

Each shared account permission carries a contacts account, whose identifier con://<n> is part of the folder identifiers of the shared account's address books. The update task adds one to every row of sharedaccount_permissions whose configuration has no contacts entry, without a contacts level of its own, so com.openexchange.sharedaccount.contacts.defaultPermissionSet applies (see SCR-2169). No table changes; rows that already have a contacts entry are skipped. The cached permissions (cache module sacPrms) move to version 2.

Note for operators: do not create shared account permissions until all nodes run the update, since a node of the previous version creates them without a contacts account. Changes made through setSharedAccountPermissions keep the contacts entry from 8.54 on (see SCR-2167).

SCR-1957

Summary: New Column sentCopyToGranter in Table deputy for the Copy of Mails a Deputy Sends for the Granting User

Effective: applies to the 8.55 release line

Warning

Update Taskcom.openexchange.deputy.impl.groupware.DeputyStorageAddSentCopyToGranterColumnTask

The table deputy gets the new column sentCopyToGranter. It tells whether the granting user gets a copy of the mails the deputy sends for them in their standard Sent folder (see SCR-1956).

ALTER TABLE deputy ADD COLUMN sentCopyToGranter TINYINT(1) NOT NULL DEFAULT 0;

Fresh schemas: com.openexchange.deputy.impl.groupware.DeputyStorageCreateTableService. Existing schemas: com.openexchange.deputy.impl.groupware.DeputyStorageAddSentCopyToGranterColumnTask (idempotent, depending on DeputyStorageCreateTableTask). Existing deputy permissions get the value 0, so their behavior does not change.

The previous version does not know the column, so during a rolling upgrade nodes of that version make no copy of the mails sent through them, a deputy permission they re-create (com.openexchange.deputy.preferRecreateInsteadOfUpdate) loses it, and a right to send they withdraw leaves sentCopyToGranter stored. Nodes of this version treat such a stored copy as withdrawn: they neither make nor list it, and a right to send granted again through them does not bring it back; one granted again through a node of the previous version does, without that node showing it. Grant sentCopyToGranter only once all nodes run this version.

Packaging/Bundles

SCR-2174

Summary: New Bundle com.openexchange.mail.deputy for the Copy of Mails Sent by Deputies

Effective: applies to the 8.55 release line

The package open-xchange-deputy ships the new bundle com.openexchange.mail.deputy. It puts the copy of the mails a deputy sends for a granting user into that user's standard Sent folder, see SCR-1956.

SCR-2168

Summary: New Bundle com.openexchange.contact.provider.sharedaccount for the Address Books of Shared Accounts

Effective: applies to the 8.55 release line

The package open-xchange-sharedaccount ships the new bundle com.openexchange.contact.provider.sharedaccount. It provides the contacts provider sharedaccount, which offers the address books of a shared account to the users holding a permission for it, as com.openexchange.chronos.provider.sharedaccount does for its calendars.

8.55.481

3rd Party Libraries/License Change

SCR-2208

Summary: Patched re2j 1.8 so that compiling a case-insensitive pattern holding certain Cyrillic letters returns

Effective: 8.55.481 and later

The bundle com.google.re2j now ships as re2j-1.8-ox1-custom.jar, with Unicode.simpleFold patched as proposed upstream in google/re2j#213. Before, Pattern.compile never returned for a case-insensitive pattern holding one of the characters U+1C80 to U+1C88, which kept the calling thread busy. These characters now match only themselves case-insensitively; all other case folding is unchanged.

SCR-2119

Summary: Upgraded forty-two third-party libraries to their latest patch or minor release

Effective: 8.55.481 and later

Upgraded forty-two third-party libraries to their latest patch or minor release. No API or configuration change; no operator action is required.

  • Google Calendar API client from v3-rev20260708-2.0.0 to v3-rev20260925-2.0.0, embedded in com.google.api.client.

  • snappy-java from 1.1.10.8 to 1.1.10.12 in the target platform.

  • javax.inject from the 2.4.0-b10 pre-release to the final 2.4.0 release of org.glassfish.hk2.external in the target platform.

  • Jetty from 12.1.13 to 12.1.14, embedded in com.openexchange.http.jetty.

  • GlassFish CORBA OMG API from 5.0.2 to 5.0.3 in the target platform (glassfish-corba-omgapi-5.0.3-custom.jar).

  • TwelveMonkeys ImageIO from 3.15.2 to 3.15.3, embedded in com.openexchange.imagetransformation.java.

  • Netty from 4.2.18.Final to 4.2.19.Final, embedded in io.netty.

  • Apache Jackrabbit WebDAV from 2.22.4 to 2.22.5 in the target platform (jackrabbit-webdav-2.22.5-custom.jar).

  • AWS SDK for Java from 2.55.11 to 2.57.6, embedded in software.amazon.awssdk.

  • Amazon S3 Encryption Client from 4.0.2 to 4.0.3, embedded in software.amazon.awssdk.

  • gRPC from 1.84.0 to 1.85.1, embedded in the gRPC framework libraries and com.google.api.client.

  • libphonenumber from 9.0.40 to 9.0.42, embedded in com.openexchange.sms.

  • Apache CXF from 4.2.3 to 4.2.4, embedded in com.openexchange.soap.common; this also moves XmlSchema from 2.3.2 to 2.3.3.

  • Kotlin standard library from 2.4.20 to 2.4.21 in the target platform.

  • Yubico WebAuthn server library from 2.10.0 to 2.10.1, embedded in com.openexchange.webauthn.

  • ASM from 9.10.1 to 9.11.1 in the target platform.

  • Stax2 API from 4.3.1 to 4.3.2 in the target platform.

  • Apache HttpClient 5 from 5.6.4 to 5.6.5 in the target platform (httpclient5-5.6.5-custom.jar), and its cache module embedded in com.openexchange.saml.

  • Gmail API client from v1-rev20260727-2.0.0 to v1-rev20261005-2.0.0, embedded in com.google.api.client.

  • Caffeine from 3.3.0 to 3.3.1, embedded in com.google.guava.

  • Lettuce from 7.8.0 to 7.9.0, embedded in io.lettuce; this also moves redis-authx-core from 0.1.1-beta2 to 0.2.0.

  • Apache Cassandra Java Driver from 4.19.3 to 4.19.4, embedded in com.openexchange.nosql.cassandra.

  • Box Java SDK from 10.19.0 to 10.20.0, embedded in com.openexchange.file.storage.boxcom.

  • Prometheus metrics library from 1.9.0 to 1.10.0, embedded in com.openexchange.metrics.micrometer.

  • Apache Kafka client from 4.3.1 to 4.4.0 in the target platform (org.apache.servicemix.bundles.kafka-clients-4.4.0-custom.jar).

  • Jackson from 2.22.3 to 2.22.4 in the target platform. The Jakarta REST providers (jackson-jakarta-rs-*) now come from upstream again, since 2.22.4 itself accepts Jakarta REST 4.

  • jOOX from 2.0.1 to 2.0.2, embedded in com.openexchange.xml.

  • MySQL Connector/J from 26.7.0 to 26.10.0 in the target platform and in com.openexchange.datamining.

  • metadata-extractor from 2.21.0 to 2.22.0, embedded in com.drew; it limits container nesting in QuickTime/MP4/HEIF files and avoids a stack overflow on long TIFF IFD chains.

  • OkHttp from 5.5.0 to 5.5.1, embedded in com.squareup.okhttp3; it blocks the compromised eMudhra intermediate certificates.

  • Micrometer from 1.17.1 to 1.17.2, embedded in com.openexchange.metrics.micrometer.

  • Reactor Core from 3.8.7 to 3.8.8, embedded in io.lettuce.

  • Spring Framework (core, beans) from 7.0.9 to 7.0.10, embedded in com.openexchange.xml.

  • Google API Client from 2.9.1 to 2.9.2, embedded in com.google.api.client; its bundled trust store now carries Google's current root certificates.

  • Jolokia from 2.6.3 to 2.6.4, embedded in com.openexchange.jolokia. Jolokia now accepts JSON POST requests only with the content type application/json or without one, and rejects application/json with a charset parameter with HTTP 400. GET requests are unchanged. Reported upstream as jolokia#1071.

  • Gson from 2.14.0 to 2.15.0 in the target platform. Gson now deserializes untyped JSON objects into a LinkedHashMap instead of its own LinkedTreeMap (same iteration order), and reads an InetAddress only from a numeric IP address.

  • dnsjava from 3.6.5 to 3.7.0 in the target platform. It fixes five security advisories, among them unbounded pre-allocation from DNS header count fields and a TSIG MAC comparison that was not constant-time.

  • Logback from 1.6.5 to 1.6.6 in the target platform and the command-line tools. Its socket receiver now rejects crafted serialized exception graphs that looped forever, and FileNamePattern escapes regular-expression characters in file names.

  • Google Auth Library from 1.54.0 to 1.55.0, embedded in com.google.api.client and com.google.firebase.

  • Google API Common from 2.70.0 to 2.71.0, embedded in com.google.api.client.

  • Joda-Time from 2.15.0 to 2.15.1 in the target platform, with time zone data 2026f.

  • Apache Tika core from 4.1.0 to 4.2.0, embedded in com.openexchange.tika.util. It now also embeds Apache Commons Secure XML 1.1.0, which Tika uses to parse its MIME type definitions.

On 8.54, only the Apache CXF 4.2.4, Guava 33.7.2, Jackson 2.22.4 and Logback 1.6.5 upgrades are included.

SCR-2097

Summary: Upgraded Grizzly to 5.0.3

Effective: 8.55.481 and later

Upgraded Grizzly from 5.0.2 to 5.0.3, embedded in com.openexchange.http.grizzly. With HTTP/2 enabled (com.openexchange.http.grizzly.http2.enabled), large responses are no longer cut off and later requests on the same listener no longer stall until the write timeout. No API or configuration change; no operator action is required.

SCR-2089

Summary: Upgraded the Yubico WebAuthn server library to 2.10.0

Effective: 8.55.481 and later

Upgraded the Yubico WebAuthn server library (webauthn-server-core, yubico-util) from 2.9.0 to 2.10.0, embedded in com.openexchange.webauthn. No API or configuration change; no operator action is required.

SCR-2081

Summary: Upgraded Jersey and Joda-Time to their latest patch or minor release

Effective: 8.55.481 and later

Upgraded Jersey and Joda-Time to their latest patch or minor release. No API or configuration change; no operator action is required.

  • Jersey from 4.0.2 to 4.0.3 in the target platform, matching HK2 4.0.3.
  • Joda-Time from 2.14.4 to 2.15.0 in the target platform, with the time zone database 2026e.

SCR-2053

Summary: Upgraded the Apache Kafka client to 4.3.1

Effective: 8.55.481 and later

Upgraded the Apache Kafka client from 4.0.2 to 4.3.1 in the target platform (org.apache.servicemix.bundles.kafka-clients-4.3.1-custom.jar), which the Kafka log appender uses. Brokers from version 2.1 on remain supported. No API or configuration change; no operator action is required.

SCR-2052

Summary: Upgraded Commons Lang, Tika, Guava and AWS SDK to their latest patch or minor release

Effective: 8.55.481 and later

Upgraded four third-party libraries to their latest patch or minor release. No API or configuration change; no operator action is required.

  • Apache Commons Lang from 3.20.0 to 3.21.0 in the target platform.
  • Apache Tika from 4.0.0 to 4.1.0, embedded in com.openexchange.tika.util.
  • Guava from 33.7.1 to 33.7.2 in com.google.guava.
  • AWS SDK for Java from 2.55.7 to 2.55.11, embedded in software.amazon.awssdk.

SCR-2046

Summary: Upgraded Gson Fire to 1.9.0 in the OpenCloud integration

Effective: 8.55.481 and later

Upgraded Gson Fire from 1.8.5 to 1.9.0, embedded in com.openexchange.opencloud (gson-fire-1.9.0.jar). It now requires Gson [2.10,3), which the target platform satisfies. No API or configuration change; no operator action is required.

SCR-2045

Summary: Upgraded HK2, AWS SDK and Woodstox to their latest patch or minor release

Effective: 8.55.481 and later

Upgraded HK2, the AWS SDK for Java and Woodstox to their latest patch or minor release. No API or configuration change; no operator action is required.

  • HK2 (hk2-api, hk2-locator, hk2-utils, aopalliance-repackaged) from 4.0.2 to 4.0.3 in the target platform.
  • AWS SDK for Java from 2.55.6 to 2.55.7, embedded in software.amazon.awssdk.
  • Woodstox from 7.2.2 to 7.3.0, embedded in com.ctc.wstx.

SCR-2044

Summary: Removed the unused libraries eddsa, cose-java and nekohtml and replaced PBKDF2 with the JDK implementation

Effective: 8.55.481 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-java 1.1.0 and eddsa 0.3.0 from bundle com.openexchange.multifactor.provider.webauthn, together with the cbor 4.5.6, datautilities 1.1.0 and numbers 1.8.2 jars embedded alongside them. WebAuthn verification is unaffected; it runs in com.openexchange.webauthn, which still ships cbor.

  • Removed nekohtml 1.9.22 from bundle com.openexchange.common; the packages org.cyberneko.html* are no longer exported. Custom plugins that imported them from com.openexchange.common have to ship their own copy.

  • Replaced PBKDF2 1.1.4 and its transitive dependency picketbox 4.0.21.Final in bundle com.openexchange.crypto with the JDK's PBKDF2WithHmacSHA1; keys derived for legacy encrypted data are unchanged.

SCR-2042

Summary: Upgraded transitively embedded libraries whose parent libraries still pin older releases

Effective: 8.55.481 and later

Upgraded transitively embedded libraries whose parent libraries still pin older releases. No API or configuration change; no operator action is required.

  • nimbus-jose-jwt from 10.9.1 to 10.10 in com.nimbus.
  • grpc-api and grpc-context from 1.70.0 to 1.84.0 in com.google.api.client, matching the gRPC version of the target platform.
  • jose4j from 0.9.6 to 0.9.7 in com.openexchange.file.storage.boxcom.
  • okio-jvm from 3.18.1 to 3.18.2 in com.squareup.okhttp3.
  • neethi from 3.2.3 to 3.2.4 in com.openexchange.soap.common.
  • Dropwizard metrics-core from 4.2.39 to 4.2.40 in com.openexchange.saml.
  • brownies-collections from 0.9.16 to 0.9.24 in org.magicwerk.brownies.collections; the Maven groupId moved from org.magicwerk to org.magicwerk.brownies.

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.55.481 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-clients jar with the former ServiceMix OSGi manifest; the bundle symbolic name org.apache.servicemix.bundles.kafka-clients stays the same, the jar is now org.apache.servicemix.bundles.kafka-clients-4.0.2-custom.jar.

SCR-2039

Summary: Upgraded the fabric8 Kubernetes client from 7.9.0 to 8.0.0

Effective: 8.55.481 and later

Upgraded the fabric8 Kubernetes client from 7.9.0 to 8.0.0 in io.fabric8.kubernetes, which keeps using the JDK HTTP client. No configuration change; no operator action is required.

  • fabric8 8.0 moved to Jackson 3, so the bundle embeds jackson-core, jackson-databind and jackson-dataformat-yaml 3.2.3 privately; the Jackson 2 imports are gone and only com.fasterxml.jackson.annotation 2.22 is still taken from the target platform.
  • snakeyaml-engine from 3.0.1 to 3.1.1, embedded in the same bundle.

SCR-2038

Summary: Upgraded eleven third-party libraries to their latest patch or minor release

Effective: 8.55.481 and later

Upgraded eleven third-party libraries to their latest patch or minor release. No API or configuration change; no operator action is required.

  • gRPC from 1.82.2 to 1.84.0 and the Google common protos from 2.60.0 to 2.78.0 in the gRPC API jars of com.openexchange.tools.grpc.common.
  • AWS SDK for Java from 2.55.1 to 2.55.6 in software.amazon.awssdk.
  • Lettuce from 7.7.0 to 7.8.0 in io.lettuce.
  • Firebase Admin SDK from 9.10.0 to 9.11.0 in com.google.firebase.
  • Google Auth Library from 1.52.0 to 1.54.0 in com.google.api.client and com.google.firebase, and Google API Common from 2.68.0 to 2.70.0 in com.google.api.client.
  • Box Java SDK from 10.17.0 to 10.19.0 in com.openexchange.file.storage.boxcom.
  • libphonenumber from 9.0.39 to 9.0.40 in com.openexchange.sms.
  • GlassFish HA API from 3.1.13 to 3.2.0 in com.openexchange.soap.common.
  • OkHttp and its logging interceptor from 5.4.0 to 5.5.0 in com.openexchange.opencloud, matching com.squareup.okhttp3.

SCR-2037

Summary: Upgraded seven third-party libraries to their latest patch or minor release

Effective: 8.55.481 and later

Upgraded seven third-party libraries to their latest patch or minor release. No API or configuration change; no operator action is required.

  • Jackson from 2.22.2 to 2.22.3 in the target platform, including the OX repacks of the Jakarta RS providers (jackson-jakarta-rs-*-2.22.3-custom.jar) and Jackson Datatype Joda embedded in com.openexchange.imageconverter.client.

  • Logback from 1.6.3 to 1.6.5 in the target platform.

  • SLF4J and its JCL, JUL, log4j and OSGi bridges from 2.0.19 to 2.0.20 in the target platform.

  • Apache HttpCore 5 and HttpCore 5 HTTP/2 from 5.4.3 to 5.4.4 in the target platform (httpcore5-5.4.4-custom.jar, httpcore5-h2-5.4.4-custom.jar).

  • MicroProfile Health API from 4.0.1 to 4.0.2 in the target platform (microprofile-health-api-4.0.2-custom.jar).

  • Stax2 API from 4.3.0 to 4.3.1 in the target platform.

  • Protocol Buffers from 4.35.1 to 4.36.2 in the gRPC API jars of com.openexchange.tools.grpc.common, matching the version the target platform ships.

API - HTTP-API

SCR-2198

Summary: New HTTP API module accountrecovery, JSlob entries and capability account_recovery

Effective: 8.55.481 and later

  • The new module accountrecovery has the actions get, set, confirmPhone and resendVerification. They serve the user's own recovery email, phone and PIN in the Settings pane.
  • The middleware announces the capability account\_recovery while the feature is on. It can be switched off per context or user with com.openexchange.capability.account\_recovery=false.
  • Without the capability the settings pane is hidden and the forgot-password flow sends no link. An existing link answers 403.
  • A new JSlob entry io.ox/core//features/accountRecovery carries the operator flag.
  • A second new JSlob entry tells the UI which channels the deployment can deliver on.

SCR-2192

Summary: New End-Point offline_grant and Error Code MCRED-0014 for Background Mail Access

Effective: 8.55.481 and later

An OpenID Connect back-end that enables com.openexchange.oidc.offlineGrantEnabled registers <dispatcherPrefix>/oidc/<backendPath>/offline_grant, through which a client manages the user's background mail access. Every request names a live session in session and carries its secret cookie:

  • GET ?flow=authorize starts the authorization at the identity provider. The browser returns to returnTo (a path on this host) with the outcome in offlineGrant (granted, denied or failed). With display=popup, it ends instead on a page that reports the outcome to its opener and closes itself.
  • GET ?flow=status answers {"state":"none"|"granted"|"rejected","features":[...]}.
  • POST ?flow=revoke revokes the grant and answers {"revoked":true|false}.

For the users of such a back-end, scheduling a mail for later and snoozing a mail fail with MCRED-0014 (category PERMISSION_DENIED) as long as the user has not allowed background access. A client is expected to start the authorization and repeat the request. Purely additive: back-ends without the property behave as before.

See OpenID Connect 1.0 SSO for the parameters and responses.

SCR-2188

Summary: New version of shared account data in the JSlob API and error code JSNCON-0012

Effective: 8.55.481 and later

The JSlobs io.ox/core, io.ox/mail and io.ox/calendar return a version of each shared account's data in meta.sharedaccount.<id>.version, e.g. for the categories of a shared account, which all its users edit. A client that passes this version back in sharedaccount.<id>.version with jslob?action=set or jslob?action=update no longer overwrites the changes of other users: outdated but unchanged data is skipped, and outdated changed data is rejected with the new error code JSNCON-0012. Requests without a version behave as before.

SCR-2186

Summary: New folders actions home, public and sharedWithMe, and the virtual folder drive://0/

Effective: 8.55.481 and later

Clients can open the folders of a user's default file storage directly (see com.openexchange.file.storage.default) and show all storages of the user side by side:

  • folders?action=home, folders?action=public and folders?action=sharedWithMe answer the home folder, the public folders and the folders shared with the user of the default storage; where the infostore is the default, its default folder and the folders below 15 and 10.
  • The virtual folder drive://0/ in tree 1 lists the root folder of every file storage account of the user: the default storage first, then the infostore as folder 9, then the other accounts by name. It is read-only and holds no items.
  • The field isDefaultAccount of fileaccount?action=all and action=get marks the default file storage, exactly one account per user. It marked the infostore before, which is still the default unless another storage is configured.

Purely additive apart from isDefaultAccount, which only answers differently where another storage is the default. See the folders API for further details.

SCR-2158

Summary: External recipients of a share are granted access by the file storage or rejected up front

Effective: 8.55.481 and later

Sharing a folder or file with an external recipient, i.e. a person invited by their mail address, is now decided by the file storage holding the item before a guest user exists. The built-in infostore creates a guest user of the installation and notifies the recipient, as before. A storage that resolves external recipients itself, which are OpenCloud and Nextcloud, gets the recipient handed down with the address: it grants access to its own user that carries the address, Nextcloud falls back to its share by mail for an unknown address, and the storage notifies the recipient itself. Such a grant is listed as a permission of the item with type: guest, the recipient's address and display name, so that clients see and revoke it like any other permission.

  • A folder of a storage that resolves external recipients itself states external_recipients among its supported_capabilities, so that a client can offer the invitation.
  • A storage that can do neither rejects the request with the new code FILE_STORAGE-0091, an address the storage does not know with FILE_STORAGE-0092.
  • The fields com.openexchange.folder.storage.share.external of a folder and com.openexchange.file.storage.share.external of a file list the sharing links the storage holds for the item only; the shares with users and groups are the item's permissions.
  • A folder of OpenCloud states the capability permissions only where the shares of folders are expressed as their permissions (com.openexchange.file.storage.opencloud.entityResolver.folderPermissions=true), a folder of Nextcloud whenever an entity resolver is available. A request that states permissions for anyone but the user while the shares are not expressed fails with the new code FILE_STORAGE-0093 instead of being silently ignored or partially applied.

No change for the infostore and for anonymous sharing links. See the folders API and the feature documentation for further details.

SCR-2156

Summary: New field mail_identity of a fetched mail and parameter mailIdentity of inline mail image URLs in the HTTP API

Effective: 8.55.481 and later

mail?action=get returns the new string field mail_identity if the mail store knows it. The value changes when a folder is deleted and re-created under the same name, which lets the server hand out the same mail IDs again; for IMAP it is the folder's UIDVALIDITY. image/mail/picture accepts the matching optional parameter mailIdentity, so that a client can tell an inline image of the re-created folder apart from the former one.

  • mail_identity: only present if known; clients treat it as an opaque string.
  • mailIdentity: if given, it is part of the image's ETag, and a value that does not match the mail's current identity is answered like an unknown Content-ID (404). Without the parameter, the behavior is unchanged.
  • Inline image URLs that the middleware generates itself, e.g. for view=html, carry mailIdentity. These URLs change once after the update, so browsers load such images once more.

SCR-2155

Summary: New Optional Argument applyDefaultAlarms When Saving iCal Mail Attachments via the Conversion Request

Effective: 8.55.481 and later

The data handler com.openexchange.chronos.ical of the conversion request conversion?action=convert, which saves the iCal attachment of a mail as appointment, accepts the new optional argument com.openexchange.groupware.calendar.applyDefaultAlarms. When set to true, alarms contained in the iCalendar data are skipped, and the calendar user's configured default alarms are applied instead.

"datahandler": {
  "identifier": "com.openexchange.chronos.ical",
  "args": [
    {"com.openexchange.groupware.calendar.folder": "<calendar-folder-id>"},
    {"com.openexchange.groupware.calendar.applyDefaultAlarms": "true"}
  ]
}

This is the counterpart of the import parameter applyDefaultAlarms (SCR-1875) for clients that add an appointment from a mail attachment through the conversion request. The change is purely additive - without the argument, alarms are taken over as before.

See the conversion request documentation for further details.

SCR-2144

Summary: New field sendAs in the deputy permission of the HTTP API

Effective: 8.55.481 and later

The deputy permission of the deputy module in the HTTP API carries the new optional boolean field sendAs. It lets the deputy send E-Mails as the granting user, i.e. without a Sender header, as an alternative to sendOnBehalfOf. The new capability deputy_send_as tells clients whether it can be granted (see SCR-2143).

  • sendAs and sendOnBehalfOf are mutually exclusive: passing both as true fails with DEPUTY-0015. An update that sets one of them to true without passing the other one clears the other one.
  • Granting sendAs without the capability fails with DEPUTY-0016, unless the stored value already is sendAs.
  • The reverse listings action=reverse and action=reverseIds report sendAs next to sendOnBehalfOf as effective values, and return grantorAddresses for both forms. If the same granting user grants the deputy both forms through different deputy permissions, sendAs takes precedence.

When sending, only the granted form is accepted: a deputy holding sendAs who adds a Sender header is rejected with MSG-0129, also for raw messages sent with mail?action=new and for mail?action=resend (unless the granting user's address also belongs to one of the deputy's own mail accounts); a deputy holding only sendOnBehalfOf who omits the Sender header is rejected with the new code MSG-0134. Replies and forwards from the granting user's Inbox are pre-filled with the granted form, and a mail of the granting user bounced with mail?action=bounce keeps its Reply-To.

Independent of "send as" and for every user: if resolving the From and a differing Sender fails and the deputy permissions of the From user cannot be looked up either, mail?action=transport returns the original error instead of falling back to the default account's transport.

Existing payloads keep working, with two visible differences: a deputy holding only sendOnBehalfOf who omits the Sender header now gets MSG-0134 instead of the generic MSG-0056, and a client that only knows sendOnBehalfOf reads false for a grant of sendAs, while setting sendOnBehalfOf to false does not revoke sendAs. See the API documentation and the feature documentation for further details.

SCR-2123

Summary: Added push topic ox:notification:deleted for notifications removed from the notification queue

Effective: 8.55.481 and later

Notifications removed from the user's notification queue are now announced via the new push topic ox:notification:deleted with payload {"ids":[...]}: on notifications?action=delete, on notifications?action=all with delete=true, on removal by the server (e.g. a returned snoozed mail) and when the oldest notification makes room at com.openexchange.notification.queue.maxNotificationsPerUser. Live change streams subscribed to ox:notification:* receive it as event notification; via Web Push, more than about 100 identifiers are left out and the payload is flagged truncated. Expired notifications and those of deleted users or contexts are removed without an event; clients reconcile via notifications?action=all&since=0. The order of ox:notification:new and ox:notification:deleted events is not guaranteed. An unlimited queue (com.openexchange.notification.queue.maxNotificationsPerUser set to 0 or less) is no longer emptied when a notification with a key is added.

SCR-2122

Summary: Added action webpushkey and Web Push subscriptions for the App Suite UI

Effective: 8.55.481 and later

The new action pns?action=webpushkey returns the application server key (applicationServerKey) a browser needs for PushManager.subscribe(); the response carries no key if Web Push is not available. With it, client open-xchange-appsuite subscribes via pns?action=subscribe with transport webpush, topics ox:notification:* and the browser subscription in meta (pushResource, p256dh, auth, contentEncoding); other topics are rejected for this client. Web Push for the UI can be turned off via com.openexchange.pns.transport.webpush.enabled.open-xchange-appsuite=false.

SCR-2121

Summary: Added push topic ox:notification:new for notifications of the notification queue

Effective: 8.55.481 and later

A notification added to the user's notification queue (module notifications) is now announced via the new push topic ox:notification:new. Live change streams (events endpoint) subscribed to ox:notification:* receive it as the new event notification, carrying the notification as notifications?action=get returns it; Web Push and Webhooks deliver it as well. A notification skipped because one with the same key is queued already is not announced again. After a resync, clients fetch the queue via notifications?action=all&since=0. Streams need com.openexchange.pns.transport.sse.enabled, Web Push a configured VAPID key pair.

SCR-2114

Summary: Added optional field data to notifications

Effective: 8.55.481 and later

Notifications returned by the actions all and get of module notifications may carry the new optional field data: a JSON object of at most 4096 characters that lets a client act on the notification, e.g. {"module":"mail","folder":"default0/INBOX","id":"1234"} to open the mail it is about. module names the App Suite module, the other fields depend on it. The field is only present if the notification carries data; the notification about a snoozed mail that waits for a login now points to that mail. Purely additive, existing clients are unaffected.

SCR-2102

Summary: New error code OAUTHMAIL-0002 for access tokens that cannot open an external mail account's password

Effective: 8.55.481 and later

A request made with an access token that holds mail_external receives OAUTHMAIL-0002 with HTTP status 403 when the stored password of an external mail account cannot be decrypted: the token keeps no password, because it was minted under master authentication or OAuth mail access, or the user's password changed since the token was issued. Before, such a request failed with a configuration or crypto error. The primary account is not affected.

SCR-2082

Summary: Folder restriction for personal access tokens

Effective: 8.55.481 and later

accesstoken?action=new takes an optional parameter folders: a JSON object that maps a module (mail, calendar, contacts, files, tasks) to 1 to 100 folder identifiers, e.g. {"mail":["default0/INBOX"]}. On the REST collection accesstokens it goes into the POST body. accesstoken?action=all and the answer of new report it as folders. Only the MCP server /mcp accepts such a token, and it treats every other folder of a restricted module as not found. The HTTP API and all other interfaces answer it with 401 and invalid_token. A server of an earlier version ignores folders and mints an unrestricted token, so clients should offer the restriction only once all nodes are updated.

SCR-2077

Summary: New action accesstoken?action=activity

Effective: 8.55.481 and later

The access token module of the HTTP API offers accesstoken?action=activity&id=<id>&limit=<n>. It lists the calls recorded for one of the user's tokens, newest first: time, client, tool, the folder and item identifiers, preview or write, and the outcome; never content. Calls through the MCP server /mcp are recorded, under the grant of an OAuth client and under the token itself for a personal access token. id is the identifier the token listing names; limit 1 to 1000, default 1000. An identifier of no token of the user yields no entries. Requires a full session.

SCR-2075

Summary: Access token listing shows each grant of the MCP authorization server once

Effective: 8.55.481 and later

accesstoken?action=all and GET /accesstokens list what a user granted an OAuth client of the built-in authorization server of /mcp once per grant, not once per token the client gets by refreshing. Such an entry has the grant identifier as id, the client's name or redirect host as label, and expires with the grant. It carries the new object client with id, name (as the client declared it, absent if none) and redirect_host. Revoking that identifier through accesstoken?action=delete or DELETE /accesstokens/{id} revokes the grant with all its tokens. Personal access tokens are listed as before.

SCR-2067

Summary: Step-up response for restricted actions and no session identifier under external authentication

Effective: 8.55.481 and later

If external authentication is enabled, actions that require a recent authentication now answer a request whose authentication context class (ACR) is too low with HTTP status 403, the body {"error":"reauthentication_required"} and the header X-Deny-Reason: step_up_required, so a client can trigger a step-up authentication. The new error code RA-0007 ("Please re-authenticate to perform this action.") backs it.

Also the responses of login and system?action=whoami omit the field session, and the login response omits the multifactor flags. Without external authentication both responses are unchanged.

SCR-2062

Summary: Qualified Folder Identifiers in the Attachment Module

Effective: 8.55.481 and later

The folder parameter of the attachment module and the folder field of AttachmentData are now declared as string. Besides the plain numeric identifier, they accept the qualified folder identifier of a groupware account, e.g. con://37/727 for a contact in an address book shared from another context. That way, attachments of contacts in such address books can be managed like any other. Responses echo the folder identifier as requested, and attachments of foreign contacts report no creator.

Plain numeric identifiers work as before. Clients generated from the OpenAPI definition now pass the folder as a string.

See the HTTP API documentation for further details.

SCR-2043

Summary: New request sharedaccount?action=permissions and capability listPermissions for the holders of a shared account

Effective: 8.55.481 and later

Users of a shared account can be allowed to see who else has access to it.

GET /ajax/sharedaccount?action=permissions&accountId=[user]@[context] answers with one entry per user or group holding a permission, including the holders in other contexts. An entity of a foreign context is identified by its qualified [user]@[context] form alone and carries no numeric entity, the same way it appears in a folder permission. The listing reports who holds a permission, not which permission each of them holds, and is preliminary.

Access is decided by the new shared account capability listPermissions, evaluated like every other capability of a shared account. It is not granted by default: add it to com.openexchange.sharedaccount.defaultCapabilities so that everyone using a shared account sees the list, or grant it per permission so that only selected users do. Denying it for a single permission takes it away from that holder only. Without it the request is answered with SAC-0009.

Purely additive, and without configuration nobody can call it.

See the feature documentation, as well as the HTTP API documentation for further details.

SCR-2028

Summary: Changed status and scope handling of OAuth account requests

Effective: 8.55.481 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-2020

Summary: New Mobile API endpoints for message lists, conversations and search

Effective: 8.55.481 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.

POST /mobile/v1/mail/search also searches all mail accounts of the user with scope.allAccounts = true: the subscribed folders that hold messages, trash and spam left out, merged newest first. An account that cannot be searched is named in unavailableAccounts of the page instead of failing the search. Such a search counts once per account against com.openexchange.mobile.api.rateLimit.requests.

A search across accounts counts all its accounts against the budget at once: if not enough are left, it fails with 429 without counting any, and Retry-After covers all of them. The primary account, too, is named in unavailableAccounts if it cannot be searched in time.

POST /mobile/v1/mail/search searches one mail account with scope.accountId: its subscribed folders that hold messages, trash and spam left out, as with scope.allAccounts. Such a search counts once against the request budget. An account the user does not have is answered with 400; an account that cannot be searched in time is named in unavailableAccounts of an empty page.

With scope.allAccounts and scope.accountId, a folder that cannot be searched, e.g. because access to it was withdrawn, is left out of the page and named in unavailableFolders; the other folders are searched as usual.

SCR-2018

Summary: New Mobile API endpoints to read a message, its body, source, attachments and attachment previews

Effective: 8.55.481 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 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, and proxied images get absolute URLs; .../raw and .../attachments/{attachmentId} return the source and an attachment with Range support (206 with the part's content type, 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 or for a later page.

Where virus scanning is enforced, source, attachments and previews are scanned first; infected content is refused with 403 and code content_blocked. A message that offers one-click unsubscription (RFC 8058) carries listUnsubscribeOneClick, and readReceiptRequested is always false in the sent and drafts folders.

Sending the read receipt a message asks for: POST /mobile/v1/mail/messages/{messageId}/read-receipt (scope write_mail, honors Push-Subscription-Id) sends it and sets $MDNSent, so readReceiptRequested turns false on every device and no other device offers the receipt again. The receipt goes out from the user's address the message was sent to, an alias included, else from the send address; for an external account from that account's address. A receipt sent before is not sent again and answered with 204. A message that asks for no receipt, and every message in the sent and drafts folders, is answered with 409 and the new code read_receipt_not_requested.

The read receipt accepts an optional Idempotency-Key and is marked sent before it goes out, so it is sent at most once even if several devices send it at the same time.

SCR-2017

Summary: New Mobile API endpoint to change many messages at once

Effective: 8.55.481 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.

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.

SCR-2014

Summary: New Mobile API operations for live events and batch requests

Effective: 8.55.481 and later

The Mobile API gets two operations:

  • GET /mobile/v1/events: live changes as Server-Sent Events. state events carry an EventPayload with the API's folder identifiers and the topics mail.message.* and mail.folder.changed. The stream needs scope read_mail, skips changes made by its own device and answers 404 where com.openexchange.pns.transport.sse.enabled is false for 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 and Idempotency-Key apply 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 code, e.g. invalid_token, and a type of https://documentation.open-xchange.com/mobile/v1/problems/<code>. Status codes and headers are unchanged.

A batch entry with forEach runs once per element of an array an earlier entry returned, e.g. once per folder; $item stands for the element. Its body lists the result of each run. All runs of one batch are bounded by com.openexchange.mobile.api.batch.maxFanOut (Me.limits.maxBatchFanOut); an entry beyond fails with 413. Each run counts against com.openexchange.mobile.api.rateLimit.requests.

In mode idp, a Push-Subscription-Id that names none of the user's subscriptions is ignored when a stream opens, so it cannot get around the limit of streams per user. A forEach over an operation that requires an Idempotency-Key, e.g. sending, fails with 400 and does not run.

SCR-2013

Summary: New Mobile API endpoint for the message changes of a mail folder

Effective: 8.55.481 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.

SCR-2012

Summary: New Mobile API endpoints for mail folders

Effective: 8.55.481 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.

SCR-2010

Summary: New Mobile API endpoints for sign-in, account data and recipient suggestions

Effective: 8.55.481 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, stableFolderIds and a state: ok, credentials_invalid, unreachable or secret_unavailable (the stored password cannot be decrypted for the request, e.g. an external account whose password is encrypted with the login password, which the access token does not provide; folder and message requests for such an account get 403 with mail_access_unavailable); 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.

The login name of POST /mobile/v1/auth/token is limited to 256 characters.

In mode token, POST /mobile/v1/auth/token/rotate issues a new secret for the presented token and renews its lifetime: the new expiresAt is now plus the lifetime the token was minted with, capped by com.openexchange.accesstoken.maxLifetimeDays. Identifier, scopes, push subscriptions, the kept mail credential and scheduled mails stay. The presented secret stays valid for graceSeconds (300) or until the next rotation; a retry with the same secret and Idempotency-Key gets the same new secret. Of two rotations with the same secret, the second gets 409 with code conflict; a rotation with a secret that another rotation replaced more than a minute earlier revokes the token. The response of GET /mobile/v1/auth/token and of a sign-in now carries rotateAfter, a quarter of the lifetime. In mode idp the operation answers 404, and it cannot be part of a batch.

SCR-2007

Summary: New Mobile API endpoints for push subscriptions

Effective: 8.55.481 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.

Revoking an access token releases the push credential only if it was copied from that token; one kept from a password session or another token stays. A PATCH of the topics or the environment sets up the mail listener for new-message pushes like a registration does.

SCR-1995

Summary: New Mobile API for native mail apps at /mobile/v1

Effective: 8.55.481 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 with 401.
  • GET /mobile/v1/auth/config needs 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 code 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.

The server bounds how many requests a user makes per time window and how many run at once; each entry of a batch counts, the event stream does not. Beyond that, a request is answered with 429, code rate_limited and Retry-After without any effect; the client waits that long and retries. The limits are set through com.openexchange.mobile.api.rateLimit.*, com.openexchange.mobile.api.idempotency.* and com.openexchange.mobile.api.uploads.maxConcurrent. In mode idp, a token the identity provider issued to another client is answered with 401 and invalid_token unless com.openexchange.mobile.auth.idp.checkAudience is false.

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.

A failing key-value store is answered with 503 and Retry-After instead of 500. While the store cannot be reached, an operation that requires an Idempotency-Key, such as sending, performs nothing and is answered the same way; one with an optional key runs without the protection. GET /mobile/v1/mail/folders returns no state while an account could not be read; an account without mail access, e.g. in state secret_unavailable, counts as read without folders. At the snapshot limit, superseded states are dropped first. For JMAP accounts, a delta with counts reports every change for all folders of the account.

Clients send Accept: application/problem+json together with application/json on every request; the problem body is guaranteed only when Accept allows it. A 401 may carry more than one WWW-Authenticate challenge; clients evaluate the Bearer challenge and ignore the others. The middleware itself keeps sending the problem body whatever Accept says, with a single challenge, so a client that follows both rules behaves the same whether or not an authentication layer checks tokens in front of the middleware.

POST /mobile/v1/mail/folders/{folderId}/empty removes all messages of a folder in one call: they go to the trash unless the folder is in the trash, permanent=true is given or the user deletes for good. It requires Idempotency-Key. POST /mobile/v1/mail/folders/{folderId}/mark-read marks all messages of a folder as read. GET /mobile/v1/mail/accounts/{accountId}/quota tells how much of its storage and message quota a mail account uses; a quota the mailbox does not have is left out.

With categoryId, mark-read marks only the messages of that inbox category, the same ones listMessages with categoryId lists; it is accepted for the inbox of the primary account only.

SCR-1988

Summary: Calendar Folders of a Shared Account Report Their Own Folder Type

Effective: 8.55.481 and later

The calendar folders of a shared account report the folder type they have within the account. Previously they were reported as "shared" to the requesting user, whatever their type in the account was.

  • type is 1 ("private") for the account's calendars, where it was 3 ("shared") before.
  • The folders are listed in the private section of folders?action=allVisible instead of the shared one.
  • A Trash folder of the account is reported with its own type 16.
  • Appointments of the account are found by the contact halo, which searches the calendar user's private and public folders.

A client that reads type == 1 as "belongs to the acting user" will now include a shared account's calendars in that answer. The property to test instead is com.openexchange.calendar.provider, which is sharedaccount for exactly these folders.

The contents of the folders are unaffected. Classification, default participation status and permissions are decided within the shared account's own session, where the calendar user is the account itself: an appointment classified as PRIVATE in such a calendar is delivered to every user of the account in full, as before.

See the HTTP API documentation for further details.

SCR-1974

Summary: New capabilities access_tokens and mcp

Effective: 8.55.481 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. 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.

SCR-1946

Summary: New HTTP API module accesstoken for personal access tokens

Effective: 8.55.481 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.

The same actions are also available REST-like on the collection accesstokens below the dispatcher prefix, in the style of the mail compose REST API: GET accesstokens lists the tokens, POST accesstokens mints one from a JSON body with the fields label, scopes (an array or a comma-separated string) and expires, and DELETE accesstokens/{id} revokes one. As with the module, errors are answered with 200 and the error in the JSON body.

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-1873

Summary: New Restore Action and Soft-Delete Parameters in the Chronos Module

Effective: 8.55.481 and later

The calendar Trash folder adds one action and several parameters to the HTTP API. The action and the parameters are additive, but the behavior of an ordinary delete changes with them: the feature is disabled by default (com.openexchange.calendar.trashFolder.enabled); where it is switched on for a user, PUT /chronos?action=delete moves an appointment into the Trash instead of removing it, and a caller that wants the permanent delete passes harddelete=true. The OpenAPI definitions live in http-api/http_api/.

  • PUT /chronos?action=restore - puts a soft-deleted event back, answering with ChronosRestoreResultResponse (RestoreData, RestoreDetails).
  • PUT /chronos?action=delete - new harddelete, bypassing the Trash, and new undo, answering with an undo_token for the undo module.
  • PUT /chronos?action=update and PUT /chronos?action=updateAttendee - new harddelete, for the occurrences an update takes out of a series.
  • PUT /folders?action=delete - the existing hardDelete now also decides the fate of what the deleted folder holds.
  • New dryRun, outcome and targetFolder parameters, the new trashed event flag, the new trash folder capability and the calendar_trash capability.
  • New read-only event fields trashedFrom, trashedBy and trashedAt, present while an event sits in the calendar user's Trash folder - where it came from, who deleted it and when, the last being what a retention window is counted against. Reported on the event whichever row stores them, so a client reads them in one place.
  • warnings is now declared on the calendar result responses (ChronosCalendarResultResponse, ChronosMultipleCalendarResultResponse, ChronosRestoreResultResponse), where the envelope has always been able to carry it.
  • New meta object on the calendar result of PUT /chronos?action=delete and PUT /chronos?action=update, currently holding trashFolderId: the Trash folder provisioned while carrying out that very request, as a composite folder identifier. The folder is created on the calendar user's first soft deletion, so it appears in the folder tree as a side effect of a request about appointments; naming it lets a client add it to a tree it already holds. Present only on the request that created the folder, and carried in the payload rather than as a warning, a warning on the envelope being lost when a client bundles its requests into a multiple.
  • Ten new error codes: CAL-4123 to CAL-4129 and CAL-41210 to CAL-41212.

A restore is evaluated before it is applied, the calendar having moved on while the event sat in the Trash: the action answers with the ways it can come back, dryRun returns that evaluation without writing anything, and outcome commits to one of them. Where a trashed date's series is in the Trash as well, the evaluation names that series' own Trash entry in verdicts.sourceEntry (folder, id), which the CAL-4124 refusal carries too: a binned series is stored under a fresh unique identifier, so nothing on the date's own entry leads to it. An undo_token is only handed out where the deletion can be reversed whole - several occurrences of one series deleted at once share a Trash entry and yield none. A result reports what became of the entry in the folder views the request covers, so a client that wants the Trash entry named - on a delete, or on a restore that empties it - passes expand=true.

Each way back carries a translated label and, where the label alone does not say what it does, a one-sentence consequence; situation carries one sentence per thing standing in the way, followed by one consequence for other people where the restore has it: an organizer's restore invites the participants again. Dates are left out of the sentences; verdicts.occurrences names them where an entry holds several. The outcome rejoinAsAddedDate is reserved and never offered. restoreRebased is offered only where following the series actually changes the date; otherwise the plain restore is the only way back. With checkConflicts=true, a taken slot is reported as conflicts with status refused, as on create and update, rather than as an error.

An incoming cancellation is not applied unattended where it would leave dates the user has soft-deleted with nothing to go back into: a series called off while individual dates of it sit in the user's Trash folder answers with CAL-41212 instead, so the decision is theirs. Applying it explicitly is unaffected; the interactive analysis of such a message names the dates concerned.

Two effects reach beyond the calendar: hardDelete=true on /folders?action=delete now removes a deleted folder's contacts permanently instead of moving them to the contacts Trash, and the undo module now reports a failure where an undone operation answers with an error, instead of reporting success having restored nothing.

See the Chronos API and the Folders API, as well as the "Trash Folder / Soft Delete" section of the calendar implementation details for further details.

API - Java

SCR-2175

Summary: DBPool Moved to the Package com.openexchange.server.database

Effective: 8.55.481 and later

The class DBPool moved from the package com.openexchange.server.impl to com.openexchange.server.database; code using it has to import it from there, and a bundle needs com.openexchange.server.database in its Import-Package. The server-internal classes Starter, InMemoryReplicas and Constants of that package moved to the unexported package com.openexchange.server.internal. The classes are otherwise unchanged. The packages com.openexchange.server.impl, com.openexchange.groupware.container, com.openexchange.groupware.contact.helpers, com.openexchange.groupware.search and com.openexchange.ajax.fields are now exported by the new bundle com.openexchange.groupware.container instead of com.openexchange.server, under the same names, so existing imports keep resolving.

SCR-2068

Summary: Replaced the scope flags of the RestrictedAction annotation by an ACR check

Effective: 8.55.481 and later

The annotation com.openexchange.ajax.requesthandler.annotation.restricted.RestrictedAction replaces the attributes enforceCustomRestrictedAccessCheckForBasicScope and requiresFullAuth by requiresACRCheck (default false) and getMinACRValue (new enum com.openexchange.session.ACRValue, default FRESH). GeneralScopes.BASIC and GeneralScopes.ELEVATED are merged into GeneralScopes.ALL (*), and Scope.hasBasicAccess() is removed.

Custom actions using the removed attributes or constants have to be adapted.

API - REST

SCR-2197

Summary: New REST API /account-recovery for password reset and administrative recovery details

Effective: 8.55.481 and later

Public endpoints without authentication:* POST /account\-recovery/v1/forgot\-password starts a recovery. The response is the same whether the address matched or not.

  • GET /account\-recovery/v1/reset?token=… validates a token and returns the primary address in masked form.
  • POST /account\-recovery/v1/reset consumes the token and changes the password.
  • GET /account\-recovery/v1/password\-requirements returns the password policy. An optional token parameter selects that user's policy.
  • GET|POST /account\-recovery/v1/verify\-email confirms a recovery email address.
  • The legacy paths /account\-recovery/request, /account\-recovery/waypoint, /account\-recovery/reset and /account\-recovery/requirements behave the same for existing integrations.

Status codes:

  • 410 for an unknown, expired or used token.
  • 403 for a user without the account\_recovery capability.
  • 503 when no password change backend is available.

Administrative endpoints with HTTP basic authentication as master or context administrator. They take values in the JSON body only:

  • GET|PUT /admin/v1/account\-recovery/details/email/{email}
  • PUT /admin/v1/account\-recovery/password/email/{email}

SCR-2147

Summary: New attribute sendAs in the SCIM resource Deputies

Effective: 8.55.481 and later

The SCIM resource Deputies (schema urn:ietf:params:scim:schemas:extension:openxchange:2.0:Deputy) carries the new boolean attribute sendAs. It lets the deputy send E-Mails as the grantor without a Sender header and is mutually exclusive with sendOnBehalfOf; a request setting both to true is rejected, and setting sendOnBehalfOf to false does not revoke sendAs, so both have to be false to revoke any sending right. As with sendOnBehalfOf, a deputy permission granted to a group applies to every member; if the same deputy holds both forms from one grantor, e.g. directly and through a group, sendAs takes precedence. Like the other administrative interfaces, SCIM is not restricted by com.openexchange.deputy.sendAsEnabled, but while it is disabled for the grantor, sendAs acts as sendOnBehalfOf (see SCR-2143).

Existing SCIM clients keep working. Since every deputy resource now carries sendAs, its meta.version (ETag) changes once with this update: a conditional request with an ETag read before the update fails once with 412 Precondition Failed, and the client has to read the resource again. See the SCIM documentation for further details.

SCR-2141

Summary: New MailCredentialAdminService operation to search a context's kept mail credentials

Effective: 8.55.481 and later

The com.openexchange.grpc.provisioning.MailCredentialAdminService has the new operation SearchMailCredentials (HTTP gateway: GET /prov/v1/contexts/{context_id}/mail-credentials). It returns the credentials kept in a context with the user each is kept for, optionally filtered by state and feature, page_size at a time (default 100, at most 1000); next_page_token leads to the next page. MailCredential now carries user_id in every answer, so ?state=rejected names the users behind appsuite_mailcredential_rows_rejected. To use it over HTTP, add the service to the gateway's services.

SCR-2106

Summary: New MailCredentialAdminService operations to repair or refresh a kept mail credential

Effective: 8.55.481 and later

com.openexchange.grpc.provisioning.MailCredentialAdminService has two new operations. Each acts on one kept mail credential right away instead of waiting for the maintenance job, and answers with an outcome and the credential as it stands afterwards.

  • RepairMailCredential (HTTP gateway: POST /prov/v1/contexts/{context_id}/users/{user_id}/mail-credentials/{credential_id}:repair) repairs a credential whose refresh token the identity provider rejected, from a live session of the user. Outcomes: repaired, no_live_session, failed, not_rejected, caller_key, not_found.
  • RefreshMailCredential (HTTP gateway: POST …/mail-credentials/{credential_id}:refresh) refreshes its tokens at the identity provider regardless of schedule. Outcomes: refreshed, not_refreshable, rejected, deferred, failed, caller_key, not_found.

To use them over HTTP, add the service to the gateway's services.

SCR-2098

Summary: New provisioning service MailCredentialAdminService for a user's kept mail credentials

Effective: 8.55.481 and later

The provisioning API gets the service MailCredentialAdminService (mailcredential.proto). A context or master administrator lists and releases the mail credentials the vault keeps for a user. Over the provisioning HTTP gateway it answers under /prov/v1/contexts/{context_id}/users/{user_id}/mail-credentials:

  • GET .../mail-credentials: the user's kept credentials, oldest first, with id, feature, kind (session, exchanged, master), callerKey, created, expires, state (kept_alive, due, not_kept_alive, rejected, expired) and refreshDue. Nothing returned reveals what a credential holds or the login it was kept for.
  • DELETE .../mail-credentials/{credential_id}: releases one; a credential of another user is not released (released: false).
  • DELETE .../mail-credentials?feature=<feature>: releases all of a feature, e.g. push.

A release logs out exchanged tokens. Over RMI the same is OXMailCredentialInterface (OXMailCredential). The gateway does not expose the service by default; add it to the gateway's services. Purely additive.

SCR-2065

Summary: New and changed REST end-points under /authentication/v1 for the external authentication service

Effective: 8.55.481 and later

The REST end-points of bundle com.openexchange.external.authentication, protected by Basic authentication, gain the resources the external authentication service drives:

  • POST /authentication/v1/oidc/authorize-url, /handoff, /refresh, /logout, /end-sessions and /logout-token/verify - the OpenID Connect flow, from building the authorization URL to verifying a back-channel logout token
  • POST /authentication/v1/multifactor?action=list|startAuthentication|finishAuthentication - the multifactor devices of a user and their authentication
  • POST /authentication/v1/login-handlers - triggers the middleware's login handlers out of band, since no login runs through the middleware
  • POST /authentication/v1/login?loginAction= with the new actions guest, shareResolve and resetGuestPassword; the action share is replaced by shareResolve

A login answered with a challenge (e.g. a password-protected share or a second factor) now returns it in the response body instead of a plain failure.

All errors of these end-points are answered with the standard error JSON. A rejected request is answered with 400 or 403 and is not worth retrying, e.g. invalid credentials, an unknown or expired OIDC flow token, an invalid logout token or a refresh token the provider rejected for good (new error code OIDC-0029); a server fault is answered with 5xx.

See the REST API documentation for further details.

SCR-2025

Summary: New provisioning service MailFilterAdminService for a user's mail filter rules

Effective: 8.55.481 and later

The provisioning API gets the service MailFilterAdminService (mailfilter.proto). A context or master administrator reads and changes the mail filter rules of a user without the user's password. Over the provisioning HTTP gateway it answers under /prov/v1/contexts/{context_id}/users/{user_id}/mailfilter:

  • GET .../mailfilter: whether administrative access applies, the strategy in use, the Sieve capabilities and the script_hash of the active script
  • GET and POST .../mailfilter/rules, GET, PATCH and DELETE .../mailfilter/rules/{rule_id}, POST .../mailfilter/rules:reorder and POST .../mailfilter:purge
  • GET .../mailfilter/script: the active script and its hash

A rule is the mail filter JSON of HTTP API v2 in the field definition. Every write takes an optional if_match; if the active script changed since that hash was read, the call yields 409 and writes nothing; the check is best effort, so a write by another node or ManageSieve client in between can still be overwritten. Access goes through a token, DoveAdm or master authentication, see SCR-2023. A caller can pass a token the mail server accepts in the header mail-authorization: Bearer <token>; it applies only to the user the call addresses by identifier, and a token the mail server rejects yields 403. DeputyPermissionService accepts the same header. The gateway does not expose the service by default, see SCR-2024. Purely additive.

A token login on a ManageSieve server that lacks a matching SASL mechanism fails with MAIL_FILTER-0051 and gRPC status FAILED_PRECONDITION instead of falling back to PLAIN; an unknown user_id yields NOT_FOUND.

API - RMI

SCR-2209

Summary: RMI status and listing methods require administrator credentials

Effective: 8.55.481 and later

The methods getMigrationStatus and getLockStatus of the RMI interface DBMigrationRMIService take an additional Credentials parameter and require the master administrator, as forceMigration, rollbackMigration and releaseLocks already do. Before, they checked no credentials. The command-line tool dbmigrations already asks for these credentials and now passes them on. Clients calling the RMI interface directly must pass credentials. Both methods can now also be forwarded to the site that holds the schema, which SCR-1882 left open.

The read methods of four more RMI interfaces take an additional Credentials parameter, too. Before, they checked no credentials:

  • ConsistencyRMIService: all listMissingFiles* and listUnassignedFiles* methods require the master administrator.

  • FileChecksumsRMIService: listFilesWithoutChecksumInContext requires the context administrator, listFilesWithoutChecksumInDatabase and listAllFilesWithoutChecksum the master administrator.

  • LoginCounterRMIService: getLastLoginTimeStamp requires the context administrator.

  • ShareRMIService: listShares by context, or by context and guest, requires the context administrator; by token, the master administrator.

Over RMI, UpdateTaskRMIService now rejects getExecutedTasksList, getPendingTasksList and runUpdateFor without credentials; use the variants that take credentials.

The tools checkconsistency, calculatefilechecksums, shares and lastlogintimestamp already ask for administrator credentials and now pass them on. lastlogintimestamp used to ignore them and now requires the context administrator.

SCR-2199

Summary: Recovery details in the RMI and SOAP provisioning API

Effective: 8.55.481 and later

  • com.openexchange.admin.rmi.dataobjects.User gains recoveryEmail, recoveryPhone and recoveryPin, each with get, set and is…Set methods.
  • createuser and changeuser through RMI write these fields to user\_recovery\_details. An empty string clears a field, and an unset field leaves it unchanged.
  • The SOAP User data object of the user, context and reseller services carries the same three fields.
  • A provisioned password change revokes the user's open reset links.

SCR-2184

Summary: ExternalAccountRMIService list methods require administrator credentials; listexternalaccounts and deleteexternalaccounts accept the context administrator

Effective: 8.55.481 and later

All list methods of the RMI interface ExternalAccountRMIService take an additional Credentials parameter and authenticate it against the given context, as delete already does. Before, they checked no credentials at all. The context administrator is accepted; the master administrator only if MASTER_ACCOUNT_OVERRIDE is enabled. Accordingly, listexternalaccounts and deleteexternalaccounts now take the context administrator's credentials. Scripts that call listexternalaccounts as master administrator need MASTER_ACCOUNT_OVERRIDE=true or have to switch to the context administrator. Clients calling the RMI interface directly must pass credentials.

SCR-2177

Summary: DataExportRMIService requires credentials for read operations and offers administrative data exports

Effective: 8.55.481 and later

The read operations of DataExportRMIService take an additional Credentials parameter and reject callers without valid administrator credentials:

  • getDataExportTask and getDataExportTasks(int, Credentials) require the context administrator, the master administrator only where MASTER_ACCOUNT_OVERRIDE is enabled
  • getDataExportTasks(Credentials), getOrphanedWorkItems, getOrphanedFileStoreLocations and getOrphanedResultFiles require the master administrator

The command-line tools listdataexports and dataexportconsistency already ask for these credentials and now pass them on. RMI clients calling the old signatures have to be updated. The read operations of a context can now also be forwarded to the site that holds it, which SCR-1882 left open.

New operations let an administrator export the data of a user without the user logging in, e.g. for an authority. They take the same credentials as getDataExportTask:

  • submitAdministrativeDataExportTask starts the export, with a mandatory case reference of at most 255 characters
  • getAdministrativeDataExportTask, cancelAdministrativeDataExportTask and deleteAdministrativeDataExportTask act on it
  • readAdministrativeDataExportPackage reads a result package in chunks of at most 4 MiB

A user has at most one administrative export, kept apart from the user's own; the user is not notified about it. Without a supplied mail credential, mail is exported through master authentication where com.openexchange.mail.admin.strategies lists master; otherwise mail is skipped, as is any module the user has no permission for. The task metadata gains origin, case reference, requested by and skipped modules, the result file metadata sha256. Submitting, downloading, canceling and deleting are logged to com.openexchange.gdpr.dataexport.auditTrail.

submitAdministrativeDataExportTask gains an overload with an AdministrativeMailCredential for the user's mailbox, used instead of master authentication: a password, ofPassword(masterUser, password), or an OAuth access token with optional refresh token, expiry and OpenID Connect back-end, ofOAuth(masterUser, accessToken, accessTokenExpires, refreshToken, oidcBackendPath, mechanism) with mechanism XOAUTH2 (default) or OAUTHBEARER. MW sends it through PLAIN or that mechanism with the user's mail login, whatever mail authentication is configured. Either may be a master user's: MW then logs in as <user's mail login><separator><master user> with com.openexchange.mail.admin.masterUserSeparator, so the administrator names the master user only, never the mailbox. For an OAuth master user, the Dovecot master passdb has to refuse non-master users with USER_DISABLED. The credential is kept encrypted in the mail credential vault for the lifetime of the export only, checked against the mail server on submit and never logged; mail is skipped if the mail server refuses it.

SCR-2146

Summary: New field sendAs in the deputy permission data objects of the RMI and gRPC interfaces

Effective: 8.55.481 and later

The RMI data objects com.openexchange.admin.rmi.dataobjects.DeputyPermission (and thereby ActiveDeputyPermission) and DeputyPermissionDescription carry the new boolean field sendAs, which lets the deputy send E-Mails as the granting user without a Sender header. The description object tracks it with the companion isSendAsSet flag, so an absent field keeps the current value on update.

The gRPC provisioning API mirrors it in deputy.proto: DeputyPermission.send_as (field 8) and the description pair sendAs (15) with SendAsSet (16).

Semantics match the SOAP interface: sendAs is mutually exclusive with sendOnBehalfOf (DEPUTY-0015), setting sendOnBehalfOf to false does not revoke sendAs, and com.openexchange.deputy.sendAsEnabled does not restrict administrative provisioning, but while it is disabled for the granting user, a grant of sendAs acts as sendOnBehalfOf (see SCR-2143). Apart from the missing Sender header, sendAs behaves like sendOnBehalfOf: a deputy permission granted to a group applies to every member (if the same deputy holds both forms from one granting user, e.g. directly and through a group, sendAs takes precedence), and com.openexchange.deputy.simplePermissionMode restricts neither form. The shared account interface OXSharedAccount keeps sendAs when it re-creates deputy permissions. 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.

SCR-2142

Summary: New OXMailCredentialInterface method to search a context's kept mail credentials

Effective: 8.55.481 and later

OXMailCredentialInterface has the new method searchMailCredentials, which returns the new MailCredentialPage: the credentials found and the token of the next page. MailCredentialInfo has the new getUserId, which this and the other methods fill.

SCR-2116

Summary: Admin RMI calls for a context on another site report errors the client can load

Effective: 8.55.481 and later

In a multi-site deployment, OXMailCredentialInterface and OXMailFilterInterface forward a call for a context on another site over gRPC. An error from that site reached the RMI client as an UnmarshalException: the client cannot load the server-side exception class. Now the client gets the StorageException or InvalidDataException the other site reported, or a StorageException with its message. A user of a context on another site has to be given by its identifier; a user given by name only is rejected with an InvalidDataException instead of being looked up as user 0. Missing credentials for a context on another site are rejected with an InvalidCredentialsException instead of a NullPointerException; this applies to all provisioning RMI interfaces.

SCR-2107

Summary: New OXMailCredentialInterface methods to repair or refresh a kept mail credential

Effective: 8.55.481 and later

OXMailCredentialInterface has two new methods, repairMailCredential and refreshMailCredential. They take the same parameters as releaseMailCredential and return the new MailCredentialActionResult: the outcome and the credential as it stands afterwards. The outcomes match the operations RepairMailCredential and RefreshMailCredential of MailCredentialAdminService.

API - SOAP

SCR-2145

Summary: New element sendAs in the deputy permissions of the OXDeputyPermissionsService

Effective: 8.55.481 and later

The data objects DeputyPermission and ActiveDeputyPermission of the OXDeputyPermissionsService carry the new optional, nillable boolean element sendAs. It lets the deputy send E-Mails as the granting user, i.e. with the granting user as From and without a Sender header, as an alternative to sendOnBehalfOf.

The element is appended as the last element of both complex types, so the element sequence of the existing WSDL stays unchanged. It is accepted by the grant, grantMultiple and update operations and returned by get, list and, through the embedded ActiveDeputyPermission objects, by listGivenTo.

On update an absent element keeps the current value; setting sendAs or sendOnBehalfOf to true without passing the other one clears the other one. Passing both as true fails with a fault carrying DEPUTY-0015. Setting sendOnBehalfOf to false does not revoke sendAs; to revoke any sending right, pass both as false. The property com.openexchange.deputy.sendAsEnabled does not restrict this interface, but while it is false for the granting user, a grant of sendAs acts as "send on behalf of" (see SCR-2143). Existing SOAP clients are unaffected. Example grant 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:Body>
    <soap:grant>
      <soap:deputyPermission>
        <xsd:userId>7</xsd:userId>
        <xsd:modulePermissions>
          <xsd:moduleId>mail</xsd:moduleId>
          <xsd:admin>false</xsd:admin>
          <xsd:folderPermission>2</xsd:folderPermission>
          <xsd:readPermission>4</xsd:readPermission>
          <xsd:writePermission>0</xsd:writePermission>
          <xsd:deletePermission>0</xsd:deletePermission>
        </xsd:modulePermissions>
        <xsd:folderMode>default</xsd:folderMode>
        <xsd:sendAs>true</xsd:sendAs>
      </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-2212

Summary: Enforced the Mail Permission Set of Shared Accounts for All Mail Operations

Effective: 8.55.481 and later

On the mail server, users of a shared account act as the shared account itself. So far, the mail permission set of a shared account permission was only reflected in the indicated own_rights, and checked for hard deletion and import. Now each mail operation is checked against the IMAP rights the permission set allows.

As a result, users with the viewer or editor permission set can no longer move, delete or archive mails, set \Deleted, expunge or clear folders, or copy mails into the shared account. Other flags and color labels remain settable. Users holding sendAs or sendOnBehalf still get the copies of the mails they send stored in the shared account's Sent folder. A refused deletion now reports IMAP-2006 instead of MSG-0070, for all mail accounts, not only shared ones.

No operator action is required; users who need to file away or delete mails need the author permission set. See the shared accounts documentation for how the permission sets map onto IMAP rights.

SCR-2207

Summary: Changed the resolution of a local time in the repeated hour at the end of daylight saving time

Effective: 8.55.481 and later

A local time that occurs twice because the clocks are set back, e.g. 02:30 on 25 October 2026 in Europe/Berlin, now refers to its first occurrence, still in daylight saving time, as RFC 5545 requires. Before, the middleware took the second occurrence, one hour later. This applies to date-times with a time zone in the HTTP API, to iCal and CalDAV imports and to the occurrences of recurring events. GregorianCalendarMetrics is patched for this in the repackaged bundle org.dmfs.rfc5545 (0.17.1-ox1-custom).

  • Events and occurrences in that hour that are created or saved again start one hour earlier. Existing change and delete exceptions keep matching their occurrences.
  • A series stored before whose start lies in that hour shows its first occurrence one hour before the stored series start.
  • Local times skipped at the start of daylight saving time are resolved as before.

No operator action is required.

SCR-2205

Summary: Restart the OpenID Connect login for a callback with an unknown or expired state

Effective: 8.55.481 and later

An authentication callback declined for an unknown or expired state restarts the login with a fresh authentication request instead of ending on an error page; with the session at the OpenID Provider still alive, the login completes without interaction. Restarts are bounded to three within two minutes per provider session, after which the callback fails as before, or lands at com.openexchange.oidc.failureRedirect if set.

  • com.openexchange.oidc.restartOnUnknownState, default true, reloadable, not config-cascade aware; false restores the previous behavior.

An expired authentication session reported by the OpenID Provider restarts the login the same way, at most three times for one flow; a callback whose token exchange failed keeps its state only for the remaining lifetime of the request.

SCR-2190

Summary: Weakforced connector sends session attributes with report requests and the requested host name with every request

Effective: 8.55.481 and later

The Weakforced connector now sends the session attributes configured through com.openexchange.weakforced.attributes with the report request as well. Every allow and report request also carries the attribute serverName, the host name the login request was addressed to, next to clientID. A policy can thus tell apart logins arriving through different host names, e.g. OIDC and form login. No admin action.

SCR-2187

Summary: The capability drive opens the Drive module to users without the infostore

Effective: 8.55.481 and later

In order to let a user work with other file storages than the built-in infostore, or with none yet, the capability drive now also entitles a user without the infostore permission to the Drive module: the Drive app and the folders of the user's file storages. So far it only enabled the OX Drive clients. A user with the infostore permission has the module as before, whatever the capability says; denying drive to such a user still keeps the OX Drive clients away only.

No change by default: the capability is still granted through contextSets/drive.yml to the users with the infostore permission only. Grant com.openexchange.capability.drive per context or user through the config cascade to a user who uses other file storages only, e.g. together with com.openexchange.file.storage.default pointing to an OpenCloud account.

See the feature documentation for further details.

SCR-2183

Summary: Changed Handling of Dropped Delete Exception Dates and Repeated Occurrence Deletes

Effective: 8.55.481 and later

Two changes that came with the calendar Trash folder apply to every user, whether or not the feature (com.openexchange.calendar.trashFolder.enabled) is switched on.

  • An update that drops a delete exception date of a series other people are on, i.e. makes the series produce that date again, puts the date in front of them once more: it becomes an overriding instance on which they are asked for an answer, announced by a request for that one date. This is the same step a restore from the Trash takes for a date going back into its series.
  • Deleting an occurrence the series already excludes is refused with CAL-4061 instead of being accepted again.

The third such change, the folder type of a shared account's calendars, is described in SCR-1988.

See the "Effects regardless of the setting" section of the calendar implementation details for further details.

SCR-2176

Summary: MCP server tools that write to Drive, and the OAuth scopes write_files and share_files

Effective: 8.55.481 and later

Drive now offers the OAuth scopes write_files and share_files; neither is granted by default. write_files opens the Drive write actions of the HTTP API to a token, deleting files and managing file storage accounts included. Sharing with others, that is permissions for other users, groups or guests, share links and notifications, takes share_files on top, because a share outlives the token that made it. Application passwords keep their configured scopes, so their write_files still shares. The MCP server /mcp offers file_create (write_files, a text or Markdown file of at most 512 KiB), mail_attachment_save (read_mail and write_files) and mail_draft_attach_file (write_mail and read_files), each listed only for a token that holds all its scopes and only where com.openexchange.mcp.write.enabled is true. They never overwrite or delete a file, never send mail, and need the confirmToken of a preview: true call. A 403 for an MCP tool now names the scopes the token lacks in error_description. No admin action unless writing is wanted.

SCR-2167

Summary: A Module Omitted When Setting Shared Account Permissions Falls Back to the Default Permission Set

Effective: 8.55.481 and later

setSharedAccountPermissions (RMI, SOAP, gRPC and SCIM) used to remove a module that was omitted for an entity, so a configured com.openexchange.sharedaccount.calendar.defaultPermissionSet or com.openexchange.sharedaccount.mail.defaultPermissionSet never applied to it. A module that is omitted, or supplied without permission level, now keeps its account and gets the configured default, as with createSharedAccountPermissions. Permissions read before can therefore be sent back unchanged, and the folder identifiers of a shared account stay the same when a permission set is revoked and granted again.

With the default none, access is unchanged. With a higher default, such entities now get it; to keep a module closed to some users, leave the default at none and grant permission sets explicitly, or lower the default for them through the config cascade, e.g. per context or user.

SCR-2166

Summary: Changing the IMAP Subscriptions of a Shared Account Requires the manageImapSubscriptions Capability

Effective: 8.55.481 and later

In order to prevent a single user from changing the visible mail folders of everyone else, changing the subscription status of a shared account's mail folder now requires the new shared account capability manageImapSubscriptions. The subscriptions belong to the shared account's mailbox, so a change applies to all of its users alike. Without the capability, the update is refused with MSG-0072. To users without it, the shared account's mail folders are indicated without the subscription bit (bit 4) in their capabilities, so that clients do not offer the change in the first place. Creating folders, and folder updates that only send the unchanged subscription status, are not affected.

The capability is not granted by default. After the update, nobody can change the subscriptions of a shared account until it is granted, either per shared account permission through provisioning (SOAP or command line), or to everyone through com.openexchange.sharedaccount.defaultCapabilities. Adding it there keeps the previous behavior.

See the feature documentation for further details.

SCR-2162

Summary: SSL white-list no longer exempts connections tunneled through a white-listed HTTP proxy

Effective: 8.55.481 and later

The SSL white-list (com.openexchange.net.ssl.whitelist) is no longer matched against the address of an HTTP proxy that tunnels a TLS connection (mail.<protocol>.proxy.host, https.proxyHost/https.proxyPort); only the target host name, or the target IP address if the host is configured by it, is matched. Before, with com.openexchange.net.ssl.trustlevel=restricted, every target reached through a proxy whose address was white-listed, by default any proxy on 127.0.0.1 or localhost, was accepted without certificate and host name verification.

  • Such connections are now verified like any other: a target with an untrusted or self-signed certificate behind such a proxy fails with SSL-0001 unless the target itself is white-listed.
  • If a TLS-intercepting proxy is in use, add its CA certificate to the custom trust store (com.openexchange.net.ssl.custom.truststore.enabled, com.openexchange.net.ssl.custom.truststore.path, com.openexchange.net.ssl.custom.truststore.password) instead of relying on the white-list.
  • Connections through SOCKS proxies and direct connections are unchanged.

SCR-2159

Summary: Drive Mail stores its attachments in the default file storage and applies auto-delete only where the storage deletes on its own

Effective: 8.55.481 and later

Drive Mail stores the attachments of a mail in the user's default file storage (see com.openexchange.file.storage.default) instead of a hard-wired infostore folder. Where that storage manages sharing links itself, as OpenCloud and Nextcloud do, the attachments are shared through a read-only link of the storage, created with the password and expiration date of the mail, and the mail carries that link; the share reference of such a mail holds no share token of the server. The expiration date of the mail expires the link.

The middleware does not delete data held by an external file storage, so for a user whose default storage is not the infostore the auto-delete option is neither applied nor forced, and com.openexchange.mail.compose.share.forceAutoDelete has no effect there. The new setting io.ox/mail//compose/shareAttachments/autoDeleteSupported tells clients whether the option is available, forceAutoDelete answers false where it cannot be honored.

No change for users whose default file storage is the infostore. See the feature documentation for further details.

SCR-2154

Summary: Removed Asterisk as Substitute for the @ Character in CalDAV and CardDAV Login Names

Effective: 8.55.481 and later

CalDAV and CardDAV no longer replace an asterisk (*) in the login name of a Basic authentication with an @ character. The substitution was introduced for very old macOS clients that could not enter an @ in the user name field, and is no longer needed. Clients still configured with a login like user*example.com need to switch to the regular login name, e.g. user@example.com. Along with this, the interface com.openexchange.tools.webdav.LoginCustomizer and its implementation AllowAsteriskAsSeparatorCustomizer have been removed from the bundle com.openexchange.server.

SCR-2150

Summary: Deputy permission updates routed to another site through gRPC apply their fields

Effective: 8.55.481 and later

In deployments with several sites, SiteAwareOXDeputyPermissions routes an administrative update of a deputy permission (OXDeputyPermissionsInterface.updateDeputyPermission) for a context of another site through the gRPC provisioning client. Its converter DeputyPermissionConverter.toProtoDeputyPermissionDescription did not set the *Set flags of entityId, group, sendOnBehalfOf, modulePermissions and folderMode. The receiving node applies flagged fields only, so it matched the stored deputy permission against entity 0 and rejected every such update with DEPUTY-0007 ("No such deputy permission").

The flags are now set, so such updates succeed and write what they carry, e.g. the module permissions are replaced by the ones passed and sendOnBehalfOf is applied. Provisioning scripts that ignored or retried the error will change deputy permissions from now on and should be checked. Updates of a context on the local site are not affected.

SCR-2149

Summary: Go entrypoint forwards termination signals to the middleware

Effective: 8.55.481 and later

With useLegacyBashScripts: false the Go entrypoint runs as PID 1. It now forwards SIGTERM, SIGINT, SIGHUP and SIGQUIT to the middleware, waits for it and exits with its exit code, or 128 plus the signal number if a signal ended it. Stopping a pod therefore shuts the middleware down gracefully within awaitShutDownSeconds instead of killing it, SIGQUIT prints a thread dump instead of ending the container, and orphaned processes are reaped. The default mode with tini is unchanged.

SCR-2138

Summary: Changed handling of secrets that cannot be obfuscated and of the contact trash clean-up at login

Effective: 8.55.481 and later

Secrets protected with the session obfuscation key are no longer stored or used in the wrong form. A login, a mail account change or another write fails with a retryable error instead of storing a password or token in plain text when it cannot be encrypted. While Argon2 key derivations are busy (CRP-0009), a request fails for now instead of using the still encrypted value as password, and such a session is read again on the next request instead of being kept with the wrong password. The removal of old contacts from the trash at login runs at most once per hour per user and node and removes at most 1000 contacts per run, reading only their identifiers and timestamps instead of loading all contacts in the trash.

SCR-2136

Summary: Changed timing of live change stream pings, session checks and presence

Effective: 8.55.481 and later

Live change streams now keep their pings on time regardless of Redis latency: the node timer only pings and hands session checks, token revalidation and mail watch renewal to background rounds, and stream presence is written to Redis in batches. Each distinct session is checked once every 30 seconds instead of every stream every 5 seconds, so a session ended on another node closes its streams within about 40 seconds plus two check rounds; a logout on the same node still closes them at once. While Redis or the identity provider is unavailable, one session or token is probed per pass and no stream is closed for that reason. The presence entries now expire after twice the ping interval plus 10 seconds. New metrics appsuite_pns_sse_tick_seconds and appsuite_pns_sse_checks_seconds report the duration of a timer pass and of a check round.

Streams are now also closed within seconds when their session is removed on another node: session removals are announced cluster-wide with a marker that nodes of older versions ignore, and the periodic session check remains as fallback. Closing takes about 5 to 8 seconds, the default buffering delay of Redis pub/sub.

The mailbox watcher for live changes no longer checks claims and sessions one after another on its timer. It does so in a bounded background round and checks a session at most every 30 seconds, so a slow or unavailable Redis no longer delays mail live changes or floods the log. A session that has ended may still be used to look at the mailbox for up to 30 seconds.

A live change stream request whose session still awaits its second factor is now answered with 401 and code second_factor_required instead of 503 with Retry-After. Failures that last, such as a mandatory second factor not set up yet or a token user who may not sign in, get 403 permission_denied. Only failures that may pass by themselves, e.g. of the session storage, keep 503 and their warning.

Streams of a node that went away without closing them, e.g. one that was killed, no longer count against com.openexchange.pns.transport.sse.maxConnectionsPerUser once the node's announcement in the new Redis hash ox-pns-sse-nodes expires after 15 seconds. When streams on other nodes exceed the limit, the oldest stream is closed about 20 seconds later instead of right away.

A node that holds maxConnectionsPerNode streams or shuts down answers further stream requests with 503 and a random Retry-After of 1 to 10 seconds instead of a fixed 30 seconds. Other temporary failures answer with a random 20 to 40 seconds.

A node holding more than 125% of the cluster's average number of streams, e.g. after a failover, moves streams to the other nodes. It closes at most 10% of them per minute with reason lifetime until it holds 110% of the average, and nothing moves while only one node is alive. If more than 75% of the moved clients come back to the same node, as behind a load balancer with session affinity, it pauses moving for 10 minutes, doubling up to an hour.

SCR-2135

Summary: Refresh kept OAuth tokens on use after a refresh without expires_in, and handle a refused client like an unusable credential

Effective: 8.55.481 and later

Kept OAuth tokens of the mail credential vault are refreshed on use when they expire within a minute. Where the identity provider left expires_in out of a refresh (RFC 6749 only recommends it), the new access token was kept with an unknown expiry and never refreshed on use again, so an expired token went to the mail server. Now the expiry is taken from the access token's exp claim where it is a JWT, otherwise from the lifetime of the token it replaces. Only where neither is known does it stay unknown.

A refresh the configuration does not allow was taken for a temporary failure: the identity provider answers invalid_client or unauthorized_client, e.g. after the client secret was rotated, or the OpenID Connect back-end the tokens name is no longer configured. Every use of an expired token failed with MCRED-0010, and a snoozed or scheduled mail waited silently, up to a week past its due date. Now such a use fails with the new code MCRED-0013. Snoozed and scheduled mail handle it like a refused credential: they go on through a live session of the user, or tell the user once that the mail waits for a login. The kept credential is neither rejected nor released, and push keeps it. The cause is logged on ERROR once an hour per back-end. MCRED-0010 remains for refreshes another node holds or that fail for now.

Live sessions go on with the tokens at hand, as before. TokenRefreshResponse.ErrorType gains CONFIGURATION and RefreshResult.FailReason gains CONFIGURATION_ERROR.

SCR-2134

Summary: Revoke access tokens only for mail password refusals that go on without a pause

Effective: 8.55.481 and later

Personal access tokens are revoked once the primary mail server keeps refusing the mail password a token keeps (com.openexchange.accesstoken.revokeOnRefusedPassword). Until now, any later refusal within the confirmation delay plus fifteen minutes confirmed the first one, even with no refusal in between. Without a live session of the user to tell a lockout from a changed password, two short lockouts about half an hour apart, e.g. provoked with failed logins, revoked the user's tokens. Where a live session tells that the password has changed, a refusal was confirmed up to 45 minutes later with the default delay, not five to fifteen minutes as documented.

Now the refusals have to go on: a pause of fifteen minutes without a refusal starts afresh. Tokens go after five minutes of refusals where a live session tells that the password has changed, and after com.openexchange.accesstoken.revokeOnRefusedPasswordWithoutSessionDelay (default 30 minutes) where none does. A client that tries less often than every fifteen minutes never confirms a refusal, as before. The cluster map mailRefusedCreds now holds the first and the latest refusal; nodes that still run an earlier version keep noting refusals on their own during a rolling upgrade.

SCR-2133

Summary: Repairing an exchanged mail credential no longer revokes the tokens it just obtained

Effective: 8.55.481 and later

When the mail credential vault repairs a credential kept with exchanged tokens, it no longer logs out the replaced tokens if they belong to the same back-end. Exchanges from the same SSO session share one client session at the identity provider. With Keycloak, revoking the replaced tokens also ended the client session of the new ones, and the next refresh was rejected. Replaced tokens now expire on their own; those of another back-end are still logged out. The documentation now states that exchanged tokens are logged out only together with the user's last exchanged credential for their back-end.

SCR-2132

Summary: Mail credential vault keeps credentials it cannot read for the moment and handles failed OAuth token refreshes

Effective: 8.55.481 and later

The mail credential vault no longer treats a mail credential as unreadable when it cannot read it only for the moment. Two cases were reported as unreadable before: no key derivation slot becoming free in time under load (CRP-0009), and a database failure while the maintenance job's walk after a change of com.openexchange.sessiond.encryptionKey reads or writes a row. Such a credential is no longer released from push or reported to its owner as unusable, and the walk does not finish until it has looked at it again.

The migration of legacy mail credentials keeps a credential when rewriting its row fails without a clear outcome, e.g. because the connection dropped before the commit was confirmed. It discards the credential only when the database rolled the rewrite back (lock wait timeout, deadlock) or the row now holds something else.

Kept OAuth tokens of the mail credential vault are refreshed on use when they expire within a minute. Where the identity provider left expires_in out of a refresh (RFC 6749 only recommends it), the new access token was kept with an unknown expiry and never refreshed on use again, so an expired token went to the mail server. Now the expiry is taken from the access token's exp claim where it is a JWT, otherwise from the lifetime of the token it replaces. Only where neither is known does it stay unknown.

A refresh the configuration does not allow was taken for a temporary failure: the identity provider answers invalid_client or unauthorized_client, e.g. after the client secret was rotated, or the OpenID Connect back-end the tokens name is no longer configured. Every use of an expired token failed with MCRED-0010, and a snoozed or scheduled mail waited silently, up to a week past its due date. Now such a use fails with the new code MCRED-0013. Snoozed and scheduled mail handle it like a refused credential: they go on through a live session of the user, or tell the user once that the mail waits for a login. The kept credential is neither rejected nor released, and push keeps it. The cause is logged on ERROR once an hour per back-end. MCRED-0010 remains for refreshes another node holds or that fail for now.

Live sessions go on with the tokens at hand, as before. TokenRefreshResponse.ErrorType gains CONFIGURATION and RefreshResult.FailReason gains CONFIGURATION_ERROR.

The new counter appsuite_mailcredential_refresh_misconfigured_total (tag refresh: use, keepalive) counts every refresh the configuration does not allow.

Where com.openexchange.oidc.opRevocationEndpoint is left empty, the middleware now takes the token revocation endpoint from the issuer's OpenID Connect discovery document (<opIssuer>/.well-known/openid-configuration, field revocation_endpoint). It is fetched on first use, cached for an hour, and used only if the document names the configured issuer and an https endpoint. A configured value still wins, and if discovery fails, exchanged tokens are left to expire as before.

The mail credential vault also logs a WARN (once a day per back-end) and counts appsuite_mailcredential_keepalive_too_late_total when a kept refresh token's own exp comes before its next keep-alive; lower com.openexchange.mail.credential.oauth.keepAliveInterval or use longer-lived refresh tokens in that case.

A busy key derivation for the former key during a change of com.openexchange.sessiond.encryptionKey no longer makes an intact credential unreadable. The keep-alive puts off credentials of a misconfigured back-end by one interval (new outcome misconfigured of appsuite_mailcredential_keepalive_total) and no longer ends its run for them, so they do not hold up healthy ones. Where another mail authenticator serves the user, the vault's authenticator leaves a credential that holds nothing for the mail login to that one. A mail data export retries a credential whose expired tokens cannot be refreshed for now (MCRED-0013) instead of failing at once. Exchanged tokens are left to expire while a live session of the same OpenID Connect back-end may share their client session, and released ones are logged out off the request thread.

A scheduled or snoozed mail whose kept credential is unusable falls back only to a session of its user that is alive at that time. Before, it could go out through the session it was scheduled or snoozed with, even after its user had logged out or an administrator had released the credential.

SCR-2129

Summary: OpenID Connect keeps refreshed tokens while the identity provider's signing keys cannot be fetched

Effective: 8.55.481 and later

When a token refresh returns a new ID token and the OpenID Connect provider's signing keys (JWK set, com.openexchange.oidc.opJwkSetEndpoint) cannot be fetched at that moment, the refreshed access and refresh tokens are now kept with the former ID token, and the next refresh checks the new one. Before, this counted as an invalid ID token: the user's session was ended, and the mail credential vault marked the kept credential as rejected, losing the tokens the identity provider had just issued. An ID token that fails the check (bad signature, unknown key, wrong issuer or audience, or another user) is still rejected.

SCR-2128

Summary: Mail credential vault keeps credentials it cannot read on a password change

Effective: 8.55.481 and later

On a password change, the mail credential vault no longer deletes kept mail credentials it cannot read at that moment; they keep the password from before the change. Previously it deleted them even when the cause was temporary, such as the session obfuscator being reloaded or a new com.openexchange.sessiond.encryptionKey configured before the former key was set as com.openexchange.sessiond.previousEncryptionKey. A credential that cannot be opened only for the moment is no longer treated as unreadable, and the maintenance job's walk after a key change does not finish until it has looked at it.

SCR-2126

Summary: Access tokens survive a provoked lockout and an unavailable session storage

Effective: 8.55.481 and later

Once the primary mail server refuses the password an access token keeps, the tokens are revoked within minutes only where a live session of the user shows that the password changed. Without a live session that logged in with a password, for example after someone provoked a directory lockout with failed logins, the refusal has to last com.openexchange.accesstoken.revokeOnRefusedPasswordWithoutSessionDelay (default 30 minutes) first. While the user's sessions cannot be looked up, e.g. because the session storage is unavailable, no token is revoked, the mail credential vault refreshes no kept OAuth tokens and repairs no rejected credentials; an administrator's repair answers failed then.

SCR-2125

Summary: Administrator's repair of a kept mail credential answers deferred while another node works on it

Effective: 8.55.481 and later

An administrator's repair of a kept mail credential (RepairMailCredential, POST …/mail-credentials/{credential_id}:repair, OXMailCredentialInterface.repairMailCredential) answers deferred while another node holds the credential and works on it. Before, it answered repaired although the credential was still rejected. A repair that lost the credential to another write that left it rejected now goes on with the next live session of the user instead of stopping.

SCR-2124

Summary: Mail credential vault no longer ties up pool threads when logging out the tokens of deleted users and contexts

Effective: 8.55.481 and later

When users or contexts are deleted, the mail credential vault logs out their exchanged tokens at the identity provider after the deletion is committed. Before, every deletion held a pool thread while waiting for one of four logout slots, so a mass deletion against a slow identity provider could take up the whole thread pool and stall the node. Now the logouts of all deletions wait in a node-wide queue of at most 20,000 rows and never hold a pool thread while waiting. Tokens of further deletions are left to expire at the identity provider, with one warning until the node has caught up.

SCR-2118

Summary: Releasing one exchanged mail credential no longer revokes the others

Effective: 8.55.481 and later

Mail credentials that the mail credential vault obtained through a token exchange share one client session at the identity provider when they were exchanged from the same SSO session. Revoking the tokens of one of them, e.g. when a snoozed mail returns or a credential is released over the provisioning API, ended that client session and the tokens of all the others. Those were rejected until a live session of the user repaired them. Now exchanged tokens are logged out only with the last exchanged credential of the user for the same OpenID Connect back-end; until then they are left to expire. Tokens exchanged for a credential that could not be stored are now logged out too, under the same rule. Two releases at the same time may both leave their tokens to expire.

SCR-2117

Summary: Legacy credential migration skips rows that are being handled and remembers walks that span several runs

Effective: 8.55.481 and later

The jobs that move the credentials of snoozed and scheduled mails written before the mail credential vault no longer rewrite a row while that mail is being returned, sent or unsnoozed; a later run moves it, unless its lock is older than a day. A walk through a table that spans several runs - e.g. because each run stops after twenty rows it cannot read - now counts as a walk: walked in mail_credential_migration is set when it ends and is negative while it is under way. Before, such a table was read and decrypted again every second run.

SCR-2113

Summary: Mail credential vault no longer presents an expired access token when the identity provider cannot be reached

Effective: 8.55.481 and later

If the identity provider could not be reached when a kept OAuth access token was due for a refresh, the vault presented the expired token to the mail server. The mail server rejected it as a bad credential: a returning snoozed mail told its owner that the kept credential was unusable, and push released the credential if the user had no live session. Now such a use fails with MCRED-0010 and is retried later. An access token that has not expired is still used as before. If refreshed tokens cannot be stored, they are used for that access instead of the expired ones. A refreshed access token the identity provider issues without an expiry no longer keeps the former token's expiry.

SCR-2112

Summary: Mail credential vault schedules kept tokens while the keep-alive is switched off

Effective: 8.55.481 and later

With com.openexchange.mail.credential.oauth.keepAliveInterval=0, the mail credential vault still schedules kept OAuth tokens, using the default interval. When the keep-alive is switched on again, the maintenance job takes up the credentials kept in the meantime; before, it never refreshed them. While the keep-alive is off, the gauge appsuite_mailcredential_rows_due reports zero and the provisioning API lists these credentials as not_kept_alive. The update task com.openexchange.mail.credential.impl.groupware.MailCredentialRefreshDueAfterSwitchOffTask schedules credentials that earlier releases left unscheduled, spread over the default interval.

SCR-2111

Summary: Front- and back-channel logout end a session again after the mail credential vault refreshed its tokens

Effective: 8.55.481 and later

When the mail credential vault refreshed tokens it keeps from a session, it mapped the identity provider's session (sid) to its own internal carrier instead of the user's session. A front- or back-channel logout at the identity provider then missed the user's session, which stayed logged in. The vault's refreshes no longer touch that mapping, so such a logout ends the user's session again.

SCR-2110

Summary: Mail credential vault refreshes a live session's tokens before a repair takes them over

Effective: 8.55.481 and later

When the mail credential vault repairs a kept credential with a live session's own OAuth tokens, it now refreshes those tokens at the identity provider first, except right after a login. Before, a session the identity provider had already ended passed its dead tokens to the credential, which then failed again at the next mail access. Now the refused refresh terminates that session, and the next live session of the user is tried. Each such repair costs one token refresh at the identity provider; concurrent repairs on several nodes make only one.

SCR-2109

Summary: Mail credential vault tries further live sessions when one cannot repair a credential

Effective: 8.55.481 and later

If a live session cannot repair a kept mail credential whose refresh token was rejected (e.g. a session the identity provider ended but that is still stored, whose tokens it no longer exchanges), the vault now tries the user's next live session, up to three, starting with those whose tokens were refreshed last. Before, it tried only the first session it found, and the credential stayed unrepaired until that session timed out. After a mail access fails to repair a credential, including when the repair itself fails, it waits a minute before trying again.

SCR-2108

Summary: Mail credential vault gauges report the cluster-wide count on every node

Effective: 8.55.481 and later

Every node now reports appsuite_mailcredential_rows_rejected and appsuite_mailcredential_rows_due as the count over all schemas, read from a cluster map that the maintenance job fills per schema. The gauges are registered at startup. Read the value from any one node; do not sum over the nodes. Without a cluster map, a node still reports only the schemas it ran the job for in the last 70 minutes. appsuite_mailcredential_repaired_total has the new trigger value admin; appsuite_mailcredential_keepalive_total uses it too, for refreshes an administrator requests.

SCR-2105

Summary: Access tokens keep the password for secret resolution under master authentication and OAuth mail access

Effective: 8.55.481 and later

Where com.openexchange.secret.secretSource contains <password>, an access token the middleware issues (a personal access token, a grant of the MCP authorization server or a Mobile API token) now keeps the minting session's password even where the mail login does not use it, i.e. under master authentication (com.openexchange.mail.passwordSource=global) or OAuth mail access. The password is sealed with the token's secret and serves secret resolution only, e.g. to open the stored password of an external mail account for a token with mail_external; it is never sent to a mail server. A token minted from a session without a password, such as a single sign-on session, keeps none, and its external accounts still answer OAUTHMAIL-0002; such deployments need a secret source without <password>. Tokens issued before this change keep no password; issuing a new one helps. No admin action.

SCR-2104

Summary: Mail credential vault repairs personal access token credentials on use and revokes exchanged tokens

Effective: 8.55.481 and later

A personal access token whose kept mail credential has a rejected refresh token is repaired the next time the token is presented, from a live session of its user; until then the token reaches no mail. Exchanged tokens that came without an ID token are revoked at the identity provider when their credential is released or expires, where com.openexchange.oidc.opRevocationEndpoint is set for the back-end. With Keycloak this ends only the exchanging client's session, not the user's.

SCR-2101

Summary: Access tokens reach the user's other mail accounts with the scope mail_external

Effective: 8.55.481 and later

The new OAuth scope mail_external lets an access token the middleware issues (a personal access token, a grant of the MCP authorization server or a Mobile API token) reach the user's secondary and external mail accounts. It widens what read_mail and write_mail reach and grants nothing on its own; users with webmail may grant it, and the MCP consent page offers it only to users with another mail account. Without it, such a token reaches the primary account only, and that now holds on every path: moving or copying a message into another account is refused with OAUTHMAIL-0001 as well. Secondary accounts log in with the token's kept credential. External accounts need the user's password to open their stored password where com.openexchange.secret.secretSource contains <password>, which a token minted under master authentication or OAuth mail access does not keep. No admin action.

SCR-2100

Summary: Mail credential vault keeps short-lived refreshed tokens, tells snoozed mail owners and reports more metrics

Effective: 8.55.481 and later

A refresh whose new access token expires within the refresh threshold of the OpenID Connect back-end (com.openexchange.oidc.oauthRefreshTime) no longer marks a kept mail credential as rejected. The new tokens are kept, a rotated refresh token included, and used while they last; a warning is logged at most once an hour.

The owner of a snoozed mail that waits for a login because its kept credential is unusable is told once through the notification queue. The notification goes once the mail is returned.

New metrics: the gauges appsuite_mailcredential_rows_rejected and appsuite_mailcredential_rows_due and the counter appsuite_mailcredential_shortlived_total.

SCR-2096

Summary: IMAP temporary-down back-off no longer triggered by account-specific failures after login

Effective: 8.55.481 and later

After a failed connect attempt, the middleware marks the IMAP server as temporarily down for com.openexchange.imap.imapTemporaryDown milliseconds and rejects all further connects to that server on the node with MSG-1016. A connection failure after the server accepted the credentials, such as a reset because a single mailbox is being migrated, no longer sets that mark: it only fails the affected request, other accounts are not blocked. Failures before authentication (connection refused or timed out, TLS handshake, greeting) set it as before. No admin action.

SCR-2095

Summary: Mail credential vault keeps more OAuth tokens alive and reports metrics

Effective: 8.55.481 and later

The mail credential vault keeps more OAuth tokens alive. Tokens without an ID token, e.g. from a token exchange, are now refreshed too. A personal access token's credential is kept alive when the token is presented, at most once an hour per token and without delaying the request. It is skipped while the session it was created in, or a copy of it, holds the same refresh token. A rejected credential is repaired from a live session that holds what mail access needs (tokens or a password), not from the first session found. New counters appsuite_mailcredential_* at /metrics count keep-alives by trigger and outcome, rejections, repairs, re-sealed and unreadable credentials.

SCR-2091

Summary: MCP server returns the text of PDF and office documents

Effective: 8.55.481 and later

The MCP server /mcp now returns the text of PDF and office documents (Word, Excel, PowerPoint, OpenDocument, RTF) through file_get, mail_attachment_get and the resource reads of files and attachments, cut at 4000 characters in tools and 100000 in resource reads. Office documents need the document converter. PDF files are read only where com.openexchange.mcp.documents.localPdf allows it. Without a converter, or when it fails, items are served as before: metadata in tools, bytes in resource reads. Documents above 5 MiB are not converted, a conversion is abandoned after 20 seconds, and each one takes one more permit of the user's tool call limit. The new bundle com.openexchange.mcp.documents.impl ships with open-xchange-mcp.

SCR-2090

Summary: Outgoing requests to user-supplied URLs check every address of the target host

Effective: 8.55.481 and later

Where the middleware fetches a URL that a user or client supplied (RSS feeds, iCal subscriptions, snippet images, vCard photos, mail auto-configuration, DAV subscriptions, web push and webhooks, the proxy servlet and MCP client metadata documents), it checks the target's address against the denied ranges. It now checks every address the host resolves to and connects to the address it checked, instead of checking one and letting the HTTP client resolve again. A host with any denied address is refused, which also blocks a second DNS record or a changed DNS answer that points inside. A connection no longer falls back to another address of the host; an IPv4 address is preferred. Through an HTTP proxy, a host that resolves locally is checked the same way, so a request the proxy would have carried to an internal address is now refused. A host that does not resolve locally is still left to the proxy.

SCR-2088

Summary: Mail credential vault keeps idle OAuth tokens alive and repairs rejected ones

Effective: 8.55.481 and later

OAuth tokens the mail credential vault keeps are now refreshed by an hourly job once they went unused for com.openexchange.mail.credential.oauth.keepAliveInterval (default 3D), so snoozed mail, scheduled mail and permanent push keep working past the identity provider's idle limit; set 0 where that limit is meant to cut off inactive users. A refresh token the identity provider rejects is marked and no longer presented; a live session of the user repairs the credential, at the latest on the user's next login. A credential that cannot be decrypted is no longer repaired from a live session, so that a copy of a personal access token's credential cannot outlive the token's revocation.

SCR-2084

Summary: New MCP server tools to read a conversation, find a meeting time and report what changed

Effective: 8.55.481 and later

The MCP server /mcp has three new read tools. mail_thread_get (scope read_mail) returns the conversation of a message, at most 50 messages, from its folder and the inbox and sent folder of its account. calendar_find_slot (scope read_calendar) proposes times when the user, up to 10 people and up to 5 rooms are all free, within the working hours the user set in App Suite; the find_meeting_slot prompt uses it. changes_since reports what changed since a point in time up to 31 days back: mail the inbox received, appointments created, changed or canceled, and tasks created, changed or completed, each part only where the token holds its read scope; the catch_up_on prompt starts with it. contacts_search now also finds contacts by the start of their company and, for queries with at least 5 digits, by phone number. No admin action needed; com.openexchange.mcp.tools.disabled switches single tools off.

SCR-2076

Summary: MCP briefing tool and daily briefing prompt

Effective: 8.55.481 and later

The MCP server /mcp offers briefing_get: what a day holds in the user's time zone, in one call. It lists the appointments, invitations awaiting an answer first; the unread inbox mail since the start of the previous working day, flagged first; the open tasks of the user's own folders due that day, then the overdue ones; and the reminders coming due by its end. Each section needs its read scope; one the token lacks is listed in omitted, so any token may call the tool. It only reads. The daily_briefing prompt now calls briefing_get instead of four tools and is listed for every token. No admin action.

SCR-2074

Summary: MCP tool results link into App Suite

Effective: 8.55.481 and later

Every mail, appointment, contact, task and file the tools of the MCP server /mcp return carries webUrl, a link that opens the item in App Suite. The link uses the host the client reached the server on, resolved per context as for share links, and the UI path com.openexchange.UIWebPath. An occurrence of a series is linked with its recurrenceId. No admin action.

SCR-2073

Summary: MCP server tools that change mail, the vacation notice and mail filter rules, and output schemas for all tools

Effective: 8.55.481 and later

The MCP server /mcp offers mail_draft_create, mail_mark, mail_move, mail_delete, mail_folder_create, mail_folder_rename, mail_send, vacation_set, mailfilter_create, mailfilter_update and mailfilter_delete, listed and callable only where com.openexchange.mcp.write.enabled is true and the token holds write_mail; mailfilter_list reads the rules with read_mail. mail_delete moves messages to the trash and never deletes for good. mail_send sends a draft from the drafts folder; while com.openexchange.mcp.write.externalAttendees is false, every recipient has to be a user of the context, and vacation_set takes no new text for a notice that replies to every sender. Filter rules can move, flag, mark read, move to the trash and stop; a rule that forwards can only be switched off. Every write needs the confirmToken of a preview: true call, which does not prove that a person saw the preview. com.openexchange.mcp.tools.disabled=mail_send turns off sending alone. No admin action unless writing is wanted.

mail_send now hands a confirmed draft to scheduled mail where the user can schedule mails: it goes out after com.openexchange.mcp.mail.sendDelay seconds, 60 by default, or at the time given in sendAt, and mail_scheduled_cancel or App Suite can stop it until then. The new tools mail_scheduled_list and mail_snoozed_list (read_mail) list the user's scheduled and snoozed mails; mail_scheduled_cancel, mail_snooze and mail_unsnooze (write_mail) stop a scheduled mail and snooze or return mails as App Suite does, each confirmed like every other write. Mails held this way count toward com.openexchange.mail.scheduled.maxNumberOfScheduledMailsPerHour (100 by default); raise it where users let an assistant send more.

From protocol revision 2025-06-18 on, tools/list gives every tool an outputSchema, a JSON Schema of its structuredContent; under 2025-03-26 the list is unchanged.

SCR-2071

Summary: Built-in OAuth authorization server for the MCP server

Effective: 8.55.481 and later

Warning

Update Taskcom.openexchange.accesstoken.impl.groupware.AccessTokenAddGrantColumnsTask

com.openexchange.accesstoken.impl.groupware.AccessTokenGrantCreateTableTask

With com.openexchange.mcp.oauth.enabled the middleware acts as OAuth 2.0 authorization server for /mcp, so that a client implementing the MCP authorization spec, such as claude.ai, ChatGPT or VS Code, connects with the endpoint URL alone. The App Suite origin is the issuer: it serves /.well-known/oauth-authorization-server, dynamic client registration at /oauth/register, the code flow with PKCE at /oauth/authorize, and /oauth/token and /oauth/revoke; route /.well-known/oauth-authorization-server and /oauth/ to the middleware like /mcp. The user signs in through the regular login, so second factor and single sign-on work unchanged, and confirms the scopes on a consent page the middleware serves; write scopes are never preselected. On the same page the user can limit the grant to some folders per module, subfolders excluded; every token of the grant keeps the restriction. The client gets oxa_ access tokens valid for an hour and a refresh token that rotates on every use; presenting a replaced refresh token revokes the grant. Grants appear in the access token list; a password change revokes them all. An external authorization server stays first in authorization_servers. The new table access_token_grant and the new columns grant_id and grant_key of access_token are added by update tasks. A client can also skip registration and name itself by the https URL of a client ID metadata document, as Claude Code does: the middleware fetches that document directly, never from a non-public address, and caches it for at most an hour. For such URLs, an update task widens the column client_id of access_token_grant from 64 to 255 characters. The server's pages appear in the user's language, or before sign-in the browser's, where a translation exists; otherwise in English.

SCR-2070

Summary: MCP server tools that change tasks and contacts

Effective: 8.55.481 and later

The MCP server /mcp offers task_create, task_update, task_complete, contact_create and contact_update. They are listed and callable only where com.openexchange.mcp.write.enabled is true and the token holds write_tasks or write_contacts. As with the calendar tools, a call writes only with the confirmToken of a preview: true call with the same arguments. Nobody gets mail from these changes. Contacts can be written in any address book the user may write, except other users' entries in the global address book. task_get and contact_get now report the timestamp these tools take for optimistic locking. No admin action unless writing is wanted.

SCR-2066

Summary: Middleware keeps no sessions of its own when external authentication is enabled

Effective: 8.55.481 and later

While com.openexchange.external.authentication.enabled is true, the middleware resolves every request from the JWT the external authentication service issues and keeps no session of its own:

  • the Redis session store does not register; com.openexchange.sessiond.noop provides a session service that stores nothing
  • the login servlet (/ajax/login), the SOAP session service and the sessionmanagement module are not registered - login and session management are offered by the external service
  • share and guest links, including password-protected links and the guest password reset, are resolved through the external service; a failed share request is answered with a redirect instead of a bare error
  • the OpenID Connect flow creates no session but redirects to com.openexchange.external.authentication.oidc.handoffRedirectURL
  • DAV and the DAVx5 onboarding authenticate via the bearer token
  • an internal App Suite service forwarding an already authenticated request may name the acting client in the header X-OX-Client-Override; the header is ignored without external authentication
  • personal access tokens are issued by the external service: the accesstoken module of the HTTP API and the capability access_tokens are not registered

With external authentication disabled (the default) the behavior is unchanged.

SCR-2061

Summary: Address Books Can Be Shared Across Context Boundaries

Effective: 8.55.481 and later

Address books can now be shared to users of other contexts, like calendars and mail folders, subject to the cross-context trust zones. The grantee sees them as foreign address books, over the HTTP API and CardDAV.

  • A foreign grantee receives at most author rights, and needs contacts and groupware access.
  • Contacts flagged private are not shared across contexts.
  • A contact deleted by a foreign grantee goes into the trash folder of the address book's owner.
  • Foreign address books take part in searches and synchronization. CardDAV exposes them once the grantee enables them for synchronization.
  • An address book shared by e-mail address to a user of another context becomes a cross-context grant instead of a guest share, as for calendars.

See the feature documentation for further details.

SCR-2054

Summary: MCP server tools that change calendar appointments and reminders, and confirming writes with the user

Effective: 8.55.481 and later

The MCP server /mcp offers calendar_create_event, calendar_update_event, calendar_respond, calendar_delete_event and calendar_set_reminder. They are listed and callable only where com.openexchange.mcp.write.enabled is true and the token holds write_calendar, which a client has to ask for explicitly: a 401 challenge from /mcp names the read scopes only. Each tool takes preview: true, which reports the change, conflicts and recipients without saving anything, and returns a confirmToken; the tool writes only when called again with that token and exactly the previewed arguments, within five minutes and before the object changes. Appointments are scheduled as in App Suite, iTIP mail included. calendar_set_reminder sets or removes the user's own reminder of an appointment or one occurrence and sends no mail. calendar_get_event reports the timestamp these tools take for optimistic locking. No admin action unless writing is wanted.

Where a request of protocol revision 2026-07-28 declares the client capability elicitation with form mode, the user confirms every MCP write, not only those of the calendar tools: a call without preview is answered with resultType: "input_required", a question that says what the call would do, and a requestState. Only the client's retry with the user's accept and that state writes; decline and cancel end as the tool errors confirm_declined and confirm_canceled. With such a client a confirmToken alone no longer confirms a write. Clients without the capability and the earlier protocol revisions keep the preview and confirmToken flow. No admin action.

SCR-2050

Summary: Composition spaces in the database or file storage are encrypted with AES/GCM

Effective: 8.55.481 and later

Composition spaces kept in the database or file storage (com.openexchange.mail.compose.mailstorage.enabled set to false) with com.openexchange.mail.compose.security.encryptionEnabled set to true were encrypted with AES/CBC, a fixed IV and no integrity protection: identical content produced identical ciphertext, and altered data went undetected. New content and attachments are now encrypted with AES/GCM and a random IV; attachments are sealed in 16 KiB segments. Existing data stays readable. The default draft-based storage is not affected.

Nodes on 8.46 to 8.54 read the new format only from the patch release that includes this change. Update them to that patch release before a rolling update to 8.55, and do not downgrade below it afterwards; otherwise composition spaces written by 8.55 cannot be opened.

SCR-2048

Summary: User pseudonyms in exported feedback are keyed by a secret

Effective: 8.55.481 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.55.481 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.

For JMAP, com.openexchange.jmap.trustAll now defaults to false: a TLS connection without an X509 trust manager is refused instead of trusting every certificate. With com.openexchange.jmap.requireTls set to true, which is the default, a JMAP server addressed via plain HTTP is now refused with error code JMAP-0017; deployments that reach their JMAP server via HTTP need to set com.openexchange.jmap.requireTls (or its primary. / secondary. variant) to false. The OX session (com.openexchange.jmap.oxSessionAuth) is now only forwarded to the JMAP server configured for the primary or a secondary account, never to a server of an account a user added; for such an account, requests to other hosts named in the server's session document must reach a public address.

SCR-2040

Summary: White-Listed Hosts Are Exempt From Host Name Verification

Effective: 8.55.481 and later

In order to allow the restricted trust level (com.openexchange.net.ssl.trustlevel=restricted) for internal services presenting a certificate issued for other host names, e.g. a mail server reached through a cluster-internal service name, hosts covered by com.openexchange.net.ssl.whitelist are now also exempt from the host name verification performed after the TLS handshake. Previously, the white-list only skipped the certificate checks of the trust managers, so connections to such hosts still failed while com.openexchange.net.ssl.hostname.verification.enabled was set.

The exemption applies to connections opened through the middleware's TLS socket factory, i.e. mail connections and HTTP clients without a TLS setup of their own, and only with the restricted trust level. The default white-list only covers loopback addresses and localhost. See the property documentation for further details.

The white-list also no longer resolves host names: a host name entry matches the name used to connect, an address entry the address a connection actually goes to. Previously, a host name was accepted as soon as any address of its DNS answer was white-listed, so a crafted DNS answer could skip the certificate checks. The white-list now also applies to Redis, Cassandra and the asynchronous S3 client; for these, an address entry only matches a host configured by that address.

SCR-2015

Summary: Move Contacts Deleted From Shared Folders Into the Owner's Trash

Effective: 8.55.481 and later

Contacts soft-deleted from a shared folder now go into the trash folder of the folder's owner, not the deleting user's. The owner's com.openexchange.contact.trashFolder.enabled setting applies, and the owner can restore them. Public folders are unchanged.

The deleting user can still undo the deletion: the undo now addresses the source folder. Accordingly, the restore action of the addressbooks and contacts modules also accepts the source folder and restores exactly the contacts the acting user deleted from it, provided they may still create contacts there.

See the contact trash folder documentation for further details.

SCR-1945

Summary: New MCP server endpoint for read-only access to mail, calendar, contacts, files and tasks

Effective: 8.55.481 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, 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.

CLT

SCR-2200

Summary: New options --recoveryemail, --recoveryphone and --recoverypin for createuser and changeuser

Effective: 8.55.481 and later

  • \-\-recoveryemail sets the recovery email address.
  • \-\-recoveryphone sets the recovery phone number, in international format with a leading \+.
  • \-\-recoverypin sets the recovery PIN.

An empty string clears a value. An omitted option leaves it unchanged.

SCR-2178

Summary: listdataexports and dataexportconsistency check the administrator credentials; new tools submitdataexport and downloaddataexport

Effective: 8.55.481 and later

The command-line tools listdataexports and dataexportconsistency always asked for -A/-P, but the server did not check these credentials. It now does:

  • listdataexports -c <contextId> accepts the context administrator, the master administrator only where MASTER_ACCOUNT_OVERRIDE is enabled
  • listdataexports without -c and dataexportconsistency require the master administrator

Scripts that call these tools with wrong credentials, or list all data exports as context administrator, now fail with an authentication error.

New tools handle an export an administrator starts on behalf of a user:

  • submitdataexport starts it for the given modules and a mandatory case reference
  • downloaddataexport downloads all packages, verifies each against its SHA-256 checksum and, with --delete, deletes the export once every package is verified
  • listdataexports -a and canceldataexports -a show and cancel it; the listing includes skipped modules and checksums

Examples:

# Data exports of context 1234, as its context administrator
listdataexports -c 1234 -A oxadmin -P secret

# Data export of user 3 in context 1234, including work items and result files
listdataexports -c 1234 -i 3 -A oxadmin -P secret

# All data exports - master administrator only
listdataexports -A oxadminmaster -P secret

# Orphaned entries of file storage 34 - master administrator only
dataexportconsistency -f 34 -A oxadminmaster -P secret

# Export mail and calendar of user 3 in context 1234 for case AZ 4711/26
submitdataexport -c 1234 -i 3 -m mail,calendar -r "AZ 4711/26" -A oxadmin -P secret

# Show that export, then download, verify and delete it
listdataexports -c 1234 -i 3 -a -A oxadmin -P secret
downloaddataexport -c 1234 -i 3 -o /var/tmp/case-4711 --delete -A oxadmin -P secret

The underlying RMI change is described in SCR-2177.

submitdataexport takes a credential for the user's mailbox, used instead of master authentication. Secrets are read from files, - for standard input, so that they show up neither in the process list nor in the shell history:

  • --mail-password-file, or --mail-access-token-file with --mail-access-token-expires, --mail-refresh-token-file, --mail-oidc-backend and --mail-oauth-mechanism (XOAUTH2 default, or OAUTHBEARER)
  • --mail-master-user if the password or token is a master user's; MW composes the login from the user's mail login and com.openexchange.mail.admin.masterUserSeparator
# Mail through the master user "exporter", the password read from standard input
submitdataexport -c 1234 -i 3 -m mail,calendar -r "AZ 4711/26" --mail-master-user exporter --mail-password-file - -A oxadmin -P secret
# Mail through an OAuth access token of the user, sent through OAUTHBEARER
submitdataexport -c 1234 -i 3 -m mail -r "AZ 4711/26" --mail-access-token-file - --mail-oauth-mechanism OAUTHBEARER -A oxadmin -P secret

Changed defaults

SCR-2152

Summary: Changed defaults for SMTP pipelining and chunking

Effective: 8.55.481 and later

The SMTP transport now uses command pipelining and plain DATA by default, which saves round trips when sending mail.

  • com.openexchange.smtp.enablePipelining If the SMTP server announces PIPELINING, the MAIL FROM and RCPT TO commands are sent without waiting for each reply. Set to false to restore the previous behavior. Unset com.openexchange.smtp.primary.enablePipelining and com.openexchange.smtp.secondary.enablePipelining follow this value. Default true (was false). Reloadable, config-cascade aware. No dedicated properties file.

  • com.openexchange.smtp.chunksize Message data is now sent with DATA instead of BDAT chunks, even if the server announces CHUNKING. Set to 131072 to restore the previous behavior. Unset com.openexchange.smtp.primary.chunksize and com.openexchange.smtp.secondary.chunksize follow this value. A value less than or equal to 0 (zero) disables chunking. Default 0 (was 131072). Reloadable, config-cascade aware. No dedicated properties file.

SCR-2056

Summary: Changed default of com.openexchange.mcp.instructions

Effective: 8.55.481 and later

The default of com.openexchange.mcp.instructions, the guidance MCP clients pass to their model, no longer calls the server read-only and asks the model to preview changes and confirm them with the user. Deployments that set their own text should check it before they set com.openexchange.mcp.write.enabled to true.

Configuration

SCR-2210

Summary: New configuration option for pooled mail credentials

Effective: 8.55.481 and later

The mail credential vault can let the features of a user share the credential they keep from the same login.

  • com.openexchange.mail.credential.pooling Whether permanent push, snoozed and scheduled mail and the data export refer to one pooled credential per user and login, told apart by the session at the identity provider and by the password, instead of keeping one each. The pooled credential is refreshed, kept alive, repaired and rewritten on a password change once for all of them. Exchanged tokens, the offline grant, credentials of personal access tokens and credentials an administrator supplied stay a feature's own. A credential that refers to a pooled one keeps a copy, updated with every change of the pooled one, which earlier versions use as a credential of their own: nodes of both versions can run side by side, and going back needs no preparation. Credentials kept before are pooled on their next use; switched off, the maintenance job gives every one its own credential back. Default true. Reloadable, config-cascade aware. No dedicated properties file.

SCR-2206

Summary: New configuration option for keeping the Sieve capabilities of a user

Effective: 8.55.481 and later

New configuration option for the connection to the Sieve end-point of the mail filter.

  • com.openexchange.mail.filter.capabilitiesCacheIdleTime Specifies how long, in milliseconds, the capabilities that the Sieve end-point advertises to an authenticated user are kept after their last use. While they are kept, querying the mail filter configuration needs no connection to the Sieve end-point. A value of 0 (zero) means not to keep them. Default 0. Reloadable, not config-cascade aware. No dedicated properties file.

SCR-2204

Summary: New property com.openexchange.oidc.stateLifetimeSeconds for pending OpenID Connect logins

Effective: 8.55.481 and later

The lifetime of a pending OpenID Connect authentication request, i.e. how long its state, nonce and PKCE code verifier are remembered before a callback is declined, is now configurable instead of fixed to five minutes.

  • com.openexchange.oidc.stateLifetimeSeconds, default 1800, reloadable, not config-cascade aware, settable per backend.

The default matches Keycloak's login timeout of 30 minutes; align it with the identity provider in use. No operator action is required.

SCR-2202

Summary: New Property com.openexchange.undo.maxTimeToLive Limits the Lifetime of Undo Operations

Effective: 8.55.481 and later

The new property com.openexchange.undo.maxTimeToLive limits how long an undoable operation of the HTTP API can be undone, in milliseconds. The period is chosen by the client through the undo parameter, which so far was taken over without any bound for the undoable actions of the modules mail, contacts, folders and chronos. A longer period is now capped at the configured value, while the operation itself is still performed as requested.

It defaults to 300000 (five minutes), is reloadable and config-cascade aware. A value of 0 or below removes the limit, which restores the previous behavior. No operator action is required, as App Suite UI asks for at most 70 seconds. See the configuration documentation for further details.

SCR-2195

Summary: New properties com.openexchange.passwordchange.admin.* for administrative password changes by database or webhook

Effective: 8.55.481 and later

Account recovery changes the password through a new administrative password-change layer that works without a user session. Two backends ship:* database is active while the database authentication service is registered. It writes the users table and ends the user's sessions.

  • webhook calls a configured endpoint.

With neither backend active, the reset answers 503.

Backend selection:

  • com.openexchange.passwordchange.admin.\[providerId\].enabled, for example com.openexchange.passwordchange.admin.webhook.enabled

Password policy of the database backend:

  • com.openexchange.passwordchange.admin.minLength
  • com.openexchange.passwordchange.admin.digitRequired
  • com.openexchange.passwordchange.admin.lowerCaseRequired
  • com.openexchange.passwordchange.admin.upperCaseRequired
  • com.openexchange.passwordchange.admin.specialCharRequired

Webhook backend:

  • com.openexchange.passwordchange.admin.webhook.endpoint
  • com.openexchange.passwordchange.admin.webhook.username
  • com.openexchange.passwordchange.admin.webhook.password, which belongs in the secret properties
  • com.openexchange.passwordchange.admin.webhook.httpsOnly

Any password change revokes the user's open reset links, provisioning included. Deleting the user revokes them as well.

SCR-2194

Summary: New properties com.openexchange.accountrecovery.* configure self-service account recovery

Effective: 8.55.481 and later

New lean configuration properties for the account recovery feature. All of them are config-cascade aware, except com.openexchange.accountrecovery.enabled.

Switch and links:

  • com.openexchange.accountrecovery.enabled switches the feature on. It defaults to false, applies server-wide and is read at bundle start.
  • com.openexchange.accountrecovery.api.resetUrl sets the page the reset link opens. Without it the link opens <host>/appsuite/?login\_type=set\_password. Links never use the request's host.
  • com.openexchange.accountrecovery.api.verifyUrl sets the page the verification link opens.

Limits:

  • com.openexchange.accountrecovery.api.rateLimit limits requests per minute per address and per matched user, default 5. It also limits each caller of the admin REST endpoints. 0 or \-1 disables it.
  • com.openexchange.accountrecovery.api.rateLimitPerClient limits requests per minute per client address, default 20.
  • com.openexchange.accountrecovery.api.maxOutstandingTokensPerUser limits unexpired tokens per user, default 2.
  • com.openexchange.accountrecovery.api.maxMatchesPerRequest limits the users that get a link per request, default 10.
  • com.openexchange.accountrecovery.api.tokenLifetimeMinutes sets the lifetime of a reset link in minutes, default 60.

Notification channels:

  • com.openexchange.accountrecovery.notify.request.primary\-email
  • com.openexchange.accountrecovery.notify.request.backup\-email
  • com.openexchange.accountrecovery.notify.request.sms
  • com.openexchange.accountrecovery.notify.reset.primary\-email
  • com.openexchange.accountrecovery.notify.reset.backup\-email
  • com.openexchange.accountrecovery.notify.reset.sms

Sender and phone numbers:

  • com.openexchange.accountrecovery.mail.senderName
  • com.openexchange.accountrecovery.mail.replyTo
  • com.openexchange.accountrecovery.phone.defaultRegion

Verification of recovery contacts:

  • com.openexchange.accountrecovery.verification.requireVerifiedEmail
  • com.openexchange.accountrecovery.verification.requireVerifiedPhone
  • com.openexchange.accountrecovery.verification.emailLinkTtlMinutes
  • com.openexchange.accountrecovery.verification.smsCodeTtlMinutes
  • com.openexchange.accountrecovery.verification.smsCodeMaxAttempts
  • com.openexchange.accountrecovery.verification.smsCodeLength

The feature also reads the existing property com.openexchange.mailmapping.lookUpByDomain. With the default false, only recovery addresses lead to a token. Resolving a primary address would query every schema per request.

SCR-2191

Summary: New Properties to Let Users Allow Background Mail Access Through Their OpenID Connect Login

Effective: 8.55.481 and later

Users who log in through OpenID Connect can allow background mail access once: an authorization code flow with the offline_access scope, whose tokens the mail credential vault keeps as the user's offline grant. Features that keep mail credentials in the vault can refer to that grant instead of keeping a credential of their own, so they work without master authentication or a token exchange; in this version, scheduled and snoozed mail do. Until the user has allowed it, such a feature fails with MCRED-0014, which App Suite UI takes as its cue to ask.

  • com.openexchange.oidc.offlineGrantEnabled lets the users of the back-end allow it and registers the end-point offline_grant. Defaults to false; read when the back-end starts, so not reloadable.
  • com.openexchange.oidc.offlineGrantScope is requested in addition to openid and the login's scope. Defaults to offline_access, reloadable.
  • com.openexchange.oidc.offlineGrantPrompt sets an optional prompt for the identity provider, e.g. consent. Empty by default, reloadable.
  • com.openexchange.oidc.offlineGrantAdditionalParameters adds further parameters to the authorization request. Empty by default, reloadable.
  • com.openexchange.mail.credential.oauth.offlineGrantIdleTime releases a grant once it has gone unused this long. Defaults to 90D (0 keeps it until revoked); reloadable and config-cascade aware.

The OpenID Connect properties may be set per back-end, e.g. com.openexchange.oidc.<backend>.offlineGrantEnabled. A configured token exchange takes precedence. A feature refers to the grant only where it gets along with the grant's tokens, which the mail server may narrow to the folders the feature works on; features that read the user's mail, such as push or the data export, keep their own credentials. The administrative listing of kept credentials shows a grant with the kind offline, and the credentials referring to it with the kind delegated. Nodes of an earlier version cannot read what the vault keeps for a grant, so enable it only once every node runs this version. Nothing changes as long as offlineGrantEnabled is not set.

See the Background Mail Access article for the setup of middleware, identity provider and mail server.

SCR-2189

Summary: New configuration option for the post-auth allow request of the Weakforced connector

Effective: 8.55.481 and later

The Weakforced connector now sends the session attributes configured through com.openexchange.weakforced.attributes with the report request.

  • com.openexchange.weakforced.attributes.postAuthAllow Whether those session attributes are additionally passed through the post-auth allow request, whose result is ignored. Set it to false once the Weakforced policy reads them in its report function. Default true. Not reloadable, not config-cascade aware. File: weakforced.properties.

SCR-2185

Summary: New Mail and Mail Filter Authentication Properties for Shared Accounts

Effective: 8.55.481 and later

Shared accounts and plain secondary accounts used the same authentication properties with secondary infix. Hence, a deployment could not let secondary accounts log in with a master password while shared accounts log in with the acting user's own credentials, for example. Each of these properties now has a counterpart with sharedaccount infix that applies to shared accounts only, and falls back to the secondary one if not set:

  • com.openexchange.mail.sharedaccount.passwordSource
  • com.openexchange.mail.sharedaccount.masterPassword
  • com.openexchange.mail.sharedaccount.authType
  • com.openexchange.mail.sharedaccount.masterUserSeparator
  • com.openexchange.mail.sharedaccount.authorizationId.enabled
  • com.openexchange.mail.transport.sharedaccount.authType
  • com.openexchange.mail.filter.sharedaccount.passwordSource
  • com.openexchange.mail.filter.sharedaccount.masterPassword
  • com.openexchange.mail.filter.sharedaccount.credentialSource
  • com.openexchange.mail.filter.sharedaccount.preferredSaslMech
  • com.openexchange.mail.filter.sharedaccount.masterUserSeparator
  • com.openexchange.mail.filter.sharedaccount.authorizationId.enabled

None of them has a default value, and all are reloadable and config-cascade aware. An explicitly empty masterUserSeparator turns off the master login for shared accounts, even if the secondary one is set. Plain secondary accounts are unaffected and keep using the secondary properties, so no operator action is required. See also SCR-2151.

SCR-2180

Summary: New configuration option for deleting left-over shared attachment folders of drafts

Effective: 8.55.481 and later

New configuration option for deleting the left-over shared attachment folders of drafts.

  • com.openexchange.mail.compose.share.leftOverFolders.minAge Clearing a mail folder spends at most 5 seconds checking the cleared mails for references to folders that hold shared attachments. If that check does not complete, the user's draft attachment folders that no mail refers to anymore are deleted in the background, provided they are older than this many milliseconds. A younger folder might belong to a draft that is just being stored. A value less than or equal to 0 (zero) is ignored and the default is used. Default 3600000. Reloadable, config-cascade aware. No dedicated properties file.

SCR-2179

Summary: Provisioning HTTP gateway exposes DataExportService by default; new properties com.openexchange.gdpr.dataexport.administrative.maxTimeToLive and com.openexchange.mail.admin.masterUserSeparator

Effective: 8.55.481 and later

The default of provisioningGateway.services in the core-mw chart now includes com.openexchange.grpc.provisioning.DataExportService, so the provisioning HTTP gateway offers the data export routes:

  • GET /prov/v1/contexts/{context_id}/data-export-tasks
  • GET /prov/v1/contexts/{context_id}/users/{user_id}/data-export-task
  • POST /prov/v1/contexts/{context_id}/data-export-tasks:cancel
  • POST /prov/v1/contexts/{context_id}/users/{user_id}/data-export-task:cancel
  • POST, GET and DELETE /prov/v1/contexts/{context_id}/users/{user_id}/administrative-data-export-task to start, show and delete an export an administrator starts on behalf of a user, and POST …/administrative-data-export-task:cancel to cancel it
  • GET …/administrative-data-export-task/packages/{number} to download a result package as a stream, GET …/packages/{number}:read to read it in chunks

They take the credentials of the context administrator, the master administrator only where MASTER_ACCOUNT_OVERRIDE is enabled. A deployment that sets provisioningGateway.services itself keeps its list; to not offer the routes, leave the service out.

The new lean property com.openexchange.gdpr.dataexport.administrative.maxTimeToLive sets how long the results of an administrative export are kept. It is empty by default, which keeps them as long as the user's own exports (com.openexchange.gdpr.dataexport.maxTimeToLive), and is neither reloadable nor config-cascade aware.

POST /prov/v1/contexts/{context_id}/users/{user_id}/administrative-data-export-task accepts an optional mailCredential for the user's mailbox: password, or accessToken with optional accessTokenExpires, refreshToken, oidcBackendPath and oauthMechanism (XOAUTH2 default, or OAUTHBEARER); plus masterUser if it is a master user's.

The new lean property com.openexchange.mail.admin.masterUserSeparator sets the separator of the master login MW composes for a master user's credential, e.g. * for Dovecot's auth_master_user_separator. It is empty by default, so no master user can be used, and is reloadable and config-cascade aware.

SCR-2163

Summary: New configuration options for the synchronization of POP3 accounts

Effective: 8.55.481 and later

New configuration options to limit the synchronization of external POP3 accounts.

  • com.openexchange.pop3.maxConcurrentSyncs The max. number of POP3 synchronizations running in parallel on a node. Formerly fixed to 10. Default 10. Not reloadable, not config-cascade aware. No dedicated properties file.

  • com.openexchange.pop3.maxConcurrentSyncsPerUser The max. number of POP3 synchronizations running in parallel for one user, so a user with many slow POP3 accounts cannot occupy all slots of com.openexchange.pop3.maxConcurrentSyncs. Further accounts of that user stay due and are synchronized afterwards. A value less than or equal to 0 (zero) means no per-user limit. Default 1. Reloadable, not config-cascade aware. No dedicated properties file.

  • com.openexchange.pop3.syncTimeBudget The time budget of a single POP3 synchronization in milliseconds. Once exceeded, no further messages are retrieved and the rest stays on the server for the next synchronization; at least one message is retrieved per synchronization. A value less than or equal to 0 (zero) means no budget. Default 300000. Reloadable, not config-cascade aware. No dedicated properties file.

SCR-2161

Summary: New properties com.openexchange.file.storage.opencloud|nextcloud.identityProvider.* for the storage account of the identity the user logged in with

Effective: 8.55.481 and later

In a deployment whose identity provider grants access to the groupware and to OpenCloud or Nextcloud alike, e.g. keycloak in front of OX App Suite and OpenCloud, a user has an account in the storage under the reserved identifier idp without creating one: the server exchanges the token of the session for one meant for the storage (RFC 8693 token exchange). OpenCloud is accessed with that token, so no credentials of OpenCloud are stored anywhere. Nextcloud is accessed with an app password the server obtains once with that token, as Nextcloud logs the user in anew for every request that carries a token and lists each of these logins among the user's devices and sessions. The app password is kept per user and instance, encrypted with the secret of the user, and replaced by a new one if the user removes it. The account is listed with the storage's product name next to the accounts the user created, cannot be created, changed or deleted, and only exists for a session that was logged in through the identity provider. It can be made the default file storage with com.openexchange.file.storage.default=opencloud://idp.

New lean configuration properties, reloadable and config-cascade aware:

  • identityProvider.url, with the prefix com.openexchange.file.storage.opencloud. and com.openexchange.file.storage.nextcloud., without default: the base URL of the storage instance; set, the account exists
  • identityProvider.audience, with both prefixes, without default: the client the exchanged token is meant for, as registered at the identity provider
  • identityProvider.scope, with both prefixes, without default: the scope the exchanged token is requested with; with keycloak openid together with the client scope that carries the audience mapper for the storage, e.g. openid opencloud-audience, that scope bound to the groupware's client as an optional scope
  • com.openexchange.file.storage.nextcloud.identityProvider.deviceName: the name the app password shows up with among the user's devices and sessions in Nextcloud; sent as user agent, so ASCII only; it defaults to OX App Suite (required, do not delete)

Note for operators: the identity provider has to allow the exchange. For keycloak that is standard token exchange on the groupware's client, and one client scope per storage with an audience mapper for the storage's client and the mappers for the claims the storage maps its users by, username, email and full name, added to userinfo, bound to the groupware's client as an optional scope and named in identityProvider.scope together with openid, as the storages read the claims of the user from the userinfo endpoint, which keycloak only serves to a token with that scope. An exchange without the right scope fails with "Requested audience not available", which the log shows. Nextcloud's OpenID Connect app has to be configured to check bearer tokens, and Nextcloud has to allow app passwords (auth_can_create_app_token, enabled by default). For Nextcloud, com.openexchange.secret.secretSource must not depend on the password, which a session logged in through the identity provider lacks; without a usable secret, the token itself is used and a warning is logged once per session. A failed exchange is logged as a warning, and the user has no account in the storage for the next five minutes. An app token or app password of the storage configured by an operator for a user is not supported.

See the feature documentation for further details.

SCR-2160

Summary: New property com.openexchange.gdpr.dataexport.provider.infostore.includeExternalDefaultFileStorage

Effective: 8.55.481 and later

The GDPR data export of the Drive module always covers the built-in infostore. Where another storage is the user's default file storage (see com.openexchange.file.storage.default), such as OpenCloud or Nextcloud, its personal, public, shared and trash folders are exported in addition, below a directory named after the account. The files are read from that storage over the network at export time, and what the archive holds depends on what the storage serves: deleted files of OpenCloud, for example, are exported with their structure only, which the report of the export notes. A storage that cannot be accessed is retried as long as the export may retry, then the infostore is exported alone and the report says so.

The new lean configuration property com.openexchange.gdpr.dataexport.provider.infostore.includeExternalDefaultFileStorage lets an operator leave the external default storage out, in which case the infostore is exported alone and the report says so. It defaults to true, is reloadable and config-cascade aware.

SCR-2157

Summary: New property com.openexchange.file.storage.default selects the default file storage of a user

Effective: 8.55.481 and later

In order to let new files of a user go to an external file storage while the built-in infostore stays available, the new lean configuration property com.openexchange.file.storage.default selects the user's default file storage among the storages the user holds. Drive Mail stores its attachments there, mail attachments are saved there, its folders are the default folders for documents, pictures, music, videos and templates, and the folders actions home, public and sharedWithMe answer its folders. The value is a qualified account identifier of the form <service-id>://<account-id>, e.g. opencloud://10. It defaults to com.openexchange.infostore://infostore, is reloadable and config-cascade aware.

Only the infostore, OpenCloud and Nextcloud can be the default. Any other value, one that cannot be parsed included, is logged as a warning and the infostore serves instead, as it does for a user without an account in the configured storage. The resolver is provided by the new bundle com.openexchange.file.storage.primary in the open-xchange-core package.

Note for operators: the folder tree is the same for every user, the system folders 9, 10 and 15 serve the infostore, and no data is migrated when the default changes. The OX Drive synchronization clients only synchronize the services listed in com.openexchange.drive.enabledServices. Folder and file identifiers of OpenCloud and Nextcloud accounts are stable across renames and moves since this change; identifiers issued before keep resolving as long as the path of the item is unchanged.

See the feature documentation for further details.

SCR-2153

Summary: New configuration options for the IMAP store cache

Effective: 8.55.481 and later

The IMAP store cache keeps a connection longer for accounts that send requests in short intervals, within a per-node budget.

  • com.openexchange.imap.storecache.maxIdleSeconds Max. time in seconds an idle IMAP connection is kept for an account that borrows again in short intervals. The actual time is 1.5 times the account's typical gap between returning a connection and borrowing the next one, but never less than com.openexchange.mail.mailAccessCacheIdleSeconds. If it would exceed this value, com.openexchange.mail.mailAccessCacheIdleSeconds applies. A value not greater than com.openexchange.mail.mailAccessCacheIdleSeconds disables the adaptation. Default 30. Reloadable, not config-cascade aware. No dedicated properties file.

  • com.openexchange.imap.storecache.maxExtendedIdleStores Number of idle IMAP connections per node up to which a connection is kept longer than com.openexchange.mail.mailAccessCacheIdleSeconds. Once reached, every returned connection is kept for com.openexchange.mail.mailAccessCacheIdleSeconds only. 0 (zero) derives the value from the max. heap size: one connection per 3 MiB, e.g. 682 for a 2 GiB heap. A negative value means unlimited. Default 0. Reloadable, not config-cascade aware. No dedicated properties file.

SCR-2151

Summary: New Properties to Log In to Secondary and Shared Accounts via SASL PLAIN With the Acting User's Password

Effective: 8.55.481 and later

In order to access secondary and shared accounts with the acting user's own login and session password, App Suite can authenticate via SASL PLAIN as the acting user and pass the account's login as authorization identity. The following properties are introduced:

  • com.openexchange.mail.secondary.authorizationId.enabled for IMAP and SMTP
  • com.openexchange.mail.filter.secondary.authorizationId.enabled for Sieve

They default to false, are reloadable and config-cascade aware. They only take effect with the session password (passwordSource=session) and only for IMAP, SMTP and Sieve. If a master user separator (SCR-2131) is configured as well, they take precedence. The mail server has to accept such logins and verify that the acting user may access the account, e.g. through a master passdb. For mail sent through such an account, the data retention identifier and the audit log name the acting user's login.

See the feature documentation and the property documentation for further details.

SCR-2143

Summary: New property com.openexchange.deputy.sendAsEnabled and capability deputy_send_as

Effective: 8.55.481 and later

The new lean configuration property com.openexchange.deputy.sendAsEnabled controls whether a granting user may let a deputy send E-Mails as the granting user, i.e. with the granting user as From and without a Sender header, so recipients cannot tell that the deputy sent the E-Mail. It defaults to false, is reloadable and config-cascade aware, and is evaluated for the granting user. Clients learn about it through the new capability deputy_send_as.

  • While the property is false, granting "send as" is rejected with DEPUTY-0016; passing back an already stored value unchanged is still accepted.
  • Disabling it takes effect on existing grants immediately: a grant of "send as" then acts as "send on behalf of", and notification mails sent to the deputy meanwhile name that form. The stored grant is kept and takes effect again once the property is enabled.
  • Administrative provisioning (SOAP, RMI, gRPC, SCIM) is not restricted by the property. Grants of "send as" provisioned while it is disabled take effect as soon as it is enabled, also when it is enabled for the first time.

"Send as" covers E-Mails only: notification and iTIP mails a deputy triggers in the granting user's calendar are always sent on behalf of the granting user. Technical traces of the deputy outside the mail headers remain possible, see the known limitations.

Note for operators: the SMTP server has to accept the granting user's address as From for the deputy's login without a Sender header. During a rolling upgrade, enable the property and provision "send as" only once all nodes run this version: older nodes ignore the stored flag and delete a grant of "send as" without any module as soon as they read it, e.g. when the deputy opens App Suite on such a node.

See the feature documentation for further details.

SCR-2139

Summary: New properties to send SMS via sipgate with a personal access token and a chosen SMS extension

Effective: 8.55.481 and later

In order to authenticate against the sipgate REST API with a personal access token, which sipgate intends as the long-term replacement for account password logins, the following properties are introduced for the sipgate SMS provider:

  • com.openexchange.sms.sipgate.tokenId and com.openexchange.sms.sipgate.token for the personal access token, which needs the scope sessions:sms:write
  • com.openexchange.sms.sipgate.smsId for the SMS extension that text messages are sent from; it defaults to s0, the extension used so far

All are reloadable and config-cascade aware. com.openexchange.sms.sipgate.username and com.openexchange.sms.sipgate.password are deprecated: they are only used while no personal access token is configured, and a warning is logged once per node when they are. Existing configurations keep working without operator action.

See the property documentation for further details.

SCR-2137

Summary: New and changed configuration options for nodes that come and go, e.g. on Kubernetes

Effective: 8.55.481 and later

These settings help nodes that come and go, e.g. on Kubernetes: one lets all nodes share the upload directory, so that a file outlives the node that received it, one lets another node take over background update tasks a dead node left unfinished, one keeps slow clients connected while a node drains its requests, and the core-mw Helm chart now limits how many pods a node drain stops at once.

  • com.openexchange.filemanagement.sharedUploadDirectory Whether the upload directory set by com.openexchange.server.UPLOAD_DIRECTORY is shared by all nodes, e.g. a mounted NFS volume. If enabled, files that other nodes may access later on, like attachments uploaded before a mail is sent or large results of jobs that ran in the background, are kept there together with their properties: any node finds them, a node that shuts down leaves them in place, and they are deleted once no node accessed them within their time-to-live. On start-up, a node then only deletes remnants older than one day. Set it the same on all nodes. Default false. Not reloadable, not config-cascade aware. No dedicated properties file.

  • com.openexchange.groupware.update.background.idleMillis The idle time in milliseconds after which the lock of a schema's background update tasks counts as timed out, so that another node runs the remaining tasks. With the former default, a lock left behind by a node that died while running background update tasks was never released. Must exceed com.openexchange.groupware.update.refreshIntervalMillis. Default changed from 0 to 60000. Reloadable, not config-cascade aware. No dedicated properties file.

  • com.openexchange.http.jetty.shutdownIdleTimeoutMillis The idle timeout in milliseconds that open connections get once a node shuts down and drains its requests (see com.openexchange.connector.awaitShutDownSeconds). A connection without I/O for that long is closed, an idle keep-alive connection as well as a slow upload that pauses. Formerly fixed at 1 second, which dropped such uploads. A value less than or equal to 0 (zero) keeps the regular idle timeout of com.openexchange.http.jetty.readTimeoutMillis. Default 5000. Not reloadable, not config-cascade aware. No dedicated properties file.

  • pdb Value of the core-mw Helm chart. The chart now creates a PodDisruptionBudget per type, so that a node drain or another voluntary disruption stops one pod of a type at a time; that never blocks a drain, not even with a single replica. Defaults changed: pdb.create from false to true, pdb.maxUnavailable from empty to 1. pdb.minAvailable takes precedence if set; a value equal to the number of replicas blocks drains. Set pdb.create to false to go without.

  • keda New values of the core-mw Helm chart. With keda.enabled set to true, the chart creates a KEDA ScaledObject per type that scales on the average number of HTTP API requests in progress per pod, derived from appsuite_httpapi_requests_seconds_sum, optionally also on CPU utilization. Required: keda.prometheus.serverAddress and keda.prometheus.threshold. keda.minReplicaCount must be at least 1; KEDA and hpa.create are mutually exclusive. Default keda.enabled is false.

Readiness: a failed health check is evaluated again after 2 seconds instead of reusing the result for 10 seconds, so a pod is ready again right after a short outage of a backing service such as a Redis failover. A successful result is still reused for 10 seconds.

Session removals: a logout or an administrator's removal of sessions while the main Redis is unreachable is applied to Redis within a few seconds after it is reachable again. Formerly such sessions became valid again once Redis was back. A node that shuts down meanwhile logs how many removals it could not apply.

  • com.openexchange.push.permanent.maxStartsPerSecond New. The number of permanent push listeners holding resources (IMAP IDLE, mail notify) a node starts per second at most, and stops per second without being asked for them by another node. When nodes come or go, users move at this rate per starting node; a user keeps its listener on the former node until the new one is about to start it, so it goes without push for a few seconds only. Moving takes the number of listeners a node takes over, one per user and push manager, divided by this rate. Default 10. Reloadable, not config-cascade aware. File mail-push.properties.

  • com.openexchange.push.rescheduleOnRegistration Removed, together with the master node that started all permanent push listeners. Each registered user now belongs to one node, determined by rendezvous hashing over the live nodes; that node starts the user's permanent listeners, also for registrations made on other nodes.

  • POD_NAME_STABLE New environment variable the core-mw Helm chart sets for StatefulSets only. A pod of a StatefulSet then keeps its identifier across restarts and gets its users back. Other pods use a random identifier per start. Nothing to configure.

Permanent push listeners: IMAP IDLE and mail notify listeners are now spread over all nodes instead of running on one master node. Nodes announce themselves and hold a lease per user in Redis; Lua scripting must be enabled there (the default). A node that cannot renew its leases for 60 seconds stops its permanent listeners, so an outage of the main Redis shorter than 30 seconds stops nothing. The users of a node killed without shutting down start on the others once its leases expired, after about 90 seconds; the node holding a user's lease takes over the IMAP IDLE lock a stopped permanent listener left behind, but never the lock of a listener for a session. While a node of a former version still runs its master, permanent listeners keep starting where they are registered.

Mail notify without multicast: a node receiving a notification for a mailbox it holds no listener for forwards it via Redis Pub/Sub to the node holding the permanent listener. The metric appsuite.push.mailnotify.forward.dropped counts notifications dropped as too many were waiting. Dovecot push registrations are no longer renewed when the master changes, as there is none.

  • javaOpts.aotCache New value of the core-mw Helm chart. The image now ships a JDK AOT cache, trained on its bundles when the image is built, and starts the JVM with it (JAVA_OPTS_AOT): the middleware starts about a fifth faster. The cache fits only G1 with Compact Object Headers and a heap below 32 GB; with javaOpts.zgc or without javaOpts.compactObjectHeaders the chart turns it off. Default true. Set it to false to start without the cache.

  • probe Values of the core-mw Helm chart. The startup probe checks every 2 seconds after 10 seconds, so that a started pod becomes ready within a few seconds instead of up to 15. Defaults changed: probe.startup.initialDelaySeconds from 30 to 10, probe.startup.periodSeconds from 10 to 2, probe.startup.failureThreshold from 30 to 160, which keeps the start-up budget at 330 seconds. probe.readiness.initialDelaySeconds changed from 30 to 5, as the readiness probe only runs once the startup probe succeeded.

  • startupCPUBoost New values of the core-mw Helm chart. The main container of a starting pod gets startupCPUBoost.cpu and is resized in place to resources once the pod is ready, without a restart: with a CPU limit of 500m, a pod was ready after 53 to 63 seconds instead of 155 to 162. By default (startupCPUBoost.enabled auto) only a CPU limit above the request is raised, and only the limit, so the pod is scheduled as without the boost; this needs Kubernetes 1.33 or later and the chart's Role, which then allows patching pods/resize. A pod without a CPU limit or with a limit equal to its request is not boosted then. With true those are boosted too: the request is raised along with the limit, so the pod keeps its QoS class and is scheduled with the boosted request. false turns the boost off. After startupCPUBoost.maxSeconds the CPU is lowered even if the pod has not become ready. Defaults: startupCPUBoost.enabled auto, startupCPUBoost.cpu 2, startupCPUBoost.maxSeconds 600.

  • initWait.middlewareTimeout Removed from the core-mw Helm chart, together with the init container's INIT_MW_WAIT_TIMEOUT. With enableInitialization, the init container no longer starts the middleware for the initial bootstrap; it only creates the ConfigDB, and the main container registers server, file stores and databases and creates the demo contexts once the middleware has started there. A fresh stack thus starts the middleware once instead of twice. The pod becomes ready when the bootstrap is done.

SCR-2131

Summary: New Properties to Log In to Secondary and Shared Accounts With the Acting User's Credentials

Effective: 8.55.481 and later

In order to access secondary and shared accounts with the acting user's OAuth access token on mail servers that require the authenticated user name to match the token, such as Dovecot 2.4 and Dovecot Pro 3.1, App Suite can log in as <account login><separator><acting user login>. The following properties are introduced:

  • com.openexchange.mail.secondary.masterUserSeparator for IMAP and SMTP
  • com.openexchange.mail.filter.secondary.masterUserSeparator for Sieve

They are empty by default, which keeps the current behavior, and are reloadable and config-cascade aware. The login is only composed if the acting user's own credentials are passed, i.e. an OAuth access token or the session password. The mail server is then responsible for verifying that the acting user may access the account, e.g. through a master passdb. For mail sent through such an account, the data retention identifier is the composed login.

See the feature documentation for further details.

SCR-2130

Summary: New configuration options for Argon2 key derivations

Effective: 8.55.481 and later

Argon2 key derivations, used for the session obfuscation key and other server-side encryption, are now bounded per node, and the session obfuscation key reuses one random salt per node for up to a day, so creating or loading sessions no longer derives a key per session.

  • com.openexchange.crypto.argon.maxConcurrentDerivations Defines how many Argon2 key derivations run at once on a node; others wait up to 30 seconds for a free slot and then fail with CRP-0009. Each derivation holds com.openexchange.crypto.argon.memory of heap while it runs. A value less than or equal to 0 (zero) derives the number from the maximum heap, between 2 and 16. Default 0. Reloadable, not config-cascade aware. No dedicated properties file.

  • com.openexchange.crypto.argon.keyCacheIdleMinutes Defines how many minutes a derived key stays cached on a node without being used. Each value encrypted with its own salt, such as the password of an external mail account, needs its own key, so an expired key costs another derivation the next time that value is read. Values less than 1 fall back to the default. Default 60 (previously fixed at 10 minutes). Reloadable, not config-cascade aware. No dedicated properties file.

8.35 and 8.47 to 8.53 get only the bound on concurrent derivations: callers wait for a free slot without a time limit instead of failing with CRP-0009, and the session obfuscation key still uses a random salt per session.

SCR-2127

Summary: New configuration option for revoking access tokens after a refused mail password

Effective: 8.55.481 and later

Access tokens whose kept mail password is refused are revoked after a wait where no live session can tell a lockout from a password change.

  • com.openexchange.accesstoken.revokeOnRefusedPasswordWithoutSessionDelay How long the primary mail server has to keep refusing the password an access token keeps before tokens are revoked, where no live session of the user logged in with a password. It should outlast the lockouts the user directory imposes after failed logins. A time span such as 30m or 2h; values below five minutes are raised to five, values above one day lowered to one day. Where a live session shows that the password changed, tokens are revoked after five minutes regardless. Default 30m. Reloadable, not config-cascade aware. No dedicated properties file.

SCR-2103

Summary: New option to revoke tokens without an ID token at the OpenID Connect provider

Effective: 8.55.481 and later

An OpenID Connect back-end can revoke tokens that came without an ID token.

  • com.openexchange.oidc.opRevocationEndpoint The OpenID Connect provider's token revocation end-point (RFC 7009). Tokens without an ID token, e.g. ones the mail credential vault obtained through a token exchange, cannot be ended with an end-session request at com.openexchange.oidc.opLogoutEndpoint. Where this is set, they are revoked here: the refresh token if there is one, else the access token. May be set per back-end, e.g. com.openexchange.oidc.background.opRevocationEndpoint. For Keycloak: https://<host>/realms/<realm>/protocol/openid-connect/revoke. Default empty: such tokens expire on their own. Reloadable, not config-cascade aware. No dedicated properties file.

SCR-2099

Summary: New option to ask for a refresh token in the token exchange of the mail credential vault

Effective: 8.55.481 and later

A token exchange of the mail credential vault can ask for a refresh token.

  • com.openexchange.mail.credential.oauth.tokenExchange.requestedTokenType The token type to ask for when exchanging tokens: access_token, the default, or refresh_token; the full URN of either type is taken as well, anything else is logged and an access token asked for. With refresh_token, an identity provider that allows it hands out a refresh token as well, so the exchanged credential is refreshed and kept alive instead of expiring with its access token. Keycloak does so where the client allows refresh tokens in its standard token exchange. May be set per feature by appending the feature identifier, e.g. com.openexchange.mail.credential.oauth.tokenExchange.snoozed-mail.requestedTokenType. Default access_token. Reloadable, config-cascade aware. No dedicated properties file.

SCR-2093

Summary: New option for changing the session encryption key without losing sessions

Effective: 8.55.481 and later

The session encryption key can now be changed without losing sessions or kept mail credentials.

  • com.openexchange.sessiond.previousEncryptionKey The former value of com.openexchange.sessiond.encryptionKey after a change. Data obfuscated with it stays readable: sessions stored before the change, and the credentials of the mail credential vault, which the vault seals again with the new key. Only the current key obfuscates. In a cluster updated node by node, first set the new key here on every node, then swap both values. Keep the former key as long as the longest session lives and until the vault reports for every schema that it is no longer needed. Other data obfuscated with the key, e.g. passwords of secondary mail accounts, is not sealed again and becomes unreadable once the former key is removed. Default empty. Reloadable, not config-cascade aware. No dedicated properties file.

  • com.openexchange.sessiond.encryptionKey Now reloadable.

  • com.openexchange.mail.credential.previousEncryptionKey Falls back to com.openexchange.sessiond.previousEncryptionKey when empty, so one setting covers sessions and kept credentials.

SCR-2092

Summary: New configuration option to read the text of PDF files through MCP

Effective: 8.55.481 and later

This option controls whether the MCP server /mcp reads the text of PDF files.

  • com.openexchange.mcp.documents.localPdf Whether file_get, mail_attachment_get and the resource reads of files and attachments read the text of a PDF file on the node. It does not depend on the document converter; office documents go through the converter regardless. A PDF above 5 MiB, one that takes longer than 20 seconds, a protected one, or one whose streams decode to more than 32 MiB is served as without this option. Default false. Reloadable, not config-cascade aware. No dedicated properties file.

SCR-2086

Summary: New configuration options for the token keep-alive and key rotation of the mail credential vault

Effective: 8.55.481 and later

The mail credential vault gets two options: one keeps the OAuth tokens it stores alive, the other carries the former session encryption key through a change of that key.

  • com.openexchange.mail.credential.oauth.keepAliveInterval How long OAuth tokens kept by the vault go without a refresh before an hourly job refreshes them, so that the identity provider does not end their session for being idle; Keycloak ends offline sessions after 30 days by default. A time span such as 3D; 0 switches the keep-alive off, a value below one hour is raised to one hour, and an invalid value is logged and the default used. Default 3D. Reloadable, not config-cascade aware. No dedicated properties file.

  • com.openexchange.mail.credential.previousEncryptionKey The former value of com.openexchange.sessiond.encryptionKey after that key was changed. Credentials sealed with it stay readable and are sealed again with the new key, when used and by an hourly job, which logs per schema when the former key is no longer needed. Without it, kept credentials are lost with a change of the key. Default empty. Reloadable, not config-cascade aware. No dedicated properties file.

SCR-2085

Summary: New Configuration Property to Use Pushed Authorization Requests in the OpenID Connect Authorization Code Flow

Effective: 8.55.481 and later

The new lean configuration property com.openexchange.oidc.opPushedAuthorizationRequestEndpoint enables Pushed Authorization Requests (PAR, RFC 9126) for OpenID Connect logins. It defaults to an empty value, is reloadable and not config-cascade aware.

If set, the parameters of each authentication request, including state, nonce and the PKCE code challenge, are pushed to the OpenID Provider over the back channel, and the browser is only redirected with the returned request_uri. The OpenID Provider has to support PAR and can then require it; there is no fallback if the push fails. No behavior change for existing deployments.

See the property documentation and the feature documentation for further details.

SCR-2078

Summary: New configuration options for the MCP activity log

Effective: 8.55.481 and later

These options bound what the MCP server /mcp records about the calls made through access tokens.

  • com.openexchange.mcp.activity.maxEntriesPerToken How many calls of one access token are recorded at most; an hourly clean-up drops the oldest beyond. A value less than or equal to 0 (zero) records nothing. Default 1000. Reloadable, not config-cascade aware. No dedicated properties file.

  • com.openexchange.mcp.activity.retentionDays How many days recorded calls are kept; the hourly clean-up drops older ones. A value less than or equal to 0 (zero) keeps them as long as com.openexchange.mcp.activity.maxEntriesPerToken allows. Default 30. Reloadable, not config-cascade aware. No dedicated properties file.

SCR-2072

Summary: New configuration options for the built-in OAuth authorization server of the MCP server

Effective: 8.55.481 and later

These options govern the built-in OAuth authorization server of the MCP server /mcp.

  • com.openexchange.mcp.oauth.enabled Whether the middleware acts as authorization server for /mcp. The endpoints without a user (metadata, registration, token) follow the server-level value and answer 404 while it is off; the sign-in follows the user's value, so a context or user can be excluded, and a user excluded later cannot refresh any more. Default false. Reloadable, config-cascade aware. No dedicated properties file.

  • com.openexchange.mcp.oauth.allowedRedirectHosts Comma-separated hosts a client may register redirect URIs for, for example claude.ai, chatgpt.com, loopback; loopback stands for 127.0.0.1, [::1] and localhost. Empty accepts every host; redirect URIs must use https, or http on a loopback host, either way. Registration checks the server-level value, the sign-in the user's as well. Default empty. Reloadable, config-cascade aware. No dedicated properties file.

  • com.openexchange.mcp.oauth.registrationsPerHour How many new clients one address may register per hour; an IPv6 address counts by its /64 prefix, and a public client registering the same metadata again gets the same registration without counting. Authorization requests are limited to 30 times this value per address. A value less than or equal to 0 (zero) disables both limits. Default 10. Reloadable, not config-cascade aware. No dedicated properties file.

  • com.openexchange.mcp.oauth.accessTokenMinutes How many minutes an access token issued by the built-in authorization server is valid; the client then refreshes it. A grant ends after com.openexchange.accesstoken.maxLifetimeDays, or 365 days if that is 0. Default 60. Reloadable, not config-cascade aware. No dedicated properties file.

  • com.openexchange.mcp.oauth.clientMetadataDocuments Whether a client may name itself by the https URL of a client ID metadata document instead of registering. The middleware then fetches that document directly, not through a proxy, and only from public addresses; without direct outbound HTTPS to the clients' hosts, set it to false. Default true. Reloadable, not config-cascade aware. No dedicated properties file.

SCR-2069

Summary: New properties for the OX Guard client's token storage under external authentication

Effective: 8.55.481 and later

Under external authentication the OX Guard client keeps the per-session Guard authentication tokens in Cassandra, since the session is transient on the middleware: the table guard_auth_tokens holds one row per token, keyed by the external session and the crypto type, and each row expires with the lifetime the user chose for the token. The table is created on first use. Classic sessions keep their tokens on the groupware session.

  • com.openexchange.guard.cassandraTokenKeyspace The Cassandra keyspace holding the tokens; has to be a valid unquoted CQL identifier. Default guard.

  • com.openexchange.guard.cassandraTokenAutoCreateKeyspace Whether the keyspace is created when missing; meant for test and development setups. Default false.

The Guard client's own credentials travel in the header X-OX-GUARD-BASIC-AUTH instead of Authorization, which carries the user's bearer token; this requires a Guard server that accepts it.

SCR-2064

Summary: New properties to enable external authentication and the OIDC handoff

Effective: 8.55.481 and later

The following lean properties control external authentication, where an external service authenticates the user, owns the session and hands the middleware a JWT per request:

  • com.openexchange.external.authentication.enabled Enables external authentication. Default false. Not reloadable, not config-cascade aware.

  • com.openexchange.external.authentication.oidc.handoffRedirectURL Base URL of the external service's OIDC handoff resource; the middleware appends the flow token as last path segment once the OpenID Connect flow has obtained the tokens. Only used while external authentication is enabled. Default empty. Reloadable, not config-cascade aware.

With the defaults nothing changes for existing deployments.

SCR-2060

Summary: New Property com.openexchange.contact.crosscontext.enabled

Effective: 8.55.481 and later

The new property com.openexchange.contact.crosscontext.enabled enables the cross-context contacts provider, which surfaces address books shared from other contexts. Users it is available to get the capability contact_crosscontext. It is the instance of the generic com.openexchange.contact.[providerId].enabled for the provider crosscontext. It defaults to true, and is reloadable and config-cascade aware.

The generic com.openexchange.contact.[providerId].enabled is now evaluated per user and context, as documented; before, it was only read at server scope. Values set at context or user level for other contacts providers therefore take effect now.

See the property documentation and the feature documentation for further details.

SCR-2055

Summary: New configuration options for MCP tools that change data

Effective: 8.55.481 and later

These options govern the tools of the MCP server /mcp that change data.

  • com.openexchange.mcp.write.enabled Whether tools that change data are listed by tools/list and callable at /mcp. A token still needs the module's write scope, such as write_calendar. The option governs /mcp only: a token with a write scope writes through the HTTP API as before. Default false. Reloadable, config-cascade aware. No dedicated properties file.

  • com.openexchange.mcp.mail.sendDelay How many seconds mail_send holds a confirmed mail in scheduled mail before it goes out, so that it can still be stopped with mail_scheduled_cancel or in App Suite. Values above 600 count as 600; 0 sends at once, as before. Where the user cannot schedule mails, the mail goes out at once and the result says so. Default 60. Reloadable, config-cascade aware. No dedicated properties file.

  • com.openexchange.mcp.write.confirmSecret The secret the confirmation tokens of MCP write calls are signed with. A tool that changes data answers preview: true with a confirmToken, valid for five minutes, and writes only when called again with that token and exactly the previewed arguments. Set the same value of at least 32 characters on every node; empty or shorter leaves writing unavailable. Default empty. Reloadable, config-cascade aware. No dedicated properties file.

  • com.openexchange.mcp.rateLimit.writeCalls How many calls of tools that change data a user may make per window of com.openexchange.mcp.rateLimit.windowSeconds, counted across all nodes and on top of com.openexchange.mcp.rateLimit.calls. A call with preview: true counts as a read. A value less than or equal to 0 (zero) disables this limit. Default 20. Reloadable, not config-cascade aware. No dedicated properties file.

  • com.openexchange.mcp.write.externalAttendees Whether the tools may reach addresses outside the installation, in the calendar and by mail alike: add them as attendees or send them text, send them mail with mail_send, or give a vacation notice that replies to them a new text. While it is false, such a call is refused before anything is saved; answers and changes without text to external attendees already on an appointment still go out. To allow external attendees but no mail to external recipients, also set com.openexchange.mcp.tools.disabled=mail_send. Default false. Reloadable, config-cascade aware. No dedicated properties file.

SCR-2051

Summary: New Property for Refreshing the LDAP Contacts Provider Cache in the Background

Effective: 8.55.481 and later

In order to spare requests from waiting for the cache of an LDAP contacts provider to be loaded, a cache in use is now refreshed in the background before it expires. The first access once the cache has reached a certain age triggers loading its data anew, while the current data continues to be served until the refreshed data is available. An unused cache still expires after com.openexchange.contacts.ldap.cache.expire and is loaded again upon the next access, as before.

The new lean configuration property com.openexchange.contacts.ldap.cache.refresh defines that age. It is empty by default, which means 90% of com.openexchange.contacts.ldap.cache.expire (108 minutes with its default of 2h), and is reloadable, but not config-cascade aware. Setting it to 0 disables refreshing in the background and restores the previous behavior.

See the property documentation for further details.

SCR-2049

Summary: New configuration options for user feedback pseudonyms

Effective: 8.55.481 and later

A new option keys the pseudonyms of users in exported feedback.

  • com.openexchange.userfeedback.pseudonym.secret The 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.

SCR-2036

Summary: New Configuration for Preconfigured iCal Subscriptions

Effective: 8.55.481 and later

Administrators can now provide read-only calendars from iCal feeds, e.g. public holidays or a canteen menu, that are defined once and provisioned automatically to all users they are enabled for, across contexts. Each user gets such a calendar in the public calendars section, with own color, subscription, schedule transparency, CalDAV synchronization and alarms. The following configuration is new:

  • The file calendar-provider-preconfiguredical.yml defines the available subscriptions, one section per subscription with name, uri, optional credentials, refreshInterval and sharedCache, as well as the per-user settings color, scheduleTransp, shownInTree and usedForSync, each either as initial value or protected from changes. A template is available as calendar-provider-preconfiguredical-template.yml.
  • com.openexchange.calendar.preconfiguredical.subscriptions enables subscriptions for users as comma-separated list of their keys. Empty by default, config-cascade aware.
  • com.openexchange.calendar.preconfiguredical.enabled enables the calendar provider preconfiguredical with capability calendar_preconfiguredical. Defaults to true, config-cascade aware.
  • com.openexchange.calendar.preconfiguredical.usedForSync controls whether these calendars can be synchronized via CalDAV at all. Defaults to true, config-cascade aware.
  • com.openexchange.calendar.preconfiguredical.cleanup.gracePeriod defines how long a daily clean-up job waits before removing the calendars and cached data of subscriptions that were removed from the file. Defaults to 7D.
  • The feeds are retrieved with an own HTTP client preconfiguredical, configurable through com.openexchange.httpclient.preconfiguredical.*. Other than for personal subscriptions, internal hosts are allowed, while redirects are only followed to the same host.

All properties are reloadable. No operator action is required; without subscriptions defined and enabled, nothing changes.

See the documentation, as well as the property documentation for further details.

SCR-2034

Summary: New configuration options for keeping the mail login inside the context

Effective: 8.55.481 and later

Options that keep the mail login and mail server of users and shared accounts inside their context.

  • com.openexchange.mail.login.restrictToContext Whether the mail login of users and shared accounts is kept inside their context. If true, context administrators are bound to the login mappings of the context and to com.openexchange.admin.user.mailServersForContextAdmin, and administrative mail access refuses a stored login that a mail login resolver maps to another user. Read for the context only, never for a user. Default false. Reloadable, config-cascade aware. No dedicated properties file.

  • com.openexchange.admin.user.mailServersForContextAdmin Comma-separated mail servers that context administrators may set as imapServer while com.openexchange.mail.login.restrictToContext is true. A plain host name stands for imap://<host>:143. Default empty, meaning none. Reloadable, not config-cascade aware. No dedicated properties file.

SCR-2027

Summary: New configuration option for the audience of JWTs accepted by the MCP server

Effective: 8.55.481 and later

The MCP server endpoint /mcp can require that a JWT was issued for it.

  • com.openexchange.mcp.audience Comma-separated audiences of which a JWT has to name at least one in its aud claim to be accepted by /mcp; any other JWT is answered with 401 and invalid_token. Set it where the authorization server also issues tokens for other clients, such as the mobile app, since com.openexchange.oauth.provider.jwt.audience applies 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 if com.openexchange.oauth.provider.jwt.jwksUri is set or the OAuth provider runs in mode token_introspection. Personal access tokens are not affected. Default empty. Reloadable, not config-cascade aware. No dedicated properties file.

Each audience is compared as a whole, so one with spaces such as OX App Suite, the default audience of the external authentication service, is matched, and a single word of it is not. The same applies to com.openexchange.mobile.auth.idp.audience of the Mobile API.

SCR-2024

Summary: The provisioning HTTP gateway can expose MailFilterAdminService

Effective: 8.55.481 and later

com.openexchange.grpc.provisioning.MailFilterAdminService can be added to provisioningGateway.services of the core-mw chart. It is not in the default list, because it reads and changes the mail filter rules of any user of a context. Over gRPC and RMI the service is available to administrators regardless of the gateway, as every provisioning service is.

SCR-2023

Summary: New configuration options for administrative mail access

Effective: 8.55.481 and later

Options for how the middleware reaches a user's mailbox and Sieve scripts without the user's password, e.g. for deputy permissions on mail and the mail filter provisioning API.

  • com.openexchange.mail.admin.strategies The comma-separated order of the strategies token, doveadm and master. IMAP and Sieve are resolved separately; per protocol the first strategy that applies wins. token applies only when a token is at hand: one passed by the provisioning call, or one kept in the credential vault for a feature named in com.openexchange.mail.admin.token.vaultFeatures. An empty value switches administrative mail access off, which also stops the Dovecot push registration without a session and the retraction of cross-context mail shares. Default token,doveadm,master. Reloadable, config-cascade aware. No dedicated properties file.

  • com.openexchange.mail.admin.token.vaultFeatures The comma-separated features of the credential vault whose kept OAuth tokens may serve administrative mail access. Tokens kept for other features are never used this way. Default empty, which uses no kept token. Reloadable, not config-cascade aware. No dedicated properties file.

  • com.openexchange.mail.admin.considerResolver Whether the login that addresses a mailbox administratively comes from the mail login resolver. It replaces the undocumented com.openexchange.deputy.provider.imap.doveadm.considerResolver, which is still honored. Default false. Reloadable, config-cascade aware. No dedicated properties file.

SCR-2009

Summary: New Configuration Property to Use PKCE in the OpenID Connect Authorization Code Flow

Effective: 8.55.481 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.55.481 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-2003

Summary: New configuration options for Mobile API push notifications

Effective: 8.55.481 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.notificationContent What a visible new-message push reveals to Apple or Google when a subscription asks for nothing: none (a neutral text), sender or senderAndSubject. Default none. An unknown value counts as none.

  • com.openexchange.mobile.push.maxNotificationContent The most a subscription may ask a new-message push to reveal; a higher request is lowered to it, also for existing subscriptions. Default senderAndSubject. An unknown value counts as none.

  • com.openexchange.mobile.push.maxSubscriptionsPerUser The most push subscriptions a user may hold across all Mobile API apps; a further registration is refused with 422. Registering a device again does not count. 0 means no limit. Default 20.

SCR-1978

Summary: New configuration options for live change events

Effective: 8.55.481 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.enabled Whether the transport is enabled. It can be refined per client and topic by appending .<client> and .<client>.<topic>. Default false. Reloadable, config-cascade aware. No dedicated properties file.

  • com.openexchange.pns.transport.sse.pingInterval The interval in milliseconds in which an open stream refreshes its presence and sends a ping event to the client; a presence that is not refreshed expires after twice this interval. Default 30000. Reloadable, config-cascade aware. No dedicated properties file.

  • com.openexchange.pns.transport.sse.maxConnectionsPerUser The maximum number of streams a user may have open across the cluster, counted per endpoint. Streams at <dispatcher prefix>events and at /mobile/v1/events do not replace each other; at /mobile/v1/events the limit applies per device (access token, or Push-Subscription-Id in mode idp). Opening one more closes the oldest stream of the same endpoint and device with reason replaced. A value less than or equal to 0 (zero) disables the limit. Default 5. Reloadable, config-cascade aware. No dedicated properties file.

  • com.openexchange.pns.transport.sse.maxConnectionsPerNode The maximum number of streams open on one node. Further requests are rejected with HTTP status 503 and a Retry-After header, so the client can reconnect to another node. A value less than or equal to 0 (zero) disables the limit. Default 5000. Reloadable, not config-cascade aware. No dedicated properties file.

  • com.openexchange.pns.transport.sse.maxLifetime The maximum lifetime of a stream in milliseconds. Once reached, the stream ends with reason lifetime and the client reconnects. A value less than or equal to 0 (zero) is ignored and the default is used. Default 1800000 (30 minutes). Reloadable, config-cascade aware. No dedicated properties file.

  • com.openexchange.pns.transport.sse.tokenCheckInterval The 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 reason token_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. Default 120000. Reloadable, config-cascade aware. No dedicated properties file.

  • com.openexchange.imap.liveChanges.mode How changes made outside the middleware, e.g. by another mail client, are noticed for live change streams of users whose primary account is IMAP. poll looks at the mailbox of a user with an open stream once per pollInterval, in one LIST "" "*" 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. off leaves such changes unnoticed. Changes made through the middleware are reported either way. Default poll. Reloadable, config-cascade aware. No dedicated properties file.

  • com.openexchange.imap.liveChanges.pollInterval The 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. Default 10000. Reloadable, config-cascade aware. No dedicated properties file.

  • com.openexchange.imap.liveChanges.maxWatches The 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. Default 5000. Reloadable, not config-cascade aware. No dedicated properties file.

  • com.openexchange.mail.liveChanges.echoWindow The 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. Default 15000. Reloadable, config-cascade aware. No dedicated properties file.

SCR-1944

Summary: New configuration options for the MCP server

Effective: 8.55.481 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.enabled Whether the MCP endpoint /mcp and its resource metadata document /.well-known/oauth-protected-resource/mcp are registered. Default false. Reloadable, not config-cascade aware. No dedicated properties file.

  • com.openexchange.mcp.allowedOrigins Comma-separated Origin header values a browser-based client may send. A request with an Origin that is not listed is answered with 403. Default empty, which refuses every request carrying an Origin header. The endpoint sends no CORS headers and answers OPTIONS with 405, so a browser client on another origin needs CORS at the ingress. Reloadable, not config-cascade aware. No dedicated properties file.

  • com.openexchange.mcp.authorizationServers Comma-separated issuer URLs advertised as authorization_servers in the RFC 9728 resource metadata. Default empty; com.openexchange.oauth.provider.allowedIssuer is advertised then, if set. Reloadable, not config-cascade aware. No dedicated properties file.

  • com.openexchange.mcp.serverName The name reported as serverInfo.name. Default open-xchange-mcp. Reloadable, not config-cascade aware. No dedicated properties file.

  • com.openexchange.mcp.instructions The natural-language guidance a client receives from server/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.calls How 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 with isError and a Retry-After header, a resource read with 429 and Retry-After. A value less than or equal to 0 (zero) disables the limit. Default 60. Reloadable, not config-cascade aware. No dedicated properties file.

  • com.openexchange.mcp.rateLimit.windowSeconds The window of the tool call limit in seconds. A value less than 1 is treated as 1. Default 60. Reloadable, not config-cascade aware. No dedicated properties file.

  • com.openexchange.mcp.maxConcurrentCalls How 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, with Retry-After: 1, takes no permit of the rate limit and is audited with outcome busy. Per node, since a call in flight lives on the node that runs it. A value less than or equal to 0 (zero) switches the bound off. Default 4. Reloadable, not config-cascade aware; a changed value applies to calls that start after the reload. No dedicated properties file.

SCR-1871

Summary: New Configuration Properties for the Calendar Trash Folder

Effective: 8.55.481 and later

Three new properties introduce the calendar Trash folder, i.e. the soft deletion of appointments into a reserved folder they can be restored from. The feature is off by default and is switched on deliberately, per user, context or server; all three are reloadable and config-cascade aware.

  • com.openexchange.calendar.trashFolder.enabled - enables the feature. Defaults to false, unlike com.openexchange.contact.trashFolder.enabled.
  • com.openexchange.calendar.trashFolder.retentionDays - days an event is kept before the automatic clean-up removes it permanently. Defaults to 30; 0 or below switches the clean-up off.
  • com.openexchange.caldav.trashFolder.enabled - whether CalDAV deletions are soft deletions as well. Defaults to false.

Where the feature is switched on, deleting an appointment becomes recoverable for the users concerned, and deleted appointments occupy quota until the retention window expires. Switched off again for a user who already has entries in the Trash, the folder leaves the folder tree and deletes are permanent again, while the entries are kept and the clean-up leaves them alone; both are back once the feature is on again.

The CalDAV switch is a separate decision: with only the first property on, App Suite deletions go to the Trash while a synchronizing client's deletions remain permanent. That split is deliberate.

With the feature enabled, a Trash folder appears in the user's folder tree on their first deletion and the calendar_trash capability is announced to clients. The retention default is overridden per user through the chronos/trashRetentionDays setting of the io.ox/calendar JSlob; the clean-up runs without a session, so a value defined per host name (as-config.yaml) does not apply to it.

A shared account's Trash folder is purged by the account's own retention, not by the acting deputy's. The io.ox/calendar JSlob hands it out per account under sharedaccount/<account id>/chronos/trashRetentionDays, next to trashFolderId; a deputy holding the writeJSlob capability for the account may change it there.

CalDAV has no equivalent of the HTTP API's harddelete parameter, so one switch answers for every deletion the protocol can express. It can only withhold the Trash from CalDAV, never grant it.

See the configuration documentation, as well as the "Trash Folder / Soft Delete" section of the calendar implementation details for further details.

Database

SCR-2211

Summary: New column pool in table mail_credential for pooled mail credentials

Effective: 8.55.481 and later

Warning

Update Taskcom.openexchange.mail.credential.impl.groupware.MailCredentialAddPoolColumnTask

The update task com.openexchange.mail.credential.impl.groupware.MailCredentialAddPoolColumnTask adds the nullable column pool (BINARY(16)) and the key pool to the table mail_credential. Through it, a kept mail credential refers to the pooled credential of its user, see com.openexchange.mail.credential.pooling. A referring row keeps its own copy, so earlier versions use it as before. Existing rows get NULL and are used as before.

SCR-2203

Summary: New indexes on (cid, id) in table login2user and on (alias, cid) in table user_alias

Effective: 8.55.481 and later

Warning

Update Taskcom.openexchange.groupware.update.tasks.Login2UserAddIdIndexTask

The update task adds the index id over (cid, id) to the table login2user in the user databases. The primary key covers (cid, uid) only, so each lookup of a login by user identifier reads all rows of the context: when loading users, in the provisioning user search and in getMultipleData.

In a context with 3,000 users, a pattern search spent 1.4 seconds in the database alone. Schemas that already have an index on exactly these columns, under any name, keep it. The index holds two integer columns per user and is created online.

A second update task adds the index alias over (alias, cid) to the table user_alias. Its primary key leads with the user, so each lookup of a user by alias read all aliases of the context, and the mail mapping read all aliases of the schema. In a context with 10,000 aliases, a lookup now reads one row instead of 10,010. Here, too, an existing index on exactly these columns is kept.

SCR-2196

Summary: New tables user_recovery_details and user_recovery_email_index for account recovery

Effective: 8.55.481 and later

Warning

Update Task com.openexchange.account.recovery.rdb.groupware.RecoveryDetailsCreateTableTask, com.openexchange.account.recovery.rdb.groupware.RecoveryEmailIndexBackfillTask

  • user\_recovery\_details in every groupware schema holds a user's recovery email, phone and PIN with their verification state. The update task com.openexchange.account.recovery.rdb.groupware.RecoveryDetailsCreateTableTask creates it. The task runs only while com.openexchange.accountrecovery.enabled=true.
  • user\_recovery\_email\_index in the ConfigDB holds a copy of every recovery email, with the columns cid, id and recovery\_email. With it the public lookup costs one query instead of one per schema.
  • The Liquibase change log accountRecoveryConfigdbChangeLog.xml creates the index table. The update task com.openexchange.account.recovery.rdb.groupware.RecoveryEmailIndexBackfillTask fills it from existing details and rebuilds it when re-run with forceupdatetask.
  • A failed index write is counted in appsuite.account.recovery.index{outcome="failed"}.
  • Deleting a context or a user removes their rows from both tables.

SCR-2181

Summary: New columns for administrative data exports in dataExportTask and dataExportFilestoreLocation

Effective: 8.55.481 and later

Warning

Update Taskcom.openexchange.gdpr.dataexport.impl.groupware.DataExportAddAdministrativeColumnsTask

8.x:dataExportTask:addAdministrativeColumns

To keep an export an administrator starts on behalf of a user apart from the user's own, the update task adds these columns to the user databases, and the changeset 8.x:dataExportTask:addAdministrativeColumns to the global database:

  • dataExportTask: origin (user or admin, default user), caseReference, requestedBy and skippedModules
  • dataExportFilestoreLocation: sha256, the checksum of a result package

The unique key of dataExportTask changes from (cid, user) to (cid, user, origin). The task is idempotent and only alters the data export tables, which hold one row per pending or kept export. Rolling the changeset back deletes the administrative exports.

SCR-2165

Summary: New columns for the rotation of personal access tokens in table access_token

Effective: 8.55.481 and later

Warning

Update Taskcom.openexchange.accesstoken.impl.groupware.AccessTokenAddRotationColumnsTask

The update task com.openexchange.accesstoken.impl.groupware.AccessTokenAddRotationColumnsTask adds the columns rotated, lifetime, previous_hash, previous_key and previous_until and the index previous on (cid, previous_hash) to table access_token. A rotation of a personal access token writes them: when it happened, the lifetime the token was minted with, and the hash and sealed key of the replaced secret together with the end of its grace period. Until the update task has run, access tokens cannot be validated.

SCR-2148

Summary: New column sendAs in table deputy

Effective: 8.55.481 and later

Warning

Update Taskcom.openexchange.deputy.impl.groupware.DeputyStorageAddSendAsColumnTask

The deputy table gains the column sendAs, which stores whether a deputy may send E-Mails as the granting user without a Sender header. Behavior-neutral for existing rows: the default 0 keeps existing deputy permissions with their current "send on behalf of" setting.

ALTER TABLE deputy ADD COLUMN sendAs TINYINT(1) NOT NULL DEFAULT 0;

Fresh schemas: com.openexchange.deputy.impl.groupware.DeputyStorageCreateTableService. Existing schemas: com.openexchange.deputy.impl.groupware.DeputyStorageAddSendAsColumnTask (idempotent, depending on DeputyStorageCreateTableTask). The deputy table is small on typical deployments, so no noticeable ALTER cost is expected.

Note for operators: during a rolling upgrade, nodes of the previous version do not know the column. They delete a deputy permission that grants neither a module nor "send on behalf of" as soon as they read it; a deputy who opens App Suite on such a node is enough. So a deputy permission granting only sendAs without any module must not be created before all nodes run the new version. The same applies after a rollback to a version without sendAs: the column is then ignored, deputies granted only "send as" can no longer send E-Mails for the granting user, and such deputy permissions without any module are deleted. To prevent that, run the following statement in every context database schema before rolling back; the deputies then send E-Mails on behalf of the granting user, and "send as" has to be granted anew after upgrading again:

UPDATE deputy SET sendOnBehalfOf = 1, sendAs = 0 WHERE sendAs = 1;

SCR-2115

Summary: Added column data to table notificationQueue

Effective: 8.55.481 and later

Warning

Update Taskcom.openexchange.notification.queue.impl.groupware.NotificationQueueAddDataColumnTask

The table notificationQueue gets the new nullable column data (TEXT, utf8mb4) holding a notification's optional structured data. A blocking update task adds it to existing schemas; it is idempotent and runs after NotificationQueueCreateTableTask. New schemas get the column on creation. The ALTER only touches this table, which holds at most 100 pending notifications per user by default.

SCR-2094

Summary: Index refresh_due of table mail_credential now starts with caller_key

Effective: 8.55.481 and later

Warning

Update Taskcom.openexchange.mail.credential.impl.groupware.MailCredentialRefreshDueByCallerKeyTask

The index refresh_due of table mail_credential now spans (caller_key, refresh_due). The update task com.openexchange.mail.credential.impl.groupware.MailCredentialRefreshDueByCallerKeyTask replaces the index and marks rows with refresh_due 0 that hold a credential as due, in batches of 10,000 rows, so the keep-alive picks them up again.

SCR-2087

Summary: New column refresh_due in table mail_credential

Effective: 8.55.481 and later

Warning

Update Taskcom.openexchange.mail.credential.impl.groupware.MailCredentialAddRefreshDueColumnTask

Table mail_credential gets the column refresh_due (BIGINT, default 1) and the indexes refresh_due and id, added by the update task com.openexchange.mail.credential.impl.groupware.MailCredentialAddRefreshDueColumnTask without rewriting existing rows. The column holds when kept OAuth tokens are due for a keep-alive, or -1 once the identity provider rejected their refresh token. Run the update tasks of a schema before nodes of this release serve it: every access to a kept credential reads the column.

SCR-2083

Summary: New column folders in table access_token

Effective: 8.55.481 and later

Warning

Update Taskcom.openexchange.accesstoken.impl.groupware.AccessTokenAddFoldersColumnTask

The update task com.openexchange.accesstoken.impl.groupware.AccessTokenAddFoldersColumnTask adds the nullable TEXT column folders to table access_token. It holds the folder restriction of a personal access token; existing tokens stay unrestricted. A restricted token is stored under a hash that earlier versions do not look up, so after a rollback it is refused instead of accepted without restriction.

SCR-2079

Summary: New table mcp_activity

Effective: 8.55.481 and later

Warning

Update Taskcom.openexchange.mcp.activity.ActivityCreateTableTask

The new table mcp_activity in every user database schema holds the calls made through access tokens at /mcp, for their owners: context, user, grant, time, client, tool, object identifiers, mode and outcome, never content. The update task com.openexchange.mcp.activity.ActivityCreateTableTask creates it. Rows go with their token, user and context; an hourly clean-up keeps the table within com.openexchange.mcp.activity.retentionDays and com.openexchange.mcp.activity.maxEntriesPerToken.

SCR-2035

Summary: Added index on the targeted shared account

Effective: 8.55.481 and later

Warning

Update Taskcom.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.

SCR-1872

Summary: New Trash Marker Columns in the Calendar Tables

Effective: 8.55.481 and later

Warning

Update Task com.openexchange.chronos.storage.rdb.groupware.CalendarAddTrashColumnsTask

The calendar Trash folder records what was soft-deleted, by whom and where it came from on the rows themselves. The columns are applied by the update task com.openexchange.chronos.storage.rdb.groupware.CalendarAddTrashColumnsTask; freshly created schemas get them from ChronosCreateTableService.

  • calendar_event and calendar_event_tombstone - trashedFrom VARCHAR(255), trashedBy INT4 UNSIGNED, trashedAt BIGINT(20), trashedUid VARCHAR(1024)
  • calendar_attendee and calendar_attendee_tombstone - trashedFrom VARCHAR(255), trashedBy INT4 UNSIGNED, trashedAt BIGINT(20)

All default to NULL, which is what "not in the Trash" means, so existing rows are left as they are and no data is migrated. trashedUid is on the event tables only: it preserves the unique identifier an event gave up on the way in, which is a property of the event rather than of an attendee's view on it.

No index is added, trashedAt being NULL for practically every row; the retention clean-up scans instead. These are the largest tables of the calendar schema, which is worth taking into account when estimating how long the update task runs.

See the "Trash Folder / Soft Delete" section of the calendar implementation details for further details.

Packaging/Bundles

SCR-2193

Summary: New package open-xchange-account-recovery for self-service password reset

Effective: 8.55.481 and later

The middleware gains self-service account recovery. A user who forgot the password asks by recovery email address, primary address or alias, and receives a single-use reset link by mail or SMS. The feature ships in the new package open\-xchange\-account\-recovery with these bundles:

  • com.openexchange.account.recovery
  • com.openexchange.account.recovery.json
  • com.openexchange.account.recovery.rdb
  • com.openexchange.account.recovery.rest

Details:

  • The package stays inactive until com.openexchange.accountrecovery.enabled=true is set. The value is read at bundle start. The core-mw chart disables the package by default. Enable it with features.status.account\-recovery: enabled.
  • Redis is required once the feature is on. Reset tokens and SMS verification codes are stored there, tokens only as a hash.
  • Packages that extend account recovery import its bundles, for example open\-xchange\-cloudplugins\-account\-recovery. Disable the feature with the property, not by removing only the core package. Otherwise the middleware never becomes ready.
  • Documentation: documentation/login\_and\_sessions/account\_recovery.md.

SCR-2182

Summary: Added bundle com.openexchange.external.authentication.multifactor

Effective: 8.55.481 and later

The external authentication no longer requires the optional package open-xchange-multifactor. Until now, the bundle com.openexchange.external.authentication did not start without it. Its multifactor end-point /authentication/v1/multifactor moved into a bundle of its own:

  • com.openexchange.external.authentication.multifactor, shipped with the package open-xchange-multifactor

With the multifactor package installed, nothing changes. Without it, the external authentication starts and the multifactor end-point is not available.

SCR-2164

Summary: New package open-xchange-guard-backend-plugin-cassandra for the OX Guard token storage under external authentication

Effective: 8.55.481 and later

The Cassandra storage of the OX Guard authentication tokens of external sessions moved out of the bundle com.openexchange.guard.client into the new bundle com.openexchange.guard.client.cassandra, shipped with the new package open-xchange-guard-backend-plugin-cassandra. The OX Guard client therefore no longer depends on open-xchange-cassandra.

The package is disabled by default in the core-mw chart. Deployments with external authentication have to enable it along with open-xchange-cassandra:

packages:
  status:
    open-xchange-cassandra: enabled
    open-xchange-guard-backend-plugin-cassandra: enabled

Under external authentication the OX Guard client registers its services only once such a token storage is available, so without the package OX Guard is unavailable. Classic deployments require no action.

SCR-2063

Summary: New bundles for external authentication, the no-op session store and session obfuscation

Effective: 8.55.481 and later

Added the following bundles to the open-xchange-core package:

  • com.openexchange.external.authentication.utils - shared helpers of the external authentication bundles
  • com.openexchange.sessiond.noop - a session store that keeps nothing, active only while external authentication is enabled
  • com.openexchange.session.obfuscation - the obfuscation of session secrets, moved out of com.openexchange.sessiond.redis and provided as ObfuscatorService to any session store

The property com.openexchange.sessiond.encryptionKey keeps its name and meaning; no operator action is required.

SCR-2059

Summary: Added the Bundle com.openexchange.contact.provider.crosscontext

Effective: 8.55.481 and later

New OSGi bundle com.openexchange.contact.provider.crosscontext, shipped in open-xchange-core (added to open-xchange-core.psf, no install-list change). It contains the cross-context contacts provider, which surfaces address books shared from other contexts. Activator com.openexchange.contact.provider.crosscontext.osgi.CrossContextContactsProviderActivator.

See the feature documentation for further details.

SCR-2019

Summary: New Bundle com.openexchange.groupware.common.storage for the Shared Calendar and Contacts Account Storage

Effective: 8.55.481 and later

The package open-xchange-core ships the new bundle com.openexchange.groupware.common.storage. It holds the relational account storage that the calendar and contacts stacks now share, instead of the two separate implementations in com.openexchange.chronos.storage.rdb and com.openexchange.contact.storage.rdb.

SCR-2005

Summary: Package open-xchange-mobile-api ships push subscriptions and requires open-xchange-pns-impl

Effective: 8.55.481 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.

SPI (Service Provider Interface)

SCR-2201

Summary: New Java interfaces for account recovery and administrative password changes

Effective: 8.55.481 and later

New services in com.openexchange.account.recovery:

  • RecoveryRequestGuard vetoes recoveries for single users, for example through a block-list. Core ships no guard.
  • RecoveryMailService and RecoverySmsService replace the default notifications and templates when registered with a higher ranking.
  • ResetUrlResolver builds the reset, verification and login links.
  • RecoveryDetailsListener is notified about changed recovery details.
  • RecoveryDetailsService and RecoveryTokenService are the storage services.

New services in com.openexchange.passwordchange.admin:

  • AdminPasswordChangeService changes a password without a session. The database and webhook backends implement it.
  • AdminPasswordChangeRegistry holds the registered backends.

New mail and SMS templates:

  • notify.account\-recovery.request.mail.html.tmpl, notify.account\-recovery.request.mail.txt.tmpl, notify.account\-recovery.request.sms.tmpl
  • notify.account\-recovery.reset.mail.html.tmpl, notify.account\-recovery.reset.mail.txt.tmpl, notify.account\-recovery.reset.sms.tmpl
  • notify.account\-recovery.verify.mail.html.tmpl, notify.account\-recovery.verify.mail.txt.tmpl, notify.account\-recovery.verify.sms.tmpl
Prev
Important Changes