- How is Kalahari different from E2B or Daytona?
- E2B and Daytona are managed services: every command crosses the network, sandboxes carry per-second billing, and your code runs on their infrastructure. Kalahari runs sandboxes on the local machine via KVM (Linux) or Hypervisor.framework (macOS). Calls are in-process, there is no metered runtime, and data never leaves your network. The shim modules expose the same method names, so existing code switches with a one-line import change.
- What runs under the hood?
- A from-scratch Rust VMM. Not another Firecracker wrapper. Linux and macOS are both first-class.
View on npm
npm install @amlalabs/kalahariLocal agent microVM sandboxes.
Free and open source. KVM on Linux, Hypervisor.framework on macOS.
Quickstart
Create a sandbox in eight lines
import { KalahariClient } from "@amlalabs/kalahari";
const client = new KalahariClient({ image: "python:3.12-alpine" });
const sandbox = await client.createSandbox();
await sandbox.writeFile("/main.py", "print('hello')");
const result = await sandbox.run("python3", { args: ["/main.py"] });
console.log(result.stdout);
await sandbox.destroy();For one-off commands: await runCommand({ image, command, args }).
Zygotes
Boot once. Spawn many.
Configure a sandbox the way you want it (install dependencies, write files, warm a runtime), then freeze it into a zygote. Each spawn() hands back a fresh child in milliseconds. No re-installs, no re-warmup.
import { KalahariClient } from "@amlalabs/kalahari";
const client = new KalahariClient({ image: "node:22-alpine" });
const sandbox = await client.createSandbox();
await sandbox.mkdir("/workspace");
await sandbox.writeFile("/workspace/state.txt", "base\n");
const zygote = await sandbox.zygote();
const a = await zygote.spawn();
const b = await zygote.spawn();
await a.writeFile("/workspace/state.txt", "child a\n");
console.log(await a.readFile("/workspace/state.txt"));
console.log(await b.readFile("/workspace/state.txt"));
await Promise.all([a.destroy(), b.destroy()]);
await zygote.destroy();Children are isolated. Writes from one spawn() never leak to siblings.
A child can itself become a zygote, so you can branch and re-branch.
Frequently Asked Questions
Drop-in for your existing SDK
Same shape as the upstream package. Only the import path changes.
E2B
import { Sandbox } from "@amlalabs/kalahari/e2b"; const sandbox = await Sandbox.create({ image: "node:22-alpine" }); const result = await sandbox.commands.run("ls /"); console.log(result.stdout); await sandbox.kill();Daytona
import { Daytona } from "@amlalabs/kalahari/daytona"; const daytona = new Daytona(); const sandbox = await daytona.create({ language: "python" }); const result = await sandbox.process.codeRun("print('hello')"); await sandbox.delete();ComputeSDK
import { compute } from "computesdk"; import { kalahari } from "@amlalabs/kalahari/computesdk"; const sdk = compute({ provider: kalahari({ image: "node:22-alpine" }), }); const sandbox = await sdk.sandbox.create(); const result = await sandbox.runCommand("node --version"); console.log(result.stdout); await sandbox.destroy();Harbor
import { harbor } from "@amlalabs/kalahari/harbor"; const env = harbor({ image: "node:22-alpine" }); const result = await env.run({ command: "node --version" }); console.log(result.stdout);