Skip to content
Tech AI Wire

Babashka 1.13.220 adds FFI for calling native libraries

Babashka 1.13.220 adds an FFI built on Java's Panama API, so a Clojure script can call a native library without writing a pod or any Java.

By Tech AI Wire Team

3 min read

XLinkedIn
A terminal running Babashka 1.13.220, where an ffi-demo.clj script loads zlib through the new foreign function interface and prints version 1.3.1.

Babashka, the fast-starting Clojure scripting runtime, can now call native C libraries directly. Version 1.13.220 shipped on 31 August 2026 with a foreign function interface, and author Michiel Borkent explained the design on his blog. Until now, reaching native code from a Babashka script meant writing a pod: a separate program that Babashka talks to over a socket. The new FFI removes that step.

A foreign function interface is the plumbing that lets one language call another. Here it lets Clojure call C.

What the new interface does

The implementation is built on java.lang.foreign, the Foreign Function and Memory API that arrived in the JDK through Project Panama. Borkent notes that the existing coffi library builds on the same foundation. So this is not a bespoke mechanism; it is the JVM's own supported route to native code, exposed to scripts.

The release covers more than simple function calls. According to the GitHub release notes, the work spans pull requests #2031 through #2077 and includes memory segments, arena management, struct and union layouts, and callbacks. Callbacks matter: they let a C library call back into your Clojure code.

Memory is the part that demands attention. Native memory sits outside the garbage collector, so Babashka makes you manage it explicitly through arenas. An arena owns a block of native memory and frees it when it closes. Clojure's with-open gives you that cleanup automatically at the end of a block. Get this wrong and you do not get an exception. You get a segfault.

A first call is small. Loading zlib and asking for its version returns the string 1.3.1.

Why the SQLite example matters

The example Borkent highlights is not a toy. The FFI lets you define a Clojure function and register it inside a native database, so a SQLite query can call it during execution.

That was not possible with pods. A pod runs in its own process, and SQLite cannot reach across a socket in the middle of evaluating a query. The function has to live in the same process as the database engine. Direct FFI puts it there.

This is the general shape of the change. Anything requiring your code and the native library to share one process and one memory space was previously out of reach. Callbacks, custom allocators and in-process extension points now open up.

A quieter change to Linux binaries

One line in the release affects people who never touch the FFI. On Linux, the default Babashka build has moved from fully static to mostly static, which means glibc is now linked dynamically. macOS and Windows builds already worked this way.

The reason is the FFI itself: loading arbitrary native libraries at runtime does not sit well with a fully static binary. The trade is portability. A fully static binary runs anywhere; a mostly static one expects a compatible glibc on the host. If you copy Babashka into a minimal container or an Alpine image, test it before you upgrade.

What this means for developers

If you already use Babashka for scripts and have been avoiding a task because it needed a C library, re-check that task. The pod workaround was real friction, and it is gone.

If you maintain a pod, this is worth reading closely. Pods still make sense for isolation and for wrapping large native SDKs. They no longer make sense purely to reach a handful of C functions.

If you are new to this, start with the arena rules rather than the call syntax. Calling zlibVersion is easy. Knowing which memory you own, and when it is freed, is what separates a working script from an intermittent crash. Wrap every arena in with-open until you have a reason not to.

And if you deploy Babashka in containers, check your base image for glibc before taking 1.13.220. That change ships whether or not you use the new interface.

Sources

  1. Babashka FFI - Michiel Borkent
  2. Babashka releases: v1.13.220 - GitHub

Related articles

The daily brief

Three to five stories a day, and what each one means for the people who build software. Free, no spam.