/////////////////////////////////////////////////////////////////////////////// Copyright (c) 2000, 2026, Oracle and/or its affiliates. Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. /////////////////////////////////////////////////////////////////////////////// = Security Hardening :toc: left :toclevels: 4 :sectnums: [[_serialization_hardening_ser_01]] [[security-hardening-26-10]] This guide covers deployment and migration security for *Coherence CE 26.10*, which requires *Java 21 or later*. It includes the serialization and remote execution reference previously published as Serialization Hardening (SER-01). * An unset `coherence.security.mode` selects *`hardened`* in CE 26.10. * For a staged migration, set `-Dcoherence.security.mode=compatibility` explicitly *before the first upgraded member or client starts*. Leaving the property unset does not preserve the previous release's behavior. * Keep the existing `coherence.mode` runtime/license setting separate. * `coherence.remote.dynamic.unauthenticated` defaults to *`allow` in both profiles* in the delivered 26.10 release. Set `deny` explicitly if your policy refuses dynamic remote code; hardened mode alone does not select it. Hardened defaults constrain input and execution. Deployments must also configure trusted identities, operation authorization, TLS, endpoint exposure, and secret handling. Complete configuration examples below name their required deployment inputs; Java declarations containing omitted application types or `...` and XML elements shown without their enclosing document are fragments. == Overview Serialization hardening covers bytecode deny-list checks, JEP 290 integration, strict POF type handling, dynamic class-definition validation, generated application allowlists, executable classification, and per-boundary telemetry. The same security profile also changes selected HTTP, management, TLS-verifier, and replay defaults. Other hard protections apply in both profiles, and some controls require their own configuration. Use the matrix below to distinguish these contracts. === Why not JEP-290 alone? `java.io.ObjectInputFilter` (JEP-290) is a transport-level filter wired to `ObjectInputStream`. Coherence's dominant wire formats (POF, `ExternalizableLite`, `PortableObject`) bypass `ObjectInputStream` entirely, so a JEP-290 filter alone would silently miss the majority of deserialization traffic. SER-01 also needs the same allowlist to drive three distinct gates -- class allowlist on deserialization, lambda-bytecode acceptance, and remote-execution policy -- and to compose across many modules at build time with annotation-driven generation. JEP-290's single allow/reject bit and flat filter string cannot express that. Coherence therefore uses `security-config.xml` as the source of truth, has every wire-format reader consult it, and registers a JEP-290 filter that delegates back to the same allowlist for the paths that do run through `ObjectInputStream`. JEP-290 is a hook, not a substitute, and customer-supplied `jdk.serialFilter` settings still compose normally for non-Coherence streams. == Compatibility The matrix describes the shipped CE 26.10 defaults. An explicit feature setting can override a profile default only where that feature permits it. .Security behavior by control [cols="2,3,3", options="header"] |=== |Control |Explicit `compatibility` |Unset / `hardened` |Application serialization allowlist and named remote executables |Missing classifications can produce `would_reject` diagnostics. |Required allowlist and executable classifications are enforced. Dynamic installs have the separate exception described under <>. |Serializer container limits |Default-unbounded; shadow thresholds can warn. Explicit finite limits apply. |64 MiB per container, 262144 elements, 131072 map entries at supported consumers. |Extend InvocationService / ConcurrentProxy defaults |Enabled by the shipped configuration. |Disabled by the shipped configuration. Explicit exposure needs review and retains executable policy checks. |REST input policy |Historical expression/pass-through behavior; ordinary direct HTTP queries still require enablement. Legacy SSE has a compatibility bypass. |Declared resources, approved aliases, enabled direct queries and safe-query checks; engaged but unusable authentication fails closed. |gRPC client-selected serializers and automatic diagnostics |Unapproved formats shadow; automatic diagnostics remain richer. |Unapproved formats reject; automatic Channelz is disabled and error disclosure is safe. This does not enable gRPC authentication or TLS. |Management and metrics HTTP authentication |Shipped default `none`. |Shipped default `basic`. Configure the login module and credentials first. |TLS hostname verification |Historical permissive fallback can remain, with shadow diagnostics. |Default hostname verifier enforced; explicit unsafe `allow` rejected, while the shipped fallback maps to the default verifier. TLS must still be selected. |Persisted topic executable-policy drift |Default `warn-allow`. |Default `reject`; explicit `reject` also applies in compatibility. |Dynamic remote-code policy — independently selected |Default `allow`; explicit `deny` enforced. |Default `allow`; explicit `deny` enforced. |Hard protections — both profiles 2+|Built-in serialization/bytecode denylists, malformed input and class-definition checks, JVM/bridge input-filter rejections, Reporter malformed-input/traversal checks, and supported management diagnostic-command restrictions remain active. Compatibility does not undo these protections. |Deployment controls — separately configured 2+|TLS selection, trusted identity/token validation, storage authorization, HTTP/gRPC authentication integration, network restrictions, Reporter approved locations, health mutators and peer-proof requirements each need their own configuration. Hardened mode does not provision them. |=== == Preparation Checklist Before upgrading any process: * Inventory members, Extend/gRPC/REST clients, management tools, scrapers, health administrators, optional modules, and stored executable metadata. Record Java, client and module versions for each deployed route. * Choose the initial profile explicitly. Put compatibility settings in the actual service/container/JVM launch configuration before its first upgraded startup if you need a staged rollout. Include upgraded Java clients, not only storage members. Test older-server workflows separately. * Inventory payload and executable classes, including lambda captures and custom functional interfaces. Generate metadata in every owning application JAR; verify shaded JAR merging and receiver class-loader visibility. * Decide the dynamic-code policy independently. Measure DYNAMIC lambda, script and reflective processor use before selecting `deny`. Review named executable classification separately from dynamic installation. * Provision trusted token validation, authorization rules, TLS identities and hostname-correct certificates. Stage login modules and protected secret sources for management/metrics and gRPC authentication where enabled. * Declare REST resources/aliases, review gRPC serializer names, choose finite serializer limits from legitimate largest payloads, and restrict management, Reporter, health and RAG network/filesystem access. * Back up persistence, snapshots and topic metadata with the classes and configuration needed for recovery. Test restore and policy-drift handling in isolation. Record the rollback procedure for independently enforced changes. == Quick Migration Guide . If staging is required, deploy the following JVM setting to every upgraded participating member and Java client *before its first startup*: + [source,shell] ---- -Dcoherence.security.mode=compatibility ---- + This is a JVM option, not a standalone shell command. Keep `coherence.mode` unchanged. Choose `coherence.remote.dynamic.unauthenticated` separately; an explicit `deny` also rejects dynamic code in compatibility. . Upgrade with application JARs containing generated allowlists and executable classifications. Keep protocol/client compatibility and class deployment coordinated; the security profile does not make arbitrary versions compatible. . Exercise real reads, writes, queries, invoke/aggregate, triggers, topics, executor tasks, REST/gRPC calls, management, scrapes and recovery. Inspect `coh.executable.policy_check{result=would_reject}` plus each subsystem's bounded warnings; management and Reporter shadows are not executable-policy counters. . Fix each missing class or control at its owner. Review before allowlisting; do not use broad package exceptions to silence an unknown payload. Check every receiving JAR and class loader, not only the client's build output. . In an isolated preproduction deployment, set `-Dcoherence.security.mode=hardened` with the same identities, endpoints, serializer policy and persisted data as production. Verify intended success and denied identity, operation, payload, alias, hostname and recovery cases. . Roll out hardened policy consistently across participating processes after those checks pass. Removing the explicit compatibility setting also selects hardened in CE 26.10. Monitor authentication failures, rejection reasons, denied operations, recovery and scrape health throughout the rollout. . If reverting the profile is necessary, set compatibility explicitly and restart the affected processes under the tested rollback plan. Restore independent feature settings deliberately. A profile rollback does not undo hard type/format checks, rewrite persisted data or supply missing old classes. Applications missing allowlist metadata can receive `security-config-missing`. An explicit dynamic-code `deny` can produce `dynamic-remote-denied-by-mode`. Use <> and <> to identify the owner of a failure before changing policy. == Dynamic Management and Remote JMX The standalone discovery client requires JDK 21 or later, matching the Coherence runtime minimum. Dynamic management registers Coherence MBeans on the elected management member and aggregates MBeans from the other members. When an `MBeanServerFinder` supplies an existing connector URL, Coherence publishes that URL without starting another connector. Otherwise Coherence starts an RMI connector for the selected MBeanServer and publishes its standard JDK `service:jmx:rmi://host:port/stub/...` address. No JVM remote management agent or RMI registry is required for this automatic connector. An encoded stub contains serialized RMI endpoint metadata. Coherence validates that metadata with a bounded byte parser, without Java deserialization, payload class loading, DNS resolution or network access. The parser accepts only the recognized JDK static or proxy `RMIServer` stub, built-in `UnicastRef` or `UnicastRef2` reference, and optional exact built-in `SslRMIClientSocketFactory`. It rejects custom factories, other object graphs, class annotations, malformed encodings and trailing data. Encoded payloads are limited to 4096 bytes and endpoint host names to 1024 characters. Unknown future JDK or vendor serialization layouts fail closed and require an update to the recognized grammar. A finder can instead supply a supported `/jndi/rmi://` directory URL for a custom socket factory. Validation applies to management publication and reserved NameService JMX bindings and raw/string lookups. Hardened mode rejects unsupported incoming publications. Compatibility mode retains its warning for such publications, but outward discovery and the standalone discovery client always reject unsupported encoded forms. A normal JMX client deserializes the validated reference when it chooses to connect. That client must use trusted class loaders that resolve the recognized names to the JDK platform classes. The existing `coherence.management.remote.host`, `connectionport`, and `connectionport.adjust` settings remain applicable. Non-SSL connections retain member-port multiplexing. By default the connector uses the member address when discovery binds to a wildcard. An explicit client socket factory or `java.rmi.server.hostname` is preserved. The exported hostname must be reachable and covered by the certificate when TLS is enabled. An explicitly configured nonzero `coherence.management.remote.registryport` retains the existing opt-in registry and JNDI URL behavior. This validation does not authenticate endpoints or enable authentication or TLS. Existing automatic-connector defaults remain unchanged: authentication and TLS default to false unless password/access files or secure TCMP configuration enable the existing security defaults. An explicit nonzero registry port retains its existing authentication and TLS defaults. `com.sun.management.jmxremote.authenticate` and `com.sun.management.jmxremote.ssl` continue to apply. Configure credentials, access restrictions and TLS as appropriate for the deployment. Registry TLS, when using the explicit registry option, remains separately configured. Coherence stops its automatic connector when its management service stops. On senior failover the successor starts its own endpoint and publishes its address. If startup fails or a finder returns an unsupported URL, members clear the old address and NameService JMX discovery returns `null`; local MBeans and Coherence management continue to operate. Coherence does not stop a connector supplied by a finder. Finder addresses are advertised without liveness checks. Returning `null` from the finder allows the automatic connector fallback. == Runtime Modes Coherence runtime mode is selected by the `coherence.mode` system property. The released values are `eval`, `dev`, `development`, `prod`, and `production`. When `coherence.mode` is unset, Coherence defaults to `dev`. Blank, unknown, `legacy`, and `legacy-compatibility` values fail fast. Runtime mode controls the released license/runtime mode behavior. It does not enable the compatibility-sensitive SER-01 hardening gates. Security mode is selected by the `coherence.security.mode` system property. Supported values trim whitespace and compare case-insensitively: * `compatibility` keeps the compatibility-sensitive hardening gates disabled and emits bounded shadow telemetry where available. * unset or `hardened` enables the compatibility-sensitive hardening gates. Blank, unknown, shorthand, and boolean-style values such as `true`, `false`, `on`, `off`, `enabled`, or `disabled` fail fast. The removed pre-release `coherence.security.hardened` property is not an alias. == Identity and Authorization Treat a transport connection, an authenticated subject, and permission to perform an operation as separate decisions. Restrict cluster and proxy access to trusted networks even when application authentication is configured. For Extend, configure an `IdentityTransformer` on the client and a matching `IdentityAsserter` on the proxy. Validate a real credential or signed token and construct the subject from that validation. In hardened mode the default asserter rejects a non-null raw `Subject`; compatibility retains the historical raw-subject behavior. A null token can still produce a null subject in both profiles. Hardened mode therefore does not itself require every client to identify itself. Make anonymous access an explicit application decision and reject it on protected routes. Authenticate through the configured JAAS `LoginContext` where that integration is used. On Java 21+, `Subject.callAs` can scope application work to its verified subject. Obtain and use subject-scoped Coherence sessions and map/cache handles within that context; do not share one user's handles with unrelated users. Test subject propagation through asynchronous callbacks and reconnections. When using the Coherence access-controller framework, configure its service/cache permissions and trusted signing identity explicitly. Hardened `DefaultController` uses a modern signature policy and binds authorization to the verified signer principal; do not rely on legacy multi-principal binding or weak signatures. Review credential expiry, replacement and subject-scoped handle lifetime with the chosen identity integration. The Java Security Manager is permanently disabled on JDK 24+; use Coherence's identity and authorization integrations rather than treating a JVM policy file as an application permission model. Configure a `StorageAccessAuthorizer` on every protected partitioned-cache scheme. Its absence means there is no storage-authorizer policy in either profile. Implement role/resource decisions for the supported read/write and map-wide reasons; audit logging alone grants no business authorization. `strict-auditing` rejects a missing subject but does not prove that a non-null subject was authenticated or implement application roles. A null subject must not become an automatic internal-system bypass. Test read, write, invoke, aggregate, index add/remove, lock/unlock, and listener routes exposed by the application. Include denied users and absent subjects. Continuous Aggregation registration, removal and full-result access use aggregate authorization; projection operations use entry-processor authorization. Executable classification is an additional boundary and does not grant a user permission to access a cache. The published official https://docs.oracle.com/en/middleware/fusion-middleware/coherence/14.1.2/secure/authorizing-access-server-side-operations.html[storage-authorization chapter] provides registration and scheme-wiring background. Its older method-coverage table is version-specific; use the CE 26.10 operation checks described here and test the actual routes you deploy. == TLS and Protected Secrets Choose TLS explicitly for cluster transport, Extend/gRPC channels and HTTP listeners that carry sensitive data or credentials. Configure each listener and client path; selecting hardened mode does not convert every connection to TLS. Use mTLS where callers must prove possession of a client certificate, and still apply the required application operation policy. Provision identities and trust stores for the actual service DNS/IP names. Use supported JDK protocol/cipher policies and retain hostname verification. In hardened mode Coherence's default verifier is enforced, including replacement of the shipped permissive fallback; an explicitly unsafe `allow` verifier is rejected. A configured custom verifier remains application policy and requires review. A valid certificate chain without a matching endpoint identity is insufficient. Keep keystore, private-key and trust-store passwords out of checked-in XML and command-line arguments. Use the supported password-provider and loader interfaces with service-account-readable secret mounts. For example, the following *operational-configuration fragment* registers a password provider; its second argument selects the first line, avoiding a trailing newline in the password: [source,xml] ---- com.tangosol.net.URLPasswordProvider java.lang.String file:/run/secrets/coherence-store boolean true ---- Place it under `` in the operational override. Reference it using `store-password` at the appropriate keystore password location. Private-key and trust-store passwords have their own locations/providers. Restrict the secret file and parent directory permissions; a password file is not encrypted by this mechanism. Define named SSL socket providers in the operational override, then reference the intended provider from each cluster listener, Extend acceptor/initiator, gRPC acceptor/channel, or HTTP acceptor. The XML element remains named `` when TLS is used. The published official https://docs.oracle.com/en/middleware/fusion-middleware/coherence/14.1.2/secure/using-ssl-secure-communication.html[TLS chapter] explains the socket-provider configuration forms, key/trust managers, password providers, hostname verification and certificate refresh. Apply CE 26.10 policy and the JDK requirements above when using those forms. Verify a successful connection and rejection of an untrusted CA, wrong hostname, expired credential and missing required client certificate. Exercise identity rotation, refresh failures and reconnection before deploying a rotation plan. == REST and gRPC Endpoints === REST Resources and Expressions Expose only the resources and operations required by the application. Configure REST authentication and operation authorization explicitly; the hardening profile is not a substitute for an HTTP identity integration. Hardened REST requires declared resources instead of automatically publishing unconfigured pass-through caches. Configure named queries and bounds, sort and projection aliases, and aggregator/processor argument aliases. Approving an expression alias does not authorize arbitrary implementations or grant a user permission to invoke it. Custom registry factories are trusted server code; review construction side effects as well as the executable returned. Ordinary HTTP direct `q` queries require `` in both profiles. An empty `` does not enable them. Hardened also checks direct-query enablement on SSE and restricts expressions to the supported safe subset; legacy compatibility SSE retains its historical bypass. Prefer named queries and test the exact URLs, escaping, aliases and result bounds. When REST authentication is engaged but provides no usable principal, hardened mode returns 401. An unconfigured resource is 404; a disabled direct query is 403; an unsafe query or restricted expression is 400 at the owning input gate. Test missing credentials, a valid denied user, and approved/unknown aliases. Do not infer that an input 400 establishes any authorization decision. JSON metadata and XML parsing are separate input boundaries. In hardened Genson handling, explicit class metadata needs configured exact type aliases; package aliases do not satisfy an exact `@class` alias requirement. The gRPC JSON path also gates nested and statically typed materialization. Do not extend that guarantee to every REST marshaller. Required XML parser protections fail closed in hardened mode when the parser cannot apply them; compatibility can warn and retain fallback behavior. Avoid loading untrusted XML into a parser configuration whose protection capability has not been verified. === gRPC Transport, Authentication and Serializers Use the CE gRPC proxy/client modules for the actual server implementation and client version being deployed. Configure a named TLS socket provider, an identity asserter accepting `UsernameAndPassword` when using Basic, and a storage authorizer for protected caches. Propagating a verified subject and checking its permissions are separate steps. The following are *JVM options* for a proxy with a previously configured `server-tls` socket provider and identity integration: [source,shell] ---- -Dcoherence.grpc.server.secure-transport=required -Dcoherence.grpc.auth=basic -Dcoherence.grpc.channelz=disabled -Dcoherence.grpc.error-disclosure=safe ---- Reference `server-tls` in the gRPC acceptor's `` configuration. `secure-transport` defaults to `optional`, and gRPC authentication defaults to `none`, in both profiles. Setting transport to `required` fails startup if TLS is absent. Basic alone does not mandate TLS at startup: hardened rejects Basic requests received over cleartext, while compatibility permits them with a warning. Configure the secure client channel, CA and expected server name too. An omitted/empty serializer name selects the configured default. The built-in allowed names are `pof`, `json`, `genson`, and `com.oracle.coherence.io.json.JsonSerializer` (normalized case-insensitively). The literal `default` is not a built-in allowed name. In hardened mode explicit `java`, `DefaultSerializer`, and arbitrary custom names require deliberate approval through `coherence.grpc.serializer.allowlist` and must resolve to configured serializers. Approving a name does not bypass class, JSON metadata, resource-limit or executable policy. Compatibility records would-reject telemetry for unapproved names. With diagnostics at `auto`, hardened disables Channelz and chooses safe error disclosure; compatibility enables Channelz and diagnostic disclosure. Explicit `disabled`/`safe` settings keep that exposure choice consistent through a staged upgrade. Channelz policy leaves the normal health service available. Test missing/invalid credentials (`UNAUTHENTICATED`), an authenticated denied operation (`PERMISSION_DENIED`), and an unapproved serializer (`INVALID_ARGUMENT`). Verify the exact v0/v1, async and listener paths in use. Java server support does not establish a ready-to-copy secure recipe for every Go, Python, JavaScript, C++ or .NET client/version combination. Qualify each client's TLS, identity, serializer and failure behavior before publishing such a recipe. == Management, Reporter, Metrics and Health === Management and Reporter Limit administrative listeners to trusted interfaces and networks. Management HTTP is not enabled merely by choosing a security profile. When the endpoint is enabled, its shipped authentication default is `basic` in hardened mode and `none` in compatibility. Configure the appropriate login module and credentials before the first hardened startup. Set `coherence.management.http.auth` explicitly when a deployment needs stable endpoint behavior during migration. Supported methods include `basic`, `cert` and `cert+basic`; explicit `none` remains available and warns, so it must be a reviewed exposure decision. Basic credentials need TLS. Management invocation validation limits unsafe operation names, argument shapes and descriptors. Supported JFR diagnostic commands have an allowlist enforced in both profiles. Authentication does not make an arbitrary management request safe, and compatibility can retain shadow behavior for some invocation checks. Reporter definitions, source URLs, ObjectNames and output paths are input. Prefer trusted classpath/local report definitions. If remote definitions are necessary, configure exact approved HTTP(S) authorities in `coherence.management.report.remote.allowed`; include a port when required by the authority. This is permission to load a source, not proof of its integrity. Set `coherence.reporter.output.directory` to a dedicated approved directory (the default is the working directory), and keep report targets inside it. Hardened rejects unapproved sources, outside-root paths and disallowed broad ObjectNames; compatibility can warn and allow the compatibility-sensitive cases. Malformed names, invalid output URI schemes and traversal remain hard failures in both profiles. Protect the definition/output trees from untrusted writers and verify the exact report and MBean pattern used. JMX RMI and JMXMP have their own authentication and transport settings. JMXMP binds to loopback by default and fails closed without a usable `JMXAuthenticator`; the remote-management authenticator can be its fallback. When exposing it remotely, configure the supported authenticated secure provider and firewall policy deliberately. Dynamic RMI discovery behavior is described under <>; accepting a validated stub does not authenticate its endpoint or turn on TLS. === Metrics and Health Metrics HTTP has the same shipped authentication default as management HTTP: `basic` hardened, `none` compatibility. Configure `coherence.metrics.http.auth`, the metrics login module and TLS together. Update each scraper's CA, credentials and endpoint before migrating members, and test rejected as well as successful scrapes. Explicit `none` warns and is still an operator choice. Authentication changes do not themselves enable the metrics HTTP endpoint. Read-only health probes remain unauthenticated in both profiles. Restrict their network visibility according to the operational information exposed. `coherence.health.http.mutators.enabled` defaults to `false` in both profiles; the administrative mutators are the supported `PUT /suspend*` and `PUT /resume*` routes. Once enabled, absent/blank `coherence.health.http.auth` defaults to `basic`; `basic`, `cert` and `cert+basic` are supported, while `none`, unknown values or missing required identity fail closed. Provision TLS and authentication first. This authenticates administration; it does not add a new role-based health authorization model. Test suspend/resume only where the member can be recovered, and coordinate the resulting state with probes and load balancers. == Serializer Resource Limits Class/type policy and resource policy address different input risks. In the CE 26.10 hardened profile, serializer-owned container defaults are: .Serializer-owned limits [cols="2,1,3", options="header"] |=== |Limit |Hardened default |Property |Container bytes |67108864 (64 MiB) |`coherence.serializer.limit.maxContainerBytes` |Array/collection/object elements |262144 |`coherence.serializer.limit.maxElements` |Map entries |131072 |`coherence.serializer.limit.maxMapEntries` |=== POF consumes this policy; do not assume every third-party serializer or all object graphs enforce these three limits. Compatibility permits default-unbounded values with applicable threshold warnings; explicitly configured finite limits still constrain their consumer. Hardened requires finite limits and rejects `unlimited`. Negative, impossible, truncated and overflow-prone structures remain invalid independently of the profile. For example, these JVM options select the hardened defaults explicitly: [source,shell] ---- -Dcoherence.serializer.limit.maxContainerBytes=64m -Dcoherence.serializer.limit.maxElements=262144 -Dcoherence.serializer.limit.maxMapEntries=131072 ---- Per-serializer `` XML can override the corresponding property; omitted values inherit it. Apply the configuration to the serializer that actually receives the payload. Measure valid large arrays, batches, query results and nested containers with representative heap and concurrency. Rejection can occur while decoding, before application authorization or execution; raising a global limit to recover one workload can widen every consuming service's allocation exposure. Test the largest legitimate values and an oversized negative case on every supported decode path. == Persisted Data and Replay Boundaries Persisted bytes remain input. Restrict persistence, snapshot, archive, backup and native-index directories to trusted processes; restrict import and restore operations. Keep the compatible application classes, POF registrations, generated security resources and configuration with recoverable backups. Restore in an isolated patched deployment before production traffic is allowed. Metadata parsing, class filters and checksums do not provide cryptographic snapshot/journal authenticity or key lifecycle. Topic subscriber filters/extractors are rechecked when persisted metadata is activated. `coherence.topics.persisted.policy-drift` defaults to `reject` in hardened and `warn-allow` in compatibility; explicit `reject` applies in both. An earlier dynamic install allowance does not guarantee replay under a changed class policy. Rehearse recovery with the deployed artifacts, then classify and regenerate affected definitions before relying on a profile change. See <> for the retained details. Narrow Java object-stream bridges intersect their product-owned expected-type filter, central policy and any JVM filter. An application allowlist does not widen a fixed bridge type universe. Memcached-origin stored values also have mode-independent format checks before ordinary Coherence materialization; compatibility does not make dangerous Java/XML/ExternalizableLite terminal formats safe. Apply these controls to CE-delivered boundaries; a commercial transaction module or native/client implementation needs its own availability and behavior qualification. Peer-proof source availability is not an operator enablement procedure. Hardened mode does not implicitly require senior metadata proof. Keep proof-required storage routes and peer-provider provisioning conditional on a supported, reviewed all-member identity/readiness/rotation procedure. This guide supplies no copyable peer-proof enablement recipe and does not imply that the outstanding support/publication review is cleared. == Optional RAG Controls These controls apply when the CE RAG modules are deployed; they do not add RAG endpoints to an application that does not use those modules. Authenticate administrative access and restrict the default `admin` role. Runtime writes are limited by `coherence.rag.config.write.allowed-properties`; the shipped allowlist is `model.embedding,model.chat,model.scoring,coherence.rag.default.parser`. Sensitive settings retain their own deny policy. Test forbidden writes and secret readback as well as an allowed admin change. Document imports are denied until allowed schemes and locations are configured, in either profile. Allow only the required values in `coherence.rag.import.allowed-schemes`, the HTTP host allowlist, or the provider-specific file/cloud location allowlist. The defaults reject private HTTP destinations and bound connections to 10 seconds, reads to 30 seconds and responses to 50 MiB. Every redirect and complete DNS answer is validated, and the direct transport binds the connection to an approved address while keeping the logical HTTPS hostname. JVM-selected HTTP/HTTPS/SOCKS proxies are rejected; this contract does not provide proxy support. For local files, configure `coherence.rag.import.file.allowed-roots` as well as the `file` scheme. Parent traversal and no-follow channel opening constrain paths, and unsupported `SecureDirectoryStream` providers fail closed. A final file can still be replaced between its regular-file check and open. Keep the allowed tree writable only by trusted ingestion principals; do not claim that the race is eliminated. Local ONNX downloads from HuggingFace use `coherence.rag.security.huggingface.allowed-models`. An empty allowlist rejects in hardened, but permits downloads with a warning in compatibility. A nonempty allowlist constrains downloads in both profiles; prefer exact model names over owner-wide trailing wildcards. This gate does not govern JAR-wired CDI model providers or chat selectors, and model-name approval alone does not establish artifact integrity. Control egress, artifact storage and provenance separately. == Deployment Diagnostics Start with the failing boundary and the resolved settings on the receiving process. Record its CE/JDK/module version, security profile, serializer, endpoint auth/transport choices, and bounded rejection reason. Keep credentials, tokens, key material and untrusted payload bodies out of diagnostic output. .Deployment diagnosis [cols="2,3", options="header"] |=== |Signal |Action |Startup rejects a security profile |Use only `compatibility` or `hardened`; blank/unknown profiles fail fast. Check launch files and property spelling before changing payload metadata. |Identity token rejected / HTTP 401 / gRPC `UNAUTHENTICATED` |Verify the intended asserter/login module and credential type; the hardened default asserter does not accept a raw non-null `Subject`. Check TLS for Basic. |Authenticated request denied / gRPC `PERMISSION_DENIED` |Check the verified subject and the storage/operation policy on that route; serializer allowlisting cannot grant a user access. |REST 400/403/404 or gRPC serializer `INVALID_ARGUMENT` |Check the resource, direct-query setting, aliases, safe query and configured serializer name at the owning gate, then test an authorized valid request. |Container-size rejection |Identify the decoding serializer and limit precedence; compare legitimate payload measurements with configured finite bounds before increasing them. |JMX discovery is absent or an RMI URL is rejected |Check automatic connector startup/finder output, supported stub grammar, endpoint reachability, auth and TLS. CE 26.10 accepts recognized bounded stubs; it does not reject every `/stub/` URL. |Management or Reporter `would_reject` |Review source/output roots, authority/ObjectName and invocation policy on each member. These structured warnings are separate from executable metrics. |Scrapes fail after upgrade / health mutator denied |Stage scraper identity/CA changes before a profile flip. Health mutators require their own enablement and authentication; read-only probes are separate. |`sub_reason=replay_drift` on recovery |Restore the compatible executable metadata and owning artifacts, then test replay under the selected policy. Installation success does not prove recovery. |RAG import/model rejection |Check the scheme and destination/model allowlists, proxy selection, DNS/TLS, byte/time limits and trusted file roots; do not broaden all controls at once. |=== Use <> for serialization/executable reason strings and <> for counter labels. A compatibility run with no warnings is only useful for the routes and payloads it actually exercised; include expected denials and recovery before treating it as migration evidence. == Build-time Allowlist Generation The build-time generator scans compiled classes in the current artifact. It does not scan dependency jars. Each jar that contributes wire payload types should generate and package its own `META-INF/coherence/security-config.xml`. === Maven Use Java 21+ and Maven. This complete `pom.xml` pins the CE dependency and build plugin to 26.10. The `generate` goal runs during `process-classes`, before packaging. Place application sources under `src/main/java`. [source,xml] ---- 4.0.0 com.example secure-application 1.0-SNAPSHOT 26.10 21 UTF-8 com.oracle.coherence.ce coherence ${coherence.version} org.apache.maven.plugins maven-compiler-plugin 3.14.0 com.oracle.coherence.ce security-config-maven-plugin ${coherence.version} generate-security-config process-classes generate ---- For a minimal payload, save this complete class as `src/main/java/com/example/Invoice.java`: [source,java] ---- package com.example; import com.tangosol.util.function.Remote; import java.io.Serializable; @Remote.Allowed public record Invoice(String id, long amountInCents) implements Serializable {} ---- [source,shell] ---- mvn package jar tf target/secure-application-1.0-SNAPSHOT.jar ---- The JAR must contain both `com/example/Invoice.class` and `META-INF/coherence/security-config.xml`. Inspect the generated file under `target/classes/META-INF/coherence/` for `com.example.Invoice`. This record is data; it does not need executable classification. Deploy the owning JAR wherever its payload is decoded. A shaded/fat JAR must merge all dependency security resources with the release's security-config shade transformer rather than retain only one dependency's file. Set `-Dcoherence.security-config.skip=true` to skip generation for a build. === Gradle Use a Gradle release that supports running on Java 21 (the CE 26.10 build uses Gradle 9.7.1). The Coherence plugin requires Gradle 8 or later and the Java plugin. This complete Groovy `build.gradle` uses the delivered CE plugin artifact directly, without relying on a Gradle Plugin Portal marker: [source,groovy] ---- buildscript { repositories { mavenCentral() } dependencies { classpath 'com.oracle.coherence.ce:com.oracle.coherence.ce.gradle.plugin:26.10' } } apply plugin: 'java' apply plugin: 'com.oracle.coherence.ce' repositories { mavenCentral() } dependencies { implementation 'com.oracle.coherence.ce:coherence:26.10' } java { toolchain { languageVersion = JavaLanguageVersion.of(21) } } securityConfig { processTestClasses = false } ---- Use the same `src/main/java/com/example/Invoice.java` as the Maven example, then run `gradle clean jar` (or the application's Gradle wrapper). Verify the output JAR contains the generated security resource and the Invoice entry. The `securityConfig` task is registered with Java compilation; it scans the current artifact's compiled classes, not dependency JARs. Set `processTestClasses = true` to also generate `securityConfigTest` output. === Annotations Use `com.tangosol.util.function.Remote.Allowed` for application classes that may legitimately cross a wire-deserialization boundary as polymorphic payloads. At type level, `@Remote.Allowed` includes the annotated class and, by default, its declared nested types. Set `recursive = false` to include only the annotated class: [source,java] ---- // Generates allowlist entries for MyType AND MyType.Nested. @Remote.Allowed public class MyType { static class Nested {} } // Generates an allowlist entry for MyType only; Nested is excluded. @Remote.Allowed(recursive = false) public class MyType { static class Nested {} } ---- At package level, add `@Remote.Allowed` to `package-info.java`. The default includes classes in that package and subpackages; `recursive = false` limits generation to the package itself. `com.tangosol.io.pof.schema.annotation.PortableType` also generates allowlist entries. A class does not need both annotations unless your source code uses both concepts for clarity. == Generated `security-config.xml` The generated artifact is `META-INF/coherence/security-config.xml`. At member start, Coherence loads every such resource visible to the thread context class loader, validates each resource against `coherence-security-config.xsd`, and merges the allowed class names into a single runtime allowlist. A malformed or invalid resource fails member startup and names the offending URL. The schema is shipped as `coherence-security-config.xsd` in the Coherence jar. A generated file looks like this: [source,xml] ---- ---- Each jar on the runtime classpath contributes its own `META-INF/coherence/security-config.xml`; the runtime unions all of them into a single allowlist. This means multi-module applications do not need a single curated file - each module that sends wire payload classes ships its own generated resource. A class that is not listed in any generated file and is not added via `coherence.serialization.allowed` is rejected at the gate. [NOTE] ==== "Wire payload classes" means more than the obvious entry processors, filters, aggregators, and POF data types. Any class whose state crosses a Coherence wire boundary is subject to the allowlist gate, including classes captured by the closure of a serialized lambda. If your code writes `cache.invokeAll(filter, entry -> doSomething(entry, helper))` and `helper` is an instance of `com.acme.PricingHelper`, then `PricingHelper` is part of the lambda's serialized state and must be allowlisted on the receiver. The typical fix is to annotate the helper class with `@Remote.Allowed` or apply a package-level annotation. After upgrade, the most common `security-config-missing` rejections come from such capturer classes rather than from obvious payload types. ==== == Lambda Serialization Coherence serializes Java lambdas in one of two representations. STATIC lambda serialization sends a symbolic reference to a compiled lambda class that already exists on both sender and receiver; the receiver resolves it by name. DYNAMIC lambda serialization synthesizes bytecode on the sender and ships it to the receiver, which defines the class on arrival. DYNAMIC is Coherence's default and recommended mode because it avoids lockstep class-version coordination: a new lambda can flow from an upgraded sender to an older receiver without both sides first deploying the same compiled class. The DYNAMIC lambda gate denies wire-supplied bytecode from unauthenticated peers before class definition when `coherence.remote.dynamic.unauthenticated=deny` is explicitly set. The CE 26.10 default is `allow` in both security modes. Applications have two supported deployment paths: * Retain DYNAMIC lambda serialization with the current default or explicit `-Dcoherence.remote.dynamic.unauthenticated=allow`. This preserves the rolling-upgrade ergonomics that make it Coherence's default. It is a long-term option, not deprecated. This switch does not establish per-subject permission to execute code. Monitor rejection telemetry on the `site=lambda` counter to confirm traffic shape after enabling. * Switch to STATIC by setting `-Dcoherence.lambdas=static`. This avoids wire-supplied bytecode entirely but couples every sender and receiver to the same compiled lambda class: every producer and consumer must redeploy the same code version before traffic flows. This is appropriate only for deployments that already manage lockstep rolling upgrades; it trades upgrade ergonomics for wire-bytecode minimisation. == Remote Executable Objects Remote executable objects are application classes whose methods Coherence may invoke on behalf of a remote request. === What Counts As An Executable * Processors — `InvocableMap.EntryProcessor` implementations that run against cache entries. * Aggregators — `InvocableMap.EntryAggregator` implementations that compute server-side aggregate results. * Invocables — `Invocable` implementations submitted to the `InvocationService`. * Filters — `Filter` implementations evaluated against cache, query, topic, REST, or gRPC data. * Value extractors — `ValueExtractor` implementations that read values during server-side filtering, sorting, indexing, or projection. * Comparators — `Comparator` implementations used for server-side ordering. * Triggers — `MapTrigger` implementations installed to run during cache mutation. * Server-side event interceptors — `EventInterceptor` implementations that run in the cluster when matching events fire. * `coherence-concurrent` tasks / callables / runnables — executable work submitted through the Coherence concurrent APIs. Client-installed `MapListener`s are routed, not invoked, on the server; they do not require `@Remote.Executable`. A listener that additionally implements `MapTrigger` is governed by the `MapTrigger` rule. === The Annotation [source,java] ---- @Remote.Executable public class PriceAdjustmentProcessor implements InvocableMap.EntryProcessor { ... } ---- `@Remote.Executable` has CLASS retention and is class-level only. It implies `@Remote.Allowed`: a class that is executable is also a class the receiver may deserialize. The build-time generator emits a single entry in `security-config.xml` with `source="@Remote.Executable"` and `executable="true"`. At runtime, the security-config reader adds that class to `executableFqns()` as well as `allowedFqns()`. === XML Alternative You can also author the same classification by hand in `META-INF/coherence/security-config.xml`: [source,xml] ---- ---- Runtime merge semantics are the same as generated allowlist entries: Coherence unions every visible classpath source into one view. See <> for the full merge story. === InvocationService When security hardening disables the Coherence*Extend `InvocationService` proxy by default, operators who intentionally expose remote invocation can set `coherence.invocation.enabled=true` or configure `true`. The system property overrides operational configuration when both are present. When the proxy is enabled, every remote `Invocable` submitted through Extend is checked by `RemoteExecutablePolicy` before `InvocationService.query(...)` executes it and before any delegated `PriorityTask` scheduling, timeout, or cancellation callback can run. The invocable class must carry `@Remote.Executable` or appear with `executable="true"` in merged `security-config.xml`. Cluster-internal `Invocable` dispatch is not gated by the Extend proxy flag. If the proxy is disabled, the server log records `InvocationService proxy is DISABLED in mode by policy` once at proxy startup, while the client receives the same generic service-unavailable shape used for an unbound Extend receiver. The wire response intentionally does not name the policy decision. See the Coherence security guide section "Securing Extend Client Connections" for Extend authentication setup. === MethodInvocationProcessor / ScriptProcessor `MethodInvocationProcessor` and `ScriptProcessor` are built-in `EntryProcessor` implementations that execute behavior described by their serialized state. Both classes are `final`, annotated with `@Remote.Executable`, and checked by `RemoteExecutablePolicy` before their dynamic-remote gates run. The dynamic-remote mode gate allows both processors by default in both security modes. Explicit `coherence.remote.dynamic.unauthenticated=deny` rejects them before execution. Executable classification and hard-floor deny-list checks remain in force. The same property therefore controls unauthenticated DYNAMIC lambda bytecode, reflective method invocation through `MethodInvocationProcessor`, and script evaluation through `ScriptProcessor`. `MethodInvocationProcessor` also applies the lambda bytecode deny-list before reflection. The supplier class, the runtime target class, and the `targetClass#methodName` pair are each checked against `META-INF/coherence/lambda-bytecode-denylist.txt` and extension resources such as `META-INF/coherence/lambda-bytecode-denylist.d/*.txt`. Entries such as `java.lang.Runtime` and `java.lang.System#exit` are hard floors in every security mode. When a processor targets an absent cache entry, the supplier class is checked first, but the supplier is not executed and the entry is not populated until the dynamic-remote mode gate allows MIP. `ScriptProcessor` runs scripts in the singleton Graal `Context` with explicit host access. `allowAllAccess(true)` is not enabled. Host class lookup rejects deny-listed classes, and loadable deny-listed classes are also blocked through Graal `HostAccess.denyAccess(...)`. These HostAccess checks are hard floors in every mode. === MapTrigger Remote Install and Removal Remote `MapTrigger` installation and removal through Coherence*Extend are checked before the listener is registered with or removed from the storage tier. The trigger class must carry `@Remote.Executable` or appear with `executable="true"` in merged `META-INF/coherence/security-config.xml`; unannotated trigger classes are rejected in hardened security mode and shadowed in compatibility security mode. After the class policy check, dynamic trigger classes use the same mode gate as DYNAMIC lambdas, `MethodInvocationProcessor`, and `ScriptProcessor`. This gate defaults to allow in both security modes; explicit `coherence.remote.dynamic.unauthenticated=deny` rejects dynamic installation. For dynamic classes, allowing this gate permits installation even if class classification rejects the generated class. Named trigger classes remain subject to executable classification. Compatibility mode emits `result=would_reject` shadow telemetry for classification failures. Classes declared in cache configuration remain server-trusted and are not blocked at startup. Coherence logs a WARN for each unannotated declared `MapTrigger` or `EventInterceptor` class, de-duplicated by class name and kind, so operators can classify those classes before allowing equivalent remote installs. CE 26.10 has no remote-supplied `EventInterceptor` installation path covered by this gate; interceptor coverage here is the startup advisory. === Topic Subscriber Filter / Extractor Install Remote topic subscriber group and subscriber creation through Coherence*Extend and gRPC are checked before the subscriber metadata is installed on the storage tier. Subscriber filters use `OperationReason.EVALUATE_FILTER`; subscriber extractors and converters use `OperationReason.EXTRACT`; both publish `role=TOPICS` in telemetry. The filter or extractor class must carry `@Remote.Executable` or appear with `executable="true"` in merged `META-INF/coherence/security-config.xml`. Unannotated classes are rejected in hardened security mode and shadowed in compatibility security mode. Coherence also walks shipped topic subscriber containers that can hide nested executable state before install and again during persisted replay. This includes filter wrappers such as `ArrayFilter`, `ExtractorFilter`, `LimitFilter`, and `FilterWrapper`; extractor wrappers such as `ConditionalExtractor`, `CollectionExtractor`, `KeyExtractor`, fragment extractors, and transaction wrappers; comparator wrappers such as `ExtractorComparator`, `ChainedComparator`, and `SafeComparator`; and script-backed subscriber filters and extractors. Each nested class is checked with topic subscriber telemetry and replay-drift handling. Customer-defined composites with opaque fields are not inspected reflectively; annotate or allowlist the composite class and any nested classes exposed through the built-in containers. Dynamic or lambda-shaped subscriber filter and extractor classes use the same dynamic remote mode gate as DYNAMIC lambdas, `MethodInvocationProcessor`, `ScriptProcessor`, and remote `MapTrigger` installs. This gate defaults to allow in both security modes; explicit `coherence.remote.dynamic.unauthenticated=deny` rejects dynamic subscriber metadata. This permission can admit dynamic classes that fail class classification; named classes remain subject to executable classification, with `result=would_reject` telemetry in compatibility mode. Persisted subscriber metadata is rechecked when topic subscription metadata is activated from persistence. The `coherence.topics.persisted.policy-drift` property controls how Coherence handles metadata whose class was valid at install time but is no longer executable at replay time: * `reject` refuses the replayed subscription and emits `sub_reason=replay_drift`. * `warn-allow` emits a deduped WARN for the drifted class and allows replay. The default is `reject` in hardened security mode and `warn-allow` in compatibility security mode. An explicit `reject` value fails closed in both security modes. Operators should use `warn-allow` only as a migration bridge while annotating the affected filter/extractor classes, regenerating `security-config.xml`, and redeploying the owning artifacts. Cache-config-declared topic subscriber advisory logging is deferred. The topic scheme bootstrap paths currently do not materialize the declared filter or extractor instances early enough to emit a startup advisory without moving topic subscription realization. Remote topic subscriber installs and persisted replay rechecks are still enforced as described above. === Cache Data-Plane Processor / Aggregator / Filter / Extractor / Comparator Install Remote `NamedCache` data-plane operations through Coherence*Extend, REST, and gRPC are checked after the wire payload or registry alias resolves to a concrete executable object and before dispatch to the storage tier. Entry processors use `OperationReason.PROCESS_ENTRY`; aggregators use `AGGREGATE`; filters use `EVALUATE_FILTER`; value extractors use `EXTRACT`; comparators use `COMPARE`. Telemetry publishes `role=EXTEND_PROXY`, `role=REST`, or `role=GRPC` depending on the transport. The processor, aggregator, filter, extractor, or comparator class must carry `@Remote.Executable` or appear with `executable="true"` in merged `META-INF/coherence/security-config.xml`. Unannotated classes are rejected in hardened security mode and shadowed in compatibility security mode. Dynamic, lambda-shaped, or script-backed cache data-plane executables also pass through the dynamic remote mode gate: it defaults to allow in both security modes, while explicit `coherence.remote.dynamic.unauthenticated=deny` rejects them. The allow setting can admit dynamic classes that fail class classification. Named executable classification remains enforced in hardened mode and emits `result=would_reject` shadow telemetry in compatibility mode. The shared script gate covers `ScriptProcessor`, `ScriptFilter`, `ScriptValueExtractor`, and `ScriptAggregator`; the check runs at remote install time where the transport exposes the script object and again directly before script execution or delegate creation. Coherence also walks the built-in composite containers that can hide nested executables. This includes processor wrappers such as `CompositeProcessor`, `ConditionalProcessor`, asynchronous / priority processor wrappers, `ExtractorProcessor`, `PropertyProcessor`, and `UpdaterProcessor`; aggregator wrappers such as `CompositeAggregator`, `GroupAggregator`, `ReducerAggregator`, asynchronous / priority aggregators, and `TopNAggregator`; filter wrappers such as `ArrayFilter`, `ExtractorFilter`, `LimitFilter`, `MapEventTransformerFilter`, `WrapperQueryRecorderFilter`, and transaction `FilterWrapper`; extractor wrappers such as `ConditionalExtractor`, `CollectionExtractor`, `DeserializationAccelerator`, transaction `ExtractorWrapper`, and the fragment extractors; and comparator wrappers such as `ExtractorComparator`, `ChainedComparator`, and `SafeComparator`. Comparator payloads that are also `ValueExtractor` instances, such as `KeyExtractor`, `ChainedExtractor`, `ConditionalExtractor`, `CollectionExtractor`, and other `AbstractExtractor` subclasses, are routed through the extractor cascade as well as the comparator gate; this also applies when they are wrapped by `SafeComparator`, `InverseComparator`, `EntryComparator`, or transaction `ComparatorWrapper`. `ValueManipulator` and `ValueUpdater` instances nested inside property and updater processors are treated as executable mutation state under the processor install check. Each cascaded class records its own policy telemetry event. Customer-defined composites with opaque fields are not inspected reflectively; annotate or allowlist the composite class and any nested classes that are exposed through the built-in containers above. REST default processor and aggregator factories preflight the configured class before invoking its constructor. REST resources still check the produced processor or aggregator instance before dispatch so nested cascade checks are not lost. Custom REST `ProcessorFactory` and `AggregatorFactory` implementations remain a trusted compatibility boundary: Coherence cannot preflight arbitrary factory side effects before the custom factory chooses to construct or return an executable object. To migrate an application, identify each rejected or would-reject class, annotate the supported remote executable type with `@Remote.Executable` or add an `executable="true"` entry to generated security config, redeploy the owning jar, and rerun the workload with `-Dcoherence.security.mode=hardened`. For REST registry entries, classify the registry factory output. For CohQL queries and gRPC bytes-based requests, classify the concrete filter, comparator, extractor, aggregator, or processor class that is sent by the client. === Coherence Concurrent Executor Coherence Concurrent executor submissions are checked before task execution, scheduling, executor selection, result collection, completion callbacks, and subscriber callbacks. Submitted `Task`, `Callable`, `Runnable`, `ExecutionStrategy`, `Task.Collector`, completion predicate, completion runnable, and `Task.Subscriber` classes must carry `@Remote.Executable` or appear with `executable="true"` in merged `META-INF/coherence/security-config.xml`. Enforcement telemetry uses `OperationReason.CONCURRENT_TASK` and `role=CONCURRENT`. The concurrent gate also walks shipped executor wrappers that can carry nested callback state. `CallableTask`, `RunnableTask`, `RunnableWithResultTask`, scheduled task wrappers, and `CronTask` gate their nested callable, runnable, or task state after the task-manager subject is available. `CronTask` gates both the original and current task. `StandardExecutionStrategy` gates its candidate predicate, and `ConditionalCollector` gates its predicate and nested collector. Concurrent cache-processor wrappers such as `LocalOnlyProcessor` and `ClusteredTaskManager.ChainedProcessor` gate their nested processors before they can invoke them. Built-in predicate wrappers such as `Predicates.not(...)` and typed composites such as `Predicates.and(...)` are unwrapped and their nested predicates are checked before use. Value-style predicate wrappers gate nested payloads that could otherwise dispatch application code through `equals(...)` or `toString()`: unsafe `Predicates.equalTo(...)` values, non-built-in `Predicates.has(...)` registration options, and non-JDK `Predicates.onException(...)` throwables are checked before the predicate can run. Common scalar values, built-in registration options, exact product `Member` registration metadata, and bootstrap `java.*` throwables remain data-only. Crafted or non-product nested members inside `Predicates.has(Member.of(...))` are checked before comparison. `ValueTask` is gated as a task, but its value is result data and is not treated as an executable callback merely because it implements a callback interface. Task properties, task options, execution plans, retained results, and tracing context are data-only for this gate. Local member submissions capture the current caller `Subject` as advisory gate context and serialize it with `ClusteredTaskManager` at POF index `20` and as a final appended ExternalizableLite field. Remote-client submissions through remote cache services, including `RemoteExecutor` client paths, serialize a `null` subject; client JVM subjects are not authorization-bearing. The subject is not used with `Subject.doAs(...)` and does not grant per-subject execution permission. The `ConcurrentProxy` Extend service uses the existing `coherence.concurrent.extend.enabled` property with a security-mode-aware default: enabled in compatibility security mode and disabled in hardened security mode. A disabled proxy logs the resolved runtime mode, security mode, property/config source, and enabled state at startup, plus a startup WARN in hardened mode naming the override property. Clients still see a generic service-unavailable shape rather than policy-specific wire text. Cluster-internal executor use is independent of this proxy switch. === Functional-Interface Lambda Targets A lambda target is the functional interface named by a serialized lambda. For example, a lambda sent as a `Remote.Function` declares `com.tangosol.util.function.Remote$Function` as its functional-interface target; the generated lambda class is a separate implementation detail. Coherence pre-checks that functional interface before it runs the lambda bytecode scanner. The target check answers "may this receiver materialize a lambda for this API surface?" with a constant-time lookup in merged `security-config.xml`. The bytecode scanner still answers the separate question "does this wire-supplied bytecode contain denied references or unsupported shape?" Both checks apply; a target allowlist entry does not disable bytecode scanning. To allow a custom serializable functional interface as a remote lambda target, annotate the interface with `@Remote.Executable` and ensure it also carries `@FunctionalInterface`: [source,java] ---- @FunctionalInterface @Remote.Executable public interface PriceFunction extends java.io.Serializable { BigDecimal apply(Order order); } ---- The build-time scanner emits: [source,xml] ---- ---- You can also author that XML entry by hand. In that case the entry must name the functional interface, not the generated lambda implementation class. Unannotated functional interfaces are denied in hardened security mode before lambda bytecode is materialized. In compatibility security mode, allowlist and lambda-target enforcement are disabled; use `-Dcoherence.security.mode=hardened` in pre-production to validate lambda-target readiness before migration. The supported migration is to annotate the functional interface or add a manual `lambda-target="true"` entry to the artifact that owns it. When the target pre-check rejects a lambda, the reason is `REASON_LAMBDA_TARGET_NOT_ALLOWED` and the operator-visible reason string is `lambda-target-not-allowed`. Applications that previously relied on custom serializable functional interfaces should rebuild those artifacts with `@Remote.Executable`, verify the generated `security-config.xml`, and redeploy receivers before sending lambdas that target the new interface. === Migration `@Remote.Executable` covers both of these cases in CE 26.10: * *Functional interfaces* annotated with `@Remote.Executable` are enforced when security mode is hardened. Lambdas that target an unannotated functional interface are rejected with `lambda-target-not-allowed` before bytecode is materialized. Migrate by annotating every custom serializable functional interface used as a remote lambda target and rebuilding the artifact that owns it. See <>. * *Concrete executable classes* (entry processors, aggregators, invocables, filters, value extractors, comparators, triggers, server-side event interceptors, `coherence-concurrent` tasks) are classified by the build and enforced at the remote execution sinks described above. Classify executable classes now, regenerate `security-config.xml`, and use `coh.executable.policy_check{result=would_reject}` shadow telemetry to drive the migration; see <>. In both cases the build pipeline is the same. Rebuild every artifact that owns an executable class or functional interface; the `security-config-maven-plugin` and the Gradle `securityConfig` task pick up the annotations automatically. === Why `@Remote.Executable` And Not Just `@Remote.Allowed`? `@Remote.Allowed` answers "can this class be deserialised on the server?"; `@Remote.Executable` answers "may the server invoke its methods on behalf of a remote request?" The latter is a strict subset and deserves an explicit opt-in so that adding a class to the deserialisation allowlist does not silently grant it execution privileges. == Runtime Property Reference [cols="2,1,2,4,2", options="header"] |=== |Property |Type |Default |Description |See |`coherence.mode` |enum: `eval`, `dev`, `development`, `prod`, `production` |`dev` |Selects the released runtime/license mode. It does not enable the compatibility-sensitive SER-01 hardening gates. Blank, unknown, `legacy`, and `legacy-compatibility` values fail fast. |<> |`coherence.security.mode` |enum: `compatibility`, `hardened` |`hardened` (CE 26.10) |Selects the security hardening profile. Explicit `compatibility` preserves compatibility behavior and shadow telemetry where available; unset or `hardened` enables the compatibility-sensitive hardening gates. The removed pre-release `coherence.security.hardened` property is not an alias. |<> |`coherence.remote.dynamic.unauthenticated` |enum: `allow`, `deny` |`allow` in both security modes (CE 26.10) |Controls whether unauthenticated wire-arriving DYNAMIC lambda bytecode, reflective `MethodInvocationProcessor` execution, and `ScriptProcessor` evaluation are accepted under the shared dynamic-code policy. This setting does not authenticate the caller or grant subject-based execution permission. The property is resolved through Coherence `Config`, so the `COHERENCE_REMOTE_DYNAMIC_UNAUTHENTICATED` environment variable alias is also honoured. Blank values are treated as unset; invalid values log an error and fall back to the default. Explicit `deny` remains supported in both modes. Does not bypass named executable classification, functional-interface target allowlisting, the MIP deny-list, or Graal HostAccess hardening. |<>, <> |`coherence.invocation.enabled` |boolean |operational config value unless explicitly set |Overrides whether the Coherence*Extend `InvocationService` proxy is exposed. When unset, operational config `` is used. This property does not bypass `@Remote.Executable` enforcement when the proxy is enabled. |<> |`coherence.concurrent.extend.enabled` |boolean |security mode `compatibility`: `true`; security mode `hardened`: `false` |Overrides whether the Coherence Concurrent `ConcurrentProxy` Extend service is exposed. When unset, the shipped `ConcurrentProxy` autostart marker resolves to the security-mode-aware default. An explicit cache-config autostart override without the shipped system-property marker wins when the property is absent. This property does not bypass `@Remote.Executable` enforcement when the proxy is enabled. |<> |`coherence.topics.persisted.policy-drift` |enum: `reject`, `warn-allow` |security mode `compatibility`: `warn-allow`; security mode `hardened`: `reject` |Controls persisted topic subscriber filter/extractor policy drift on replay. `reject` refuses persisted metadata whose class is no longer executable; `warn-allow` logs a deduped WARN and allows replay for migration. The property is resolved through Coherence `Config`, so the `COHERENCE_TOPICS_PERSISTED_POLICY_DRIFT` environment variable alias is also honoured. |<> |`coherence.serialization.allowed` |class-fqn-list |empty |Adds exact class names or package wildcards, such as `com.acme.*`, to the runtime serialization allowlist. It supplements generated security config and does not bypass deny-listed gadget classes. |<> |`coherence.lambda.bytecode.allow` |class-fqn-list |empty |Incident-response override for specific class references in the lambda bytecode deny-list. Structural bytecode rules such as native methods still apply. |<> |`coherence.security-config.skip` |boolean |`false` |Maven plugin switch that skips `security-config.xml` generation for the current build. |<> |`coherence.lambdas` |enum: `dynamic`, `static` |`dynamic` |Lambda serialization mode that has shipped in prior Coherence releases. Set to `static` when migrating production applications away from DYNAMIC lambda bytecode at the cost of lockstep client/server code-version coordination. |<> |=== == Error Message Reference SER-01 rejection reasons fall into four families. When debugging a failing deployment, identify the family first, then locate the specific row in the table below. * *Missing allowlist* — `security-config-missing`, `ClassIdentity package is not allowed`, `unknown user type`, `Deserialization of class ... was rejected`. The class is not known to the receiver; fix by annotating and regenerating, or by registering with POF. * *Remote execution policy* — `dynamic-remote-denied-by-mode`, `method-invocation-denied-by-mode`, `script-eval-denied-by-mode`, `lambda-target-not-allowed`, `class-name-on-denylist`, `method-on-denylist`. A lambda-shaped payload, MIP reflection request, or script request was refused by mode policy, target-interface policy, or by the explicit deny-list. * *Bytecode shape* — `bytecode-references-gadget`, `native-method-declared`, `dynamic-class-forname`, `invalid-bytecode`. Wire-supplied bytecode failed structural or deny-list checks. * *Dynamic class definition* — `Invalid class definition: missing-magic`, `invalid-version`, `invalid-shape`, `trailing-bytes`. The class-definition payload did not pass file-format validation. [cols="2,1,3,3", options="header"] |=== |Reason or message shape |Subsystem |Typical cause |Fix |`Lambda bytecode rejected: site=%s, reason=security-config-missing, denied=%s` |Allowlist |An application class arrived as a class-name payload but is not in generated security config or the manual allowlist. |Annotate the producing class with `@Remote.Allowed` or `@PortableType`, run the Maven or Gradle generator, and package the generated config. |`Lambda bytecode rejected: site=LAMBDA, reason=dynamic-remote-denied-by-mode` |Lambda mode gate |A DYNAMIC lambda arrived while `coherence.remote.dynamic.unauthenticated` resolved to `deny`. |Switch to STATIC lambda serialization or explicitly set `-Dcoherence.remote.dynamic.unauthenticated=allow`. |`method-invocation-denied-by-mode` |MIP mode gate |A `MethodInvocationProcessor` arrived while `coherence.remote.dynamic.unauthenticated` resolved to `deny`. |Set `-Dcoherence.remote.dynamic.unauthenticated=allow` only where reflective method invocation is intentionally exposed, or replace the call with a purpose-built `@Remote.Executable` processor. |`script-eval-denied-by-mode` |Script mode gate |A `ScriptProcessor` arrived while `coherence.remote.dynamic.unauthenticated` resolved to `deny`. |Set `-Dcoherence.remote.dynamic.unauthenticated=allow` only where remote script evaluation is intentionally exposed, or replace the script with a purpose-built `@Remote.Executable` processor. |`Lambda bytecode rejected: site=%s, reason=lambda-target-not-allowed, denied=%s` |Lambda target |A serialized lambda targets a functional interface that is not marked as a lambda target in merged security config. The denied value is the functional interface FQN, not the lambda capture class. |Annotate the functional interface with `@Remote.Executable` and regenerate security config, or add a manual `` entry for that interface. |`reason=bytecode-references-gadget` |Bytecode scan |Generated or supplied lambda bytecode references a deny-listed gadget class or method. |Remove the reference or use a safe server-side class. Use `coherence.lambda.bytecode.allow` only for emergency class-reference overrides. |`reason=class-name-on-denylist` |Bytecode scan / MIP reflection |A static lambda, class-name payload, MIP supplier, or MIP target value names a deny-listed class. |Remove the deny-listed class from the payload path or reflective MIP target. |`reason=method-on-denylist` |MIP reflection |A `MethodInvocationProcessor` targets a deny-listed `class#method` pair. |Remove the reflective method call and use a purpose-built safe server-side processor. |Graal `PolyglotException` or script error naming a denied Java type |Script HostAccess |A script attempted `Java.type(...)` or guest-side host access for a deny-listed class such as `java.lang.Runtime` or `java.lang.ProcessBuilder`. |Remove the host access from the script. `coherence.remote.dynamic.unauthenticated` does not bypass the HostAccess hard floor. |`reason=native-method-declared` |Bytecode scan |Wire-supplied bytecode declares a native method. |Do not send dynamic bytecode that declares native methods. |`reason=dynamic-class-forname` |Bytecode scan |Wire-supplied bytecode calls `Class.forName` with a non-constant value. |Resolve the class server-side or remove dynamic class loading from the lambda. |`reason=invalid-bytecode` |Bytecode scan / Class definition |The supplied class bytes are empty, malformed, or cannot be parsed. |Regenerate the payload with a supported Coherence version and check for transport corruption. |`Invalid class definition: missing-magic` |Class definition |A class-definition payload does not start with the Java class-file magic number. |Reject the payload source; resend only valid class files. |`Invalid class definition: invalid-version` |Class definition |A class-definition payload uses an unsupported class-file version. |Build the producing artifact for a Java version supported by the receiving member. |`Invalid class definition: invalid-shape` |Class definition |A class-definition payload has an unexpected constant-pool, method, or class shape. |Regenerate the payload and verify client/server version compatibility. |`Invalid class definition: trailing-bytes` |Class definition |A class-definition payload contains bytes after the parsed class file. |Reject the payload source; resend a clean class definition. |`ClassIdentity package is not allowed: %s` |Class identity / Allowlist |A dynamic class identity names a package not present in generated security config or the runtime allowlist. |Annotate the producing type or package and regenerate security config. |`unknown user type: %s. Unregistered POF type ... strict mode` |POF strict / Allowlist |A POF payload names a user type that is not registered in the receiver's POF configuration. |Register the class in ``, annotate it with `@PortableType`, or add `@Remote.Allowed` and regenerate security config. |`Deserialization of class %s was rejected` |JEP-290 filter |The JEP-290 filter rejected a Java-serialization class in a fail-closed path. |Use a registered safe type or add the application type to generated security config or `coherence.serialization.allowed`. |`coh.executable.policy_check{result=would_reject, class=, reason=, role=}` |Executable policy (shadow) |Compatibility security mode classified a remote executable object that would be rejected by `DefaultRemoteExecutablePolicy.enforce(...)` in hardened security mode. |Treat each tuple as migration work: annotate the class with `@Remote.Executable`, regenerate `security-config.xml`, or add a manual `executable="true"` entry before setting `-Dcoherence.security.mode=hardened`. |=== == Telemetry Serialization gate decisions are visible through `SerializationGates` MBeans. ObjectNames include the route, gate, result, reason, and mode tuple, for example: [source,text] ---- Coherence:type=SerializationGates,route=GRPC,gate=filter,result=rejected,reason=serialization-allowlist-rejected,mode=prod ---- Lambda bytecode checks also maintain the in-process counter key: [source,text] ---- coh.lambda.bytecode_check{result=rejected,reason=dynamic-remote-denied-by-mode,site=lambda} ---- Use these counters to identify which boundary is rejecting traffic before loosening any runtime property. Live remote executable enforcement checks in hardened security mode maintain: [source,text] ---- coh.executable.policy_check{reason=, role=, result=allowed|rejected, mode=} ---- For Extend `InvocationService`, the tuple uses `reason=INVOKE` and `role=EXTEND_PROXY`. `MethodInvocationProcessor` and `ScriptProcessor` use `reason=PROCESS_ENTRY` and `reason=SCRIPT_EVAL`, respectively. Remote `MapTrigger` installation uses `reason=TRIGGER` and `role=EXTEND_PROXY`. Topic subscriber filter/extractor installation and persisted replay use `reason=EVALUATE_FILTER|EXTRACT` and `role=TOPICS`. Cache data-plane processor, aggregator, filter, extractor, and comparator installs through Extend, REST, and gRPC use `reason=PROCESS_ENTRY|AGGREGATE|EVALUATE_FILTER|EXTRACT|COMPARE` and `role=EXTEND_PROXY|REST|GRPC`. Coherence Concurrent executor task and callback installs use `reason=CONCURRENT_TASK` and `role=CONCURRENT`. Live tuples may also include `sub_reason=policy|mode_gate|denylist|replay_drift`; the tag is additive and callers must not assume a closed set. Successful policy checks use `sub_reason=policy`; mode and deny-list sub-reasons are emitted only for refusals. Topic replay under `warn-allow` records `result=allowed,sub_reason=replay_drift` to make migration drift visible. Remote executable policy shadow checks in compatibility security mode maintain: [source,text] ---- coh.executable.policy_check{result=would_reject, class=, reason=, role=} ---- This counter is emitted only by `DefaultRemoteExecutablePolicy.enforce(...)`. It tells you which remote executable classes would be rejected after migration to hardened security mode. The `mode` tag is intentionally absent from these shadow tuples, while `class` is included to make per-class dry-run diagnostics possible. It does not measure serialization allowlist or lambda bytecode readiness; validate those gates by running the workload under `-Dcoherence.security.mode=hardened` in a non-production environment. Topic subscriber replay records one `would_reject` tuple per compatibility shadow; install-time dynamic shadows can record policy and mode-gate tuples because replay has no dynamic mode-gate analog.