Temporal has been the backbone of many of my orchestration-heavy projects, but I always wanted a first-class Bun experience instead of shimming through Node. Over the last few months I built exactly that inside proompeng/lab: a Zig implementation of the Temporal.io worker runtime that compiles into a native Bun module. This post walks through the motivations, the architecture decisions, and what to expect when it lands on npm.
While the prototype started as a proof of concept, the Zig codebase now owns the full lifecycle of a Temporal worker: wiring
activities, workflows, and transports without ever leaving the Bun process. That keeps latency tight for co-located services and
eliminates the flaky IPC bridge I previously had to maintain between Node and Bun. The runtime is packaged as a standard
temporal-bun-native addon so teams can drop it into existing Bun apps with a one-line import once the npm release ships.
Why Zig for the Temporal runtime?
Zig has become my go-to language for native extensions. Its build system makes it painless to target multiple platforms, and its explicit memory model keeps FFI boundaries predictable. Bun expects that level of control for its native plugin ABI, so leaning on Zig means I can expose Temporal worker bindings without dragging in a heavyweight C++ toolchain or paying for an extra JS bridge.
Worker bindings and runtime design
The runtime starts as a thin wrapper around Temporal's core Rust library. Zig handles the FFI boundary, mapping Temporal's worker lifecycle into Bun-friendly callbacks. A small shim exposes Temporal's replay-safe core to Bun through a stable ABI surface, and the Bun side registers handlers that feel identical to the TypeScript SDK. Some highlights:
- Deterministic workflow host. Temporal's replay semantics are preserved by translating workflow invocations into structured Zig enums that Bun can pattern match.
- Activity sandboxing. Each activity runs inside an isolated Bun runtime with cancellation hooks bridged directly from Temporal's core timers.
- Logging and telemetry. The Zig layer emits structured logs that are forwarded to Bun's logger, which lets me tap into existing observability pipelines without new adapters.
On the Zig side, the worker is compiled with zig build -Dtarget=$platform -Dcpu=baseline so I can generate portable artifacts.
Each build step also emits a compatibility manifest that maps Bun's ABI version to the compiled symbols; that file is bundled
next to the .bun binary and read during initialization to guard against mismatches when Bun inevitably revs its plugin API.
Once the bindings are compiled, the build emits a single .bun native addon that you can load like any other Bun module:
import { createTemporalWorker } from 'temporal-bun-native'
const worker = await createTemporalWorker({
workflowsPath: './workflows',
taskQueue: 'zig-bun-lab',
})
await worker.run()Testing the Bun-native path
I maintain an integration suite alongside the runtime that exercises replay determinism, activity cancellation, and signal
fan-out. The tests run across macOS and Linux targets via GitHub Actions and catch any ABI drift between Bun releases. Temporal's
core library updates are automatically pulled into the matrix so the native bindings stay in lockstep. A Bun smoke-test runs each
example workflow from the packages/temporal-bun-native/examples directory, while a Zig test harness validates the raw FFI layer
to ensure memory is released correctly across the JS boundary.
Publishing to npm
The release pipeline ships directly from the lab repository to npm under the temporal-bun-native name. Publishing there keeps
installation trivial for any Bun project while centralizing Temporal version upgrades. Each release publishes a matrix of
prebuilt binaries (macOS x64/arm64, Linux x64/arm64) and falls back to a source build when a platform-specific artifact is not
available. Releases are drafted automatically via Changesets and a GitHub Action that signs the Zig artifacts before uploading
them to npmjs.org, ensuring the binaries match the source that lives in proompeng/lab.
What comes next
Before tagging the first stable release I am tightening the integration tests that cover Temporal workflows under real load. Once those pass consistently, the package will go live on npm with end-to-end setup docs inside the proompeng/lab repository.
If you are curious or want to collaborate, watch the repository or open a discussion thread—updates will land there first before the npm announcement.