v2.1.0
Tier 7, complete. Four claims that were previously compiled, argued or asserted are now things a build
proves: the REST engine runs on every target kdrant-core does and the shared contract runs from a
native binary, a scoped token is a credential the client knows about, a native image is built and made
to search, and every published POM names the platforms that module actually has.
Added
kdrant-transport-restis multiplatform (M38).kdrant-corehad compiled for eight
Kotlin/Native targets since2.0.0, and not one of them could send a request: the engine lived in
src/mainand was Ktor CIO on the JVM, so an iOS build got the models, the query DSL and the filter
builders with nothing to put them on the wire.RestQdrantTransportis incommonMainnow and the
engine is chosen per target — CIO on the JVM, unchanged; Darwin on iOS and macOS; Curl on Linux;
WinHttp on Windows. An iOS or Linux consumer depends on the samekdrant-transport-restcoordinate a
JVM one does.
Two engine choices have consequences worth knowing before a stack trace tells you: Darwin is
NSURLSession and inherits App Transport Security, so a plaintexthttp://Qdrant is refused by the
platform before Kdrant sees the request, and Curl links against the system libcurl, which a slim
container image may not have. Kotlin/JS stays out for the reason already written inkdrant-core.kdrant-testkitis multiplatform, which is what makes the above more than a compilation
exercise. The behavioural contract both engines are held to moved tocommonMainas
QdrantClientContractSuite, which knows no test framework; the JUnit and Testcontainers wrapper
stays on the JVM and still declares one test per behaviour. CI runs the same suite from alinuxX64
and amacosArm64binary against a real Qdrant.- Scoped access (M39).
KdrantConfigtakes abearerTokenbesideapiKey, mutually exclusive
with it: a Qdrant JWT narrowed to read-only, to named collections, or to a payload filter deciding
which points a caller may see at all. Both engines send it —Authorization: Bearerover REST, the
same header as gRPC metadata — because the credential belongs to the config rather than to the wire.
kdrant-testkitsigns one for tests throughQdrantJwt; minting tokens for a running system stays
Qdrant's job. KdrantException.Forbidden, a subclass ofUnauthorized, for HTTP 403 and gRPC
PERMISSION_DENIED. A read-only token refused on a write is a different fact from a missing key, and
only one of them is worth retrying. Being a subclass keeps an existing
catch (e: KdrantException.Unauthorized)catching it and keeps awhenover the sealed hierarchy
exhaustive.kdrant-otel(M41), a new module: one OpenTelemetry client span per operation, on the transport
seam, so one implementation covers both engines and a third would inherit it. Attributes follow
OpenTelemetry's database conventions rather than an invented vocabulary. No payload value, vector or
filter reaches an attribute, and a failed span carries the exception type rather than the server's
message, because Qdrant quotes the request back in its errors. It depends on the OpenTelemetry API,
never the SDK, so the exporter stays the consumer's.kdrant-migrate(M42), a new module:migrateCollection(from, to, alias)copies a collection
into one with a different vector size, verifies the result, and moves an alias so readers cross in one
step. It resumes from a cursor after an interruption rather than starting over, and the alias moves
only after the counts match and a sample of queries returns the same neighbours from both collections
above a stated recall. A failed check throwsMigrationVerificationFailedwith the numbers in it and
leaves the alias where it was: a tool that swaps because the copy finished without throwing is a tool
that will one day point production at an empty collection.decorateTransportonKdrant(...)andKdrantGrpc(...), the hookkdrant-otelneeds and the
place a caching or rate-limiting decorator of your own goes.ScrollBuilder.startAt, the id cursor a resumable job over a collection needs. It came out of M42
and is worth more than the migration.- A GraalVM native image (M40).
example-native-imageis compiled with--no-fallbackin CI and made
to answer a real search against a real Qdrant, so the README's claim is a job that fails the day a
dependency starts reflecting rather than a sentence in a table. Measured: 37 ms from process start
to first search, in a 42 MB static binary. The comparison table quotes that instead of the word
friendly.
Building it settled the claim in the second of the two ways it could go. One thing does reflect: Ktor
resolves a serializer from the response type at run time, and kotlinx-serialization answers by looking
for the compiler-generated$$serializer, which a native image cannot find unless the class is
registered.kdrant-transport-restnow ships that registration in its own jar, generated from the
classes on the classpath rather than written by hand, so a model added tomorrow is in the file the
same day and a consumer building a native image writes nothing.
Changed
- Every published module's POM description now names the platforms that module actually has, and
verifyPublishedDescriptionfails the build when one stops being true. The description Maven Central
serves forkdrant-core:2.0.0ends with "Core module for RAG and embedding search on the JVM",
which klibs.io was about to put next to badges reading iOS, macOS, Linux and Windows generated from
the same artifact's own tooling metadata. It did not go stale by accident: everything else moved at
2.0.0and the POM did not, because nothing reads it. The em dashes went with the correction. - A credential no longer requires TLS when the host is a loopback address. A key sent in the clear
across a network is a key someone else has; a request to127.0.0.1never reaches a network. This
only accepts configurations that were previously rejected, and it is what makes a local Qdrant with
an API key work without a certificate. - Three signatures changed shape, and all three need a recompile rather than a jar swap.
Kdrant(...)andKdrantGrpc(...)gaineddecorateTransport, andKdrantConfiggained
bearerToken. Every parameter is optional and every2.0.0call site compiles unchanged, but a
default parameter changes the signature Kotlin emits, so an application compiled against2.0.0
that swaps in the2.1.0jar without rebuilding will not find them. This is the case
STABILITY.md already describes for data classes,
now stated for functions and constructors too.git diff v2.0.0 v2.1.0 -- '*/api/*.api'shows the
seven removed lines, and nothing else was removed. KdrantException.Unauthorizedisopen, soForbiddencan extend it. Opening a class removes
nothing a caller could use.kdrant-transport-rest's JVM classes are published askdrant-transport-rest-jvm, the same move
kdrant-coremade at2.0.0. A Gradle build resolves the variant from the plain coordinate and
changes nothing; a Maven build namingkdrant-transport-resthas to move to the-jvmone.
Fixed
ScrollRequest.offsetwas documented as the id to start after. It is inclusive, which is what
the paging code has always relied on and what Qdrant returns asnext_page_offset.- The count of operations Qdrant serves over HTTP only was eleven in five places and is fourteen.
It was written into a KDoc once and never counted, and from there it reached the README, this
changelog, the stability policy and the migration guide.grep -o 'restOnly("[a-zA-Z]*")' | sort -u | wc -lsettles it, and the number now comes from that rather than from memory. STABILITY.mdsaid2.0.0broke nothing but the artifact layout. It broke two things: that, and
ScrollRequest/SearchRequestgaining ashardKeyparameter, which changed their generatedcopy
andcomponentN. The upgrade section names both.
Internal
- The release workflow's linked-artifacts step can no longer fail a release. On the
v2.0.0tag it
returned 404 for a digest that does have an attestation and took down a job whose jars were already
published and attested. A metadata step that can undo a successful publish is worth less than the
metadata, so it iscontinue-on-errorwith a per-artifact fallback; the run's log still says which
records were not written. - The same step's artifact list now names
kdrant-transport-rest-jvm, and gainskdrant-otel,
kdrant-migrate-jvmandkdrant-koog, which had been missing since the step shipped. - CI gained three jobs: the client contract from a
linuxX64and amacosArm64binary, and the
GraalVM native image. The two native jobs setKDRANT_QDRANT_REQUIRED, which turns the contract's
skip into a failure, because a job that was meant to run it and silently skipped would report green
for having proven nothing.