Koala — Linux VMs on your Mac or Linux machine
Preview · 0.1.0-preview.15 · macOS 26+ on Apple silicon · Linux on x86-64
Agents and Linux workloads, one VM each.
Koala runs jobs, services, development environments and AI agents in Linux VMs, on Apple silicon Macs and on x86-64 Linux machines with KVM. Each VM has its own kernel, disk and network, and gets only the access you grant it.
$`brew install kunalkushwaha/koala/koala`
_~/project — zsh_
``` $ koala run --image docker.io/library/alpine:3 -- \ /bin/sh -c 'echo hello from the guest; uname -m' hello from the guest aarch64
a VM that stays up after the terminal closes
$ koala create dev --image docker.io/library/alpine:3 --wait $ koala start dev --wait $ koala exec -it dev -- /bin/sh / # ```
Watch
Koala, run agents with confidence
Section titled “Koala, run agents with confidence”
The explainer is also on YouTube.
01 · Install
Install, then run as you
Section titled “Install, then run as you”
- macOS
`brew install kunalkushwaha/koala/koalakoala system install # set up and start the manager for your user`
You need a Mac with Apple silicon and macOS 26 or newer, and images built for `linux/arm64`. To install from a release archive, see getting started.
After the install, everyday use needs no `sudo`: the same `koala` commands on both.
Shared on both hosts: the `koala` CLI, the `/v1` API, VM specs, policy templates, the security model, and a per-user manager that runs as you.Below that, each host provides its own parts.Each host runs the manager in the background and provides the hypervisor.Each host has its own sandboxes and its own store for secrets.Images: `linux/arm64` on a Mac, `linux/amd64` on Linux. Install with Homebrew or a Debian package.**One code base: only the parts the host provides differ.**
1. Shared on both hosts: the koala CLI, the same /v1 API, the same VM specs, policy templates and security model, and a per-user manager that runs as you, with no root daemon. 2. The stack splits by host: macOS on Apple silicon, macOS 26 or newer; Linux in preview on x86-64 Ubuntu 24.04, kernel 6.12 or newer, with /dev/kvm. 3. Background manager: a launchd agent on macOS, a systemd user service on Linux. Hypervisor: Apple's Virtualization framework on macOS, KVM through Cloud Hypervisor on Linux.
4. Sandboxes: App Sandbox for the helpers and a Seatbelt profile per VM run on macOS; Landlock and seccomp, and namespaces where Koala's AppArmor profile is loaded, on Linux. Secrets: the login Keychain on macOS; the desktop keyring (Secret Service) or encrypted files on Linux. 5. Images: linux/arm64 on macOS, linux/amd64 on Linux. Install: Homebrew on macOS, a Debian package on Linux. 6. On each host, every VM has its own kernel, disk and network. One code base: only the parts the host provides differ.
02 · Try it
From throwaway job to sandboxed agent
Section titled “From throwaway job to sandboxed agent”
A command in a fresh VM that is removed when the command ends:
`koala run --image docker.io/library/alpine:3 -- /bin/sh -c 'echo hello from the guest; uname -m'`
**One command:** the agent's VM, its key kept outside the VM, its volume and its setup, after showing you the plan.**Checks first.** If something is wrong, it stops and nothing changes.**Shows the plan** before it makes anything.**Asks before it goes on.** With --yes, it doesn't ask.**Makes it:** stores the key and the policy, creates the volume and the VM, and starts the VM.**Waits until the agent is ready,** then runs the setup commands in the VM.
1. Checks first: the VM name is free, any policy of the same name is identical, and everything the VM uses that the template doesn't create exists. If something is wrong, it stops and nothing changes.
2. Shows the plan: VM hermes (lean service, internet off, 127.0.0.1:8642 published, volume hermes-data, broker grant openai to policy hermes-openai); credential hermes-openai, for which koala asks for the key; policy hermes-openai (openai, models gpt-4.1-mini); new volume hermes-data; and the setup commands. 3. Asks Continue? [y/N]. With --yes, it doesn't ask. 4. Makes it: asks for the missing key without echoing it and stores it (in the login Keychain on a Mac), stores the policy, creates the volume and the VM, and starts the VM. The first start downloads the image. 5. Waits until the agent is ready (its health check), runs the setup commands in the VM, and restarts the VM as the template asks. 6. Prints hermes is running, with where the agent listens: 127.0.0.1:8642.
03 · What you get
Built for untrusted workloads
Section titled “Built for untrusted workloads”
The AI agent runs in its own VM with the internet off. Inside, `OPENAI_API_KEY` is only a placeholder, and the agent calls `http://openai.broker.koala.internal/v1`.The request leaves the VM to `koala-net`, which passes it to `koala-broker` over the VM's private channel. The broker has no listening port.The broker checks the policy: the allowed requests, the allowed models and the limits.Allowed: the broker adds the real key from the key store on the way out only, and sends the request over TLS. The response comes back; the key never travels toward the VM.Not allowed (another path or model): refused with 403. It never reaches the service.**A compromised agent can use only the access you granted, within your limits; it can't steal the key.**
1. An AI agent runs in its own VM with the internet off. Inside the VM, OPENAI_API_KEY is only a placeholder, and the agent calls http://openai.broker.koala.internal/v1; plain HTTP is fine because the traffic stays on the host. 2. The request leaves the VM to koala-net, the VM's network helper on the host, which passes it to koala-broker over the VM's private channel. The broker has no listening port. 3. The broker checks the policy: the allowed requests, such as chat completions and the model list; the allowed models, such as gpt-4.1-mini; and limits.
4. An allowed request gets the real key from the key store (the login Keychain on a Mac, encrypted files on Linux) on the way out only, and goes to api.openai.com over TLS. The response comes back to the agent. The key never travels toward the VM. 5. A request for another path or model is refused with 403 and never reaches the service. 6. A compromised agent can use only the access you granted, within your limits; it can't steal the key.
Throwaway jobs
A command in a fresh VM, removed when it ends. For builds, tests and scripts you would rather not run on your computer. Jobs and VMs →
Services and environments
VMs that keep running after your terminal closes. `exec`, attach a shell, `cp` files in and out. Working inside a VM →
Keys stay outside the VM
An agent’s API keys stay on your computer: the login keychain on a Mac, the broker’s own encrypted files on Linux. The broker adds a key only to the requests a policy allows. The broker →
Agents in one command
`koala create hermes --template hermes` makes an agent’s VM, its key’s policy, a volume and its setup, after showing the plan. VM templates →
The images you have
OCI images from Docker Hub or a private registry, pinned by digest when a VM is created. Images →
Lean or full
_Lean_ runs one image with a minimal init. _Full_ runs Ubuntu 24.04 with systemd and Docker Engine. Profiles →
Deny by default
No host files, no secrets, no LAN, no other VMs, until you grant them. Checked on the host side. Networking →
Runs as you
A per-user manager (a launchd agent on a Mac, a systemd user service on Linux) keeps track of your VMs. No root daemon. Getting started →
04 · Tested by attacking it
Hostile guests, crashes and fuzzing, on real VMs
Section titled “Hostile guests, crashes and fuzzing, on real VMs”
**Koala is tested by attacking it.** Root in the guest attacks the boundary, fuzzers hit every parser of guest data, and crash checks kill the manager, VM processes and helpers at chosen moments.
1. A Koala VM inside its boundary; sandboxed around it on the host: the VM's process, the network helper koala-net and the broker koala-broker.
2. Root inside the guest attacks the guest channels (lengths, credits, order), the network (spoofing, fragments, floods under resource limits), storage and credentials. Each is refused at the boundary; a control VM beside it keeps working. On Linux, an escape probe under each production sandbox has every attempt refused. 3. More than 40 fuzz targets in Swift, Go and C hit every parser of guest data, each with at least 100,000 generated inputs; the Swift and C ones run under AddressSanitizer.
4. About 1,400 crash checks on each platform kill the manager, each VM's process and every helper (image extractor, file transfer, network helper, broker) at chosen moments. Each case checks that nothing is lost or left behind and that the VM starts again. 5. A canary key is hunted through the VM's disk, its volumes and the image: not found there. It appears only on the upstream request to the API service, outside the VM. 6. The same suites run on a Mac and on Linux, and on every release.
- **Root inside the VM attacks the boundary:** the guest channels, the network (spoofing, fragments, floods), storage and credentials, beside a healthy control VM, on lean and full VMs.
- **About 1,400 crash checks on each platform** kill the manager, each VM’s process and every helper at chosen moments, then check that nothing is lost or left behind and the VM starts again.
- **Every parser of guest data is fuzzed:** more than 40 targets, each for at least 100,000 inputs, the Swift and C ones under AddressSanitizer.
- **A canary key is hunted** through the VM’s disk, volumes and image; it may appear only on the upstream request.
- **Every release is checked as you get it:** a simulated fresh Mac, an upgrade from the previous release with a VM running, and a clean install on each platform.
More in the README.
05 · Defaults
What a VM can reach
Section titled “What a VM can reach”
| Resource | Default | When you grant it | | --- | --- | --- | | Internet | Public addresses only | Can be switched off | | Files on your computer | None | Folders you share; read-only unless you ask for read-write | | Secrets | None | Named secrets, to one VM, as a file or an environment variable | | API keys | None | A broker grant: the key stays on your computer, added only to the requests a policy allows | | Services on your computer | None | One address and specific ports | | Your LAN | None | A specific prefix and ports | | Other VMs | Never | Not available in v0.1 | | Published ports | None | On `127.0.0.1`, or on the LAN when you ask |
Koala treats everything inside a VM as untrusted, and trusts you, the logged-in user. On a Mac, VMs stop when you log out; on Linux, they keep running with lingering on. Nothing starts by itself after a restart.