Architecture
How pols turns an API call into a running VM, where each part runs, and how sandbox traffic is kept apart from the API and the website.
pols has three parts: a control plane that keeps the record of every sandbox, a runtime on each sandbox host that runs the VMs, and an edge that terminates TLS and routes traffic. All of them run on dedicated servers at Hetzner in Germany.
pols CLI · MCP server · TypeScript client · curl browsers, ssh
│ REST, org API key │
▼ ▼
edge ── api.pols.so · <sandbox>-<port>.on.pols.so · ssh.pols.so
│ │
▼ │ ports, desktop,
control plane (polsd + Postgres, reconciler) │ ssh over the host's
│ create, start, stop, fork, delete, │ private network
│ exec, files, computer use │
▼ ▼
sandbox host: QEMU/KVM virtual machines on ZFS, one isolated project per org
Desired state and the reconciler
Lifecycle calls do not wait for the VM. POST /v1/sandboxes, stop, resume, fork and DELETE record what you want, the sandbox’s desired_state (running, stopped or deleted), and return at once. A reconciler in the control plane then drives the host to that state, retries with backoff when something fails, and notices when a VM stopped on its own.
A sandbox’s status says where it actually is: pending, running, stopped, deleted or error. A client waits until status equals desired_state; the CLI, the MCP server and the TypeScript client’s waitFor do that for you. See Lifecycle.
Commands, file transfers and computer-use actions are different: they go straight to the running VM and answer when they are done.
The runtime
Each sandbox is a QEMU/KVM virtual machine managed by Incus. Its disk is a ZFS dataset, which is why copies are cheap: a new sandbox, a fork and a template are all clones of an existing disk and take well under a second to make. Booting the VM takes the rest of the roughly ten seconds a new sandbox needs.
Every org gets its own Incus project, and each sandbox has its own network ACL for its egress policy.
The edge
The edge runs on the sandbox host and serves every public name:
| Name | Serves |
|---|---|
api.pols.so |
the REST API |
<sandbox>-<port>.on.pols.so |
a published port |
<sandbox>-desktop.on.pols.so |
the desktop in the browser |
<sandbox>-cdp.on.pols.so |
the sandbox’s Chrome for Playwright or Puppeteer |
ssh.pols.so |
the SSH gateway |
<sandbox> is the sandbox ID with - in place of _. The edge keeps no state of its own besides its certificates: for every request and every SSH login it asks the control plane whether, and where, the traffic may go.
All sandbox content is served under on.pols.so, never under pols.so or api.pols.so. A page served by a sandbox therefore cannot read the cookies of the website or the API.
Data and accounts
The control plane keeps its records in Postgres on the same infrastructure: orgs, users, sandboxes, templates, usage intervals and the vault, whose values are encrypted with a key kept outside the database. Of API keys, login links and sessions it stores only a SHA-256 hash.
Usage is metered from the running intervals: every start and stop is recorded in the same transaction as the status change, and usage is computed per second from them.