KCL Multi-Language SDK Architecture: How Rust, Go, and Python Bindings Work
KCL's multi-language SDK architecture exposes a Rust-based core through a stable C-API and Protocol Buffer definitions, enabling idiomatic Rust, Go, and Python SDKs to interact with the compiler via native linking, CGO, or CFFI respectively.
KCL (KCL Constraint-based Record & Functional Language) is implemented in Rust, but its configuration language capabilities extend to Go and Python ecosystems through a unified multi-language SDK architecture. By exposing a public C-API together with a Protocol Buffers (protobuf) specification in the kcl-lang/kcl repository, the project enables thin, idiomatic language wrappers that share a single compiler implementation while respecting each language's memory management and type system conventions.
Core Architecture: The Rust Foundation and C-ABI Layer
The architecture centers on three distinct layers that separate the compiler implementation from language-specific bindings.
The Rust Core and Driver
The KCL compiler and runtime, responsible for parsing, type-checking, and evaluating KCL programs into JSON/YAML, resides entirely within the Rust codebase. In crates/driver/src/lib.rs, the core compilation driver handles the heavy lifting of program execution. This crate serves as the engine that all language SDKs ultimately invoke, ensuring consistent behavior across every supported platform.
The Stable C-API Interface
To bridge the gap between Rust and other languages, the repository exposes a stable Application Binary Interface (ABI) through crates/api/src/lib.rs. This file implements extern "C" functions—such as kcl_load_files—that compile to a shared library (libkcl.so on Linux, .dylib on macOS, .dll on Windows). These C functions provide a low-level, language-agnostic entry point that hides the complexity of Rust's ownership model while exposing essential lifecycle methods for session management and program execution.
Protocol Buffer Specifications
Data exchange between the core and language SDKs follows a strict schema defined in crates/api/spec.proto. This protobuf spec describes the data model for KCL execution results, error messages, and RPC services. By standardizing on Protocol Buffers, KCL ensures that complex data structures serialize consistently across language boundaries, whether passed through FFI boundaries or transmitted via gRPC.
Language-Specific Implementation Strategies
Each official SDK adopts the FFI strategy most idiomatic to its target language while sharing the same underlying C library and protobuf definitions.
Rust SDK: Direct Native Linking
The Rust SDK (maintained in kcl-lang/kcl-sdk-rust) requires no Foreign Function Interface (FFI) overhead. It directly links to the kcl-api crate as a dependency, re-exporting the Rust API as idiomatic types through kcl::api. This approach eliminates marshaling costs and provides zero-cost abstractions over the core compiler functionality.
Go SDK: CGO Bindings
The Go SDK (kcl-lang/kcl-sdk-go) utilizes cgo to import and call symbols from the shared libkcl.so library. The SDK maps Go structs to the protobuf message definitions, handling the conversion between Go's memory management and the C-ABI requirements. When you invoke methods like ctx.LoadFile(), the SDK marshals parameters across the cgo boundary, executes the corresponding kcl_* function, and deserializes the protobuf-encoded result into native Go structs.
Python SDK: CFFI Integration
The Python SDK (kcl-lang/kcl-sdk-py) employs cffi (C Foreign Function Interface) or ctypes to dynamically load libkcl.so at runtime. This approach exposes high-level Python classes—such as kcl.load() and kcl.run()—that handle the low-level C function calls and protobuf deserialization transparently. Python dictionaries map directly to the protobuf message structures defined in spec.proto, allowing Pythonic access to KCL evaluation results.
Execution Flow and Data Serialization
Understanding how a function call travels from application code to the Rust core clarifies the performance characteristics and error handling patterns.
The Invocation Chain
When an SDK method executes—such as loading a KCL file—the call follows a predictable path:
- Language SDK invokes a wrapper function (e.g.,
LoadFile) - FFI Layer marshals arguments through cgo (Go) or cffi (Python) into C-compatible types
- C-API receives the call via
kcl_load_filesincrates/api/src/lib.rs - Rust Core executes the compilation driver and returns protobuf-encoded bytes
- Deserialization converts the bytes into language-native structures (Go structs, Python dicts, or Rust types)
Optional gRPC Server Mode
Beyond direct FFI integration, KCL supports a kcld daemon that exposes the protobuf service over gRPC. In this mode, language SDKs can connect to a remote KCL instance rather than loading libkcl.so locally. This architecture proves useful for distributed build systems or scenarios requiring sandboxed execution, though it introduces network latency compared to in-process FFI calls.
Implementation Examples
The following code snippets demonstrate the consistent API surface across languages, each performing the equivalent operation: loading a KCL file and serializing the result to JSON.
Rust
use kcl_sdk::KclPackage;
fn main() -> anyhow::Result<()> {
let pkg = KclPackage::load("example.k")?;
let json = pkg.to_json()?;
println!("{}", json);
Ok(())
}
Source: kcl-lang/kcl-sdk-rust examples
Go
package main
import (
"fmt"
kcl "github.com/kcl-lang/kcl-sdk-go"
)
func main() {
ctx, err := kcl.NewContext()
if err != nil { panic(err) }
pkg, err := ctx.LoadFile("example.k")
if err != nil { panic(err) }
jsonStr, err := pkg.ToJSON()
if err != nil { panic(err) }
fmt.Println(jsonStr)
}
Source: kcl-lang/kcl-sdk-go examples
Python
import kcl
pkg = kcl.load("example.k")
json_str = pkg.to_json()
print(json_str)
Source: kcl-lang/kcl-sdk-py examples
Critical Source Files in kcl-lang/kcl
The following files constitute the single source of truth for the multi-language SDK architecture:
crates/api/spec.proto– Defines the protobuf messages and service interfaces used by all SDKs for data exchangecrates/api/src/lib.rs– Implements theextern "C"functions that form the stable C-ABI, exported inlibkcl.socrates/driver/src/lib.rs– Contains the core compilation driver invoked by the C-API layerCargo.toml(workspace root) – Declares thekcl-apicrate versioning that ensures SDK compatibility
Summary
- KCL's multi-language SDK architecture centers on a Rust core that exposes a stable C-API and Protocol Buffer specifications through the
kcl-apicrate - The Rust SDK links natively to the core crate, while Go uses cgo and Python uses cffi to interact with the shared
libkcl.solibrary - All language bindings share identical message definitions in
crates/api/spec.proto, ensuring consistent serialization and behavior - Execution follows a path from language-specific wrappers through FFI layers to
kcl_*functions incrates/api/src/lib.rs, with results returned as protobuf-encoded bytes - An optional gRPC server mode allows remote execution via the same protobuf interface used for local FFI calls
Frequently Asked Questions
Why does KCL use a C-API instead of direct Rust bindings for Go and Python?
The C-API provides a stable Application Binary Interface (ABI) that insulates language SDKs from internal Rust compiler changes, allowing the core to evolve without breaking Go or Python integrations. This approach avoids the complexity and compilation overhead of distributing Rust source code to non-Rust ecosystems. By compiling to a standard shared library (libkcl.so) with C linkage, KCL ensures maximum portability across operating systems and language runtimes.
How does the KCL SDK architecture handle version compatibility between the core compiler and language bindings?
Version compatibility is enforced through the kcl-api crate versioning declared in the workspace Cargo.toml and the corresponding protobuf schema in crates/api/spec.proto. All three language SDKs target specific releases of the libkcl.so shared library, ensuring that message serialization formats and C function signatures remain synchronized. Developers must ensure that the installed shared library version matches the SDK version to prevent ABI mismatches during FFI calls.
What is the performance overhead of using Go's cgo or Python's cffi compared to the native Rust SDK?
The Rust SDK incurs zero FFI overhead because it links directly to the native crate, while Go's cgo and Python's cffi introduce marginal latency primarily from crossing the language boundary and protobuf deserialization. In practice, this overhead is negligible compared to the cost of KCL compilation and evaluation, which dominates execution time. The optional gRPC server mode introduces additional network latency but eliminates local shared library dependencies.
Can I use the KCL Python SDK without installing the Rust toolchain?
Yes, the Python SDK distributes pre-compiled shared libraries (libkcl.so or platform equivalents) via package managers like pip, allowing end users to install and run KCL without installing the Rust compiler. The cffi loader dynamically locates and loads the binary shared object at runtime, abstracting away the underlying Rust implementation. This distribution model mirrors the Go SDK approach, where users need only the compiled library, not the source code.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →