Skip to contentNew accounts start with $100 in credits Get an API key
← all posts

BoxLite v0.10.0: Your agent needs more than a sandbox

Mandalore Wang7 min read
Cover Image for BoxLite v0.10.0: Your agent needs more than a sandbox

Tags: ai-agents, microvm, rust

Cover description: BoxLite 0.10.0 gives agents a filesystem that outlives the box, an address the world can reach, and a ceiling that holds under concurrency.

That is one sentence. Look at what it demands.

The agent has to install a toolchain and keep it. It has to write files and still have them an hour later. It has to run a dev server that someone who is not the agent can open in a browser. And it has to do all of that inside limits, because "an agent that provisions compute" and "an agent that provisions compute you pay for" are the same sentence.

In 0.9.7 you could not do that through the API. In 0.10.0 you can. That is the release.

246 commits since 0.9.7, and the three largest areas of change are the API, the infrastructure, and the console — not the VM. The runtime was never the bottleneck. Everything an agent needs around the runtime was.

One request, three new capabilities

POST /v1/boxes
{
  "image": "python:3.11-slim",
  "volumes": [{ "managed_volume": "agent-home", "guest_path": "/workspace" }],
  "network": {
    "outbound": { "mode": "enabled", "allow_net": ["api.openai.com"] },
    "inbound":  { "mode": "enabled" }
  }
}

Three lines that were not possible over REST before this release: a managed volume attached by name, an egress allowlist that actually holds, and an inbound switch that makes the box's services publicly reachable.

A filesystem that outlives the box

Managed volumes existed in 0.9.x — if you were the internal box-creation path. Over REST they were unreachable, which meant every agent building on the public API rebuilt its world on every run.

Now:

  • POST, GET, GET /:id, DELETE on /v1/volumes — full lifecycle over REST

  • Volumes attach in the same call that creates the box

  • Addressed by id or name. managed_volume: "agent-home" and managed_volume: "vol_01K2EXAMPLE" both resolve

  • Single-file mounts, for when a directory is the wrong unit

  • S3-backed volumes through the same interface

  • A volumes console, and a rebuilt create-box dialog

The volume:// scheme is gone. It used to live inside a polymorphic source string, which is a parser nobody meant to write, and the CLI, the SDKs and the wire had all drifted on how to read it. VolumeSpec now carries managed_volume and host_path as separate typed fields. Two typed fields cannot disagree.

This is the half of stop-and-wake that makes it safe to use. A box that stops mid-run loses its disk. A volume does not, and the next box mounts it.

An address the world can reach

"Preview on a public URL" is a networking problem, and it landed here:

  • Public box service traffic routes through the API, proxy, runner, and guest-port tunnel path

  • Box network tunnels are proxied through the API

  • Local TCP port publication with automatic or fixed host ports, surfaced through the box-info APIs across REST, CLI and SDKs

  • Tunnel listener forwarding — local TCP and Unix listeners on prepared tunnels, a fresh tunnel per accepted client, wire protocol unchanged

Two constraints shipped with it, both on purpose. OCI EXPOSE is metadata, never an instruction that opens a host listener. Remote runtimes stay tunnel-first. Each published listener is bounded at 256 concurrent proxied connections.

And a fix that matters more than its size: a tunnel CONNECT against a stopped box used to drop the stream silently. It now rejects. Silent failure is the most expensive kind in an agent loop, because the agent will retry it forever.

A ceiling that holds under concurrency

Every platform ships quota. Most ship it checked on create, which is where it does the least good.

0.10.0 enforces per-organization ceilings — cpu, memory, disk, gpu, plus maximum concurrent boxes — on every path that adds or grows usage: create, start, warm-pool assignment, proxy auto-resume, and resize. Warm-pool assignment and auto-resume both add usage without anyone calling create. That is precisely how a "we have quotas" platform ends up with a runaway bill.

Underneath: a Redis pending-reservation using atomic Lua with TTL self-heal, checked against summed box-table usage. A projected total over a ceiling is rejected with a 400 before anything is allocated.

Metering grew up with it. Finalized box usage exports to the commerce service through an outbox, open allocations are snapshotted, and the console now binds Usage to the quota API, quotes box price from published usage rates, and confirms plan changes on a page that shows exactly what is queued.

If you are the person who signs off on an agent platform, this section is the one that matters.

A tighter boundary

Three holes in allow_net enforcement are closed. Each was a case where the policy applied in one direction but not another:

  • UDP egress was not covered

  • The host alias IP was not covered

  • Host checks were bound to the requested destination, not the resolved one

The third is the one to sit with. A hostname that passes the check and then resolves somewhere else was never checked at all.

All three are breaking changes, because traffic that used to escape no longer does. That is the correct direction, and it is worth reading carefully if you were relying on the gap without knowing it. We would rather tell you than let you find out.

Alongside: structured container capability controls, no capability mutation from privileged mode, host sandbox grants separated from guest networking, and a minimal guest rootfs to boot from.

Errors that tell you what to do instead

A small thing that says a lot about where the API is going. Send host port publication to the REST API and you no longer get property ports should not exist. You get:

host port publication is local-only; use the box network tunnel endpoint to reach a guest service

Four implementations of CreateBoxRequest had disagreed about which options a remote caller may set. One let a client switch the sandbox off. One refused nothing. Five fields were validated, audit-logged, and then silently dropped at the mapper. They are now either wired through or refused with a sentence that names the alternative.

Upgrading

0.10.0 earns the middle number. Before you bump:

  • auto_pause is now auto_stop, across the whole stack. The field never described a pause — the core has no pause operation, and the policy performs a real stop with no memory retention. The old name also collided with BoxStatus::Paused, which is the genuine SIGSTOP freeze taken during clone and export.

  • volume:// is removed from the wire. Use managed_volume or host_path.

  • The REST API rejects host-only box options instead of dropping them silently.

  • allow_net now applies to UDP egress, the host alias IP, and resolved destinations.

  • The ssh-gateway is removed.

  • Box images move to the v0.1.0 line, relocated under apps/.

  • Tunnel half-close is owned by the core, which tolerates a hung-up peer.

Start here

The open-source runtime and the managed Cloud share the same SDK call runs against a box on your laptop or a box on our fleet — a URL and an API key is the whole difference.

New accounts start with $100 for BoxLite Cloud in credits: https://app.boxlite.ai

Repo: https://github.com/boxlite-ai/boxlite

Docs: https://docs.boxlite.ai