Skip to main content
Each SDK ships a build-time tool that derives VPoint schemas from source, without running your service. Wiring it into your build gives you:
  • Validation at insert time — schemas exist before the first request, so a bad CEL expression is rejected when you insert it rather than failing at call time.
  • Early failures — schema problems (untyped parameters, fields that can’t cross the FFI boundary) fail the build instead of a test run.
  • A queryable catalog — VPoints appear in the coordinator’s catalog before the service even starts.
  • Typed local stages — in Python and TypeScript, the scanners also emit per-VPoint helpers that type ctx.args and the override’s return.

Python

The varianz-scan CLI is installed with the varianz wheel. It imports your modules and extracts VPD schemas offline:
Hook it into your build in whatever runner you use:
Because it imports your modules, the scanning environment needs your dependencies installed (grpcio, proto stubs, and so on). For Docker-based services, run it inside the container or install deps in the host virtualenv. A second entry point, python -m varianz.buildscan, emits Python instead of JSON: varianz_descriptors.py, which pre-warms the descriptor cache, plus varianz_stages.py with one typed attach_<vpoint>_stage helper per VPoint.
varianz_stages.py imports nothing native, so it’s safe to import while Varianz is disabled, and the generator refuses to overwrite a file that doesn’t carry its header — pointing --stages-output at hand-written code fails the build rather than eating it. Usage: Python SDK → Typed helpers.

TypeScript

@varianz/scanner provides a pure-static scanner built on the TypeScript compiler API — it never imports or executes your code. It derives a VPointManifest covering every createVPoints and @vpoint registration, including typed parameter, return, and struct schemas. The SDK auto-loads the manifest at module init, so undecorated registrations carry full schemas at runtime.
Configuration lives in varianz.config.json (all fields optional):
include entries are root files handed to the TypeScript compiler, not globs or directories. A directory entry such as ["src"] matches nothing: the scan reports 0 vpoint(s), 0 struct(s), writes an empty manifest, and exits 0. List entry-point files explicitly — anything they import is pulled in with them.
followModules allow-lists npm packages whose types should resolve into struct schemas; everything else surfaces as Unknown (CEL field access on Unknown still works at runtime — only struct-literal construction needs a typed schema). Alongside the manifest the scanner writes stages.gen.js and stages.gen.d.ts into the same output directory — one typed attach<VPoint>Stage helper per VPoint, each bound to its own VPoint so a mismatched VPointRef throws at attach time. Usage: TypeScript SDK → Typed helpers. With the scanner in place, @struct/@field decorators become fine-grained overrides rather than requirements: reach for them when you need a canonical name different from the TS symbol, a specific numeric kind (Kind.Int32 instead of the default Float64 for number), or a registration with no static call site. Set VARIANZ_DISABLE_AUTO_LOAD=1 to skip manifest auto-loading (useful in tests).

Go

varianz-gen is a static-analysis tool that scans Go source for VPoint registrations — both the functional form (varianz.VPoint(...)) and the Embed service form — and generates optimized code:
When the generated file is present the runtime uses it instead of reflection; when absent, reflection kicks in automatically — your code is unchanged either way. varianz-gen also acts as a build gate: it exits non-zero when a request/response type has a field that can’t cross the FFI boundary (chan, func, unsafe.Pointer), so run it in CI.
varianz-gen only resolves types declared in the package it scans. A request or response type from another package — which for a gRPC service means every generated protobuf message — is emitted as an undefined kind with no fields, and the tool still exits 0. It accepts one directory, so varianz-gen ./... produces an entirely empty manifest; and it keys struct names off the local import alias, so scanning the proto package separately produces names that do not match.For a gRPC service, rely on the reflection path — it derives full schemas correctly — and use varianz-gen as the FFI build gate only. Check what was actually generated with varianz.HasGeneratedType("<pkg>.<Type>"), which returns false when nothing was.

Java and Kotlin

The JVM SDKs need no separate scan step — the annotation processor (Java) or KSP processor (Kotlin) generates the <ClassName>VPoints aggregates and embeds VPD schemas during normal compilation. The Gradle and Maven plugins configure this for you. To verify it ran: with Gradle, ./gradlew compileJava logs Note: [Varianz] Generated adapters...; with Maven, the varianz-maven-plugin:scan goal reports discovered VPDs after compilation.

Skipping codegen

VARIANZ_SKIP_CODEGEN=true makes the build-time tools emit nothing — useful for builds that must not touch generated output. It’s independent of VARIANZ_ENABLED: skipping codegen doesn’t disable the SDK at runtime, and the tools resolve full schemas whether or not the SDK is enabled, so a build machine needs no Varianz environment at all. See Configuration.