> ## Documentation Index
> Fetch the complete documentation index at: https://docs.varianz.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Kotlin SDK

> Instrument Kotlin services with @VPoint via the KSP-based io.varianz.sdk.kotlin plugin — setup gotchas, language limitations, and what it shares with Java.

Kotlin uses the same runtime, agent, annotations, and registry binding as the [Java SDK](/sdks/java) — read that page for interception, `RegistryBinding`, control annotations, and troubleshooting. What differs is the compile-time half: Kotlin code is processed by a **KSP plugin**, `io.varianz.sdk.kotlin`, not the Java annotation processor.

## Setup

```kotlin theme={null}
plugins {
    id("com.google.devtools.ksp") version "2.1.10-1.0.29" // must come before the Varianz plugin
    id("io.varianz.sdk.kotlin")
}

dependencies {
    ksp("com.github.javaparser:javaparser-core:3.27.0")   // the plugin does not add this
}

configure<io.varianz.gradle.kotlin.VarianzKotlinExtension> {
    sdkVersion.set("0.2.1")
}
```

Four things trip up first-time setup:

1. **The plugin id is `io.varianz.sdk.kotlin`**, not `io.varianz.sdk`.
2. **Apply KSP first** — the plugin throws unless `com.google.devtools.ksp` precedes it in the `plugins {}` block.
3. **Add JavaParser to `ksp` yourself** — without it the processor dies with `NoClassDefFoundError: com/github/javaparser/ast/Node`.
4. **Configure via the typed extension** (`configure<VarianzKotlinExtension>`) — the generated `varianz {}` accessor isn't available when the plugin is applied dynamically.

The `pluginManagement` repository setup is the same as [Java's](/installation#java).

## Defining VPoints

```kotlin theme={null}
import io.varianz.annotations.vpoint.VPoint

class PricingService {
    @VPoint(name = "pricing/calculate")
    fun calculatePrice(amount: Double, discount: Double): Double =
        amount * (1 - discount)
}
```

* Annotation attributes require **named arguments**: `@VPoint(name = "…")`, never positional.
* Data-class fields map naturally: `val` becomes read-only in the schema, `var` read-write. Names convert to snake\_case for CEL.

### Not supported in `@VPoint` signatures

Two Kotlin features are rejected at compile time with clear errors:

* `@JvmInline value class` parameters or returns
* `suspend fun` — wrap the suspending call in a plain function and instrument that

## Runtime and deployment

Everything on the [Java page](/sdks/java) applies: the `varianz-agent` must be attached (automatic for Gradle `test`/`run`, explicit for production launchers), `RegistryBinding.init(...)` + `<Class>VPoints.bind(instance)` at startup, and `--enable-native-access=io.varianz.native_loader` on Java 22+.

For session propagation with coroutines, carry the session in a context the caller thread can read — with OpenTelemetry, `opentelemetry-extension-kotlin`'s `asContextElement()` keeps Baggage flowing across suspensions. See [Propagate sessions](/guides/propagate-sessions#java-and-kotlin).

## Testing

Kotlin tests use the JUnit 5 extension (see [Testing → JUnit](/testing/junit)); a Kotest extension (`io.varianz:varianz-kotest`) provides the same session lifecycle for Kotest specs:

```kotlin theme={null}
class PricingTests : FunSpec({
    val varianz = VarianzExtension()
    extension(varianz)

    test("stage overrides price") {
        val session = varianz.session()
        session.insert(cel("pricing/calculate", """Quote { price: 999.0, currency: "USD" }"""))
        // ...
    }
})
```
