Compatibility & dependencies
Released minor versions preserve supported SDK behavior and persisted/wire contracts so applications can upgrade without rewriting their domain code.
However, you should always check the changelog for any breaking changes.
SDK and Runtime upgrades
Section titled “SDK and Runtime upgrades”- A newer Runtime accepts released workflows from older SDK versions.
- A newer SDK works with an older Runtime while the application uses only protocol operations that Runtime supports.
- A new SDK capability that needs a new Runtime operation fails explicitly as unsupported at its first request. It does not silently write a partial or legacy representation.
Upgrade the Runtime first before enabling a newly introduced Model protocol capability. Applications that do not use that capability remain compatible and can upgrade the two components independently within their supported release ranges. Upcasting is application-side SDK behavior; the Runtime neither executes nor replaces application upcasters.
Historical DOCUMENT-only Graph fallback uses the existing Model protocol, with expanded Runtime support for historical head-only metadata. The SDK can be upgraded first without changing its strict-property default. Current reads, writes and reconstructible histories retain their behavior. An older Runtime can still reject historical reads for which it cannot supply that metadata, as it did before the SDK upgrade. The new fallback becomes available after the Runtime update; no application setting or storage migration is required. See Historical Graphs and strict reads.
Compact Model-event reads accept historical MessagePack and binary message records within the same storage block, including compressed blocks. No application-side conversion or storage rewrite is required. This reader support does not remove the Runtime’s responsibility to serve compatible responses to older SDK clients.
The SDK advertises Fluxzero-Supports-Mixed-Model-Event-Blocks: true on every WebSocket connection.
A compatible Runtime can pass mixed blocks directly to this reader; clients without this capability receive
compatible responses prepared by the Runtime. Older runtimes may ignore the optional header.
The SDK uses ZSTD for default WebSocket compression and document serialization and requires a ZSTD-capable Runtime.
Document and legacy message encoding uses bounds-checked Java array operations. Existing MessagePack storage
and transport representations remain unchanged; applications need no migration or JVM-wide MessagePack property.
The upstream MessagePack library is used only as a test reference and is no longer a transitive runtime dependency.
Applications that directly use org.msgpack classes must declare org.msgpack:msgpack-core explicitly.
The SDK also reads historical LZ4 documents using bounds-checked Java compression and decompression, without Unsafe or
native LZ4. SDK connections advertise Fluxzero-Supported-Document-Compression: ZSTD,LZ4,NONE, independently of
outer WebSocket compression. A Runtime supporting this header preserves stored document bytes whenever the client
supports their codec and converts only unsupported formats. No bulk storage migration is needed. Explicit LZ4,
GZIP and NONE WebSocket configurations remain available. These compression defaults are not gated by
fluxzero.defaults.version.
CompressionAlgorithm.LZ4 remains available in Java and Kotlin, with the same historical size-prefixed wire format.
Fluxzero compression IDs remain NONE=0, LZ4=1 and ZSTD=2. The document MessagePack content and format identifier are
unchanged. Direct reads of historical LZ4 blobs therefore remain supported. The LZ4 dependency remains included.
Clients without document capabilities or historical ZSTD support receive size-prefixed LZ4. Explicitly advertised codecs include support for their Fluxzero-framed envelopes; NONE documents use that envelope to identify uncompressed content. For unsupported stored codecs, Runtime selects ZSTD, then LZ4, then NONE from the client’s supported set. Transport compression preferences do not force document conversion. Custom document formats stay opaque.
For a mixed fleet, upgrade Runtime before introducing ZSTD document writes if any connected SDK cannot read ZSTD. Such an SDK needs the new Runtime conversion when reading documents written with ZSTD. An older Runtime cannot supply this new negotiation behavior merely because a newer producer is connected. Explicit document capabilities take precedence. Without them, advertised or selected ZSTD transport identifies older Java SDKs that already read Fluxzero-framed LZ4, ZSTD and NONE documents. Only without this support information does Runtime fall back to size-prefixed LZ4. Document writers retain the established size-prefixed LZ4 or Fluxzero-framed formats; ZSTD documents use the Fluxzero envelope.
Java Version
Section titled “Java Version”Fluxzero SDK requires JDK 25 or higher to compile and run, including its common, test-server and proxy artifacts.
Java and Kotlin projects must target JVM 25 or newer. SDK artifacts are compiled for Java 25, and the SDK build
requires JDK 25.0.3 or newer before compilation to avoid the virtual-thread timer bug
JDK-8370887. Use a maintained Java 25 patch for both Java and Kotlin
applications. The SDK repository pins its development JDK in mise.toml; CI selects the latest Temurin 25 patch.
No preview features are required. Builds on newer JDKs must keep maven.compiler.release=25 and the Kotlin JVM
target at 25. Lombok 1.18.48 or newer is required when compiling on JDK 26 to avoid a runtime dependency from
@SneakyThrows-generated code.
This requirement does not change the Java baseline of the SDK 1.x release line.
Runtime Dependencies
Section titled “Runtime Dependencies”Serialization
Section titled “Serialization”| Dependency | Scope |
|---|---|
com.fasterxml.jackson.core:jackson-databind | runtime |
com.fasterxml.jackson.datatype:jackson-datatype-jsr310 | runtime |
com.fasterxml.jackson.datatype:jackson-datatype-jdk8 | runtime |
at.yawk.lz4:lz4-java | runtime |
com.github.luben:zstd-jni | runtime |
Payload Validation
Section titled “Payload Validation”Fluxzero includes its own SDK-focused Jakarta Validation implementation. Normal payload validation does not require Hibernate Validator, Expression Language, or their runtime dependencies.
| Dependency | Scope |
|---|---|
jakarta.validation:jakarta.validation-api | compile |
Web and Transport
Section titled “Web and Transport”| Dependency | Scope |
|---|---|
io.undertow:undertow-websockets-jsr | runtime |
io.jooby:jooby | runtime |
Optional Integrations
Section titled “Optional Integrations”Spring Support
Section titled “Spring Support”| Dependency | Scope |
|---|---|
org.springframework:spring-context | optional |
org.springframework:spring-test | test |
Kotlin support
Section titled “Kotlin support”Fluxzero provides full support for Kotlin, including:
- Use of data classes, which behave like Java records
- Optional types (e.g.,
String?) instead of@Nullable - Seamless use of Kotlin-specific language features (e.g.,
when,sealed class,companion object) - Support for
Class<T>parameters passed asMyClass::classorMyClass::class.java - Automatic recognition of Kotlin modules during deserialization (see below)
Automatic Jackson integration
Section titled “Automatic Jackson integration”Fluxzero includes Jackson Kotlin Module integration when
available on the classpath. You do not need to manually register the module or use a service loader.
If the jackson-module-kotlin dependency is present, it will be loaded dynamically for JSON (de)serialization.
Kotlin dependencies
Section titled “Kotlin dependencies”| Dependency | Scope |
|---|---|
com.fasterxml.jackson.module:jackson-module-kotlin | optional |
org.jetbrains.kotlin:kotlin-stdlib-jdk8 | optional |
org.jetbrains.kotlin:kotlin-reflect | optional |
org.jetbrains.kotlin:kotlin-test | test |
Test Dependencies
Section titled “Test Dependencies”| Dependency | Scope |
|---|---|
org.junit.jupiter:junit-jupiter-engine | test |
org.junit.platform:junit-platform-launcher | test |
org.mockito:mockito-core | test |
org.jooq:joor | test |
org.hamcrest:hamcrest-library | test |
org.jboss.resteasy:resteasy-undertow | test |
ℹ️ Dependencies marked as
optionalare not required unless you use the corresponding integration. For example, Spring or Kotlin support is automatically detected when available but safely ignored otherwise.
Versioning Policy
Section titled “Versioning Policy”Fluxzero aims to stay up-to-date with its core dependencies. We strive to:
- Use the latest stable versions where possible,
- Avoid breaking backward compatibility for common transitive consumers,
- Update frequently, especially for frameworks like Jackson, Spring, Jakarta Validation, and JUnit.
Asynchronous key-value writes in SDK 2.x
Section titled “Asynchronous key-value writes in SDK 2.x”KeyValueStore.store(...) and delete(...) now return CompletableFuture<Void>; storeIfAbsent(...) returns
CompletableFuture<Boolean>. These methods do not block. This changes their binary signatures; recompile callers
and update custom KeyValueStore implementations. Java callers needing the conditional result use
store.storeIfAbsent(key, value).join(); Kotlin callers can likewise use .join() or await the future in a coroutine.
The ordinary writes use DEFAULT, resolving to STORED in 2.x unless the application explicitly overrides it.
storeIfAbsent always requests the stored Boolean result, independently of the default guarantee.
For a dependency that must be durable before publishing, await store.store(key, value, Guarantee.STORED) first.
The SDK’s data-protection writes and removals retain explicit durable completion. A tracking batch also awaits
registered outgoing write futures before committing position; unrelated background work is outside that scope.
SDK 1.x retains its synchronous key-value API and operation-specific guarantees.
Consumer write completion in SDK 2.x
Section titled “Consumer write completion in SDK 2.x”The consumer option awaitSendAndForgetFutures is renamed to awaitOutgoingWrites in SDK 2.x.
Update explicit Java/Kotlin @Consumer arguments, ConsumerConfiguration builder calls and accessors, then
recompile applications and dependent libraries. The old name has no alias or fallback in 2.x; SDK 1.x keeps it.
For an intentional opt-out, use @Consumer(name = "independent-writes", awaitOutgoingWrites = false)
in either Java or Kotlin. Rebuild annotated handlers too: a previously compiled annotation using the old member
does not configure the new one, so its old opt-out is ignored.
The default remains true: outgoing writes started within a tracked batch must reach their selected delivery
guarantee before its position is committed. This rename does not change delivery guarantees or the separate
awaitAsyncResults option for returned handler results. See Message tracking.
© 2026 Fluxzero