App Suite Releases
  • 8.47
  • 8.35
  • 7.10.6
Imprint
  • 8.47
  • 8.35
  • 7.10.6
Imprint
  • Release 8.53Upcoming
    • Noteworthy Changes
      • Important Changes
      • App Suite Middleware
    • Changelogs
      • App Suite UI
      • App Suite Middleware
      • Additional Components
        • AI Service
        • Booking Service
        • OX Guard UI
        • Switchboard
        • UI Service
    • Helm Charts
      • AI-Service documentation
      • App Suite Stack Chart
      • Booking
      • Helm Chart core-cacheservice
      • Helm Chart core-documentconverter
      • Helm Chart core-imageconverter
      • core-mw
      • UI Service
      • Switchboard
  • Release 8.52
  • Release 8.51
  • Release 8.50
  • Release 8.49
  • Release 8.48
Maintained. Older releases are best effort.
Upcoming
Not released yet
LTS
Long-term support branch

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.maxResults Defines the maximum number of contacts returned by an addressbooks?action=autocomplete request that does not supply a right_hand_limit of 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, default 5m
  • initWait.dbInterval / INIT_DB_WAIT_INTERVAL, default 2s
  • initWait.dbDnsTimeout / INIT_DB_DNS_TIMEOUT, default 30s, 0 disables the early exit
  • initWait.middlewareTimeout / INIT_MW_WAIT_TIMEOUT, default 5m
  • initWait.middlewareInterval / INIT_MW_WAIT_INTERVAL, default 5s

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 via TrustedAppPrincipal#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.enabled Now 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. Default true. Reloadable, not config-cascade aware. File: redis.properties.

  • com.openexchange.cache.v2.redis.inMemoryCacheEnableThreshold Now evaluated even while com.openexchange.cache.v2.redis.inmemory.enabled is false, where it previously fell back to its default; the same applies to com.openexchange.cache.v2.redis.inMemoryCacheDisableThreshold. Setting it to 0 on 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. Default 100. Not reloadable, not config-cascade aware. File: redis.properties.

  • com.openexchange.cache.v2.redis.inmemory.remoteInvalidation Now evaluated even while the in-memory cache layer is disabled, where it previously fell back to its default. Default true. 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.enabled Controls 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. Default true. Not reloadable, not config-cascade aware. No dedicated properties file.

  • com.openexchange.grpc.server.port The TCP port the provisioning gRPC server listens on. Default 8066. Not reloadable, not config-cascade aware. No dedicated properties file.

  • com.openexchange.grpc.server.ssl.enabled Controls 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. Requires com.openexchange.grpc.server.ssl.certificateChainFile and com.openexchange.grpc.server.ssl.privateKeyFile; the bundle fails to start when either is unset or does not name a readable file. Default false. Not reloadable, not config-cascade aware. No dedicated properties file.

  • com.openexchange.grpc.server.ssl.certificateChainFile Path to the PEM-encoded certificate chain the provisioning gRPC server presents to clients. Only evaluated when com.openexchange.grpc.server.ssl.enabled is true. No default. Not reloadable, not config-cascade aware. No dedicated properties file.

  • com.openexchange.grpc.server.ssl.privateKeyFile Path to the PEM-encoded private key belonging to the configured certificate chain. Only evaluated when com.openexchange.grpc.server.ssl.enabled is true. No default. Not reloadable, not config-cascade aware. No dedicated properties file.

  • com.openexchange.grpc.server.shutdownTimeoutSeconds The number of seconds a shutdown waits for calls that are still in flight before they are canceled. Default 10. 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.shareCounters Controls 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. Default false. 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.minRecordIntervalMinutes Defines 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.enabled Whether the cleartext HTTP network listener serves HTTP/2. While off, the middleware speaks HTTP/1.1 only and answers an h2c upgrade as though the Upgrade header were absent. Switching it on serves HTTP/2 both via the h2c upgrade handshake and via a direct prior-knowledge connection; the HTTPS listener is not covered, because h2 over TLS requires ALPN. Requests carrying a payload keep being answered with HTTP/1.1, since clients such as Apache CXF advertise Upgrade: h2c on ordinary POSTs without being able to switch protocols. Note that com.openexchange.http.grizzly.strictHeaderNameValidation and com.openexchange.http.grizzly.strictHeaderValueValidation do not apply to HTTP/2 traffic, and that com.openexchange.http.grizzly.maxNumberOfConcurrentRequests is 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.maxStreamsPerSecond The 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 with GOAWAY. 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 while com.openexchange.http.grizzly.http2.enabled is 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.maxConcurrentStreams The 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 while com.openexchange.http.grizzly.http2.enabled is 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 its SETTINGS frame so that clients never learn about it. Default 128. Not reloadable, not config-cascade aware. No dedicated properties file.

  • com.openexchange.http.grizzly.http2.maxHeaderListSize The maximum decoded size in bytes of an HTTP/2 header list. A value of 0 (zero) follows com.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 while com.openexchange.http.grizzly.http2.enabled is 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 (empty SHOW 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, or 0 for a cross-context grantor whose bare identifier has no meaning in the requesting user's context
  • grantorIdentifier - the qualified identifier as <userId>@<contextId>
  • grantorEntityInfo - pre-resolved entity information for rendering the grantor
  • deputyIds - the identifiers of the deputy permissions this user granted
  • sendOnBehalfOf - 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 grant
  • grantorAddresses - the addresses to send from, present only when sendOnBehalfOf is true; 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.

  • folder and id identify the message and are mandatory.
  • ttlMillis shortens the token's lifetime; a value above the configured default is capped to it.
  • oneTime invalidates the token once it has been redeemed, default false.
  • checkIp restricts the download to the client address the token was issued for, default false.

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, PropertyFilter and related types moved to the com.openexchange.config.common package and bundle; import statements and bundle manifests must be repointed.
  • The UserConfiguration API moved to com.openexchange.config.universal; ServerSession.getUserConfiguration() now returns UserConfigurationImpl.

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.reportBackoffThreshold concurrently 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.allowSendOnBehalfOfByFolderOwnership Lets 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. Default false. 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.enabled Whether to use the volatile in-memory cache as facade prior to accessing Redis storage. Default changed from false to true. Not reloadable, not config-cascade aware. File: redis.properties.

  • com.openexchange.cache.v2.redis.inmemory.timeToLiveMillis The time-to-live of elements in the in-memory caching layer in volatile mode. Default changed from 10000 to 5000. 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.interruptTasks Controls 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 outlives com.openexchange.requestwatcher.expiredRequestAge, its thread is interrupted; if it survives that, it is reported once more after com.openexchange.requestwatcher.interruptedThreshold further sightings and then no longer reported. An expiration age below com.openexchange.requestwatcher.maxRequestAge is 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.reportBackoffThreshold The 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, at com.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 while com.openexchange.threadpool.watcher.interruptTasks is 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.readResponsesTimeout The 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 to 0 is 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.readResponsesTimeout The same for secondary accounts. A value less than or equal to 0 is 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.maxConcurrency Limits 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 from com.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 of 0 (zero) disables the job-queue executor and runs jobs on the platform thread pool as before; no appsuite.executor.jobqueue.* meters are registered then. A value less than 0 (zero) is treated as 0 and logged. Default 256. 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.remoteInvalidation Whether an in-memory cache propagates invalidations to the other nodes' in-memory caches. Effective with com.openexchange.cache.v2.redis.inmemory.enabled=true or with dynamic enabling configured through com.openexchange.cache.v2.redis.inMemoryCacheEnableThreshold. Disable it to trade consistency for less pub/sub traffic. Default true. 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.maxConcurrency The 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 at WARN level at most once per minute. Raise the value when the meter appsuite.executor.timer.active sits at appsuite.executor.timer.concurrency.limit while appsuite.executor.timer.rejected increases. A value of 0 (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 as 0 and logged. Default 64. 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.virtualThreads Controls 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 to false to restore the previous behavior. The value is evaluated when a listener is started, so it takes effect for listeners started afterwards. Default true. 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.awaitEndPointOnStartup Whether bundle start-up awaits reachability of the Redis end-point. With the default the connector blocks until the end-point answers. Setting it to false moves 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 the RedisConnectorService is 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.awaitEndPointBudgetMillis How long start-up awaits the Redis end-point before giving up, in milliseconds. Only relevant while com.openexchange.redis.awaitEndPointOnStartup is true. 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.password Specifies the password used to authenticate against the Redis Sentinel nodes. Only effective if com.openexchange.redis.mode is set to sentinel. Sentinel authentication is separate from the credentials for the Redis nodes themselves, which remain configured through com.openexchange.redis.username and com.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 the AUTH command and the topology look-up fails. For a special Redis instance the option is available as com.openexchange.redis.[instanceId].sentinel.password, with [instanceId] being cache or 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.ttl sets how long a token issued through mail?action=emlToken stays valid, defaulting to 300000 milliseconds. 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.maxMessageSize refuses to issue such a token for messages larger than 1073741824 bytes. 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 to 0 disables the check.
  • com.openexchange.mail.attachmentToken.maxConcurrentDownloads bounds how many token downloads one user may have in flight at the same time on one node, defaulting to 20. It covers downloads of single attachments as well as of whole messages through /ajax/mail.attachment; requests beyond the limit are answered with status 429. A value less than or equal to 0 disables 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.loadTimeoutSeconds The 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.maxLoadSeconds The number of seconds after which a cache load that is still in flight counts as stuck rather than merely slow. The next caller running into com.openexchange.caching.caffeine.loadTimeoutSeconds then 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.optionsTtlSeconds The life time in seconds of a cached ICAP OPTIONS response that carries no Options-TTL header. 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 an OPTIONS round 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 classic ConfigurationService, the reload types and the lean configuration API; the com.openexchange.config package moves here from com.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.config and com.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.

Prev
Important Changes