# Cruller: Bun's Zig Runtime, Continued on Zig 0.16

**URL:** <https://ziggit.dev/t/cruller-buns-zig-runtime-continued-on-zig-0-16/16734>\
**Category:** Showcase\
**Tags:** llm, llm-deps\
**Created:** [July 16, 2026, 8:01am UTC](https://ziggit.dev/t/cruller-buns-zig-runtime-continued-on-zig-0-16/16734 "2026-07-16T08:01:11Z")\
**Posts on this page:** 13\
**Page:** 2

<div class="post-metadata">

**Author:** ![solenopsys](https://ziggit.dev/user_avatar/ziggit.dev/solenopsys/32/9589_2.png) [@solenopsys](https://ziggit.dev/u/solenopsys)\
**Post date:** [July 17, 2026, 2:19pm UTC](https://ziggit.dev/t/cruller-buns-zig-runtime-continued-on-zig-0-16/16734/22 "2026-07-17T14:19:19Z")

</div>

I think we’re looking at this from different angles. You’re focused on the compilation unit, but I’m looking at the execution boundary.

I see this as two distinct layers: an **Execution Layer** (JavaScript runtime) and a **Performance/Data Layer** (Zig core). I don’t need the JS runtime to be as fast as the network core. I can isolate them completely using `io_uring` for message passing.

Since the JS runtime itself introduces latency in the microsecond range (or higher), the overhead of an asynchronous message-passing interface becomes negligible. It’s not about finding a faster way to link modules—it’s about isolating the high-latency logic from the high-throughput data plane, so the business logic doesn’t bottleneck the network core.

---

<div class="post-metadata">

**Author:** ![g41797](https://ziggit.dev/user_avatar/ziggit.dev/g41797/32/3339_2.png) [@g41797](https://ziggit.dev/u/g41797)\
**Post date:** [July 17, 2026, 3:49pm UTC](https://ziggit.dev/t/cruller-buns-zig-runtime-continued-on-zig-0-16/16734/23 "2026-07-17T15:49:01Z")

</div>

> [@solenopsys](#):
>
> swap out the bloated C-based I/O for Zig-native code

Bun uses vendored [usockets](https://github.com/uNetworking/uSockets) C based library.

We need to remember two facts

- it’s very effective Reactor implementation of async tcp/ip communication
- current zig version does not support this functionality

---

<div class="post-metadata">

**Author:** ![cztomsik](https://ziggit.dev/user_avatar/ziggit.dev/cztomsik/32/3032_2.png) [@cztomsik](https://ziggit.dev/u/cztomsik)\
**Post date:** [July 17, 2026, 4:18pm UTC](https://ziggit.dev/t/cruller-buns-zig-runtime-continued-on-zig-0-16/16734/24 "2026-07-17T16:18:25Z")

</div>

One thing I don’t understand about the Bun Zig-\>Rust rewrite is why they didn’t just use V8? Last time I was researching this, it looked like their VM is (at least partially) available to be used for the C/C++ native objects. I have no idea if it would work in the end, but given that Google already put some energy into this and wrote quite a few articles about this problem (some objects living in native, some living in JS, and some of them being available in both and having to correctly garbage-collect non-trivial graphs of these), I’d just try that first… anyway 🙂 maybe somebody else will do.

---

<div class="post-metadata">

**Author:** ![solenopsys](https://ziggit.dev/user_avatar/ziggit.dev/solenopsys/32/9589_2.png) [@solenopsys](https://ziggit.dev/u/solenopsys)\
**Post date:** [July 21, 2026, 4:03pm UTC](https://ziggit.dev/t/cruller-buns-zig-runtime-continued-on-zig-0-16/16734/25 "2026-07-21T16:03:05Z")

</div>

## Proposed Cruller Runtime Architecture

`libcruller.so` is a stable dynamic library containing JavaScriptCore. The host, protocols, and external interfaces can be changed and rebuilt without rebuilding WebKit/JSC.

Each runtime instance runs in its own thread and has its own VM, heap, scheduler, and allocator.

```plaintext
┌─────────────────────────────────────────────────────────────────────┐
│ CRULLER HOST PROCESS │
│ │
│ HTTP/1.1 HTTP/2 HTTP/3 WebSocket ZMQ Files DB IPC │
│ │ │ │ │ │ │ │ │ │
│ └──────────┴────────┴─────────┴────────┴──────┴──────┴─────┘ │
│ │ │
│ ┌────────────▼────────────┐ │
│ │ I/O CONTROLLER THREAD │ │
│ │ │ │
│ │ protocol adapters │ │
│ │ routing │ │
│ │ load balancing │ │
│ │ runtime lifecycle │ │
│ │ drain / restart │ │
│ └───────┬─────────┬───────┘ │
│ │ │ │
│ commands│ │SQE / CQE │
│ completions│ │buffer references │
│ │ │ │
│ ┌─────────────────┘ └───────────────┐ │
│ │ │ │
│ ┌──────▼───────────┐ ┌───────▼───────────┐ │
│ │ COMMAND RINGS │ │ io_uring │ │
│ │ │ │ │ │
│ │ invoke │ │ network I/O │ │
│ │ async call │ │ file I/O │ │
│ │ completion │ │ registered buffers│ │
│ │ drain / stop │ │ fixed files │ │
│ └──────┬───────────┘ └───────┬───────────┘ │
│ │ │ │
│ │ control │ data path │
│ │ │ │
│ ┌──────▼──────────────┐ buffer references │ │
│ │ RT THREAD #1 │◄───────────────────────────┤ │
│ │ libcruller.so │ │ │
│ │ JSC / GC / JS │────────────────────────────┤ │
│ │ scheduler │ async operations │ │
│ │ allocator #1 │ │ │
│ └─────────────────────┘ │ │
│ │ │
│ ┌─────────────────────┐ buffer references │ │
│ │ RT THREAD #2 │◄───────────────────────────┤ │
│ │ libcruller.so │ │ │
│ │ JSC / GC / JS │────────────────────────────┘ │
│ │ scheduler │ async operations │
│ │ allocator #2 │ │
│ └─────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘

```

The external interfaces—HTTP/1.1, HTTP/2, HTTP/3, WebSocket, ZeroMQ, files, databases, and IPC—are implemented outside the runtime as replaceable host modules.

The controller converts interface events into messages for a selected runtime and maps runtime asynchronous calls back to the required external interface.

Command rings carry only small control messages:

```plaintext
INVOKE
ASYNC_CALL
COMPLETE
CANCEL
DRAIN
STOP

```

`io_uring` provides the shared Linux-native data path for both network and file operations. Runtime instances exchange buffer references with the controller instead of copying payloads through the command rings.

Runtime lifecycle:

```plaintext
ACTIVE → DRAINING → DESTROY → CREATE

```

The controller stops routing new work to a runtime, lets active operations finish, destroys the runtime together with its allocator, and creates a fresh instance from the same `libcruller.so`.

---

<div class="post-metadata">

**Author:** ![IbrahimOuhamou](https://ziggit.dev/user_avatar/ziggit.dev/ibrahimouhamou/32/2344_2.png) [@IbrahimOuhamou](https://ziggit.dev/u/IbrahimOuhamou)\
**Post date:** [July 21, 2026, 6:50pm UTC](https://ziggit.dev/t/cruller-buns-zig-runtime-continued-on-zig-0-16/16734/26 "2026-07-21T18:50:40Z")

</div>

to be honest, even [jetzig](https://jetzig.dev/) takes a long time to compile due to the overuse of `comptime`; it generates a new Zig file for templates and routes after any small change making it compile a new generated project everytime, making one question… things (I dont want to go OT)

> [@kristoff](#):
>
> (just remove the pound sign),

why not use the `@"#"` syntax? also, it’t tempting to make a tool which removes them automatically, or just do `:s/\.#/_` or some regex on the editor. anyways, it will be a journey migrating to Zig from Zig

> [@solenopsys](#):
>
> only to hit the inherent inefficiencies of the Linux kernel anyway.

I think I’m not experienced enough to understand? isn’t the problem in having a JS runtime in the first place?

---

<div class="post-metadata">

**Author:** ![solenopsys](https://ziggit.dev/user_avatar/ziggit.dev/solenopsys/32/9589_2.png) [@solenopsys](https://ziggit.dev/u/solenopsys)\
**Post date:** [July 21, 2026, 7:11pm UTC](https://ziggit.dev/t/cruller-buns-zig-runtime-continued-on-zig-0-16/16734/27 "2026-07-21T19:11:52Z")

</div>

> [@IbrahimOuhamou](#):
>
> I think I’m not experienced enough to understand? isn’t the problem in having a JS runtime in the first place?

Everything in my project that can be heavy already works on Zig wrappers around C++ libraries, databases, audio gateways, DAG processing, heavy slicers, while business logic and UI are built on TS because it’s cheap and efficient. Example: [GitHub - solenopsys/behemoth: Behemoth Converged Storage · GitHub](https://github.com/solenopsys/behemoth)

---

<div class="post-metadata">

**Author:** ![spirobel](https://ziggit.dev/user_avatar/ziggit.dev/spirobel/32/7026_2.png) [@spirobel](https://ziggit.dev/u/spirobel)\
**Post date:** [July 23, 2026, 11:40am UTC](https://ziggit.dev/t/cruller-buns-zig-runtime-continued-on-zig-0-16/16734/28 "2026-07-23T11:40:48Z")

</div>

great project. thank you for building this

it seems like it could be a great tool for using npm libraries as part of android apps, turn (old / secondary) android phones into local first self hosted servers, polyfill bun file access apis so code works across different platforms ( the api surface for reading and writing files in bun is simple enough so it can be polyfilled on different platforms / with different backends))

wondering if you had this in mind as well or if this is strictly focused on embedding bun in zig applications.

this approach is interesting. Bun itself contains a C compiler to conveniently do FFI with C snippets. It would be possible to do the same for zig, but this approach makes even more sense. If it leads to more stable and predictable behavior of the server executable that is great.

Definitely great to see an alternative and more developments in the bunzig space. Exciting!

---

<div class="post-metadata">

**Author:** ![solenopsys](https://ziggit.dev/user_avatar/ziggit.dev/solenopsys/32/9589_2.png) [@solenopsys](https://ziggit.dev/u/solenopsys)\
**Post date:** [July 23, 2026, 12:50pm UTC](https://ziggit.dev/t/cruller-buns-zig-runtime-continued-on-zig-0-16/16734/29 "2026-07-23T12:50:52Z")

</div>

Thanks. Yes, I actually wanted to make an isolated library with a simple interface to run anywhere, and also test it to track down memory leaks in the JS core, since it’s easier to look for them in an isolated core.

I also use QuickJS very actively in other parts of the application-embedding it in Zig for management and lightweight business logic-while Cruller is needed for heavy tasks with heavy npm libraries, but bundled into a single .js file instead of sitting in node\_modules, for maximum production performance.

---

<div class="post-metadata">

**Author:** ![solenopsys](https://ziggit.dev/user_avatar/ziggit.dev/solenopsys/32/9589_2.png) [@solenopsys](https://ziggit.dev/u/solenopsys)\
**Post date:** [July 23, 2026, 12:53pm UTC](https://ziggit.dev/t/cruller-buns-zig-runtime-continued-on-zig-0-16/16734/30 "2026-07-23T12:53:41Z")

</div>

I will deploy it to production in the next couple of days since everything works locally, and I’ll be testing the engine in a real project.

---

<div class="post-metadata">

**Author:** ![spirobel](https://ziggit.dev/user_avatar/ziggit.dev/spirobel/32/7026_2.png) [@spirobel](https://ziggit.dev/u/spirobel)\
**Post date:** [July 24, 2026, 4:47am UTC](https://ziggit.dev/t/cruller-buns-zig-runtime-continued-on-zig-0-16/16734/31 "2026-07-24T04:47:32Z")

</div>

did you just strip the build cli or the .build() method on the Bun global object as well?

it would be great if the Bun api stayed exactly the same. I depend on this api to turn tagged html template strings with embedded ts into a deliverable at start time. The set of features that bun provides are very helpful even as part of a runtime. It is easier to distribute the code as typescript and assemble the html entrypoint at runtime in memory.

this also allows for some flexibility for end users to use agents to customize their ui. In the future there won’t be a clear distinction between compile time + tools for devs and the interface for users personal agents to customize their experience

---

<div class="post-metadata">

**Author:** ![solenopsys](https://ziggit.dev/user_avatar/ziggit.dev/solenopsys/32/9589_2.png) [@solenopsys](https://ziggit.dev/u/solenopsys)\
**Post date:** [July 24, 2026, 5:10am UTC](https://ziggit.dev/t/cruller-buns-zig-runtime-continued-on-zig-0-16/16734/32 "2026-07-24T05:10:45Z")

</div>

Yes, I did that too as a raw version in one of my projects when I had TS files and they were being executed.

But why I removed it from the runtime: first, this solution will cascade and require `node_modules` since such solutions usually pull environment libraries from there; second, there is no type checking at runtime like there is in Java ; and third, compilation happens dynamically, and the exact same thing can be done by using a 50kb JS library that already exists.

And most importantly, JavaScript is the actual runtime target. TypeScript and `Bun.build()` are tools on top of it. As soon as you keep compilation inside the runtime, the scope starts growing very quickly. Now it has to walk directories, resolve imports, load packages, transform code, bundle it, minify it, and so on. In the end, the runtime becomes a large build system again.

That is the opposite of what I want. My idea is simple: the RT should take already prepared, stable JavaScript and execute it as fast and predictably as possible.

This also makes it easier to isolate memory problems and, potentially, replace JavaScriptCore with V8 or QuickJS later without dragging the whole Bun build stack along with it.

---

<div class="post-metadata">

**Author:** ![solenopsys](https://ziggit.dev/user_avatar/ziggit.dev/solenopsys/32/9589_2.png) [@solenopsys](https://ziggit.dev/u/solenopsys)\
**Post date:** [August 2, 2026, 7:20pm UTC](https://ziggit.dev/t/cruller-buns-zig-runtime-continued-on-zig-0-16/16734/33 "2026-08-02T19:20:59Z")

</div>

# Cruller is in production

Cruller is now running in production and has been stable on both glibc and musl. You can use it.

Next steps:

1. Separate the WebKit runtime into an RT library and define a common Zig interface for runtime implementations.
2. Add V8 as an RT implementation behind that interface.
3. Add QuickJS as an RT implementation behind that interface.
4. Build a test suite for performance measurement and memory-leak detection.
5. Expand networking interfaces and eliminate memory leaks in every RT implementation.

---

<div class="post-metadata">

**Author:** ![cztomsik](https://ziggit.dev/user_avatar/ziggit.dev/cztomsik/32/3032_2.png) [@cztomsik](https://ziggit.dev/u/cztomsik)\
**Post date:** [August 3, 2026, 3:45pm UTC](https://ziggit.dev/t/cruller-buns-zig-runtime-continued-on-zig-0-16/16734/34 "2026-08-03T15:45:09Z")

</div>

I’ve been always wondering if Oilpan could be used for the Zig side if Bun ever switched to V8.

> **[Oilpan library · V8](https://v8.dev/blog/oilpan-library)**
>
> V8 ships with Oilpan, a garbage collection library for hosting managed C++ memory.

BTW: It is an old post and it might require a bit more digging in order to be useful, but the eventual endgame is to have one GC shared for both JS and Zig side (and no memory leaks that are often created by such duality)

[Previous page](https://ziggit.dev/t/cruller-buns-zig-runtime-continued-on-zig-0-16/16734.md?page=1)
