App Suite Middleware
8.53.265
Configuration
SCR-1877
Summary: New configuration option for contact auto-complete result limiting
Effective: 8.53.265 and later
In order to bound the size of a contact auto-complete response, a new lean configuration property is introduced for the contacts module.
com.openexchange.contact.autocomplete.maxResultsDefines the maximum number of contacts returned by anaddressbooks?action=autocompleterequest that does not supply aright_hand_limitof its own. Such a request previously returned every matching contact, so a query of one or two characters could read and transfer a large part of the address book. A value of 0 (zero) or less disables the limit. Default 100. Reloadable, config-cascade aware. File: contact.properties.
Related change in the same area: a client-supplied right_hand_limit is now applied to auto-complete requests for every sort order. It was previously discarded whenever sort was omitted or set to one of the special sort orders, which are the ones App Suite uses for contacts.
8.53.260
API - HTTP-API
SCR-1875
Summary: New optional parameter applyDefaultAlarms for the iCal import request
Effective: 8.53.259 and later; also delivered in 8.53.260
The iCalendar import request import?action=ICAL accepts the new optional parameter applyDefaultAlarms. When set to true, alarms contained in the imported iCalendar data are skipped for appointments, and the calendar user's configured default alarms (defaultAlarmDate and defaultAlarmDateTime) are applied instead. It has no effect on tasks.
The appointment imported by the following request ends up with the user's default alarms, although the iCalendar data carries no VALARM component:
POST /ajax/import?action=ICAL&folder=cal%3A%2F%2F0%2F31&applyDefaultAlarms=true
Content-Type: multipart/form-data; boundary=--boundary
--boundary
Content-Disposition: form-data; name="file"; filename="appointment.ics"
Content-Type: text/calendar
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Example Corp//Booking//EN
BEGIN:VEVENT
UID:5a1c0f6e-3d21-4a77-9d1f-1e0b7c2a9f44
DTSTAMP:20260901T100000Z
DTSTART:20270301T100000Z
DTEND:20270301T110000Z
SUMMARY:Flight to Berlin
END:VEVENT
END:VCALENDAR
--boundary--
This addresses appointments that do not originate from the user's own calendar, e.g. one added from a mail attachment: such data usually carries either no alarm at all or an alarm chosen by whoever created the file, so the user ends up without the reminders they configured. Since the server cannot tell where the data came from, the client decides. The change is purely additive - without the parameter, alarms are imported as before, which keeps export/import round trips within the calendar module intact.
See the import request documentation for further details.
8.53.259
API - HTTP-API
SCR-1875
Summary: New optional parameter applyDefaultAlarms for the iCal import request
Effective: 8.53.259 and later
The iCalendar import request import?action=ICAL accepts the new optional parameter applyDefaultAlarms. When set to true, alarms contained in the imported iCalendar data are skipped for appointments, and the calendar user's configured default alarms (defaultAlarmDate and defaultAlarmDateTime) are applied instead. It has no effect on tasks.
The appointment imported by the following request ends up with the user's default alarms, although the iCalendar data carries no VALARM component:
POST /ajax/import?action=ICAL&folder=cal%3A%2F%2F0%2F31&applyDefaultAlarms=true
Content-Type: multipart/form-data; boundary=--boundary
--boundary
Content-Disposition: form-data; name="file"; filename="appointment.ics"
Content-Type: text/calendar
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Example Corp//Booking//EN
BEGIN:VEVENT
UID:5a1c0f6e-3d21-4a77-9d1f-1e0b7c2a9f44
DTSTAMP:20260901T100000Z
DTSTART:20270301T100000Z
DTEND:20270301T110000Z
SUMMARY:Flight to Berlin
END:VEVENT
END:VCALENDAR
--boundary--
This addresses appointments that do not originate from the user's own calendar, e.g. one added from a mail attachment: such data usually carries either no alarm at all or an alarm chosen by whoever created the file, so the user ends up without the reminders they configured. Since the server cannot tell where the data came from, the client decides. The change is purely additive - without the parameter, alarms are imported as before, which keeps export/import round trips within the calendar module intact.
See the import request documentation for further details.
8.53.247
Configuration
SCR-1854
Summary: New property com.openexchange.calendar.useNoReplyAddressForNotifications
Effective: 8.53.247 and later
In order to let deployments whose no-reply relay is not authorized for the users' mail domains pass SPF and DMARC checks, the new lean configuration property com.openexchange.calendar.useNoReplyAddressForNotifications is introduced. If enabled, the configured no-reply address replaces the From header of calendar notification mails to internal recipients that are transported via the no-reply account, and the Sender and Reply-To headers are dropped. It defaults to false, is reloadable and config-cascade aware.
External iMIP messages are never affected, as their From header has to stay aligned with the ORGANIZER property. Note that the property keys on the recipient being internal, not on the message kind: with com.openexchange.calendar.useIMipForInternalUsers enabled, internal users receive full iMIP messages and those are rewritten as well, which RFC-conformant calendar clients may reject. Enable both only if internal recipients read their invitations in App Suite.
It applies wherever the no-reply account is used, which is not limited to com.openexchange.calendar.preferNoReplyForNotifications: guests, users without webmail permission, restricted sessions and impersonation sessions take that account on their own, so mails triggered by them change as well. The value is evaluated for the acting user, not for the organizer. No operator action is required by default. See the property documentation for further details.
8.53.243
Database
SCR-1853
Summary: New table deputy_mail_acl_baseline holding the mail ACL baseline of a deputy permission
Effective: 8.53.242 and later; also delivered in 8.53.243
Warning
Update Task com.openexchange.deputy.provider.imap.groupware.DeputyMailAclBaselineCreateTableTask
In order to record a deputy permission's mail ACL baseline reliably, the mailboxes on which the deputy already held an ACL before the permission was granted are now kept in the new table deputy_mail_acl_baseline instead of in the granting user's INBOX metadata entry /shared/vendor/vendor.open-xchange/deputydir-<deputyId>.
The baseline is now recorded once, at the first grant. It was previously re-captured on every grant, so a mailbox outside the permission's folder list that had only received its ACL through the grant itself, typically a subfolder Dovecot inherits from INBOX, counted as pre-existing and kept the deputy's ACL when the permission was revoked.
CREATE TABLE deputy_mail_acl_baseline (
cid INT4 UNSIGNED NOT NULL,
uuid BINARY(16) NOT NULL,
user INT4 UNSIGNED NOT NULL,
account INT4 UNSIGNED NOT NULL DEFAULT 0,
fullname VARCHAR(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
rights VARCHAR(32) CHARACTER SET latin1 NOT NULL DEFAULT '',
PRIMARY KEY (cid, uuid, account, fullname),
KEY userId (cid, user)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
Deputy permissions granted before this change are not migrated; their baseline is still read from the metadata entry, and both locations are cleared when the permission is revoked. The update task only creates the table and does not touch existing rows. No operator action is required.
8.53.242
Database
SCR-1853
Summary: New table deputy_mail_acl_baseline holding the mail ACL baseline of a deputy permission
Effective: 8.53.242 and later
Warning
Update Task com.openexchange.deputy.provider.imap.groupware.DeputyMailAclBaselineCreateTableTask
In order to record a deputy permission's mail ACL baseline reliably, the mailboxes on which the deputy already held an ACL before the permission was granted are now kept in the new table deputy_mail_acl_baseline instead of in the granting user's INBOX metadata entry /shared/vendor/vendor.open-xchange/deputydir-<deputyId>.
The baseline is now recorded once, at the first grant. It was previously re-captured on every grant, so a mailbox outside the permission's folder list that had only received its ACL through the grant itself, typically a subfolder Dovecot inherits from INBOX, counted as pre-existing and kept the deputy's ACL when the permission was revoked.
CREATE TABLE deputy_mail_acl_baseline (
cid INT4 UNSIGNED NOT NULL,
uuid BINARY(16) NOT NULL,
user INT4 UNSIGNED NOT NULL,
account INT4 UNSIGNED NOT NULL DEFAULT 0,
fullname VARCHAR(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
rights VARCHAR(32) CHARACTER SET latin1 NOT NULL DEFAULT '',
PRIMARY KEY (cid, uuid, account, fullname),
KEY userId (cid, user)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
Deputy permissions granted before this change are not migrated; their baseline is still read from the metadata entry, and both locations are cleared when the permission is revoked. The update task only creates the table and does not touch existing rows. No operator action is required.
8.53.240
Behavioral Changes
SCR-1851
Summary: Bounded the init container's configdb and middleware readiness waits
Effective: applies to the 8.53 release line
In order to let an unreachable database fail visibly instead of hanging a pod indefinitely, the core-mw init container now bounds its readiness waits for the configdb and, during initial bootstrapping, for the middleware itself. Previously both the legacy bash and the Go init variant retried forever without sleeping, so a misconfigured MYSQL_HOST left the pod in Init:0/1 with no non-zero exit and no Kubernetes event, while the retry loop consumed a full CPU core. Once a timeout elapses, the init container names the unreachable target and exits non-zero.
A configdb host name that does not resolve is reported after a separate, shorter grace period, because an unresolvable name is a configuration error rather than a database that is slow to start. The grace period exists because on a fresh install the name legitimately stays unresolvable for a while, for instance while cluster DNS is still starting or a headless service has no endpoints yet. Temporary resolver failures do not count towards it.
The waits are controlled by new Helm chart values, which are additionally passed to the Go init binary as environment variables:
initWait.dbTimeout/INIT_DB_WAIT_TIMEOUT, default5minitWait.dbInterval/INIT_DB_WAIT_INTERVAL, default2sinitWait.dbDnsTimeout/INIT_DB_DNS_TIMEOUT, default30s,0disables the early exitinitWait.middlewareTimeout/INIT_MW_WAIT_TIMEOUT, default5minitWait.middlewareInterval/INIT_MW_WAIT_INTERVAL, default5s
Each value accepts a Go duration string such as 30s or 5m; a plain number is read as seconds. No operator action is required, the values are additive and defaulted. Deployments that legitimately need to wait longer than five minutes for their database have to raise initWait.dbTimeout; a sufficiently high value restores the previous, effectively unbounded behavior.
Database
SCR-1853
Summary: New table deputy_mail_acl_baseline holding the mail ACL baseline of a deputy permission
Effective: applies to the 8.53 release line
Warning
Update Task com.openexchange.deputy.provider.imap.groupware.DeputyMailAclBaselineCreateTableTask
In order to record a deputy permission's mail ACL baseline reliably, the mailboxes on which the deputy already held an ACL before the permission was granted are now kept in the new table deputy_mail_acl_baseline instead of in the granting user's INBOX metadata entry /shared/vendor/vendor.open-xchange/deputydir-<deputyId>.
The baseline is now recorded once, at the first grant. It was previously re-captured on every grant, so a mailbox outside the permission's folder list that had only received its ACL through the grant itself, typically a subfolder Dovecot inherits from INBOX, counted as pre-existing and kept the deputy's ACL when the permission was revoked.
CREATE TABLE deputy_mail_acl_baseline (
cid INT4 UNSIGNED NOT NULL,
uuid BINARY(16) NOT NULL,
user INT4 UNSIGNED NOT NULL,
account INT4 UNSIGNED NOT NULL DEFAULT 0,
fullname VARCHAR(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
rights VARCHAR(32) CHARACTER SET latin1 NOT NULL DEFAULT '',
PRIMARY KEY (cid, uuid, account, fullname),
KEY userId (cid, user)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
Deputy permissions granted before this change are not migrated; their baseline is still read from the metadata entry, and both locations are cleared when the permission is revoked. The update task only creates the table and does not touch existing rows. No operator action is required.
8.53.239
API - HTTP-API
SCR-1835
Summary: Changed the validate-session envelope AAD to the Basic-Auth login and added error code MAIL_REST-0009
Effective: applies to the 8.53 release line
The AES-256-GCM envelope returned by GET /preliminary/mail/v1/validate-session/<session> now binds the caller's Basic-Auth login (com.openexchange.rest.services.basic-auth.login, trimmed, UTF-8 bytes) into the GCM tag as Associated Authenticated Data, as documented for the endpoint; previously the host name the request was received on was used. Consumers have to pass the login as AAD when decrypting: the JMAP-IMAP proxy needs jmap-proxy.auth.ox-session.envelope-aad=<basic-auth login> in the same rollout, otherwise every decryption fails with a GCM tag mismatch.
Further changes:
- New error code
MAIL_REST-0009(HTTP 500) is returned if no authenticated caller identity is available; it must not be treated as an expired session (SES-0203, HTTP 401). - The per-caller rate limit of the endpoint is keyed by the Basic-Auth login.
- For all Basic-Auth protected REST endpoints,
Principal#getName()of the JAX-RS security context yields the login instead of the request host; the host remains available viaTrustedAppPrincipal#getSource().
Supersedes the AAD statement of SCR-1711. See the API documentation for further details.
API - RMI
SCR-1815
Summary: Stricter authentication and bounded retries for the usercopy RMI call
Effective: applies to the 8.53 release line
OXUserCopyInterface.copyUser keeps its signature but rejects more callers than before. A caller that is neither the master administrator nor accepted as owner of both the source and the destination context now fails with InvalidCredentialsException. A violated reseller restriction is reported as StorageException, also when it is detected after the copy has been performed, in which case the copied user is removed again before the exception is thrown.
A copy that keeps failing with a retryable database error is attempted at most five times, with a growing pause between the attempts, instead of being repeated indefinitely.
OXContextInterface.getData is affected by the corrected ownership check: for a subadmin, a set of contexts that contains a context without an owner is now rejected with InvalidCredentialsException.
API - SOAP
SCR-1816
Summary: usercopy SOAP service now enforces reseller ownership and restrictions
Effective: applies to the 8.53 release line
The service OXUserCopyService, published under /webservices/OXUserCopyService, keeps its WSDL unchanged but enforces the reseller rules of the underlying provisioning call. A caller that is neither the master administrator nor accepted as owner of both the source and the destination context now receives a fault instead of the copied user:
<soap:Fault>
<faultcode>soap:Server</faultcode>
<faultstring>Authentication failed</faultstring>
<detail>
<ns2:InvalidCredentialsException xmlns:ns2="http://soap.copy.user.admin.openexchange.com"/>
</detail>
</soap:Fault>
A violated restriction of the destination context is reported as a StorageException fault whose message names the restriction, for example Maximum overall number of users reached: 8. Such a copy is removed again before the fault is returned.
OXContextService is affected by the corrected ownership check: for a subadmin, a getData request covering a set of contexts that contains a context without an owner is now rejected with an InvalidCredentialsException fault.
Behavioral Changes
SCR-1805
Summary: New metrics and JMX MBean for the virtual-thread executor
Effective: 8.53.55 and later
The virtual-thread executor that processes HTTP requests now reports Micrometer meters under appsuite_executor_virtual_ (active tasks, concurrency limit, available permits, waiting submitters, plus submitted, completed and rejected task counters) and the same values via JMX as com.openexchange.threadpool:name=VirtualThreadPoolInformation. Saturation shows as available permits reaching zero while waiting submitters and rejections rise, not as a high number of active tasks. Since requests bypass the platform pool while virtual threads are enabled, alerts on appsuite_executor_* with name="main" no longer see request congestion and should be revisited. No new configuration.
CLT
SCR-1808
Summary: Changed datamining behavior on unreachable database schemas, with new exit codes and report entries
Effective: applies to the 8.53 release line
The datamining command-line tool no longer aborts when a database schema cannot be reached: the schema is retried once, then skipped for the remaining questions, and the report is written instead of discarded. An incomplete run is now recognizable — sanityCheck reports problems instead of ok, the new entries numberOfReachableSchemata, numberOfUnreachableSchemata and numberOfDegradedSchemata are added, unreachableSchemata and degradedSchemata name the affected schemata, and per-schema averages are divided by the schemata that actually answered. The exit code now distinguishes a complete report (0), a report written but incomplete (2) and an abort without a report (1). The previously ineffective -t/--timeout flag is now honored: the connect timeout is applied unconditionally, while -t additionally applies socketTimeout from dbconnector.yaml, which also bounds the long aggregation queries — operators already passing -t should drop it. The connection error message no longer prints the database password and reports SQL state and vendor error code instead.
Configuration
SCR-1846
Summary: Changed configuration options for the Redis in-memory cache layer
Effective: applies to the 8.53 release line
Configuration options of the Redis-backed in-memory cache layer have changed as follows.
com.openexchange.cache.v2.redis.inmemory.enabledNow re-read on a configuration reload, so a node can bypass the in-memory cache layer without a restart. Switching it off takes effect immediately; switching it on again only works on a node that started with the layer enabled, otherwise the attempt is logged and the layer stays off until the node is restarted. Either transition drops that node's in-memory replicas. Defaulttrue. Reloadable, not config-cascade aware. File:redis.properties.com.openexchange.cache.v2.redis.inMemoryCacheEnableThresholdNow evaluated even whilecom.openexchange.cache.v2.redis.inmemory.enabledisfalse, where it previously fell back to its default; the same applies tocom.openexchange.cache.v2.redis.inMemoryCacheDisableThreshold. Setting it to0on a node that also has the layer disabled yields a node that never holds an in-memory cache and therefore does not take part in the remote invalidation channel. Default100. Not reloadable, not config-cascade aware. File:redis.properties.com.openexchange.cache.v2.redis.inmemory.remoteInvalidationNow evaluated even while the in-memory cache layer is disabled, where it previously fell back to its default. Defaulttrue. Not reloadable, not config-cascade aware. File:redis.properties.
SCR-1834
Summary: New configuration options for the provisioning gRPC server
Effective: applies to the 8.53 release line
Introduces configuration options for the provisioning gRPC server, which previously listened on a hard-coded port with no way to disable it, to secure its transport, or to influence its shutdown. The defaults preserve the previous behavior.
com.openexchange.grpc.server.enabledControls whether the provisioning gRPC server is started at all. When disabled, no port is opened and none of the provisioning services are reachable over gRPC. Defaulttrue. Not reloadable, not config-cascade aware. No dedicated properties file.com.openexchange.grpc.server.portThe TCP port the provisioning gRPC server listens on. Default8066. Not reloadable, not config-cascade aware. No dedicated properties file.com.openexchange.grpc.server.ssl.enabledControls whether the provisioning gRPC server requires TLS. The endpoint accepts administrative credentials, so plain-text transport is only safe on a trusted, non-routable network. Requirescom.openexchange.grpc.server.ssl.certificateChainFileandcom.openexchange.grpc.server.ssl.privateKeyFile; the bundle fails to start when either is unset or does not name a readable file. Defaultfalse. Not reloadable, not config-cascade aware. No dedicated properties file.com.openexchange.grpc.server.ssl.certificateChainFilePath to the PEM-encoded certificate chain the provisioning gRPC server presents to clients. Only evaluated whencom.openexchange.grpc.server.ssl.enabledistrue. No default. Not reloadable, not config-cascade aware. No dedicated properties file.com.openexchange.grpc.server.ssl.privateKeyFilePath to the PEM-encoded private key belonging to the configured certificate chain. Only evaluated whencom.openexchange.grpc.server.ssl.enabledistrue. No default. Not reloadable, not config-cascade aware. No dedicated properties file.com.openexchange.grpc.server.shutdownTimeoutSecondsThe number of seconds a shutdown waits for calls that are still in flight before they are canceled. Default10. Not reloadable, not config-cascade aware. No dedicated properties file.
SCR-1833
Summary: New configuration option for replication monitor counter sharing
Effective: applies to the 8.53 release line
Introduces optional cross-node sharing of the database replication monitor's transaction counters through the distributed cache (Redis), narrowing the window in which a read replica can serve data that another cluster node has already changed.
com.openexchange.database.replicationMonitor.shareCountersControls whether the last transaction counters observed on the database masters are shared across cluster nodes through the distributed cache. By default the counters are process-local, so only the writing node redirects its reads to the master until the read replica has caught up. With sharing enabled, counters published by other nodes redirect reads as well, on a best-effort basis: the shared counter is consulted only when a node holds no locally observed counter, look-up answers are reused for about 10 seconds, and publishing is asynchronous. Requires a configured distributed cache (Redis) and an active replication monitor; without them the setting has no effect. Defaultfalse. Reloadable, not config-cascade aware. No dedicated properties file.
SCR-1823
Summary: New configuration option for last-login recording
Effective: applies to the 8.53 release line
The following configuration option has been added for the recording of a client's last login.
com.openexchange.report.login.minRecordIntervalMinutesDefines how long a client's recorded last-login time stamp is left alone before the next login of that client is recorded again. Every recording writes a user attribute and thus invalidates the cached user, so recording every single login keeps evicting that user from the cache. A login that falls inside the interval is not recorded at all, and the login reporting inherits this: a login shortly after a reporting period starts can leave the user out of that period's report. The suppression takes effect per node, because a node evaluates the time stamp of its own cached user. A value of 0 (zero) or less records every login. Default 10. Reloadable, not config-cascade aware. No dedicated properties file.
SCR-1820
Summary: New configuration options for HTTP/2 on the Grizzly HTTP listener
Effective: applies to the 8.53 release line
New configuration options introduced together with the ability to serve HTTP/2 on the cleartext HTTP network listener. The feature is experimental and switched off by default.
com.openexchange.http.grizzly.http2.enabledWhether the cleartext HTTP network listener serves HTTP/2. While off, the middleware speaks HTTP/1.1 only and answers anh2cupgrade as though theUpgradeheader were absent. Switching it on serves HTTP/2 both via theh2cupgrade handshake and via a direct prior-knowledge connection; the HTTPS listener is not covered, becauseh2over TLS requires ALPN. Requests carrying a payload keep being answered with HTTP/1.1, since clients such as Apache CXF advertiseUpgrade: h2con ordinary POSTs without being able to switch protocols. Note thatcom.openexchange.http.grizzly.strictHeaderNameValidationandcom.openexchange.http.grizzly.strictHeaderValueValidationdo not apply to HTTP/2 traffic, and thatcom.openexchange.http.grizzly.maxNumberOfConcurrentRequestsis then consumed per stream rather than per connection. Default false. Not reloadable, not config-cascade aware. No dedicated properties file.com.openexchange.http.grizzly.http2.maxStreamsPerSecondThe number of stream openings, stream resets and control frames a single HTTP/2 connection may cause per second. A connection exceeding the budget is closed withGOAWAY. This bounds what the protocol's own concurrency limit cannot see, because a stream that is opened and immediately reset never raises the concurrent-stream count yet still costs a dispatched request. Only effective whilecom.openexchange.http.grizzly.http2.enabledis set. A value of 0 (zero) disables the bound. A value less than 0 (zero) is ignored and the default is used. Default 250. Not reloadable, not config-cascade aware. No dedicated properties file.com.openexchange.http.grizzly.http2.maxConcurrentStreamsThe maximum number of concurrent HTTP/2 streams a single connection may hold open. Each stream carries its own request, so this is the factor by which one HTTP/2 connection may exceed the single in-flight request of an HTTP/1.1 connection. Only effective whilecom.openexchange.http.grizzly.http2.enabledis set. A value less than 1 (one), and the value 100, are ignored and the default is used; 100 is the bundled Grizzly version's own default, which makes it omit the limit from itsSETTINGSframe so that clients never learn about it. Default 128. Not reloadable, not config-cascade aware. No dedicated properties file.com.openexchange.http.grizzly.http2.maxHeaderListSizeThe maximum decoded size in bytes of an HTTP/2 header list. A value of 0 (zero) followscom.openexchange.http.grizzly.maxHttpHeaderSize, so both protocols share one header budget instead of HTTP/2 applying the smaller default of the bundled Grizzly version. The number of header fields is capped separately at 100 and is not configurable. Only effective whilecom.openexchange.http.grizzly.http2.enabledis set. A value less than 0 (zero) is ignored and the default is used. Default 0. Not reloadable, not config-cascade aware. No dedicated properties file.
SCR-1807
Summary: New and renamed properties for the mail server capability caches
Effective: 8.53.69 and later
A failed or degraded mail server capability probe is no longer remembered for the life-time of the process. Previously a single transient error against an SMTP server, an EHLO answered without any capability, or a rejected POP3 CAPA command was cached until the process was restarted, which marked that server as incapable of STARTTLS for good; with com.openexchange.smtp.requireTls enabled every message sent through it then failed with MSG-0092. Such a result now expires through the new properties com.openexchange.smtp.capabilitiesCacheErrorIdleTime and com.openexchange.pop3.capabilitiesCacheErrorIdleTime (smtp.properties and pop3.properties, default 30000 milliseconds, not reloadable, not config-cascade aware). Setting either to 0 restores the previous behavior. In the same release the idle time for probes that did yield capabilities was renamed from the misspelled capabiltiesCacheIdleTime to capabilitiesCacheIdleTime for com.openexchange.smtp, com.openexchange.pop3 and com.openexchange.imap; the misspelled names are still honored when the correct one is absent and their use is logged, so existing deployments need not be changed. No admin action is required by default.
SCR-1804
Summary: New property com.openexchange.mail.filter.vacationRestrictToAddresses to restrict the vacation notice to the selected addresses
Effective: 8.53.49 and later
New lean property com.openexchange.mail.filter.vacationRestrictToAddresses (default true, reloadable, config-cascade aware): a vacation notice is sent only for mails delivered to one of the addresses it was enabled for. Sieve :addresses alone cannot do this - per RFC 5230 it is additive - so the middleware wraps the vacation action into a matching test. Being on by default, this makes the behavior match the HTTP API documentation and the UI. It takes effect per user the next time one of that user's filter rules is written; existing scripts are not migrated. Requires the Sieve envelope extension.
SCR-1803
Summary: New properties for the replication monitor's replica status check
Effective: 8.53.11 and later
The replication monitor now probes read replicas' replication status (SHOW SLAVE STATUS) and redirects reads to the master while a replica reports broken replication or excessive lag. This closes the gap that a re-seeded or inconsistently restored replica could serve incomplete data undetected (middleware/core#4, follow-up to the incident behind appsuite/support#1599). Four new lean configuration properties control the behavior:
com.openexchange.database.replicationMonitor.checkReplicaStatus(default: true) - Whether to watch read replicas for broken or excessively lagging replication and redirect reads to the master meanwhile. Works out of the box: where the REPLICA MONITOR (MariaDB) or REPLICATION CLIENT (MySQL) privilege is available, the replication status statement provides fast, precise detection; without it the check falls back to a privilege-free heartbeat, a time stamp periodically written through the replication channel into the replicationMonitor table (reserved row cid=4294967295), needing only the regular INSERT/UPDATE/SELECT permissions.REPLICA MONITOR(MariaDB) or REPLICATION CLIENT (MySQL) privilege; both are global privileges, so one grant per database host covers all existing and future schemas, and initconfigdb -a now grants them automatically. Without the privilege the check suspends itself for the affected pool. Only effective if the replication monitor is active. Default: true. Reloadable: false. Config-cascade aware: false.com.openexchange.database.replicationMonitor.maxReplicaLag- Maximum tolerated replication lag in seconds before reads are redirected to the master. Default: 300. Reloadable: false. Config-cascade aware: false.com.openexchange.database.replicationMonitor.replicaStatusCheckInterval- Minimum number of seconds between two replication status probes per read pool. Default: 10. Reloadable: false. Config-cascade aware: false.com.openexchange.database.replicationMonitor.requireReplication- Whether a read host that does not act as a replica at all (emptySHOW SLAVE STATUS) is considered unhealthy. Keep false for Galera or multi-primary setups; set to true where read pools are always asynchronous replicas. Default: false. Reloadable: false. Config-cascade aware: false.
All four properties are read once at middleware start-up; changing them requires a restart. They are server-scoped (no config-cascade evaluation) and documented in documentation-generic/config/ConfigDB.yml. Defaults are safe for every topology: without the privilege or without asynchronous replication the check fails open and behavior is unchanged.
SCR-1756
Summary: Configuration properties for the optional Jetty-based HTTP engine
Effective: applies to the 8.53 release line
New optional HTTP engine based on Eclipse Jetty 12.1 (bundle com.openexchange.http.jetty), switched on with com.openexchange.http.jetty.enabled (default false); while it is active the Grizzly engine does not start. Engine-neutral properties (com.openexchange.connector., .server., .servlet., .cookie.) keep working unchanged. Grizzly-specific properties have equally named com.openexchange.http.jetty.* pendants with identical defaults, and an unset Jetty key falls back to the Grizzly one, so existing tuning carries over (exception: selectorRunnersCount). Without effect under Jetty: hasCometEnabled, shutdownFast, maxQueryStringSize, writeTimeoutMillis, keepAlive, minWriteBufferSize, supportHierachicalLookupOnNotFound and the Grizzly session-manager internals. Also new: com.openexchange.drive.events.asyncLongPolling.enabled (default true) and com.openexchange.http.jetty.hasAccessLogEnabled (default true). Install open-xchange-jetty alongside open-xchange-grizzly - it is an add-on, which keeps the rollback a configuration change; its settings belong in an administrator-created jetty.properties.
8.53.217
General
SCR-1848
Summary: New thread pool saturation metrics
Effective: 8.53.217 and later
The platform thread pool exposes two new counters at the /metrics endpoint: appsuite_executor_saturated_total counts task submissions that found the pool already grown to its maximum size, and appsuite_executor_refused_total counts tasks that could neither be run nor be queued and were therefore handed to com.openexchange.threadpool.refusedExecutionBehavior. Both carry the tag name="main" and complement the existing appsuite_executor_queued gauge, which only reports the queue depth at scrape time. They are absent unless the pool uses a scaling work queue.
SCR-1822
Summary: Added command-line tool threaddump that covers virtual threads
Effective: 8.53.217 and later
The new command-line tool threaddump writes a thread dump of the middleware that includes virtual threads. The middleware runs its HTTP work on virtual threads, which neither jstack nor the Thread.print diagnostic command lists. It therefore uses Thread.dump_to_file through JMX, which needs no jcmd binary - the runtime image ships none. The middleware writes the file itself, so the path given via -f is resolved on the middleware's host. That dump performs no deadlock analysis, hence --print-classic prints the classic dump as a second, separate dump on the terminal. Further options are --format (plain or json, where json carries no lock information) and --overwrite.
$ threaddump -f /tmp/threads.txt
Thread dump successfully written to file /tmp/threads.txt on the middleware's host
$ head /tmp/threads.txt
7
2026-08-14T06:54:19.525073364Z
25.0.4+-wolfi-r0
#3 "main" RUNNABLE 2026-08-14T06:54:19.525200Z
at ...
#28 "OXWorker-0007" virtual BLOCKED 2026-08-14T06:54:19.525300Z
at ...
- waiting to lock <java.lang.Object@2df9b86>
API - HTTP-API
SCR-1844
Summary: New deputy module action reverseIds that lists the granting users without resolving grant details
Effective: 8.53.217 and later
The deputy module gained the action GET /ajax/deputy?action=reverseIds, a light-weight counterpart to action=reverse. It lists the users that appointed the requesting user as their deputy, but resolves neither the granted folders nor the permission bits. Resolving those requires consulting every module involved; for the mail module that is a server-wide GETMETADATA sweep per mail account, whose cost grows with the number of mailboxes visible to the user. This action is answered from the deputy storage alone.
Each element of the returned array carries:
grantorId- the identifier of the granting user, or0for a cross-context grantor whose bare identifier has no meaning in the requesting user's contextgrantorIdentifier- the qualified identifier as<userId>@<contextId>grantorEntityInfo- pre-resolved entity information for rendering the grantordeputyIds- the identifiers of the deputy permissions this user grantedsendOnBehalfOf- whether at least one of those grants permits sending on the granting user's behalf, aggregated across that user's grants because the addresses belong to the granter rather than to an individual grantgrantorAddresses- the addresses to send from, present only whensendOnBehalfOfistrue; the full listing applies the same gate
Example request:
GET /ajax/deputy?action=reverseIds&session=<session-id>
Example response:
{
"data": [
{
"grantorId": 3,
"grantorIdentifier": "3@1",
"grantorEntityInfo": {
"identifier": "3@1",
"type": "user",
"display_name": "Jane Doe",
"entity": 3,
"contact": {
"first_name": "Jane",
"last_name": "Doe",
"email1": "jane.doe@example.org"
}
},
"deputyIds": [
"a3fff8061eae4078817533438c090a9b",
"0c1d7f52a1b04e6f9d2c8e3b5a7f1042"
],
"sendOnBehalfOf": true,
"grantorAddresses": [
"jane.doe@example.org",
"j.doe@example.org"
]
}
]
}
Purely additive: action=reverse is unchanged. Note that an orphaned grant, one whose module no longer backs a permission, is still listed by action=reverseIds - detecting and removing it is what the full listing does when it consults the modules.
On 8.51 and 8.50 the action is available in a reduced form: sendOnBehalfOf and grantorAddresses are present, but grantorIdentifier and grantorEntityInfo are not - those lines do not carry the qualified grantor identity internally. A client that has to work across all four lines should key on grantorId there.
SCR-1818
Summary: New HTTP API action mail?action=emlToken to download a message as .eml file without a session
Effective: 8.53.217 and later
In order to let a message be handed to software outside App Suite - dragging a mail onto Windows Explorer, a DMS or an electronic file - the new action mail?action=emlToken issues a token that allows to download the complete message in MIME format through /ajax/mail.attachment?id=<token> without a session and without cookies. The resulting URL grants access to that message including all headers and attachments and is therefore to be treated like a credential.
folderandididentify the message and are mandatory.ttlMillisshortens the token's lifetime; a value above the configured default is capped to it.oneTimeinvalidates the token once it has been redeemed, defaultfalse.checkIprestricts the download to the client address the token was issued for, defaultfalse.
Example:
GET /ajax/mail?action=emlToken&folder=default0/INBOX&id=42&session=<session>
{"data":{"id":"eml-25d14828e3a44238abf25e954a35eefa.e8dd9dfbe4e54f8d8e2c0d49ae4999e6","jsessionid":null}}
GET /ajax/mail.attachment?id=eml-25d14828e3a44238abf25e954a35eefa.e8dd9dfbe4e54f8d8e2c0d49ae4999e6
The download is answered as application/octet-stream with a file name derived from the message's subject. It is streamed, hence it carries no Content-Length and byte ranges are not served - a range request is answered in full. HEAD is rejected with 405. The token's lifetime is fixed and does not slide; only the start of the download has to fall into it, a running download is not cut off. Its default is configured through com.openexchange.mail.emlToken.ttl.
The change is purely additive for existing clients. The pre-existing action mail?action=attachmentToken is now documented as well; its ttlMillis parameter remains without effect.
API - Java
SCR-1832
Summary: Relocated configuration and user-configuration Java packages
Effective: 8.53.217 and later
Custom bundles and plugins that use the configuration API must be adapted:
com.openexchange.config.ConfigurationService,Reloadable,Interests,PropertyFilterand related types moved to thecom.openexchange.config.commonpackage and bundle; import statements and bundle manifests must be repointed.- The
UserConfigurationAPI moved tocom.openexchange.config.universal;ServerSession.getUserConfiguration()now returnsUserConfigurationImpl.
Code compiles against the old manifest imports but fails at OSGi resolution, so the manifest change is mandatory. The OX-maintained plugin repositories are adapted in the same release train.
Behavioral Changes
SCR-1850
Summary: Changed reporting of long-running tasks by the thread pool's active-task watcher
Effective: 8.53.217 and later
The thread pool's active-task watcher changed how it reports tasks that exceed com.openexchange.requestwatcher.maxRequestAge.
Each report is now prefixed with a per-task sighting counter, as the request watcher already does:
#3 Worker thread with age 8,339ms (8s 339ms) exceeds max. age of 2,000ms (2s).Log processing that matches on the previous message text still matches, but the leading counter is new. A jump in that counter shows that reporting was throttled in between.Reporting is no longer unconditional. Above
com.openexchange.threadpool.watcher.reportBackoffThresholdconcurrently long-running tasks the watcher doubles the interval between reports instead of capturing a stack trace for every task on every scan. Skipped scans emit a single summary line. Below the threshold nothing changes.The watcher can now interrupt a long-running task and eventually give up on it, which is off by default and enabled with
com.openexchange.threadpool.watcher.interruptTasks. Unlike the request watcher, the task's age is the only interrupt trigger; the session is deliberately not consulted, since that look-up may reach the session storage and a stalled storage would block the very scan meant to report the stall.
SCR-1847
Summary: Changed behavior of the thread pool saturation settings
Effective: 8.53.217 and later
The thread pool settings com.openexchange.threadpool.blocking and com.openexchange.threadpool.refusedExecutionBehavior had no effect with the default scaling pool, where tasks queued without bound once all worker threads were busy. They now apply as documented as soon as com.openexchange.threadpool.workQueueSize is set to a positive value. With its default of 0 the work queue stays unbounded and nothing changes, except that the effective saturation semantics are now logged once at start-up, with a warning if either setting was configured but cannot apply. Deployments that already combine blocking with a bounded work queue will see submitting threads actually wait for queue space from now on. Independently of the configuration, a task refused by a saturated or shut-down pool no longer leaves its future pending forever, and a task submitted with an individual refused-execution behavior is no longer handled twice.
SCR-1840
Summary: New JMX bean for the timer executor and spill-over volume in its warning
Effective: 8.53.217 and later
The executor that runs timer tasks is now readable over JMX as com.openexchange.threadpool:name=TimerThreadPoolInformation, reporting the same attributes as the existing VirtualThreadPoolInformation bean: maximum concurrency, active tasks, available permits, waiting submitters and the submitted, completed and rejected counts. It is registered only while the timer executor exists, so it is absent when com.openexchange.threadpool.timer.maxConcurrency is set to 0. This is the way to read those numbers on installations that do not scrape the appsuite.executor.timer.* meters. In addition, the warning logged when due timer tasks spill over to the platform thread pool now states how many did so since the previous warning and since node start, taken from the same counter the appsuite.executor.timer.rejected meter reports. No configuration change and no admin action.
SCR-1837
Summary: Changed timer tasks to run on virtual threads instead of the platform thread pool
Effective: 8.53.217 and later
Timer tasks scheduled through the TimerService (schedule, scheduleAtFixedRate, scheduleWithFixedDelay) execute on virtual threads named OXTimer-* instead of platform pool workers, so periodic housekeeping no longer competes with request processing for OXWorker-* threads. Scheduling, cancellation and the ScheduledFuture contract are unchanged. The number of concurrently executing timer tasks is bounded by com.openexchange.threadpool.timer.maxConcurrency (default 64); beyond that bound, due timer tasks spill over to the platform pool as before. New Micrometer meters appsuite.executor.timer.active, appsuite.executor.timer.concurrency.limit, appsuite.executor.timer.permits.available, appsuite.executor.timer.submitters.waiting, appsuite.executor.timer.submitted, appsuite.executor.timer.completed and appsuite.executor.timer.rejected mirror the existing appsuite.executor.virtual.* meters and are only registered while the timer executor is enabled. No admin action is required; setting the property to 0 restores the previous behavior.
SCR-1826
Summary: New metrics for the Redis circuit breakers and bulkhead
Effective: 8.53.217 and later
The Redis connector now reports Micrometer meters for its resilience policies: appsuite_redis_breaker_state (0 closed, 1 half-open, 2 open, -1 disabled; the breaker tag tells the two breakers apart - common guards against failing operations, connect against an unreachable end-point), appsuite_redis_breaker_rejected and appsuite_redis_bulkhead_rejected, counting the operations shed by the respective policy. The connection pool additionally reports appsuite_redis_connections_num_multiplexed, the number of operations currently using a connection. In the default shared pool mode operations are multiplexed onto a fixed number of connections, so appsuite_redis_connections_num_active is bounded by the configured pool size and borrowing never blocks - appsuite_redis_connections_num_waiters and the borrow wait times are always zero there. Saturation therefore shows as appsuite_redis_bulkhead_rejected rising and appsuite_redis_breaker_state leaving zero, not through the pool gauges; alerts built on the pool gauges should be revisited. No new configuration.
SCR-1821
Summary: Sending with a shared folder owner's address now requires an explicit permission
Effective: 8.53.217 and later
Access to another user's shared mail folder no longer implies permission to send with that user's address. Previously a reply or forward from a shared folder was composed with the folder owner's address as From and the sending user's as Sender, and the transport accepted such a message even when no deputy permission existed at all; the owner's address was used as envelope sender as well.
The folder owner's address is now only used when that user granted a deputy permission carrying "send on behalf of", or consented through the new property. Otherwise the sending user's own address is used, and a submitted message carrying a foreign From is rejected with MSG-0129.
com.openexchange.mail.allowSendOnBehalfOfByFolderOwnershipLets those who have access to a user's shared folders send with that user's address without a deputy permission. It is evaluated in the folder-owning user's scope rather than the sending user's. Defaultfalse. Reloadable, config-cascade aware. File:mail.properties.
The deprecated predecessor com.openexchange.mail.ignoreSendOnBehalfOfDetection is still honored where it withholds the privilege; an explicit false is no longer read as consent. Both keys are resolved in one walk over the config cascade, most specific scope first.
Deployments running role mailboxes that relied on the previous behavior need either a deputy permission carrying "send on behalf of" or the new property set on the mailbox user. Setting it at context or server scope is possible but means every user there lends their sender identity to anyone they share a folder with.
See the configuration documentation for further details.
SCR-1814
Summary: Reseller ownership and restrictions are now enforced when copying a user
Effective: 8.53.217 and later
With open-xchange-admin-reseller installed, the usercopy provisioning call now behaves like user creation: the calling administrator has to own both the source and the destination context, or be the parent of their owners, and a copied user counts against the restrictions of the destination context (Context.MaxUser, Subadmin.MaxOverallUser and their module access variants). Previously neither was checked, so any subadmin could copy any user between arbitrary contexts and copied users counted against no limit.
The restrictions are evaluated before the copy as a fast fail and again afterwards, which is the binding decision. Evaluating after the write is what keeps a limit from being exceeded when copies run in parallel, and it is the order user creation already uses. A rejected copy is removed again; copies issued in parallel against a context close to its limit may therefore all be rejected.
Copies performed by a subadmin require MASTER_ACCOUNT_OVERRIDE=true in AdminDaemon.properties, as every other user provisioning call performed by a subadmin does.
The check whether an administrator owns a set of contexts was corrected as well: it correlated database rows with the given contexts by position instead of by context identifier, and treated a context without an owner as owned. Such contexts remain reserved for the master administrator now.
Deployments without the reseller extension are not affected.
Changed defaults
SCR-1838
Summary: Changed defaults for the in-memory cache layer
Effective: 8.53.217 and later
The cache.v2 in-memory caching layer in front of Redis is now enabled by default with a shorter time-to-live. It removes the Redis round-trip from the request hot path (measured +31 % request throughput). Replicas are dropped on cache events and on the user, context and reseller invalidation channels; otherwise they expire after the time-to-live.
com.openexchange.cache.v2.redis.inmemory.enabledWhether to use the volatile in-memory cache as facade prior to accessing Redis storage. Default changed fromfalsetotrue. Not reloadable, not config-cascade aware. File: redis.properties.com.openexchange.cache.v2.redis.inmemory.timeToLiveMillisThe time-to-live of elements in the in-memory caching layer involatilemode. Default changed from10000to5000. Not reloadable, not config-cascade aware. File: redis.properties.
Compatibility: the deprecated com.openexchange.cache.v2.redis.useInMemoryCache is only consulted when com.openexchange.cache.v2.redis.inmemory.enabled yields false; an explicit useInMemoryCache=false therefore no longer disables the layer, an explicit inmemory.enabled=false still does. Deployments serving non-sticky clients (CalDAV/CardDAV) may want to disable the layer on those nodes, as documented.
Configuration
SCR-1849
Summary: New configuration options for the thread pool's active-task watcher
Effective: 8.53.217 and later
The thread pool's active-task watcher, which reports tasks that run longer than com.openexchange.requestwatcher.maxRequestAge, gained two configuration options.
com.openexchange.threadpool.watcher.interruptTasksControls whether the watcher may interrupt a long-running task instead of only reporting it. A trackable task is one submitted from a request-processing thread that the request watcher already tracks, so it is then governed like its parent request: once it outlivescom.openexchange.requestwatcher.expiredRequestAge, its thread is interrupted; if it survives that, it is reported once more aftercom.openexchange.requestwatcher.interruptedThresholdfurther sightings and then no longer reported. An expiration age belowcom.openexchange.requestwatcher.maxRequestAgeis logged and disables expiration by age. Interrupting aborts work the calling request may still be waiting for, hence it is off by default. Default false. Not reloadable, not config-cascade aware. File: threadpool.properties.com.openexchange.threadpool.watcher.reportBackoffThresholdThe number of concurrently long-running tasks above which the watcher reports less often. Up to this many tasks every one of them is reported on every scan, atcom.openexchange.requestwatcher.frequency. Above it the interval between reports doubles from report to report, up to every 32nd scan, and returns to every scan once the number falls back to the threshold. No task is ever dropped from reporting; only the frequency changes. This bounds what a stalled backend dependency costs, where thousands of tasks would otherwise yield thousands of foreign-thread stack traces on every scan. A value of 0 (zero) disables the backoff; it is also inactive whilecom.openexchange.threadpool.watcher.interruptTasksis enabled and configured such that escalation retires an entry by itself. Default 20. Not reloadable, not config-cascade aware. File: threadpool.properties.
SCR-1845
Summary: New Configuration Option for Expanding Nested LDAP Distribution Lists
Effective: 8.53.217 and later
In order to serve directories that model a distribution list as a member of another distribution list, the new option nestedDistributionListDepth is introduced for the contacts providers defined in contacts-provider-ldap.yml. It controls how many levels of such references are followed and resolved down to their leaf members. The option defaults to 0, which preserves the previous behavior of taking over a referenced list as a single member, and is evaluated per contacts provider section, taking effect after a configuration reload.
When expanding, an entry that is reachable through more than one of the nested lists is taken over once only, and the members of an expanded list are matched against the provider's folder filters just like any other member. A list that is referenced by one of its own members forms a cycle; such a reference is not followed a second time and is reported as a warning instead, so that a mistake in the directory does not keep the address book from being read.
No operator action is required by default. See the feature documentation for further details.
SCR-1843
Summary: New configuration options for capping how long IMAP responses are read from the primary and secondary account
Effective: 8.53.217 and later
A slow IMAP server can keep a request thread reading responses for minutes. The lean property com.openexchange.imap.readResponsesTimeout caps that, but has so far only been applied to external mail accounts. It can now be applied to the primary and to secondary accounts as well, through their own options - which, unlike the general one, carry no default and therefore never impose a cap unless an operator asks for it.
com.openexchange.imap.primary.readResponsesTimeoutThe maximum time in milliseconds spent reading the responses of a single IMAP command on the primary account. When it elapses, the command is aborted and reported to the client as a connection error. A value less than or equal to0is ignored, as is a non-numeric one. No default: while the option is absent no read responses timeout is applied at all. Reloadable, config-cascade aware. File:imap.properties.com.openexchange.imap.secondary.readResponsesTimeoutThe same for secondary accounts. A value less than or equal to0is ignored, as is a non-numeric one. No default: while the option is absent no read responses timeout is applied at all. Reloadable, config-cascade aware. File:imap.properties.
External accounts are unaffected and keep evaluating com.openexchange.imap.readResponsesTimeout with its default of 60000.
SCR-1841
Summary: New configuration option for the AJAX job queue
Effective: 8.53.217 and later
The AJAX job queue now runs its jobs on a virtual-thread executor of its own instead of drawing from the shared virtual-thread budget.
com.openexchange.threadpool.jobqueue.maxConcurrencyLimits how many AJAX job-queue jobs execute concurrently on the job-queue executor's virtual threads (OXJobQueue-*). A job holds its slot for its entire run and fans out onto the shared virtual-thread executor itself, so it draws from a budget separate fromcom.openexchange.threadpool.virtual.maxConcurrency. Size it by the number of long-running requests a node should process at once, not by heap. This is the first bound the job queue has had; previously a job occupied a platform worker for its entire run. A value of0(zero) disables the job-queue executor and runs jobs on the platform thread pool as before; noappsuite.executor.jobqueue.*meters are registered then. A value less than0(zero) is treated as0and logged. Default256. Not reloadable, not config-cascade aware. File:threadpool.properties.
SCR-1839
Summary: New configuration option for remote invalidation of the in-memory cache layer
Effective: 8.53.217 and later
New configuration option for the cache.v2 in-memory caching layer in front of Redis: an in-memory cache - enabled by configuration or dynamically under load - now propagates invalidations to the other nodes' in-memory caches. Regions that fire cache events are mirrored through those events anyway; for all other regions a dedicated, listener-free pub/sub channel carries the invalidated keys (or the pattern of a mass invalidation), so the receiving nodes drop their replicas right away instead of serving them until the time-to-live elapses.
com.openexchange.cache.v2.redis.inmemory.remoteInvalidationWhether an in-memory cache propagates invalidations to the other nodes' in-memory caches. Effective withcom.openexchange.cache.v2.redis.inmemory.enabled=trueor with dynamic enabling configured throughcom.openexchange.cache.v2.redis.inMemoryCacheEnableThreshold. Disable it to trade consistency for less pub/sub traffic. Defaulttrue. Not reloadable, not config-cascade aware. File: redis.properties.
SCR-1836
Summary: New configuration option for the timer executor's concurrency
Effective: 8.53.217 and later
Timer tasks scheduled through the TimerService now run on a dedicated virtual-thread executor instead of the platform thread pool; the following option bounds that executor.
com.openexchange.threadpool.timer.maxConcurrencyThe maximum number of timer tasks executing concurrently on the timer executor's virtual threads (OXTimer-*). Once the limit is reached, due timer tasks spill over to the platform thread pool (OXWorker-*) until permits are free again; a spill-over is logged atWARNlevel at most once per minute. Raise the value when the meterappsuite.executor.timer.activesits atappsuite.executor.timer.concurrency.limitwhileappsuite.executor.timer.rejectedincreases. A value of0(zero) disables the timer executor entirely and runs all timer tasks on the platform thread pool as in previous versions; a negative value is treated as0and logged. Default64. Not reloadable, not config-cascade aware. File:threadpool.properties.
SCR-1830
Summary: Renamed configuration properties keep resolving under their previous names
Effective: 8.53.217 and later
A number of configuration properties were renamed to consistent, fully-qualified names, for example com.openexchange.hazelcast.network.join to com.openexchange.hazelcast.network.join.mode and the bare legacy key JMXServerPort to com.openexchange.jmx.serverPort. Deployments that still configure an old name are not affected: when a renamed property is not set under its new name, the server automatically falls back to the value configured under the previous name. If both names are set, the new name wins. The fallback is a transitional measure and will be removed in a future release, so configurations should be migrated to the new names; every value served through the fallback is reported in the server log, typically at startup, as Deprecated key <previous name> detected. See the property changes documentation for the complete list of renamed properties with previous name, new name and, where a bare previous name only applies within one file, that file.
SCR-1829
Summary: Migrated middleware configuration to typed properties with built-in defaults and removed 55 shipped .properties files
Effective: 8.53.217 and later
The middleware configuration is migrated to typed property definitions whose default values live inside the server. As a consequence, 55 .properties files that only carried default values are no longer shipped to /opt/open-xchange/etc. The built-in defaults are identical to the values those files used to ship, so effective configuration is unchanged and no operator action is required. Overriding a default works as before: set the property in any .properties file in the configuration directory (the server reads them all, file names do not matter) or through the config cascade. Files that are read by name (configdb.properties, system.properties, whitelist.properties, the OAuth provider files, AdminUser.properties, Group.properties, Resource.properties, permissions.properties, caldav.properties and others) are still shipped. See the property documentation for the authoritative defaults. The removed files are listed in the property changes documentation.
SCR-1828
Summary: New configuration option for IMAP IDLE push
Effective: 8.53.217 and later
In order to decouple the number of users watched via IMAP IDLE from the size of the shared thread pool, IMAP IDLE cycles are now executed on virtual threads.
com.openexchange.push.imapidle.virtualThreadsControls whether IMAP IDLE cycles are executed on virtual threads instead of the shared timer thread pool. An IDLE cycle blocks until the IMAP server reports a change or the listener is aborted; on platform threads every idling user therefore occupies one thread of the pool that also serves HTTP requests. Set it tofalseto restore the previous behavior. The value is evaluated when a listener is started, so it takes effect for listeners started afterwards. Defaulttrue. Reloadable, not config-cascade aware. File: push_imapidle.properties.
Two meters are added to size the mechanism: appsuite.push.imapidle.listeners reports the IMAP IDLE push listeners registered on the node, and appsuite.push.imapidle.idling reports those currently waiting in an IMAP IDLE command, each holding an IMAP connection.
SCR-1827
Summary: New configuration options for the Redis connector start-up behavior
Effective: 8.53.217 and later
Two configuration options control how the Redis connector behaves while its bundle starts.
com.openexchange.redis.awaitEndPointOnStartupWhether bundle start-up awaits reachability of the Redis end-point. With the default the connector blocks until the end-point answers. Setting it tofalsemoves the wait off the start-up path: the end-point is awaited in the background, with a growing but capped interval between attempts, so a pod starts and can be terminated cleanly even while Redis is unavailable. In either mode theRedisConnectorServiceis registered only once the end-point answered, so consumer bundles never start against an unreachable Redis; until then the node is up but not serviceable - a login attempt in that window fails. A condition that cannot resolve without a configuration change - rejected credentials above all - ends the background wait; the node then has no Redis functionality until it is restarted with a corrected configuration. This property applies to the regular Redis instance only; dedicated cache instances and remote sites always start without awaiting their end-point. Default true. Not reloadable, not config-cascade aware. File: redis.properties.com.openexchange.redis.awaitEndPointBudgetMillisHow long start-up awaits the Redis end-point before giving up, in milliseconds. Only relevant whilecom.openexchange.redis.awaitEndPointOnStartupistrue. The default waits indefinitely, which is what a Redis that is merely slow to become available needs: loading a large dataset after a restart can take considerably longer than a few minutes, and every node of the installation is waiting for the same end-point. Set a value only where a bounded start-up is preferred over waiting it out; start-up then fails with an error instead of continuing to retry. A condition that cannot resolve without a configuration change - rejected credentials above all - is reported immediately regardless of this setting. A value less than or equal to 0 (zero) means no limit. Default 0. Not reloadable, not config-cascade aware. File: redis.properties.
SCR-1825
Summary: New configuration option for Redis Sentinel authentication
Effective: 8.53.217 and later
In order to connect to a password-protected Redis Sentinel, a new configuration option has been added.
com.openexchange.redis.sentinel.passwordSpecifies the password used to authenticate against the Redis Sentinel nodes. Only effective ifcom.openexchange.redis.modeis set tosentinel. Sentinel authentication is separate from the credentials for the Redis nodes themselves, which remain configured throughcom.openexchange.redis.usernameandcom.openexchange.redis.password. An empty value means the Sentinel nodes require no authentication. Set it only if the Sentinel nodes actually require authentication; otherwise they reject theAUTHcommand and the topology look-up fails. For a special Redis instance the option is available ascom.openexchange.redis.[instanceId].sentinel.password, with[instanceId]beingcacheor the identifier of a remote site. Default empty. Not reloadable, not config-cascade aware. File: redis.properties.
SCR-1824
Summary: New property com.openexchange.mail.import.enforceFromValidation
Effective: 8.53.217 and later
The new lean configuration property com.openexchange.mail.import.enforceFromValidation controls whether the From of a message imported or appended into a folder through mail?action=import or mail?action=new is validated against the user's own addresses even when the request carries force=true. It defaults to false, is reloadable and config-cascade aware.
By default a force=true request skips that check, matching the established behavior the regular client relies on to import arbitrary messages such as migrated or forwarded .eml files. Setting it to true makes the check unconditional, so a message whose From the user does not own is rejected on import too, closing the path where such a message could later be re-sent under a foreign identity, e.g. through a redirect filter rule. The trade-off is that messages with a foreign sender can then no longer be imported.
No operator action is required by default. See the configuration documentation for further details.
SCR-1819
Summary: New properties for eml token lifetime, message size limit and concurrent token downloads
Effective: 8.53.217 and later
In order to bound what a token download may consume, three lean configuration properties are introduced. All of them are reloadable and config-cascade aware.
com.openexchange.mail.emlToken.ttlsets how long a token issued throughmail?action=emlTokenstays valid, defaulting to300000milliseconds. The lifetime is fixed: it starts when the token is issued and is not extended by accessing it. Only the start of the download has to fall into that window.com.openexchange.mail.emlToken.maxMessageSizerefuses to issue such a token for messages larger than1073741824bytes. It is evaluated when the token is issued; messages whose size the mail back-end does not report pass unchecked. A value less than or equal to0disables the check.com.openexchange.mail.attachmentToken.maxConcurrentDownloadsbounds how many token downloads one user may have in flight at the same time on one node, defaulting to20. It covers downloads of single attachments as well as of whole messages through/ajax/mail.attachment; requests beyond the limit are answered with status429. A value less than or equal to0disables the limit.
Keep the last value below the number of connections the mail back-end grants a single user, so that an excess of downloads is rejected with 429 rather than running into a connection error. The counter is kept per node, hence the effective limit across a cluster is the value times the number of nodes.
That property applies to the pre-existing attachment download path as well, so a user with more downloads in flight than the limit allows now receives 429 where previously every request was served. It complements com.openexchange.servlet.maxRate, which limits requests over time rather than at a time. No operator action is required by default.
SCR-1817
Summary: New configuration options for the Caffeine cache helper and ICAP OPTIONS caching
Effective: 8.53.217 and later
New configuration options introduced together with the replacement of the remaining Guava caches by Caffeine and the shared cache-loading helper this introduced.
com.openexchange.caching.caffeine.loadTimeoutSecondsThe number of seconds a thread awaits a cache value that another thread is currently loading, before it gives up and the request fails with an error. The load itself stays in flight, so the remaining waiters and any later caller keep sharing it rather than starting a second load against a back end that is already slow. Applies only to caches that load through the shared Caffeine helper. A value less than or equal to 0 (zero) is ignored and the default is used. Default 10. Reloadable, not config-cascade aware. No dedicated properties file.com.openexchange.caching.caffeine.maxLoadSecondsThe number of seconds after which a cache load that is still in flight counts as stuck rather than merely slow. The next caller running intocom.openexchange.caching.caffeine.loadTimeoutSecondsthen drops that load and fails the threads waiting on it, so a loader that never returns cannot render its cache key unusable for the lifetime of the process. Must be set comfortably above the slowest legitimate load; a call site that declares a longer wait of its own raises the ceiling accordingly. A value less than or equal to 0 (zero) is ignored and the default is used. Default 60. Reloadable, not config-cascade aware. No dedicated properties file.com.openexchange.icap.client.optionsTtlSecondsThe life time in seconds of a cached ICAPOPTIONSresponse that carries noOptions-TTLheader. Responses that do carry that header expire after the time the ICAP server advertises; this option governs only the case where the server omits it. A value less than or equal to 0 (zero) disables caching for those responses, which makes every scan perform anOPTIONSround trip on the calling thread first. Default 300. Reloadable, not config-cascade aware. No dedicated properties file.
SCR-1806
Summary: New properties for the JWKS retrieval timeouts
Effective: 8.53.217 and later
Retrieval of the JWK set used to validate OAuth 2.0 access tokens, from the end-point configured through com.openexchange.oauth.provider.jwt.jwksUri, is now bounded by two new lean configuration properties instead of by the fixed defaults of the underlying library:
com.openexchange.oauth.provider.jwt.jwksConnectTimeout = 2000
com.openexchange.oauth.provider.jwt.jwksReadTimeout = 3000
Both are given in milliseconds and apply to a single retrieval attempt. They are reloadable and config-cascade aware. The previous effective values were 500 ms each, which was too tight for a JWKS end-point reached over a network.
A retrieval is attempted twice, so the sum of both values should stay well below 15000 ms, the time a request waits for a retrieval that another request has already started. A configured pair that does not satisfy this, or that is not positive, is ignored in favour of the defaults above and reported in the log.
Retrieval also became resilient without any configuration change: a failed retrieval is retried, the retrieved set is cached and refreshed ahead of its expiry, and while the end-point is unreachable the last retrieved set continues to be used rather than rejecting otherwise valid access tokens.
Related behavioural change, for completeness: when the signing keys cannot be obtained at all, an OAuth-authenticated HTTP API request is now answered with HTTP 503 and error: temporarily\_unavailable instead of HTTP 401 and error: invalid\_token. Access tokens that genuinely fail validation are still answered with HTTP 401 as before.
See the property documentation https://documentation.open-xchange.com/components/middleware/config/8/#mode=search&term=jwksConnectTimeout for further details.
Packaging/Bundles
SCR-1831
Summary: Consolidated configuration bundles into com.openexchange.config.common
Effective: 8.53.217 and later
The shared configuration types are reorganized into dedicated bundles. Newly introduced:
com.openexchange.config.common- contains the classicConfigurationService, the reload types and the lean configuration API; thecom.openexchange.configpackage moves here fromcom.openexchange.configread, which remains as the provider implementation.com.openexchange.config.universal- the relocated user-configuration API.com.openexchange.config.mapping- resolves renamed property keys to their previous names.com.openexchange.admin.common,com.openexchange.sessiond.configandcom.openexchange.timer- split out of their host bundles to break dependency cycles.
The bundle com.openexchange.config.lean is renamed to com.openexchange.config.lean.impl; the API package name com.openexchange.config.lean is unchanged. All affected packages are shipped as before; install lists that pin individual bundles must be updated accordingly.