Streaming-first
Serialize and deserialize in arbitrarily small chunks — you never need the whole message in memory. No length-prefixed envelope, so encoders emit data before the final size is even known.
Structured Objects For Anyone
… so optimized, feels amazing.
A compact, streamable binary format for structured data — tightly packed, schema-driven, and tiny enough for the smallest microcontroller yet fast enough for the cloud.
Each core library implements the same streamable wire format and shares a common suite of conformance test vectors — and some languages ship more than one build, each tuned for a different target.
Pick the generator or your core library, hit copy. Every core library is dependency-free and installs with a single command through the package manager you already use — only the generator itself differs per operating system.
sofabgen is a single static binary with no runtime dependencies. It turns a YAML/JSON object definition into typed code for every supported language.
Requires Nothing at all — the binary is self-contained
Full README on GitHub →1Install the CLI
Detects your OS and architecture, verifies the SHA-256 and drops the binary on your PATH. Set SOFABGEN_VERSION to pin a release, SOFABGEN_INSTALL_DIR to choose where it lands.
curl -fsSL https://raw.githubusercontent.com/sofa-buffers/generator/main/install.sh | sh
1Install the CLI
Detects your OS and architecture, verifies the SHA-256 and drops the binary on your PATH. Set SOFABGEN_VERSION to pin a release, SOFABGEN_INSTALL_DIR to choose where it lands.
curl -fsSL https://raw.githubusercontent.com/sofa-buffers/generator/main/install.sh | sh
1Install the CLI
There is no install script for Windows — this pulls the released binary straight into a folder that is already on your PATH. On an ARM machine swap in sofabgen-windows-arm64.exe.
$dst = "$env:LOCALAPPDATA\Microsoft\WindowsApps\sofabgen.exe" Invoke-WebRequest "https://github.com/sofa-buffers/generator/releases/latest/download/sofabgen-windows-amd64.exe" -OutFile $dst
2Or use the channel you already manage tools with
Every channel ships the same binary from the same release — pick one.
npm — pinned per project
npm install --save-dev @sofa-buffers/generator
PyPI — standalone CLI
uv tool install sofabgen # or: pipx install sofabgen
Go toolchain
go install github.com/sofa-buffers/generator/cmd/sofabgen@latest
3Generate typed sources
One flag per language — c, cpp, go, python, typescript, rust, csharp, java, kotlin, zig, dart — or docs for a self-contained HTML reference page.
sofabgen --version sofabgen --lang rust --in messages/ --out src/generated/
Heap-free C99 object API plus a header-only C++20 wrapper over the same core — for bare-metal MCUs up to IoT-class devices.
Requires A C99 and/or C++20 compiler (GCC or Clang) · CMake 3.10+ only for tests, benchmarks and docs
Same command on Linux, macOS and Windows
Full README on GitHub →Pull it into your build
Distributed as the port sofa-buffers-corelib-c-cpp; every route exposes the same target sofa-buffers::corelib and #include <sofab/…>. Also packaged for Conan, Arduino and PlatformIO.
include(FetchContent) FetchContent_Declare( sofa-buffers-corelib-c-cpp GIT_REPOSITORY https://github.com/sofa-buffers/corelib-c-cpp.git GIT_TAG main ) FetchContent_MakeAvailable(sofa-buffers-corelib-c-cpp) target_link_libraries(my_app PRIVATE sofa-buffers::corelib)
Written from scratch with no C backend and tuned for raw throughput.
Requires A C++20 compiler — GCC 11+, Clang 14+ or MSVC 19.30+ · header-only, no build step
Same command on Linux, macOS and Windows
Full README on GitHub →Pull it into your build
Distributed as the port sofa-buffers-corelib-cpp. A Conan package of the same name installs a CMake config exposing the same sofa-buffers::corelib target.
include(FetchContent) FetchContent_Declare( sofa-buffers-corelib-cpp GIT_REPOSITORY https://github.com/sofa-buffers/corelib-cpp.git GIT_TAG main ) FetchContent_MakeAvailable(sofa-buffers-corelib-cpp) target_link_libraries(my_app PRIVATE sofa-buffers::corelib)
The high-speed std build for big machines — the standard library and nothing else at runtime.
Requires Rust 1.70+, edition 2021
Same command on Linux, macOS and Windows
Full README on GitHub →Add the crate
Published on crates.io as sofa-buffers-corelib; the importable namespace is sofab — use sofab::{OStream, decode};
cargo add sofa-buffers-corelib
core-only, no allocator, #![forbid(unsafe_code)] — for microcontrollers.
Requires Rust 1.70+ (MSRV), edition 2021, stable
Same command on Linux, macOS and Windows
Full README on GitHub →1Add the crate
The crates.io package is sofa-buffers-corelib-no-std; the crate you use is sofab.
cargo add sofa-buffers-corelib-no-std
2Add your bare-metal target
Builds on any target — thumbv6m, thumbv7em, riscv32imc and friends. Nothing is boxed, so there is no alloc to provide.
rustup target add thumbv7em-none-eabihf
Streaming, dependency-free runtime core — standard library only, no cgo.
Requires Go 1.21+
Same command on Linux, macOS and Windows
Full README on GitHub →Add the module
The module path is github.com/sofa-buffers/corelib-go; the imported package is sofab — import sofab "github.com/sofa-buffers/corelib-go"
go get github.com/sofa-buffers/corelib-go
Pure Python with zero runtime dependencies, plus an optional native accelerator.
Requires Python 3.9+ (CPython or PyPy)
Same command on Linux, macOS and Windows
Full README on GitHub →Install the package
Distribution sofa-buffers-corelib on PyPI; the import package is sofab.
pip install sofa-buffers-corelib
Runs on Node, browsers, Deno and Bun. Zero runtime dependencies, ESM + CJS + browser IIFE.
Requires Node 20+ — or any modern browser, Deno or Bun
Same command on Linux, macOS and Windows
Full README on GitHub →Install the package
Ships ESM (.js), CommonJS (.cjs), a browser IIFE global (SofaBuffers) and full type declarations.
npm install @sofa-buffers/corelib
Allocation-light and runs anywhere a JVM does. Runtime dependencies: none.
Requires JDK 17+
Same command on Linux, macOS and Windows
Full README on GitHub →Declare the dependency
Maven coordinates org.sofabuffers:corelib; the import namespace is the package org.sofabuffers.sofab.
Maven
<dependency> <groupId>org.sofabuffers</groupId> <artifactId>corelib</artifactId> <version>0.10.0</version> </dependency>
Gradle
implementation("org.sofabuffers:corelib:0.10.0")
One multiplatform codec for JVM, JS and native targets — Kotlin standard library and nothing else.
Requires JDK 17+ · the Gradle wrapper pins Kotlin 2.4+ and Gradle 8.14.4+
Same command on Linux, macOS and Windows
Full README on GitHub →Declare the dependency
Built and tested for jvm, js (Node + browser), linuxX64 and linuxArm64; the import namespace is org.sofabuffers.sofab.
dependencies {
implementation("org.sofabuffers:corelib-kotlin-mp:0.1.0")
}
Allocation-light .NET with no reflection and no runtime codegen — friendly to trimming and Native AOT.
Requires .NET 9 or later
Same command on Linux, macOS and Windows
Full README on GitHub →Add the package
NuGet id SofaBuffers.Corelib; the assembly is SofaBuffers.dll and the public API lives under the sofab namespace.
dotnet add package SofaBuffers.Corelib
Pure Dart, streaming, zero runtime dependencies.
Requires Dart SDK 3.8.0 or newer
Same command on Linux, macOS and Windows
Full README on GitHub →Add the package
pub.dev requires lowercase names, so the published package is sofabuffers — you install sofabuffers and import it as sofab.
dart pub add sofabuffers
Allocator-free and comptime-dispatched for maximum throughput — Zig standard library only.
Requires Zig 0.16.0 or newer
Same command on Linux, macOS and Windows
Full README on GitHub →1Fetch the package
Pin the release with the #-ref — without one, zig fetch resolves whatever the default branch points at right then. --save writes the resolved hash into your build.zig.zon.
zig fetch --save git+https://github.com/sofa-buffers/corelib-zig#v0.10.0
2Wire up the module
The package name is sofa_buffers_corelib; the importable namespace is sofab — const sofab = @import("sofab");
const corelib = b.dependency("sofa_buffers_corelib", .{
.target = target,
.optimize = optimize,
});
exe.root_module.addImport("sofab", corelib.module("sofab"));
Every byte and every design decision is tuned for moving structured data fast — across slow links and onto the tiniest devices — without complicating the code you write.
Serialize and deserialize in arbitrarily small chunks — you never need the whole message in memory. No length-prefixed envelope, so encoders emit data before the final size is even known.
Varint (LEB128) encoding shrinks small integers to a single byte, and zig-zag mapping keeps negative numbers compact. Low overhead is reserved for the field types you actually use most.
Type information lives in the stream itself: each field header packs (id << 3) | type. Decoders can read, descend, or skip any field — even ones they don't recognize.
Delimiter-based sequences create fresh ID namespaces for deeply nested, hierarchical structures — without ever pre-computing a size or buffering a sub-tree.
Write a definition once; the generator emits dead-simple, language-native objects with .serialize() / .deserialize() plus streaming variants.
A tiny footprint with no heavy runtime makes it equally at home on resource-constrained MCUs and high-throughput cloud services. Little-endian on the wire, identical on every architecture.
Born out of real IoT and embedded communication needs, where every existing format made an awkward trade-off.
"Because JSON is too fat,
and Protobuf is too Google."
Simple, predictable building blocks — varints, a 3-bit type tag, and delimiter-based scopes.
Each field starts with a single varint header. The low three bits select the wire type; everything above is the field ID (0 … 2,147,483,647).
# Field header = (id << 3) | type 0x0 unsigned int (varint) 0x1 signed int (zig-zag varint) 0x2 fixlen value (f32/f64/str/blob) 0x3 unsigned int array 0x4 signed int array 0x5 fixlen value array 0x6 sequence start (nested scope) 0x7 sequence end # Varints are little-endian, 7 bits each: 300 -> 0xAC 0x02 # Zig-zag keeps small negatives tiny: -1 -> 1 -2 -> 3
# Generated objects are dead simple person = Person() person.name = "Ada" person.id = 42 # One-shot convenience data = person.serialize() person2 = Person.deserialize(data) # ...or stream it in arbitrarily small chunks, # never holding the whole message in memory.
Power users can drive the raw encoder and decoder by hand for maximum control. Everyone else writes a schema and lets the generator produce language-native classes.
Every core library is held to the same bar — proven interoperable and kept fast — by two dedicated harnesses that run across all of them.
Our Crucible differential-fuzzing harness feeds identical bytes to every language at once and treats any disagreement as a bug. The result: implementations that stay byte-for-byte interoperable, round-trip clean, and hardened against malformed input.
The Arena suite measures every library head-to-head against Protocol Buffers — from raw encode/decode throughput on server CPUs to firmware footprint on bare-metal ARM and RISC-V. Fair, reproducible, and always watching for regressions.
Browse the core libraries, read the format specification, and start serializing structured data the lightweight way.