- 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.argsand the override’s return.
Python
Thevarianz-scan CLI is installed with the varianz wheel. It imports your modules and extracts VPD schemas offline:
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.
varianz.config.json (all fields optional):
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.
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.