Skip to content

Add koltin multiplatform client SDK with codegen, gradle plugin and templates#4738

Open
FromWau wants to merge 194 commits intoclockworklabs:masterfrom
FromWau:feat/kotlin-sdk
Open

Add koltin multiplatform client SDK with codegen, gradle plugin and templates#4738
FromWau wants to merge 194 commits intoclockworklabs:masterfrom
FromWau:feat/kotlin-sdk

Conversation

@FromWau
Copy link
Copy Markdown

@FromWau FromWau commented Apr 1, 2026

Description of Changes

Adds a Kotlin Multiplatform client SDK for SpacetimeDB with full code generation support, gradle plugin to auto generate bindings, templates, docs, (ai agent) skills and smoketests.

Note: There is an existing PR for a Kotlin SDK (#4472 by @AndroidPoet). We developed independently. That PR provides a SDK without codegen, using raw byte data. So users have to manually encode/decode BSATN bytes and use string-based reducer names. This PR includes: Rust codegen generating typed table handles, reducers, and procedures, a Gradle plugin that auto-generates bindings on build, two project templates, and smoketests.

Rust cli:

  • ktfmt formatting integration
  • Language::Kotlin variant with aliases kt, KT
  • ClientLanguage::Kotlin for template discovery
  • binary file support for template generation (needed for gradle-wrapper.jar)
  • 4 smoketests (unit tests, build validation, server integration)
  • template smoketests covering basic-kt and compose-kt

Codegen (Rust):

  • Typed data classes with BSATN encode/decode for all schema types
  • Sealed interfaces for sum types, enum classes for plain enums
  • Typed table handles with count/all/iter, callbacks, and index properties
  • Typed reducer stubs with per-reducer event callbacks
  • Typed procedure stubs with SdkResult<ReturnType, ProcedureError> results
  • Module.kt entry point with extension properties (conn.db, conn.reducers, conn.procedures)
  • Type-safe query builder with WHERE predicates, comparisons (eq/neq/lt/gt/lte/gte), and semi-joins
  • subscribeToAllTables() and addQuery { qb -> qb.player().where { c -> c.health.gt(51) } }
  • Kotlin keyword escaping (backticks), ByteArray equals/hashCode, variant name collision handling
  • Snapshot tests, ktfmt formatting

SDK runtime:

  • v2.bsatn protocol, Ktor WebSocket transport (user provides HttpClient)
  • KMP: JVM, Android (API 27+), iOS (arm64, x64, simulator-arm64)
  • DbConnection.Builder with: uri, databaseName, token, compression (Brotli/Gzip/None), lightMode, confirmedReads, callbackDispatcher
  • EventContext hierarchy: SubscribeApplied, UnsubscribeApplied, Transaction, Reducer, Procedure, Error, UnknownTransaction
  • TableCache<Row, Key> with persistent immutable collections (lock-free reads)
  • UniqueIndex<Row, Col> (O(2) lookup) and BTreeIndex<Row, Col> (multi-value)
  • 5-state subscriptions: PENDING, ACTIVE, UNSUBSCRIBING, ENDED
  • Table callbacks: onInsert, onDelete, onUpdate (old+new), onBeforeDelete + remove variants
  • One-off queries: suspend with timeout + callback variant
  • SdkResult<T, E> with onSuccess/onFailure/map/getOrNull
  • Typed errors: QueryError, ProcedureError, SubscriptionError
  • Full type support: Identity, ConnectionId, Timestamp, TimeDuration, ScheduleAt, SpacetimeUuid (V5/V7), Int128/UInt128/Int256/UInt256, SpacetimeResult
  • Stats/metrics: per-request latency tracking for reducers, procedures, subscriptions, one-off queries
  • DbConnection.use { } resource management

Gradle plugin:

  • generateSpacetimeBindings task (runs spacetimedb-cli generate --lang kotlin, wired to compileKotlin)
  • generateSpacetimeConfig task (generates SpacetimeConfig.kt from spacetime.json)
  • cleanSpacetimeModule task
  • DSL: spacetimedb { cli.set(...); modulePath.set(...) }
  • Hooks into both kotlin.jvm and kotlin.multiplatform plugins
  • Graceful fallback if CLI not found

Templates:

  • basic-kt: minimal CLI client with a Rust server module
  • compose-kt: Compose Multiplatform chat app (Android + Desktop) with a full-featured Rust server module (users, messages, notes, scheduled reminders)

Example chat between android and desktop (jvm) using compose-kt template:

Jvm
jvm
Android
android

Benchmark:

Added Keynote-1 benchmark client (templates/keynote-2/spacetimedb-kotlin-client/): TPS benchmark matching the Rust client methodology.

Benchmark was run on: i7-12700H, 8GB Ram using 10 connections, 10s, alpha=1.5, confirmed reads

SDK avg TPS
Rust (raw WS) ~113,704
Kotlin ~96,486
TypeScript ~46,563

API and ABI breaking changes

CI now requires JDK 21+ installed in the smoketests job. The spacetimedb-new-runner-2 runner needs JDK 21+ available or the Kotlin tasks will fail.
No API or ABI breaking changes otherwise.

Expected complexity level and risk

2

The SDK and codegen are self-contained. The only touchpoints with existing code are:

  • crates/codegen/src/lib.rs (3 lines: pub mod kotlin + re-export)
  • crates/cli/src/subcommands/generate.rs (Kotlin variant in Language enum + dispatch)
  • crates/cli/src/subcommands/init.rs (Kotlin variant in ClientLanguage enum)
  • crates/cli/build.rs (binary file support for template embedding)
  • crates/smoketests/ (new test files + Gradle lock helper)

No changes to existing SDK behavior or server-side code.

Testing

Note: sdk and templates are tested against Android and JVM (Linux). I do not have an iOS device so i could not test against native.

  • cargo test -p spacetimedb-codegen (snapshot tests pass)
  • cargo test -p spacetimedb-smoketests --test integration kotlin (unit tests, build validation, server integration)
  • cargo test -p spacetimedb-smoketests --test integration test_all_templates (basic-kt and compose-kt)
  • sdks/kotlin/ ./gradlew :spacetimedb-sdk:jvmTest
  • sdks/kotlin/ ./gradlew :codegen-tests:test (generated bindings compile)
  • Keynote-1 TPS benchmark against local server
  • cargo fmt + clippy report no warnings
  • Reviewer: spacetimedb-cli init --template basic-kt creates a working project
  • Reviewer: spacetimedb-cli init --template compose-kt creates a working project
Step-by-step: Testing the compose-kt template locally

Prerequisites: JDK 21+, Rust toolchain, For Android testing: Android emulator or device with adb

1. Clone and build

git clone https://github.com/FromWau/SpacetimeDB.git /tmp/spacetimedb-kotlin
cd /tmp/spacetimedb-kotlin
git checkout feat/kotlin-sdk
cargo build --release -p spacetimedb-cli -p spacetimedb-standalone

2. Generate the project from template

/tmp/spacetimedb-kotlin/target/release/spacetimedb-cli init \
    --template compose-kt \
    --project-path /tmp/compose-kt-test/ \
    --non-interactive \
    compose-kt-test

3. Patch local SDK paths (needed because SDK is not yet published to Maven Central)

In /tmp/compose-kt-test/settings.gradle.kts, uncomment and set the two includeBuild lines:

pluginManagement {
    repositories { /* ... */ }
    // Need to include the Gradle plugin locally because it's not yet published to Maven Central
    includeBuild("/tmp/spacetimedb-kotlin/sdks/kotlin/spacetimedb-gradle-plugin")
}

// Need to include the SDK locally because it's not yet published to Maven Central
includeBuild("/tmp/spacetimedb-kotlin/sdks/kotlin")

In /tmp/compose-kt-test/sharedClient/build.gradle.kts, add a spacetimedb block before kotlin:

// Need to point to local CLI because it's not yet distributed via Maven Central
spacetimedb {
    cli.set(file("/tmp/spacetimedb-kotlin/target/release/spacetimedb-cli"))
}

kotlin {
    // ...
}

4. Start the server (in a separate terminal)

/tmp/spacetimedb-kotlin/target/release/spacetimedb-cli start

5. Publish the module

cd /tmp/compose-kt-test
/tmp/spacetimedb-kotlin/target/release/spacetimedb-cli publish \
    --server local --module-path spacetimedb -y compose-kt-test

6. Run the desktop client

cd /tmp/compose-kt-test
./gradlew :desktopApp:run

7. Run the Android client (requires emulator or device with adb)

cd /tmp/compose-kt-test
./gradlew :androidApp:installDebug

Next steps

This PR needs the SDK and Gradle plugin published to Maven Central before merging. Right now templates and user projects require manually setting local SDK paths and plugin includes to work. Once published, they just pull dependencies from Maven/Gradle like normal. I'd publish under com.clockworklabs.spacetimedb-sdk and com.clockworklabs.spacetimedb-plugin. Would need access to the com.clockworklabs Maven namespace or a new one. If that works for you, I'll add a Kotlin publish workflow to CI.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant