🪲 Building a Tigerbeetle Client in Unison

Kaushik Chakraborty February 26, 2026
Source

Unison's got FFI recently. And I thought of integrating with Tigerbeetle, which has recently enamoured me with its fantastic ideas and implementations. But then, I got these couple of small problems:

So, I went yak-shaving to create a shim-library, tb-unison-shim, that wraps the original TB C-library to expose a callback-free C ABI for the Unison FFI to work.

https://codeberg.org/kaychaks/tb-unison-shim External Link • codeberg.org

Tigerbeetle comes with a native C Client library, libtb_client , which is single-threaded, callback-driven, and has a strict packet-based protocol. Tigerbeetle adheres to all of that when creating its client libraries in languages such as Python, Rust, Go, Node, Java, and dotnet. Most of those clients then abstract the callback approach via their language's IO primitives. E.g., both Python and Rust provide sync and async options for programs to interact with their respective clients.

So, for my shim library, I resorted to a timeout-based blocking C ABI which Unison could then interact with. I chose to implement this in Zig - part coz it's easy to understand and replicate most of the internals if I do it in the same host language. And the other reason this gave me was that it was the best way to learn Zig. And also the hardest.

After going through the docs and client implementations especially the Rust one, I zeroed on my shim's design:

Here's the rough flow of control

For more details, please checkout the ARCHITECTURE.md

Unison Client

https://share.unison-lang.org/@kaychaks/tigerbeetle External Link • share.unison-lang.org

Once the initial version of the shim with a couple of functions was ready, the Unison Tigerbeetle client had some straightforward tasks to complete.

type SymbolTable
  = SymbolTable (Map Text (Exists Spec))

When Unison gains proper C callback function pointer support, the intended end state is to remove the shim and bind the Unison client directly to the official C client library. And hence the design of the Unison client library is based on abilities, and the current shim is just one handler to that ability. When interfacing with the underlying TB client, a new handler will be created, so consumers of this Unison client library don't have to change their programs. Effect systems FTW.

Here's a basic example of how to interface with Unison client. Detailed instruction on how to have end-to-end flow with live TB replica is present in the README.

Discussion in the ATmosphere

Loading comments...