# Technical Documentation: superradcompany/microsandbox

> ℹ️ **Provenance:** Multi-Tier Ingestion · [CodeWiki Reference](https://codewiki.google/github.com/superradcompany/microsandbox)

### Overview (`README.md`)

<div align="center">
    <a href="./#gh-dark-mode-only" target="_blank" align="center">
        <img width="35%" src="./assets/microsandbox-gh-banner-dark.png" alt="microsandbox-banner-xl-dark">
    </a>
</div>

<div align="center">
    <a href="./#gh-light-mode-only" target="_blank">
        <img width="35%" src="./assets/microsandbox-gh-banner-light.png" alt="microsandbox-banner-xl">
    </a>
</div>

<br />

<div align="center"><b>——&nbsp;&nbsp;&nbsp;easy, fast, programmable microVMs for untrusted workloads&nbsp;&nbsp;&nbsp;——</b></div>

<br />

<div align='center'>
    <a href="https://trendshift.io/repositories/24502?utm_source=repository-badge&amp;utm_medium=badge&amp;utm_campaign=badge-repository-24502" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/24502" alt="superradcompany%2Fmicrosandbox | Trendshift" width="250" height="55"/></a>
</div>

<br />

<div align='center'>
  <a href="https://github.com/superradcompany/microsandbox/releases"><img src="https://img.shields.io/github/v/release/superradcompany/microsandbox?include_prereleases&style=for-the-badge" alt="GitHub release"></a>
  <a href="https://discord.gg/T95Y3XnEAK"><img src="https://img.shields.io/discord/1315784565562019870?label=Discord&logo=discord&logoColor=white&color=5865F2&style=for-the-badge" alt="Discord"></a>
  <a href="LICENSE"><img src="https://img.shields.io/badge/License-Apache 2.0-blue.svg?style=for-the-badge" alt="Apache 2.0 License"></a>
</div>


<br />

**Microsandbox** runs **untrusted workloads** inside fast, local microVMs: AI agents, user code, plugins, CI jobs, dev environments, scrapers, and automation.

##

- <img height="14" src="https://octicons-col.vercel.app/shield-lock/A770EF"> **Hardware Isolation**: Hardware-level isolation with tiny virtual machines.
- <img height="14" src="https://octicons-col.vercel.app/repo-forked/A770EF"> **Fork & Snapshot**: Fork live sandboxes[^demo]. Save running sandbox state and restore later.
- <img height="14" src="https://octicons-col.vercel.app/globe/A770EF"> **Cross Platform**: Runs on Linux, macOS, and Windows.
- <img height="14" src="https://octicons-col.vercel.app/package/A770EF"> **OCI Compatible**: Runs standard container images from Docker Hub, GHCR, or any OCI registry.
- <img height="14" src="https://octicons-col.vercel.app/container/A770EF"> **Docker-Like Workflows**: Familiar image, command, shell, and volume workflows.
- <img height="14" src="https://octicons-col.vercel.app/zap/A770EF"> **Instant Startup**: Average boot times[^boot-time] under 100 milliseconds.
- <img height="14" src="https://octicons-col.vercel.app/plug/A770EF"> **Embeddable**: Spawn VMs right within your code. No setup server. No long-running daemon.
- <img height="14" src="https://octicons-col.vercel.app/lock/A770EF"> **Secrets That Can't Leak**: Unexploitable secret keys that never enter the VM.
- <img height="14" src="https://octicons-col.vercel.app/database/A770EF"> **Long-Running**: Sandboxes can run in detached mode. Great for long-lived sessions.
- <img height="14" src="https://octicons-col.vercel.app/terminal/A770EF"> **Agent-Ready**: Your agents can create their own sandboxes with our [Agent Skills](https://github.com/superradcompany/skills) and [MCP server](https://github.com/superradcompany/microsandbox-mcp).

<br />

## <a href="./#gh-dark-mode-only" target="_blank"><img height="13" src="https://octicons-col.vercel.app/rocket/ffffff" alt="rocket-dark"></a><a href="./#gh-light-mode-only" target="_blank"><img height="13" src="https://octicons-col.vercel.app/rocket/000000" alt="rocket"></a>&nbsp;&nbsp;Getting Started

#### <img height="14" src="https://octicons-col.vercel.app/download/A770EF">&nbsp;&nbsp;Install the CLI
> ```sh
> curl -fsSL https://install.microsandbox.dev | sh        # 🍎 macOS / 🐧 Linux
> ```
>
> ```powershell
> irm https://install.microsandbox.dev/windows | iex      # 🪟 Windows
> ```
>
> <details>
> <summary><em>&nbsp;We also support other package managers  →</em></summary>
>
> ##
>
> ```sh
> brew install superradcompany/tap/microsandbox
> ```
>
> ```sh
> npm i microsandbox
> ```
>
> ```sh
> uv tool install microsandbox
> ```
>
> ```sh
> cargo install microsandbox
> ```
>
> </details>
>
> ##
>
> Start creating sandboxes once installed:
>
> ```sh
> msb run ubuntu
> ```

#### <img height="14" src="https://octicons-col.vercel.app/move-to-bottom/A770EF">&nbsp;&nbsp;Install the SDK
> ```sh
> npm i microsandbox                                       # 🟦 TypeScript
> ```
>
> ```sh
> cargo add microsandbox                                   # 🦀 Rust
> ```
>
> ```sh
> uv add microsandbox                                      # 🐍 Python
> ```
>
> ```sh
> go get github.com/superradcompany/microsandbox/sdk/go    # 🐹 Go
> ```


##

> **Requirements**:
>
> - <img height="14" src="https://api.iconify.design/simple-icons:apple.svg?color=%23A770EF" alt="macOS"> **macOS**: Apple Silicon.
> - <img height="14" src="https://api.iconify.design/simple-icons:linux.svg?color=%23A770EF" alt="Linux"> **Linux**: KVM enabled.
> - <img height="14" src="https://api.iconify.design/simple-icons:windows.svg?color=%23A770EF" alt="Windows"> **Windows**: WHP enabled.
>
> **Warning**: Microsandbox is still **beta software**. Expect breaking changes, missing features, and rough edges.

<br />

## <a href="./#gh-dark-mode-only" target="_blank"><img height="18" src="https://octicons-col.vercel.app/terminal/ffffff" alt="cli-dark"></a><a href="./#gh-light-mode-only" target="_blank"><img height="18" src="https://octicons-col.vercel.app/terminal/000000" alt="cli"></a>&nbsp;&nbsp;CLI

The `msb` CLI provides a complete interface for managing sandboxes, snapshots, images, and volumes.

#### <img height="14" src="https://octicons-col.vercel.app/play/A770EF">&nbsp;&nbsp;Run a Command

> ```sh
> msb run python -- python3 -c "print('Hello from a microVM!')"
> ```

#### <img height="14" src="https://octicons-col.vercel.app/stopwatch/A770EF">&nbsp;&nbsp;Named Sandboxes

> ```sh
> # Create and start a named sandbox
> msb create --name app python
> ```
>
> ```sh
> # Execute commands
> msb exec app -- python -c "import this"
> msb exec app -- curl https://example.com
> ```
>
> ```sh
> # Fork a running sandbox.
> msb fork app --name app2
> msb exec app2 -- python -c "print('An independent copy!')"
> ```
>
> ```sh
> # Snapshot now, resume later.
> msb snap create --sandbox app --full -o snapshot.msb
> msb snap restore snapshot.msb --name app3
> ```
>
> Forking duplicates live execution. Restoring starts from saved state; add `--cow-mem` to share unchanged memory from a full snapshot. Snapshot generations describe progression, while branches describe distinct paths through snapshot history. The old `--forked` restore flag remains a deprecated alias. See [forking and snapshots](https://docs.microsandbox.dev/sandboxes/snapshots#forking).
>
> ```sh
> # Lifecycle
> msb stop app
> msb start app
> msb rm app
> ```

#### <img height="14" src="https://octicons-col.vercel.app/cache/A770EF">&nbsp;&nbsp;Image Management

> ```sh
> msb pull python           # Pull an image
> msb image ls              # List cached images
> msb image rm python       # Remove an image
> ```

#### <img height="14" src="https://octicons-col.vercel.app/file-code/A770EF">&nbsp;&nbsp;Configuration File

> ```sh
> msb run --conf sandbox.yaml -- octocat
> ```
>
> ```yaml
> # sandbox.yaml
> image: python:3.12
> memory: 64M
> network:
>   allow:
>     - api.github.com
> scripts:
>   octocat: |
>     python - <<'PY'
>     import urllib.request
>
>     request = urllib.request.Request(
>         "https://api.github.com/octocat",
>         headers={"User-Agent": "microsandbox-example"},
>     )
>     with urllib.request.urlopen(request) as response:
>         print(response.read().decode())
>     PY
> ```

#### <img height="14" src="https://octicons-col.vercel.app/list-unordered/A770EF">&nbsp;&nbsp;Status & Inspection

> ```sh
> msb ls                         # List all sandboxes
> msb ps app                     # Show sandbox status
> msb inspect app                # Detailed sandbox info
> msb metrics app                # Live CPU/memory/network stats
> ```

> [!TIP]
>
> Run:<br />
> · `msb --help` for quick help menu. <br />
> · `msb --tree` for complete command hierarchy and descriptions. <br />
> · `msb <command> --tree` for a specific command tree.

<br />

<a href="https://docs.microsandbox.dev/cli/overview"><img src="https://img.shields.io/badge/CLI_Docs-%E2%86%92-A770EF?style=flat-square&labelColor=2b2b2b" alt="CLI Docs"></a>

<br />

## <a href="./#gh-dark-mode-only" target="_blank"><img height="18" src="https://octicons-col.vercel.app/package-dependencies/ffffff" alt="sdk-dark"></a><a href="./#gh-light-mode-only" target="_blank"><img height="18" src="https://octicons-col.vercel.app/package-dependencies/000000" alt="sdk"></a>&nbsp;&nbsp;SDK

The SDK lets you create and control sandboxes directly from your application. `Sandbox.builder("...").create()` boots a microVM as a child process. No infrastructure required.

#### <img height="14" src="https://octicons-col.vercel.app/play/A770EF">&nbsp;&nbsp;Run Code in a Sandbox

> ```typescript
> import { Sandbox } from "microsandbox";
>
> await using sandbox = await Sandbox.builder("my-sandbox")
>   .image("python")
>   .cpus(1)
>   .memory(512)
>   .create();
>
> const output = await sandbox.exec("python", [
>   "-c",
>   "print('Hello from a microVM!')",
> ]);
>
> console.log(output.stdout());
> ```
>
> <details>
> <summary><b>&nbsp;Rust Example →</b></summary>
>
> ```rust
> use microsandbox::Sandbox;
>
> #[tokio::main]
> async fn main() -> Result<(), Box<dyn std::error::Error>> {
>     let sandbox = Sandbox::builder("my-sandbox")
>         .image("python")
>         .cpus(1)
>         .memory(512)
>         .create()
>         .await?;
>
>     let output = sandbox
>         .exec("python", ["-c", "print('Hello from a microVM!')"])
>         .await?;
>
>     println!("{}", output.stdout()?);
>
>     sandbox.stop().await?;
>
>     Ok(())
> }
> ```
>
> </details>
> <details>
> <summary><b>&nbsp;Python Example →</b></summary>
>
> ```python
> import asyncio
> from microsandbox import Sandbox
>
> async def main():
>     sandbox = await Sandbox.create(
>         "my-sandbox",
>         image="python",
>         cpus=1,
>         memory=512,
>     )
>
>     output = await sandbox.exec("python", ["-c", "print('Hello from a microVM!')"])
>
>     print(output.stdout_text)
>
>     await sandbox.stop()
>
> asyncio.run(main())
> ```
>
> </details>
> <details>
> <summary><b>&nbsp;Ruby Example →</b></summary>
>
> ```ruby
> require "microsandbox"
>
> sandbox = Microsandbox::Sandbox.create(
>   "my-sandbox",
>   image: "python",
>   cpus: 1,
>   memory: 512,
>   network: {
>     allowed_hosts: ["api.openai.com"],
>     allowed_ports: [443]
>   },
>   secrets: [{
>     env: "OPENAI_API_KEY",
>     value: ENV.fetch("OPENAI_API_KEY"),
>     allowed_host: "api.openai.com"
>   }]
> )
>
> output = sandbox.exec("python", ["-c", "print('Hello from a microVM!')"])
> puts output.stdout
>
> sandbox.stop
> ```
>
> See the [Ruby SDK guide](./sdk/ruby/README.md) for installation, lifecycle,
> networking, and backend details.
>
> </details>
>
> <details>
> <summary><b>&nbsp;Go Example →</b></summary>
>
> ```go
> package main
>
> import (
>     "context"
>     "fmt"
>     "log"
>
>     microsandbox "github.com/superradcompany/microsandbox/sdk/go"
> )
>
> func main() {
>     ctx := context.Background()
>
>     // Downloads the microsandbox runtime to ~/.microsandbox/ on first run.
>     if _, err := microsandbox.EnsureRuntime(ctx, microsandbox.RuntimeConfig{}, microsandbox.InstallOptions{}); err != nil {
>         log.Fatal(err)
>     }
>
>     sandbox, err := microsandbox.CreateSandbox(ctx, "my-sandbox",
>         microsandbox.WithImage("python"),
>         microsandbox.WithCPUs(1),
>         microsandbox.WithMemory(512),
>     )
>     if err != nil {
>         log.Fatal(err)
>     }
>     defer sandbox.Stop(ctx)
>
>     output, err := sandbox.Exec(ctx, "python", []string{"-c", "print('Hello from a microVM!')"})
>     if err != nil {
>         log.Fatal(err)
>     }
>
>     fmt.Println(output.Stdout())
> }
> ```
>
> </details>

> The first call to `create()` pulls the image if it isn't cached locally, so it may take longer depending on your connection. Subsequent runs reuse the cache.

<br />

<a href="https://docs.microsandbox.dev/sdk/overview"><img src="https://img.shields.io/badge/SDK_Docs-%E2%86%92-A770EF?style=flat-square&labelColor=2b2b2b" alt="SDK Docs"></a>

<br />

## <a href="./#gh-dark-mode-only" target="_blank"><img height="18" src="https://octicons-col.vercel.app/beaker/ffffff" alt="beaker-dark"></a><a href="./#gh-light-mode-only" target="_blank"><img height="18" src="https://octicons-col.vercel.app/beaker/000000" alt="beaker"></a>&nbsp;&nbsp;Guides

Practical ways to put microsandbox to work:

> • <img height="14" src="https://octicons-col.vercel.app/container/A770EF"> **[Docker in a Sandbox](https://docs.microsandbox.dev/how-to/docker/docker-in-sandbox)**: Run Docker without touching the host daemon.<br />
> • <img height="14" src="https://octicons-col.vercel.app/code/A770EF"> **[OpenCode](https://docs.microsandbox.dev/how-to/agents/opencode)**: Give a coding agent an isolated project workspace.<br />
> • <img height="14" src="https://octicons-col.vercel.app/globe/A770EF"> **[Browser Use](https://docs.microsandbox.dev/how-to/browser-automation/browser-use)**: Run an AI browser agent inside a microVM.<br />
> • <img height="14" src="https://octicons-col.vercel.app/globe/A770EF"> **[Playwright](https://docs.microsandbox.dev/how-to/browser-automation/playwright)**: Run headless browser jobs inside a microVM.<br />
> • <img height="14" src="https://octicons-col.vercel.app/cache/A770EF"> **[Warm Workers](https://docs.microsandbox.dev/how-to/sandboxing/warm-workers)**: Snapshot a toolchain and launch clean workers.<br />
> • <img height="14" src="https://octicons-col.vercel.app/database/A770EF"> **[Migration Rehearsal](https://docs.microsandbox.dev/how-to/data/migration-rehearsal)**: Test a database migration, then restore the baseline.<br />
> • <img height="14" src="https://octicons-col.vercel.app/workflow/A770EF"> **[GitHub Actions Runner](https://docs.microsandbox.dev/how-to/ci-cd/github-actions-runner)**: Run each self-hosted job in a disposable microVM.<br />
> • <img height="14" src="https://octicons-col.vercel.app/file/A770EF"> **[Documents to PDF](https://docs.microsandbox.dev/how-to/file-processing/libreoffice-pdf)**: Convert untrusted documents in a fresh offline worker.

<br />

<a href="https://docs.microsandbox.dev/how-to/overview"><img src="https://img.shields.io/badge/Browse_Guides-%E2%86%92-A770EF?style=flat-square&labelColor=2b2b2b" alt="Browse Guides"></a>

<br />

## <a href="./#gh-dark-mode-only" target="_blank"><img height="18" src="https://octicons-col.vercel.app/people/ffffff" alt="people-dark"></a><a href="./#gh-light-mode-only" target="_blank"><img height="18" src="https://octicons-col.vercel.app/people/000000" alt="people"></a>&nbsp;&nbsp;Community Showcase

#### <img height="14" src="https://octicons-col.vercel.app/hubot/A770EF">&nbsp;&nbsp;Agent frameworks & runtimes

> • <img height="14" src="https://octicons-col.vercel.app/workflow/A770EF"> **[Eve](https://eve.dev/docs/sandbox/microsandbox) by Vercel**: Agent framework that ships microsandbox as a sandbox backend.<br />
> • <img height="14" src="https://octicons-col.vercel.app/organization/A770EF"> **[Agentic Coding Quickstart](https://github.com/GSA-TTS/agentic-coding-quickstart) by U.S. GSA**: From zero to a running AI coding agent with USAi in minutes.<br />
> • <img height="14" src="https://octicons-col.vercel.app/package/A770EF"> **[Condukt](https://github.com/tuist/condukt) and [Once](https://github.com/tuist/once) by Tuist**: Elixir agentic engine, and cacheable actions that run in fresh sandboxes.<br />
> • <img height="14" src="https://octicons-col.vercel.app/link/A770EF"> **[langchain-microsandbox](https://github.com/kenwoodjw/langchain-microsandbox) by kenwoodjw**: Microsandbox integration for LangChain Deep Agents.<br />
> • <img height="14" src="https://octicons-col.vercel.app/history/A770EF"> **[Smithers](https://github.com/smithersai/smithers) by Smithers**: Agent workflows with full observability, rewind, fork, and replay.<br />
> • <img height="14" src="https://octicons-col.vercel.app/organization/A770EF"> **[AgentConnect](https://github.com/agentconnect-md/agentconnect)**: Bring multiple AI agents into your team's chats, issues, and pull requests.<br />
> • <img height="14" src="https://octicons-col.vercel.app/terminal/A770EF"> **[wrap](https://github.com/tobi/wrap) by Tobi Lütke**: Run coding agents and project commands in isolated Arch Linux microVMs.<br />
> • <img height="14" src="https://octicons-col.vercel.app/shield-lock/A770EF"> **[Agent VM](https://github.com/wirenboard/agent-vm) by Wiren Board**: Run AI agents in safe VMs scoped to a local folder.

#### <img height="14" src="https://octicons-col.vercel.app/cpu/A770EF">&nbsp;&nbsp;Tools & infrastructure

> • <img height="14" src="https://octicons-col.vercel.app/browser/A770EF"> **[h5i](https://github.com/h5i-dev/h5i) by h5i**: Secure, auditable browser for AI agents, written in pure Rust.<br />
> • <img height="14" src="https://octicons-col.vercel.app/cloud/A770EF"> **[Devsy](https://github.com/devsy-org/devsy) by Devsy**: Deploy devcontainers onto any cloud, Kubernetes cluster, or Docker host.<br />
> • <img height="14" src="https://octicons-col.vercel.app/briefcase/A770EF"> **[OpenWork](https://github.com/different-ai/openwork) by Different AI**: Open-source Claude Cowork alternative with a microsandbox image.

#### <img height="14" src="https://octicons-col.vercel.app/list-unordered/A770EF">&nbsp;&nbsp;Guides & showcases

> • <img height="14" src="https://octicons-col.vercel.app/list-unordered/A770EF"> **[Awesome Microsandbox](https://github.com/ya-luotao/awesome-microsandbox) by ya-luotao**: Curated list of SDKs, integrations, tools, and resources.<br />
> • <img height="14" src="https://octicons-col.vercel.app/device-desktop/A770EF"> **[msb-omarchy](https://github.com/ya-luotao/msb-omarchy) by ya-luotao**: Omarchy desktop with graphics inside a microVM on Apple Silicon.

<br />

<a href="https://discord.gg/T95Y3XnEAK"><img src="https://img.shields.io/badge/Share_a_Project-%E2%86%92-A770EF?style=flat-square&labelColor=2b2b2b" alt="Share a Project"></a>

<br />

## <a href="./#gh-dark-mode-only" target="_blank"><img height="18" src="https://octicons-col.vercel.app/dependabot/ffffff" alt="agents-dark"></a><a href="./#gh-light-mode-only" target="_blank"><img height="18" src="https://octicons-col.vercel.app/dependabot/000000" alt="agents"></a>&nbsp;&nbsp;AI Agents

#### <img height="14" src="https://octicons-col.vercel.app/book/A770EF">&nbsp;&nbsp;Agent Skills

> Teach any AI coding agent how to use microsandbox by installing the [Agent Skills](https://github.com/superradcompany/skills). Works with Claude Code, Cursor, Codex, Gemini CLI, GitHub Copilot, and more.
>
> ```sh
> npx skills add superradcompany/skills
> ```

#### <img height="14" src="https://octicons-col.vercel.app/plug/A770EF">&nbsp;&nbsp;MCP Server

> Connect any MCP-compatible agent to microsandbox with the [MCP server](https://github.com/superradcompany/microsandbox-mcp). Provides structured tool calls for sandbox lifecycle, command execution, filesystem access, volumes, and monitoring.
>
> ```sh
> # Claude Code
> claude mcp add --transport stdio microsandbox -- npx -y microsandbox-mcp
> ```

<br />

## <a href="./#gh-dark-mode-only" target="_blank"><img height="18" src="https://octicons-col.vercel.app/book/ffffff" alt="docs-dark"></a><a href="./#gh-light-mode-only" target="_blank"><img height="18" src="https://octicons-col.vercel.app/book/000000" alt="docs"></a>&nbsp;&nbsp;Documentation

For guides, API references, and examples, visit the [microsandbox documentation](https://docs.microsandbox.dev).

<br />

## <a href="./#gh-dark-mode-only" target="_blank"><img height="18" src="https://octicons-col.vercel.app/gear/ffffff" alt="contributing-dark"></a><a href="./#gh-light-mode-only" target="_blank"><img height="18" src="https://octicons-col.vercel.app/gear/000000" alt="contributing"></a>&nbsp;&nbsp;Contributing

Interested in contributing to `microsandbox`? Check out our [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines and [DEVELOPMENT.md](./DEVELOPMENT.md) for build, test, and release instructions.

<br />

## <a href="./#gh-dark-mode-only" target="_blank"><img height="18" src="https://octicons-col.vercel.app/law/ffffff" alt="license-dark"></a><a href="./#gh-light-mode-only" target="_blank"><img height="18" src="https://octicons-col.vercel.app/law/000000" alt="license"></a>&nbsp;&nbsp;License

This project is licensed under the [Apache License 2.0](./LICENSE).

<br />

## <a href="./#gh-dark-mode-only" target="_blank"><img height="18" src="https://octicons-col.vercel.app/heart/ffffff" alt="acknowledgements-dark"></a><a href="./#gh-light-mode-only" target="_blank"><img height="18" src="https://octicons-col.vercel.app/heart/000000" alt="acknowledgements"></a>&nbsp;&nbsp;Acknowledgements

Special thanks to all our contributors, testers, and community members who help make microsandbox better every day! We'd like to thank the following projects and communities that made `microsandbox` possible: [libkrun](https://github.com/containers/libkrun) and [smoltcp](https://github.com/smoltcp-rs/smoltcp)

<br />

<div align='center'>
  <a href="https://www.ycombinator.com/"><img src="https://img.shields.io/badge/BACKED%20BY-Y%20COMBINATOR-F26522?style=for-the-badge&logo=ycombinator&logoColor=white" alt="Backed by Y Combinator"></a>
</div>

<br />
<br />

[^demo]: We have cool demos showcasing vm pause/resume, snapshotting, and forking: [Mario Never Dies](https://github.com/superradcompany/mario-never-dies) and [Multiverse of Madness](https://github.com/superradcompany/multiverse-of-madness)
[^boot-time]: Boot time refers to guest boot on an M1 machine.

---

### Overview (`examples/README.md`)

# Examples

Examples showing how to use the microsandbox SDK in TypeScript, Rust, Python, Go, and Ruby.

Each language directory has its own README with setup and run instructions.

Most configuration-focused examples intentionally replace their fixed-name sandbox so every run uses the image, resources, mounts, networking, and secrets shown in the source. The interactive `shell-attach` example instead uses connect-or-create because preserving its workspace across runs is useful. Examples that generate a unique name use strict creation.

| Language | Directory | README |
|----------|-----------|--------|
| TypeScript | [`typescript/`](./typescript/) | [typescript/README.md](./typescript/README.md) |
| Rust | [`rust/`](./rust/) | [rust/README.md](./rust/README.md) |
| Python | [`python/`](./python/) | [python/README.md](./python/README.md) |
| Go | [`../sdk/go/examples/`](../sdk/go/examples/) | [../sdk/go/examples/README.md](../sdk/go/examples/README.md) |
| Ruby | [`../sdk/ruby/examples/`](../sdk/ruby/examples/) | [../sdk/ruby/README.md](../sdk/ruby/README.md) |

## Prerequisites

Some examples use git submodules for sample Alpine rootfs assets:

```sh
git submodule update --init --recursive
```

## Examples

| Example | Description |
|---------|-------------|
| `lifecycle-convergence` | Live convergence, restart, destroy, and stale-handle identity-safety checks |
| `root-oci` | Create a sandbox from an OCI image (e.g. `alpine`) |
| `root-bind` | Create a sandbox from a local bind-mounted directory |
| `root-block` | Create a sandbox from a qcow2 disk image |
| `rootfs-patch` | Pre-boot filesystem modifications (files, dirs, appends) |
| `init-handoff` | Hand PID 1 inside the guest off to systemd |
| `volume-named` | Persistent named volume shared between sandboxes |
| `volume-disk` | Mount raw / qcow2 disk images at arbitrary guest paths |
| `fs-read-stream` | Stream a large file from the sandbox in chunks |
| `metrics-stream` | Subscribe to streaming resource metrics |
| `shell-attach` | Reusable interactive terminal session using connect-or-create |
| `net-basic` | DNS resolution, HTTP fetch, interface status |
| `net-dns` | DNS filtering — block domains and suffixes |
| `net-policy` | Network policies — public-only, allow-all, no-network |
| `net-ports` | Port publishing — expose guest services on host ports |
| `net-secrets` | Secret injection with TLS placeholder substitution |
| `net-tls` | TLS interception with per-domain cert generation |

---

### Overview (`crates/filesystem/README.md`)

# microsandbox-filesystem

Filesystem backends for [microsandbox](https://github.com/superradcompany/microsandbox) virtual machines. This crate now exposes the three backends the runtime still uses: `PassthroughFs`, `MemFs`, and `DualFs`.

- **No root required** — runs unprivileged; guest-visible ownership and permissions are virtualized without touching the host filesystem
- **Runs on macOS too** — the guest sees a full Linux filesystem even when the host runs macOS
- **Zero-copy I/O** — file reads and writes flow directly between host and guest with no intermediate allocation
- **Composable** — all backends implement `DynFileSystem` and can be nested freely

## Backends

### PassthroughFs

Exposes a single host directory to the guest VM. The guest sees standard Linux ownership, permissions, and file types even when the host runs macOS.

```rust
let fs = PassthroughFs::builder()
    .root_dir("/path/to/host/dir")
    .build()?;
```

Metadata like uid, gid, and mode are stored in an extended attribute on the host, so the actual host file permissions stay untouched. Special files (devices, sockets, FIFOs) are stored as regular files with their type bits in the xattr. Path confinement prevents the guest from escaping the root directory.

### MemFs

A pure in-memory filesystem — no host I/O at all. Good for scratch space and ephemeral workloads.

```rust
use microsandbox_filesystem::SizeExt;

let fs = MemFs::builder()
    .capacity(64.mib())
    .max_inodes(10_000)
    .build()?;
```

### DualFs

Combines two backends under a single mount point with a dispatch policy that routes each operation to the right backend.

```rust
let fs = DualFs::builder()
    .backend_a(mem_fs)
    .backend_b(passthrough_fs)
    .policy(ReadBackendBWriteBackendA)
    .build()?;
```

Built-in policies:

| Policy | Behavior |
|---|---|
| `ReadBackendBWriteBackendA` | Reads from B, writes to A (default) |
| `BackendAOnly` | Everything goes to A |
| `BackendAFallbackToBackendBRead` | Reads try A first, fall back to B |
| `MergeReadsBackendAPrecedence` | Merge directory listings, A wins on conflicts |

When a write targets a file that lives on the other backend, `DualFs` copies it over automatically.

## Cache Policies

All backends support configurable kernel caching:

| Policy | Behavior |
|---|---|
| `Never` | No caching — every read hits the backend (DIRECT_IO) |
| `Auto` | Kernel decides (default for PassthroughFs and DualFs) |
| `Always` | Aggressive caching (default for MemFs, since memory is authoritative) |

```rust
let fs = PassthroughFs::builder()
    .root_dir("/path/to/dir")
    .cache_policy(CachePolicy::Always)
    .build()?;
```

---

### Overview (`crates/image/README.md`)

# microsandbox-image

Pull OCI container images and cache ready-to-mount filesystem artifacts locally. This crate handles the full image lifecycle for [microsandbox](https://github.com/superradcompany/microsandbox) — from resolving a multi-platform manifest to producing shared per-layer EROFS inputs, stitched layered roots, and flat ext4 roots.

- **Multi-platform resolution** — automatically picks the right manifest for your OS and architecture
- **Parallel materialization** — layers download, verify, ingest, and materialize concurrently
- **Content-addressable caching** — duplicate layers across images are stored once, with cross-process safety via `flock()`
- **Progress streaming** — real-time events for download, layer materialization, and fsmeta/VMDK stitching stages

The target-aware layer reuse and publication contract is specified in [MATERIALIZATION.md](MATERIALIZATION.md).

## Quick Start

```rust
use std::path::Path;

use microsandbox_image::{GlobalCache, Platform, PullOptions, Registry};

let cache = GlobalCache::new(Path::new("/path/to/cache"))?;
let platform = Platform::host_linux();
let registry = Registry::new(platform, cache)?;

let reference = "docker.io/library/alpine".parse()?;
let result = registry.pull(&reference, &PullOptions::default()).await?;

// Materialized layer diff_ids, bottom-to-top
for diff_id in &result.layer_diff_ids {
    println!("{diff_id}");
}

// Parsed image config (env, cmd, entrypoint, user, etc.)
println!("entrypoint: {:?}", result.config.entrypoint);
println!("env: {:?}", result.config.env);
```

## Authentication

```rust
use microsandbox_image::RegistryAuth;

// Public registries — no credentials needed
let registry = Registry::new(platform, cache)?;

// Private registries
let auth = RegistryAuth::Basic {
    username: "user".into(),
    password: "token".into(),
};
let registry = Registry::builder(platform, cache).auth(auth).build()?;
```

## Pull Policies

Control when the crate contacts the registry vs. uses the local cache.

```rust
use microsandbox_image::{PullOptions, PullPolicy, RootfsMaterialization};

// Use cache if available, download otherwise (default)
let opts = PullOptions { pull_policy: PullPolicy::IfMissing, ..Default::default() };

// Always fetch a fresh manifest, but reuse cached layers by digest
let opts = PullOptions { pull_policy: PullPolicy::Always, ..Default::default() };

// Cache-only — error if the image isn't already cached
let opts = PullOptions { pull_policy: PullPolicy::Never, ..Default::default() };

// Reuse shared EROFS layers, then produce flat ext4 without fsmeta/VMDK
let opts = PullOptions {
    materialization: RootfsMaterialization::Flat,
    ..Default::default()
};
```

## Progress Reporting

Stream progress events while the download runs in the background.

```rust
use microsandbox_image::PullProgress;

let (mut progress, task) = registry.pull_with_progress(&reference, &PullOptions::default());

tokio::spawn(async move {
    while let Some(event) = progress.recv().await {
        match event {
            PullProgress::Resolved { layer_count, .. } => {
                println!("Pulling {layer_count} layers");
            }
            PullProgress::LayerDownloadProgress { downloaded_bytes, total_bytes, .. } => {
                println!("Downloaded {downloaded_bytes}/{total_bytes:?} bytes");
            }
            PullProgress::Complete { .. } => {
                println!("Done!");
            }
            _ => {}
        }
    }
});

let result = task.await??;
```

## Multi-Platform Images

Fat manifests (OCI Image Index) are automatically resolved to your target platform. `Platform::host_linux()` detects the current architecture by default.

```rust
use microsandbox_image::Platform;

// Auto-detect (e.g. linux/amd64 or linux/arm64)
let platform = Platform::host_linux();

// Explicit platform
let platform = Platform::new("linux", "arm64");

// With variant (e.g. armv7)
let platform = Platform::with_variant("linux", "arm", "v7");
```

---

### Overview (`examples/python/README.md`)

# Python Examples

## Prerequisites

- Python 3.10+
- [uv](https://docs.astral.sh/uv/) and [maturin](https://www.maturin.rs/) (`uv tool install maturin`)
- `msb` + `libkrunfw` installed (bundled in the wheel, or at `~/.microsandbox/`)
- For `root-bind` and `root-block`: `git submodule update --init --recursive`

## Setup

Build the Python SDK (one-time):

```sh
cd sdk/python
uv sync --group dev
maturin develop --release
```

## Running

From the repo root:

```sh
uv run --project sdk/python python examples/python/<example>/main.py
```

For example:

```sh
uv run --project sdk/python python examples/python/root-oci/main.py
uv run --project sdk/python python examples/python/net-basic/main.py
uv run --project sdk/python python examples/python/fs-read-stream/main.py
```

Configuration-focused examples intentionally replace their fixed-name sandbox so reruns match the source. `shell-attach` uses `connect_or_create` instead, preserving its interactive workspace and configuration across runs.

## Examples

| Example | Description |
|---------|-------------|
| `lifecycle-convergence` | Live convergence and stale-handle identity-safety checks with timing metrics |
| `cloud-backend` | Cloud backend lifecycle and live logs |
| `root-oci` | OCI image rootfs |
| `root-bind` | Bind-mounted local directory |
| `root-block` | qcow2 disk image |
| `rootfs-patch` | Pre-boot filesystem patches |
| `init-handoff` | Hand PID 1 off to systemd |
| `volume-named` | Named volumes shared across sandboxes |
| `volume-disk` | Disk image volumes (raw / qcow2) at guest paths |
| `snapshot-fork` | Snapshot a stopped sandbox and boot a fresh one from it |
| `fs-read-stream` | Streaming file read |
| `metrics-stream` | Streaming resource metrics |
| `shell-attach` | Reusable interactive shell using `connect_or_create` |
| `net-basic` | Basic networking |
| `net-dns` | DNS filtering |
| `net-policy` | Network policies |
| `net-ports` | Port publishing |
| `net-secrets` | Secret injection |
| `net-tls` | TLS interception |

The `snapshot-fork` example keeps its historical directory name; it demonstrates restoring a saved disk snapshot. Live forking uses the `fork` API.

---

### Overview (`examples/rust/README.md`)

# Rust Examples

## Prerequisites

- [Rust](https://rustup.rs/) (2024 edition)
- `msb` + `libkrunfw` installed (via `cargo build` with the `download-binaries` feature, or manually)
- For `root-bind` and `root-block`: `git submodule update --init --recursive`

## Running

Each example is a standalone binary crate in the workspace:

```sh
cargo run -p <example-name>
```

For example:

```sh
cargo run -p oci-root
cargo run -p net-basic
cargo run -p fs-read-stream
```

Configuration-focused examples intentionally replace their fixed-name sandbox so reruns match the source. `shell-attach` uses `connect_or_create` instead, preserving its interactive workspace and configuration across runs.

## Examples

| Example | Command | Description |
|---------|---------|-------------|
| `lifecycle-convergence` | `cargo run -p lifecycle-convergence` | Live convergence and stale-handle identity-safety checks with timing metrics |
| `cloud-backend` | `cargo run -p cloud-backend` | Cloud backend lifecycle and live logs |
| `root-oci` | `cargo run -p root-oci` | OCI image rootfs |
| `root-bind` | `cargo run -p root-bind` | Bind-mounted local directory |
| `root-block` | `cargo run -p root-block` | qcow2 disk image |
| `rootfs-patch` | `cargo run -p rootfs-patch` | Pre-boot filesystem patches |
| `init-handoff` | `cargo run -p init-handoff` | Hand PID 1 off to systemd |
| `volume-named` | `cargo run -p volume-named` | Named volumes shared across sandboxes |
| `volume-disk` | `cargo run -p volume-disk` | Disk image volumes (raw / qcow2) at guest paths |
| `snapshot-fork` | `cargo run -p snapshot-fork` | Snapshot a stopped sandbox and boot a fresh one from it |
| `fs-read-stream` | `cargo run -p fs-read-stream` | Streaming file read |
| `metrics-stream` | `cargo run -p metrics-stream` | Streaming resource metrics |
| `shell-attach` | `cargo run -p shell-attach` | Reusable interactive shell using `connect_or_create` |
| `net-basic` | `cargo run -p net-basic` | Basic networking |
| `net-dns` | `cargo run -p net-dns` | DNS filtering |
| `net-policy` | `cargo run -p net-policy` | Network policies |
| `net-ports` | `cargo run -p net-ports` | Port publishing |
| `net-secrets` | `cargo run -p net-secrets` | Secret injection |
| `net-tls` | `cargo run -p net-tls` | TLS interception |

The `snapshot-fork` example keeps its historical directory name; it demonstrates restoring a saved disk snapshot. Live forking uses the `fork` API.

---

### Overview (`examples/typescript/README.md`)

# TypeScript Examples

## Prerequisites

- Node.js >= 22
- `msb` + `libkrunfw` available from the `microsandbox` platform package installed through optional dependencies, or `MSB_PATH` pointing at a working `msb` binary
- For `root-bind` and `root-block`: `git submodule update --init --recursive`

## Setup

Each example is a standalone Node.js project. Install dependencies first:

```sh
cd examples/typescript/<example>
npm install
```

## Running

```sh
npm start
```

For example:

```sh
cd examples/typescript/root-oci
npm install
npm start
```

Configuration-focused examples intentionally replace their fixed-name sandbox so reruns match the source. `shell-attach` uses `connectOrCreate` instead, preserving its interactive workspace and configuration across runs.

## Examples

| Example | Description |
|---------|-------------|
| `lifecycle-convergence` | Live convergence and stale-handle identity-safety checks with timing metrics |
| `cloud-backend` | Cloud backend lifecycle and live logs |
| `root-oci` | OCI image rootfs |
| `root-bind` | Bind-mounted local directory |
| `root-block` | qcow2 disk image |
| `rootfs-patch` | Pre-boot filesystem patches |
| `init-handoff` | Hand PID 1 off to systemd |
| `volume-named` | Named volumes shared across sandboxes |
| `volume-disk` | Disk image volumes (raw / qcow2) at guest paths |
| `snapshot-fork` | Snapshot a stopped sandbox and boot a fresh one from it |
| `fs-read-stream` | Streaming file read |
| `metrics-stream` | Streaming resource metrics |
| `shell-attach` | Reusable interactive shell using `connectOrCreate` |
| `net-basic` | Basic networking |
| `net-dns` | DNS filtering |
| `net-policy` | Network policies |
| `net-ports` | Port publishing |
| `net-secrets` | Secret injection |
| `net-tls` | TLS interception |

The `snapshot-fork` example keeps its historical directory name; it demonstrates restoring a saved disk snapshot. Live forking uses the `fork` API.

---

### Overview (`packages/agent-client/README.md`)

# microsandbox agent client

Transport-agnostic clients for speaking the microsandbox agent protocol.

This layer sits between the protocol package and the high-level SDKs:

```text
sdk -> agent-client -> protocol-client -> protocol
```

The agent package supplies connection handshakes, its existing message codec, and protocol-generation gates. Its public client is a specialization of the shared generic router, which owns IDs, framing, transport lifetime, and request/stream routing. SDK packages own sandbox lifecycle, name resolution, image management, volumes, and metrics.

## Layout

```text
packages/agent-client/
├── rust/
└── typescript/
```

- `rust/` publishes as `microsandbox-agent-client`.
- `typescript/` publishes as `@microsandbox/agent-client`.

The Rust crate has no default transport feature. Local SDK connections opt into `uds` on Unix or `named-pipe` on Windows. An owned `AsyncRead + AsyncWrite` transport can also be supplied, including a caller-authenticated WebSocket adapted to bytes.

The TypeScript package exposes a browser-safe default entry and keeps Node-only Unix sockets behind `@microsandbox/agent-client/node`; browser/front-end callers use `WebSocketTransport`.

## Message Model

The public API names protocol boundaries by structure:

- `TypedMessage`: message type plus a native payload object.
- `EncodedMessage`: message type plus already-CBOR-encoded payload bytes.
- `RawFrame`: routing fields and an opaque envelope body.
- `Message` (Rust) / `InboundFrame` (TypeScript): an inspectable envelope retaining its original raw frame.
- `TransportPacket`: exact transport bytes, including length prefix.

Unchecked packet writes are an explicit escape hatch for relays, tests, and specialized tooling.

Rust and TypeScript preserve native, encoded, raw, exact-packet, explicit-ID, and owned split-stream access. See each package README for the API migration and validated examples. Shared TypeScript packages build together through the private npm workspace in `packages`.

---

### Overview (`packages/microsandbox-types/README.md`)

# microsandbox types

Shared task and wire contract types for microsandbox.

These packages define the backend-neutral shapes that describe what a sandbox should be: its rootfs source, resources, mounts, patches, network, lifecycle, and the cloud HTTP request/response bodies that carry those specs over the wire. Everything that has to agree on a sandbox's shape (the Rust SDK, the CLI, the cloud API, and the TypeScript front end) depends on these contracts instead of redefining them.

This is a contract layer, not a runtime. It does not create, start, or talk to sandboxes. It only models the data those operations exchange.

## Layout

```text
packages/microsandbox-types/
├── rust/
└── typescript/
```

- `rust/` publishes as `microsandbox-types` (crate name `microsandbox_types`). It is the source of truth for every shared type.
- `typescript/` publishes as `@microsandbox/types`. Its `src/cloud.ts` (entry) and `src/domain.ts` are generated from the Rust types, never hand-edited; only the cloud contract and the domain types it references are emitted.

## Source Of Truth

The Rust crate owns the definitions. The TypeScript bindings are derived from them with [`ts-rs`](https://github.com/Aleph-Alpha/ts-rs) behind the crate's `ts` feature, so the two stay byte-for-byte aligned.

```text
rust/lib/*.rs  ──(ts-rs)──▶  typescript/src/{cloud,domain}.ts
```

To regenerate the bindings after changing a Rust type:

```bash
cargo run -p microsandbox-types --features ts --bin microsandbox-types-generate
```

CI runs the same generator with `--check` and fails when the checked-in bindings drift:

```bash
cargo run -p microsandbox-types --features ts --bin microsandbox-types-generate -- --check
```

## What Lives Here

- Sandbox specs: `SandboxSpec`, `SandboxResources`, `SandboxRuntimeOptions`, rootfs sources, mounts, patches, init, lifecycle policy.
- Networking intent: `NetworkSpec`, published ports, protocols.
- Volumes and snapshot manifests: `VolumeSpec`, `SnapshotSpec`, and their kinds.
- Exec and logging: `Rlimit`, `RlimitResource`, `LogSource`, `SandboxLogLevel`.
- Cloud wire contracts: the source-tagged `CloudCreateSandboxRequest` union,
  `CloudSandbox`, snapshot resources and operations, and
  paginated/message/error bodies.

Cloud snapshot contracts distinguish the durable resource from the asynchronous
capture operation:

- `CloudCreateSnapshotRequest` is discriminated by `kind`. Its current `disk`
  variant captures the writable disk, and the enum can gain other snapshot
  kinds without changing the envelope shape.
- `CloudSnapshot` uses the same kind discriminator and includes its canonical
  manifest, byte size, labels, and `CloudSnapshotLocation`.
- `CloudSnapshotLocation` is either a managed artifact ID or a host-volume
  path. The same type is used when restoring a sandbox so location semantics
  are not duplicated.
- `CloudSnapshotOperation` carries the requested kind and tracks capture
  through `queued`, `in_progress`, `succeeded`, or `failed`; `result` is
  populated on success.
- Validation helpers: sandbox-name and hostname rules shared across SDK, CLI, and cloud.

Backend-private materialized state (registry credentials, local cache paths, DB rows, resolved manifest digests, process handles) deliberately stays out of these packages. See each language's README for details.

## Cloud Request Compatibility

Cloud request objects ignore unknown fields, including fields inside network and runtime options. This allows newer SDKs to send additive options before the receiving Cloud release understands them. Unknown settings have no effect until that server supports them; request success does not confirm that every supplied setting was applied.

Missing fields retain their documented defaults. Required fields, recognized field types and values, and enum variants still undergo validation. Ignoring unknown object keys does not make new enum variants, renamed fields, or changed field types compatible.

This policy applies to public Cloud wire types, not host or operator configuration. SDK checks for runtime-only options remain separate from the server's treatment of future Cloud fields.

---

### Overview (`packaging/winget/README.md`)

# Windows Package Manager draft

This directory keeps the source draft for publishing microsandbox to the Windows Package Manager Community Repository.

The official winget package does not live in this repository. The ready-to-submit `0.6.0` manifests are staged under:

```text
packaging/winget/manifests/s/SuperRadCompany/Microsandbox/0.6.0/
```

Copy that directory into a fork of `microsoft/winget-pkgs` under the matching community repository path:

```text
manifests/s/SuperRadCompany/Microsandbox/<version>/
```

The package uses winget's zip + portable installer shape because the microsandbox Windows release bundle contains `msb.exe` next to `libkrunfw.dll`. The manifest sets `ArchiveBinariesDependOnPath: true` so the archive directory is placed on `PATH` and `msb.exe` can load its adjacent DLL.

## How submission works

The **first** version must be submitted to `microsoft/winget-pkgs` by hand using the staged `0.6.0` manifests (see [Submit 0.6.0](#submit-060) below). Komac, which the release automation uses, refuses to open a PR for a package that does not yet exist in the community repository.

After that first version lands, set the `WINGET_RELEASE_ENABLED` repository variable to `true` to automate subsequent releases. The `update-winget` job in [`.github/workflows/release.yml`](../../.github/workflows/release.yml) then runs on every `v*` tag: it invokes [`winget-releaser`](https://github.com/vedantmgoyal9/winget-releaser) (Komac), which copies the previously published manifests, bumps the version, rewrites the installer URLs and SHA256 hashes from the release's `microsandbox-windows-*.zip` assets, and opens a PR against `microsoft/winget-pkgs`.

One-time setup for the automation:

- After the first version is accepted into `microsoft/winget-pkgs`, set the `WINGET_RELEASE_ENABLED` repository variable to `true`. Keep it unset before then so release workflows skip the `update-winget` job.
- Add a `WINGET_TOKEN` repository secret: a classic PAT with `public_repo` scope whose owner has a fork of `microsoft/winget-pkgs`. The submit step is skipped when this secret is absent.
- If that fork lives under an account other than `superradcompany`, set a `WINGET_FORK_USER` repository variable to the fork owner's username.

Prerelease tags (`-rc`, `-alpha`, `-beta`, `-dev`) are skipped, matching the release job's prerelease detection. The manual [Render a release manifest](#render-a-release-manifest) flow below remains as a fallback if the automation is ever unavailable.

## Submit 0.6.0

1. Fork and clone `microsoft/winget-pkgs`.
2. Copy the staged manifests into the fork:

```powershell
$version = "0.6.0"
$src = "C:\src\microsandbox\packaging\winget\manifests\s\SuperRadCompany\Microsandbox\$version"
$dst = "C:\src\winget-pkgs\manifests\s\SuperRadCompany\Microsandbox\$version"
New-Item -ItemType Directory -Force -Path $dst | Out-Null
Copy-Item "$src\*.yaml" $dst
```

3. Validate and smoke-test from the `winget-pkgs` checkout on Windows:

```powershell
winget validate $dst
winget install --manifest $dst --accept-package-agreements --accept-source-agreements
msb doctor
winget uninstall SuperRadCompany.Microsandbox
```

4. Commit the copied files in the `winget-pkgs` fork and open a pull request against `microsoft/winget-pkgs`.

The local staged manifests are based on GitHub release `v0.6.0`, published on `2026-06-27`, with SHA256 values from the release `checksums.sha256` asset.

## Render a release manifest

1. Wait for a release that includes:
   - `microsandbox-windows-x86_64.zip`
   - `microsandbox-windows-aarch64.zip`
   - `checksums.sha256`
2. Copy the template files into the winget-pkgs path:

```powershell
$version = "0.5.9"
$tag = "v$version"
$dst = "C:\src\winget-pkgs\manifests\s\SuperRadCompany\Microsandbox\$version"
New-Item -ItemType Directory -Force -Path $dst | Out-Null
Copy-Item packaging\winget\SuperRadCompany.Microsandbox\*.yaml $dst
```

3. Replace template markers:
   - `{{VERSION}}` with the semver package version, for example `0.5.9`
   - `{{TAG}}` with the GitHub release tag, for example `v0.5.9`
   - `{{RELEASE_DATE}}` with the release date in `YYYY-MM-DD`
   - `{{SHA256_X64}}` with the checksum for `microsandbox-windows-x86_64.zip`
   - `{{SHA256_ARM64}}` with the checksum for `microsandbox-windows-aarch64.zip`

The checksums are available from the release asset:

```powershell
gh release download $tag --repo superradcompany/microsandbox --pattern checksums.sha256 --dir $env:TEMP --clobber
Select-String -Path "$env:TEMP\checksums.sha256" -Pattern "microsandbox-windows"
```

4. Validate and smoke-test from the winget-pkgs checkout:

```powershell
winget validate $dst
winget install --manifest $dst --accept-package-agreements --accept-source-agreements
msb doctor
winget uninstall SuperRadCompany.Microsandbox
```

5. Submit the rendered files as a PR to `microsoft/winget-pkgs`.

Do not submit these templates directly. The community repository requires concrete release URLs and SHA256 hashes.

---

### Overview (`scripts/msb-trust/README.md`)

# Application trust helper

Users download `scripts/msb-trust/msb-trust.sh`. Edit the sources here, then rebuild:

```sh
sh scripts/msb-trust/build.sh
```

`core.sh` handles application selection, dependency reporting, and summaries.
Each file in `apps/` contains one application's setup and help. The build discovers
these files alphabetically and produces a standalone POSIX shell script. No source
files or build tools are needed in the sandbox.

## Add an application

Create `apps/<name>.sh` using lowercase letters, digits, and underscores, starting
with a letter. Use an existing handler as a starting point. Define these functions:

| Function | Purpose |
| --- | --- |
| `<name>_tools` | Print required command names, separated by whitespace. |
| `<name>_packages` | Receive a package manager (`apt-get`, `dnf`, or empty), followed by missing tools. Print the corresponding package names, or nothing for manual setup. |
| `<name>_help` | Print requirements and application-specific instructions. |
| `<name>_configure` | Import the CA at `$CA_CERT`, preserving existing certificates. |

Optional functions are `<name>_description` for the help listing,
`<name>_available` for detection beyond commands on `PATH`, and
`<name>_install_hint` for additional prerequisite instructions.

Keep files limited to function definitions. Handlers run in separate subshells;
explicitly guard fallible operations with `|| fail "useful explanation"` because
batch dispatch disables shell `errexit`. Use `progress` for success messages so
batch runs can print one combined summary. Never install packages automatically.

Rebuild and include both the source and generated script in your change. CI runs
`sh scripts/msb-trust/build.sh --check` to reject stale output or missing required
functions. Validate setup, repeat runs, and failures against the real application.

---

### Overview (`sdk/go/README.md`)

# microsandbox

Lightweight VM sandboxes for Go applications that need hardware-level isolation for AI agents, tools, tests, and untrusted code.

The `github.com/superradcompany/microsandbox/sdk/go` module provides Go bindings to the [microsandbox](https://github.com/superradcompany/microsandbox) runtime. It creates microVM-backed sandboxes from OCI images or other rootfs sources, then exposes command execution, guest filesystem access, networking, secrets, volumes, metrics, logs, snapshots, and SSH/SFTP through an idiomatic Go API.

For the full API reference and longer guides, use Go docs and the microsandbox docs site:

- [pkg.go.dev](https://pkg.go.dev/github.com/superradcompany/microsandbox/sdk/go)
- [Go SDK guide](https://docs.microsandbox.dev/sdk/go/sandbox)
- [SDK overview](https://docs.microsandbox.dev/sdk/overview)
- [Repository examples](./examples)

`EnsureRuntime` reuses any complete `msb` and `libkrunfw` pair in `MSB_HOME` (or `~/.microsandbox` by default), without executing the binary or requiring its version to equal the SDK version. It refuses partial installations. The SDK version selects newly downloaded artifacts; it does not trigger replacement of an existing pair.

## Features

- Hardware VM isolation with a guest Linux kernel
- Go API over an embedded CGO FFI library
- Collected and streaming command execution
- Guest filesystem read, write, list, copy, stat, and stream operations
- Named volumes, bind mounts, tmpfs mounts, and disk-image mounts
- Network policies, DNS filtering, TLS interception, secrets, and port publishing
- Rootfs patches before boot
- Detached sandboxes that can outlive the Go process
- Metrics, logs, snapshots, SSH/SFTP, image cache, and local/cloud backend helpers

## Requirements

- Go 1.22+
- CGO enabled and a C compiler/toolchain available
- Linux with KVM, macOS with Apple Silicon, or Windows 11 with WHP enabled

## Supported Platforms

| Platform | Architecture | Notes |
| --- | --- | --- |
| macOS | ARM64 / Apple Silicon | Embedded FFI library |
| Linux | x86_64 | Embedded FFI library |
| Linux | ARM64 | Embedded FFI library |
| Windows | x86_64, ARM64 | Embedded FFI library; runtime support is in preview |

The Go binary embeds the SDK FFI library and extracts it on first use. The `msb` runtime and `libkrunfw` are installed separately into `MSB_HOME` (default `~/.microsandbox/`) by `EnsureRuntime` (`%USERPROFILE%\.microsandbox` on Windows).

## Installation

```bash
go get github.com/superradcompany/microsandbox/sdk/go
```

Call `EnsureRuntime` at process startup when your program will create local sandboxes. It is idempotent and surfaces runtime download/setup failures before the first sandbox operation.

```go
if _, err := microsandbox.EnsureRuntime(ctx, microsandbox.RuntimeConfig{}, microsandbox.InstallOptions{}); err != nil {
    log.Fatalf("microsandbox setup: %v", err)
}
```

Use `IsRuntimeInstalled(RuntimeConfig{})` for a boolean availability check or `ResolveRuntime(RuntimeConfig{})` to require an existing pair and obtain its paths without installing runtime binaries. `InstallRuntime` explicitly provisions the selected source. All setup calls use the shared Rust resolver and installer; the first call may also materialize the embedded SDK FFI library.

## Quick Start

```go
package main

import (
    "context"
    "fmt"
    "log"
    "time"

    microsandbox "github.com/superradcompany/microsandbox/sdk/go"
)

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute)
    defer cancel()

    if _, err := microsandbox.EnsureRuntime(ctx, microsandbox.RuntimeConfig{}, microsandbox.InstallOptions{}); err != nil {
        log.Fatal(err)
    }

    name := "go-readme"
    sb, err := microsandbox.CreateSandbox(ctx, name,
        microsandbox.WithImage("alpine:3.19"),
        microsandbox.WithMemory(512),
        microsandbox.WithCPUs(1),
        microsandbox.WithReplace(),
    )
    if err != nil {
        log.Fatal(err)
    }
    defer func() {
        stopCtx, stopCancel := context.WithTimeout(context.Background(), 30*time.Second)
        defer stopCancel()
        _ = sb.Stop(stopCtx)
        _ = sb.Close()
        _ = microsandbox.RemoveSandbox(context.Background(), name)
    }()

    out, err := sb.Shell(ctx, "echo 'Hello from microsandbox!'")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(out.Stdout())
}
```

### Reusable Lifecycle Convergence

Use `ConnectOrCreateSandbox` when a stable name should converge on one persisted sandbox. Existing configuration wins; options are used only if creation is necessary. Handles retain a stable `ID`, so lifecycle calls on stale receivers refuse to act on a replacement that reused the name.

```go
sb, err := microsandbox.ConnectOrCreateSandbox(ctx, "worker",
    microsandbox.WithImage("python"),
    microsandbox.WithMemory(1024),
)

fmt.Printf("%s: %s\n", sb.Name(), sb.ID())
handle, err := microsandbox.GetSandbox(ctx, "worker")
running, err := handle.ConnectOrStart(ctx)
err = running.RequestStop(ctx)
stopped, err := running.WaitForStatus(ctx, microsandbox.SandboxStatusStopped)
restarted, err := stopped.Restart(ctx)
err = restarted.Destroy(ctx)
```

## Common Examples

These snippets assume you already have a live `sb *microsandbox.Sandbox` and `ctx context.Context`. See [sdk/go/examples](./examples) for complete runnable programs.

### Fork a Live Sandbox

Forking copies a running or paused local sandbox's disk and execution state into an independent child. Memory uses copy-on-write automatically. The source keeps its previous running or paused state. Host resources require explicit bindings; see [forking and resource bindings](https://docs.microsandbox.dev/sandboxes/snapshots#forking).

```go
child, err := sb.Fork(ctx, "experiment")
if err != nil { return err }
defer child.Close()
if err := child.Stop(ctx); err != nil { return err }
```

Use `ForkMany(ctx, []string{"alice", "bob"})` to capture once for several children. Inspect every returned outcome: one child's startup failure does not remove successful siblings. See the [fork API reference](https://docs.microsandbox.dev/sdk/go/sandbox#forking).

Restoring starts from a saved snapshot instead. Use `WithCowMemory()` to request copy-on-write memory for a full-snapshot restore. A generation describes snapshot-history progression; a branch describes a distinct path through that history. The former live branch APIs and old CoW restore names remain deprecated aliases. See [restore migration notes](https://docs.microsandbox.dev/sandboxes/snapshots#migrating-restore-options) for the old-to-new names and language-specific deprecation notices.

### Command Execution

```go
out, err := sb.Exec(ctx, "python3", []string{"-c", "print(1 + 1)"})
if err != nil {
    log.Fatal(err)
}
fmt.Println(out.Stdout())
fmt.Println(out.ExitCode())

out, err = sb.Shell(ctx, "echo hello && pwd")

out, err = sb.Exec(ctx, "make", []string{"build"},
    microsandbox.WithExecCwd("/app"),
    microsandbox.WithExecTimeout(30*time.Second),
)
```

Non-zero process exits are represented on `ExecOutput`; they are not Go errors by themselves.

### Streaming Execution

```go
handle, err := sb.ShellStream(ctx, "tail -f /var/log/app.log")
if err != nil {
    log.Fatal(err)
}
defer handle.Close()

for {
    event, err := handle.Recv(ctx)
    if err != nil {
        break
    }
    switch event.Kind {
    case microsandbox.ExecEventStdout:
        _, _ = os.Stdout.Write(event.Data)
    case microsandbox.ExecEventStderr:
        _, _ = os.Stderr.Write(event.Data)
    case microsandbox.ExecEventExited, microsandbox.ExecEventDone:
        return
    }
}
```

### Filesystem Operations

```go
fs := sb.FS()

if err := fs.WriteString(ctx, "/tmp/config.json", `{"debug": true}`); err != nil {
    log.Fatal(err)
}

content, err := fs.ReadString(ctx, "/tmp/config.json")
if err != nil {
    log.Fatal(err)
}
fmt.Println(content)

entries, err := fs.List(ctx, "/etc")
if err != nil {
    log.Fatal(err)
}
for _, entry := range entries {
    fmt.Printf("%s (%s)\n", entry.Path, entry.Kind)
}
```

### Named Volumes

```go
volume, err := microsandbox.CreateVolume(ctx, "go-readme-data",
    microsandbox.WithVolumeQuota(100),
    microsandbox.WithVolumeLabels(map[string]string{"purpose": "readme"}),
)
if err != nil {
    log.Fatal(err)
}
defer volume.Remove(ctx)

writer, err := microsandbox.CreateSandbox(ctx, "go-readme-writer",
    microsandbox.WithImage("alpine:3.19"),
    microsandbox.WithMounts(map[string]microsandbox.MountConfig{
        "/data": microsandbox.Mount.Named("go-readme-data", microsandbox.MountOptions{}),
    }),
    microsandbox.WithReplace(),
)
if err != nil {
    log.Fatal(err)
}
defer writer.Close()

_, err = writer.Shell(ctx, "echo 'hello' > /data/message.txt")
```

### Network, DNS, and Ports

```go
isolated, err := microsandbox.CreateSandbox(ctx, "go-readme-isolated",
    microsandbox.WithImage("alpine:3.19"),
    microsandbox.WithNetwork(microsandbox.NetworkPolicy.None()),
    microsandbox.WithReplace(),
)
if err != nil {
    log.Fatal(err)
}
defer isolated.Close()

filtered, err := microsandbox.CreateSandbox(ctx, "go-readme-filtered",
    microsandbox.WithImage("alpine:3.19"),
    microsandbox.WithNetwork(&microsandbox.NetworkConfig{
        DenyDomains:        []string{"blocked.example.com"},
        DenyDomainSuffixes: []string{".evil.com"},
        DNS: &microsandbox.DNSConfig{
            Nameservers: []string{"1.1.1.1:53"},
        },
    }),
    microsandbox.WithReplace(),
)
if err != nil {
    log.Fatal(err)
}
defer filtered.Close()

web, err := microsandbox.CreateSandbox(ctx, "go-readme-web",
    microsandbox.WithImage("python:3.12"),
    microsandbox.WithPorts(map[uint16]uint16{8080: 80}),
    microsandbox.WithReplace(),
)
```

### Secrets

Secrets use placeholder substitution. The real value stays on the host and is substituted only for allowed network destinations.

```go
sb, err := microsandbox.CreateSandbox(ctx, "go-readme-agent",
    microsandbox.WithImage("python:3.12"),
    microsandbox.WithSecrets(microsandbox.Secret.Env(
        "OPENAI_API_KEY",
        os.Getenv("OPENAI_API_KEY"),
        microsandbox.SecretEnvOptions{Allow: []string{"api.openai.com"}},
    )),
    microsandbox.WithReplace(),
)
```

### Rootfs Patches

```go
sb, err := microsandbox.CreateSandbox(ctx, "go-readme-patched",
    microsandbox.WithImage("alpine:3.19"),
    microsandbox.WithPatches(
        microsandbox.Patch.Text("/etc/greeting.txt", "Hello!\n", microsandbox.PatchOptions{}),
        microsandbox.Patch.Mkdir("/app", microsandbox.PatchOptions{}),
        microsandbox.Patch.Text("/app/config.json", `{"debug": true}`, microsandbox.PatchOptions{}),
        microsandbox.Patch.Append("/etc/hosts", "127.0.0.1 myapp.local\n"),
    ),
    microsandbox.WithReplace(),
)
```

### Detached Mode

```go
sb, err := microsandbox.CreateSandbox(ctx, "go-readme-background",
    microsandbox.WithImage("python:3.12"),
    microsandbox.WithDetached(),
    microsandbox.WithReplace(),
)
if err != nil {
    log.Fatal(err)
}
_ = sb.Detach(ctx)

handle, err := microsandbox.GetSandbox(ctx, "go-readme-background")
if err != nil {
    log.Fatal(err)
}
reconnected, err := handle.Connect(ctx)
if err != nil {
    log.Fatal(err)
}
out, err := reconnected.Shell(ctx, "echo reconnected")
```

### Metrics

```go
metrics, err := sb.Metrics(ctx)
if err != nil {
    log.Fatal(err)
}
fmt.Printf("CPU: %.1f%%\n", metrics.CPUPercent)
fmt.Printf("Memory: %.1f MiB\n", float64(metrics.MemoryBytes)/1024/1024)

stream, err := sb.MetricsStream(ctx, 500*time.Millisecond)
if err != nil {
    log.Fatal(err)
}
defer stream.Close()

sample, err := stream.Recv(ctx)
if err == nil && sample != nil {
    fmt.Printf("CPU: %.1f%%\n", sample.CPUPercent)
}
```

### Storage Usage and Runtime Cache Cleanup

```go
usage, err := microsandbox.StorageUsage(ctx)
if err != nil {
    log.Fatal(err)
}
fmt.Println(usage.BranchMemory.LogicalBytes)

item, err := sb.StorageUsage(ctx)
if err != nil {
    log.Fatal(err)
}
fmt.Println(item.Name, item.LogicalBytes)

preview, err := microsandbox.PruneStorage(ctx, microsandbox.StoragePruneOptions{
    DryRun: true,
    OlderThanSeconds: 600,
})
if err != nil {
    log.Fatal(err)
}
fmt.Println(preview.Entries)
```

Reports contain integer byte counts; optional pointer fields are `nil` when unknown. Logical bytes are file lengths, and allocated bytes may count shared CoW blocks repeatedly. They do not measure exclusive disk ownership. `sb.StorageUsage(ctx)` observes the live sandbox's managed directory through its retained backend and stable identity; host bind mounts and shared runtime-memory caches are excluded.

Go's metadata-only `SandboxHandle`, `SnapshotArtifact`, and `SnapshotHandle` do not yet expose per-object storage methods because they do not retain a native backend-bound receiver. Use the detailed `Sandboxes.Items` and `Snapshots.Items` fields in `StorageUsage(ctx)` for observations through the selected backend.

`PruneStorage` is an explicit cleanup operation and does not prompt. `DryRun: true` only previews; calling with `DryRun: false` rechecks ownership and removes unused runtime RAM. It retains durable snapshots, named volumes, sandbox disks, and handoff locks. Check each entry for partial errors. Logical bytes removed are reported separately from physical bytes reclaimed, which remain unknown. Remote backends and native bundles predating storage support return `ErrUnsupportedOperation`.

### Typed Errors

Go SDK errors can be checked with `IsKind` and unwrapped with `errors.As`.

```go
_, err := microsandbox.GetSandbox(ctx, "missing")
if microsandbox.IsKind(err, microsandbox.ErrSandboxNotFound) {
    fmt.Println("sandbox does not exist")
}

var msbErr *microsandbox.Error
if errors.As(err, &msbErr) {
    fmt.Printf("microsandbox error kind: %s\n", msbErr.Kind)
}
```

## Runnable Examples

Each example is a self-contained `main.go` under [sdk/go/examples](./examples). Run them from `sdk/go`:

```bash
go run ./examples/basic
go run ./examples/network
go run ./examples/snapshot-fork
```

| Example | Covers |
| --- | --- |
| `basic` | Create, exec, filesystem, metrics, stop, close, remove |
| `cloud-backend` | Cloud backend lifecycle and live logs |
| `detached` | Detached lifecycle, reattach, stop, and remove |
| `disk` | Build and mount a raw ext4 disk image |
| `errors` | Error categories with `IsKind` and `errors.As` |
| `filesystem` | Guest filesystem operations and streaming |
| `image-cache` | Cached OCI image list, inspect, remove, and prune |
| `metrics` | Point-in-time and streaming metrics |
| `network` | Presets, DNS, TLS, and custom network settings |
| `patches` | Pre-boot rootfs patches |
| `ports` | Guest TCP port publishing |
| `secrets` | Secret placeholder injection |
| `snapshot-fork` | Snapshot a stopped sandbox and restore a fresh sandbox |
| `streaming` | Streaming exec, signals, and cancellation |
| `tls` | TLS interception configuration |
| `volumes` | Named volume lifecycle |

The `snapshot-fork` example keeps its historical directory name; it demonstrates restoring a saved disk snapshot. Live forking uses the `Fork` API.

## More Documentation

- [Sandbox lifecycle](https://docs.microsandbox.dev/sdk/go/sandbox)
- [Execution](https://docs.microsandbox.dev/sdk/go/execution)
- [Filesystem](https://docs.microsandbox.dev/sdk/go/filesystem)
- [Networking](https://docs.microsandbox.dev/sdk/go/networking)
- [Secrets](https://docs.microsandbox.dev/sdk/go/secrets)
- [Volumes](https://docs.microsandbox.dev/sdk/go/volumes)
- [Snapshots](https://docs.microsandbox.dev/sdk/go/snapshots)
- [Images](https://docs.microsandbox.dev/sdk/go/images)
- [SSH](https://docs.microsandbox.dev/sdk/go/ssh)
- [Agent client](https://docs.microsandbox.dev/sdk/go/agent-client)

## Development

The SDK embeds the FFI library at build time, so normal unit tests do not require a Rust toolchain:

```bash
cd sdk/go
go test -count=1 ./...
```

To iterate on the FFI shim itself, build the native library from the repository root, then run Go commands from `sdk/go` with `MICROSANDBOX_FFI_PATH` and the `microsandbox_ffi_path` build tag:

```bash
cargo build -p microsandbox-go

cd sdk/go
export MICROSANDBOX_FFI_PATH=../../target/debug/libmicrosandbox_go_ffi.dylib
go run -tags microsandbox_ffi_path ./examples/basic
```

Use `libmicrosandbox_go_ffi.so` for Linux. Full integration tests require local virtualization support and runtime artifacts:

```bash
go test -tags "smoke microsandbox_ffi_path" -count=1 .
go test -tags "integration microsandbox_ffi_path" -v -count=1 ./integration/...
```

The storage smoke test uses a fresh process and temporary home, requires no VM, and only prunes its own synthetic RAM files:

```bash
MSB_STORAGE_NATIVE_LIBRARY="$PWD/../../target/debug/libmicrosandbox_go_ffi.dylib" \
    go test -tags storage_native ./internal/ffi -run '^TestStorageNativeReportsAndPruning$' -count=1
```

## License

Apache-2.0

---

### Overview (`sdk/node-ts/README.md`)

# microsandbox

Lightweight VM sandboxes for Node.js and TypeScript applications that need hardware-level isolation for AI agents, tools, tests, and untrusted code.

The `microsandbox` npm package provides TypeScript bindings to the [microsandbox](https://github.com/superradcompany/microsandbox) runtime through a native addon. It creates microVM-backed sandboxes from OCI images or other rootfs sources, then exposes command execution, guest filesystem access, networking, secrets, volumes, metrics, logs, snapshots, and SSH/SFTP through an async TypeScript API.

For the full API reference and longer guides, use the docs site:

- [TypeScript SDK guide](https://docs.microsandbox.dev/sdk/typescript/sandbox)
- [SDK overview](https://docs.microsandbox.dev/sdk/overview)
- [Repository examples](../../examples/typescript)

A complete runtime in the configured home (`MSB_HOME`, or `~/.microsandbox` by default) takes precedence over platform-package binaries. Explicit binary paths still win. A partial home installation errors instead of falling back to the package. This also applies to the `msb` and `microsandbox` CLI entry points.

## Features

- Hardware VM isolation with a guest Linux kernel
- ESM-first API with CommonJS `require()` support and generated native bindings
- Collected and streaming command execution
- Guest filesystem read, write, list, copy, stat, and stream operations
- Named volumes, bind mounts, tmpfs mounts, and disk-image mounts
- Network policies, DNS filtering, TLS interception, secrets, and port publishing
- Rootfs patches before boot
- Detached sandboxes that can outlive the Node.js process
- Metrics, logs, snapshots, SSH/SFTP, image cache, and local/cloud backend helpers

## Requirements

- Node.js 22+
- Linux with KVM, macOS with Apple Silicon, or Windows 11 with WHP enabled
- Windows support is currently preview; see the [Windows troubleshooting guide](https://docs.microsandbox.dev/troubleshooting/windows) for WHP and runtime setup notes.

The package root supports both ESM imports and CommonJS `require()` on Node.js 22+:

```typescript
import { Sandbox } from "microsandbox";
```

```javascript
const { Sandbox } = require("microsandbox");
```

## Supported Platforms

| Platform | Architecture | Platform package |
| --- | --- | --- |
| macOS | ARM64 / Apple Silicon | `@superradcompany/microsandbox-darwin-arm64` |
| Linux | x86_64 | `@superradcompany/microsandbox-linux-x64-gnu` |
| Linux | ARM64 | `@superradcompany/microsandbox-linux-arm64-gnu` |
| Windows | x86_64 | `@superradcompany/microsandbox-win32-x64-msvc` |
| Windows | ARM64 | `@superradcompany/microsandbox-win32-arm64-msvc` |

The matching platform package carries the native addon plus runtime binaries. Keep npm optional dependencies enabled: omitting them also removes the required addon, which `MSB_PATH` cannot replace.

## Installation

```bash
npm install microsandbox
```

Runtime setup is normally included. To provision `msb` + `libkrunfw` separately, use the [CLI installer](https://docs.microsandbox.dev/getting-started/quickstart) or explicitly install them from your application:

```typescript
import { ensureRuntime } from "microsandbox";

const runtime = await ensureRuntime();
console.log(runtime.msbPath, runtime.libkrunfwPath);
```

Use `MSB_PATH` and `MSB_LIBKRUNFW_PATH` to select an external runtime; this does not remove the npm package's bundled files. See [Runtime setup](https://docs.microsandbox.dev/sdk/setup) for custom paths and versions.

## Quick Start

```typescript
import { MiB, Sandbox } from "microsandbox";

await using sandbox = await Sandbox.builder("ts-readme")
  .image("alpine")
  .cpus(1)
  .memory(MiB(512))
  .replace()
  .create();

const output = await sandbox.shell("echo 'Hello from microsandbox!'");
console.log(output.stdout().trim());
```

`await using` calls `Sandbox.stop()` when the handle leaves scope. Use a plain `const sandbox = ...` and call lifecycle methods yourself when you need finer control.

### Reusable Lifecycle Convergence

Use `connectOrCreate` when a stable name should converge on one persisted sandbox. Existing configuration wins; the builder is used only if creation is necessary. Handles retain a stable `id`, so lifecycle calls on stale receivers refuse to act on a replacement that reused the name.

```typescript
const sandbox = await Sandbox.builder("worker")
  .image("python")
  .memory(MiB(1024))
  .connectOrCreate();

console.log(`${sandbox.name}: ${sandbox.id}`);
const running = await (await Sandbox.get("worker")).connectOrStart();
await running.requestStop();
const stopped = await running.waitForStatus("stopped");
const restarted = await stopped.restart();
await restarted.destroy();
```

## Local Storage Usage And Cleanup

`Storage.usage()` reports images, saved snapshots, sandboxes, volumes, branch RAM, and rebuildable snapshot RAM for the selected local backend. Unknown measurements are `null`. Logical byte counts and allocated blocks do not measure exclusive physical ownership because files can share disk blocks. All byte fields are `bigint`, preserving the full unsigned 64-bit range; object counts are numbers. To serialize a report with `JSON.stringify`, supply a replacer that converts bigints to decimal strings.

```typescript
import { Storage } from "microsandbox";

const usage = await Storage.usage();
console.log(usage.branchMemory.logicalBytes); // bigint | null

const preview = await Storage.prune({ dryRun: true, olderThanSeconds: 3600 });
console.log(preview.entries);

const result = await Storage.prune({ olderThanSeconds: 3600 });
console.log(result.logicalBytesRemoved);
for (const entry of result.entries) {
  if (entry.error !== null) console.error(entry.path, entry.error);
}
```

Pruning removes only unused published runtime RAM. Live mappings, retained baselines, and pending handoffs remain protected. Saved snapshots, images, sandbox disks, volumes, and stable lock files are retained. `olderThanSeconds` must be a whole non-negative safe integer; the default is zero. `dryRun` defaults to false. The SDK does not prompt for confirmation, and each apply call rechecks ownership. Individual failures remain in `entries` alongside completed removals; `physicalBytesReclaimed` is unknown (`null`). Cloud backends reject these local-only operations. Each call captures its backend before returning the promise; changing the default while it runs cannot redirect it. An older native addon without these methods raises `UnsupportedOperationError` when they are called.

`SandboxHandle.storageUsage()`, `Snapshot.storageUsage()`, and `SnapshotHandle.storageUsage()` report one object using its captured backend, with the same bigint byte fields and null unknowns. Snapshot records returned by `Snapshot.list()` contain metadata only; fetch a native handle with `Snapshot.get()` before inspecting its storage.

## Common Examples

These snippets assume you already have a live `sandbox: Sandbox`.

### Fork a Live Sandbox

Forking copies a running or paused local sandbox's disk and execution state into an independent child. Memory uses copy-on-write automatically. The source keeps its previous running or paused state. Host resources require explicit bindings; see [forking and resource bindings](https://docs.microsandbox.dev/sandboxes/snapshots#forking).

```typescript
const child = await sandbox.fork("experiment");
await child.stop();
```

Use `forkMany(["alice", "bob"])` to capture once for several children. Inspect every returned outcome: one child's startup failure does not remove successful siblings. See the [fork API reference](https://docs.microsandbox.dev/sdk/typescript/sandbox#forking).

Restoring starts from a saved snapshot instead. Use `.cowMemory()` to request copy-on-write memory for a full-snapshot restore. A generation describes snapshot-history progression; a branch describes a distinct path through that history. The former live branch APIs and old CoW restore names remain deprecated aliases. See [restore migration notes](https://docs.microsandbox.dev/sandboxes/snapshots#migrating-restore-options) for the old-to-new names and language-specific deprecation notices.

### Command Execution

```typescript
const result = await sandbox.exec("python3", ["-c", "print(1 + 1)"]);
console.log(result.stdout());
console.log(result.code);

const output = await sandbox.shell("echo hello && pwd");
console.log(output.stdout());

const configured = await sandbox.execWith("python3", (exec) =>
  exec
    .args(["script.py"])
    .cwd("/app")
    .env("PYTHONPATH", "/app/lib")
    .timeout(30_000),
);

const handle = await sandbox.execStream("tail", ["-f", "/var/log/app.log"]);
for await (const event of handle) {
  if (event.kind === "stdout") process.stdout.write(event.data);
  if (event.kind === "stderr") process.stderr.write(event.data);
  if (event.kind === "exited") break;
}
```

### Filesystem Operations

```typescript
const fs = sandbox.fs();

await fs.write("/tmp/config.json", '{"debug": true}');
console.log(await fs.readToString("/tmp/config.json"));

for (const entry of await fs.list("/etc")) {
  console.log(`${entry.path} (${entry.kind})`);
}

await fs.copyFromHost("./local-file.txt", "/tmp/file.txt");
await fs.copyToHost("/tmp/output.txt", "./output.txt");

if (await fs.exists("/tmp/config.json")) {
  const meta = await fs.stat("/tmp/config.json");
  console.log(`size: ${meta.size}, kind: ${meta.kind}`);
}
```

### Named Volumes

```typescript
import { MiB, Sandbox, Volume } from "microsandbox";

const data = await Volume.builder("ts-readme-data").quota(MiB(100)).create();

{
  await using writer = await Sandbox.builder("ts-readme-writer")
    .image("alpine")
    .volume("/data", (mount) => mount.named(data.name))
    .replace()
    .create();
  await writer.shell("echo 'hello' > /data/message.txt");
}

{
  await using reader = await Sandbox.builder("ts-readme-reader")
    .image("alpine")
    .volume("/data", (mount) => mount.named(data.name).readonly())
    .replace()
    .create();
  console.log((await reader.shell("cat /data/message.txt")).stdout().trim());
}

const entries = await data.fs().list("");
for (const entry of entries) {
  console.log(`${entry.path} (${entry.kind})`);
}
```

### Network, DNS, and Ports

```typescript
import { NetworkPolicy, Rule, Destination, Sandbox } from "microsandbox";

await using isolated = await Sandbox.builder("ts-readme-isolated")
  .image("alpine")
  .network((network) => network.policy(NetworkPolicy.none()))
  .replace()
  .create();

const filteredPolicy = NetworkPolicy.builder()
  .defaultAllow()
  .egress((egress) =>
    egress
      .denyDomain("blocked.example.com")
      .denyDomainSuffix(".evil.com"),
  )
  .build();

await using filtered = await Sandbox.builder("ts-readme-filtered")
  .image("alpine")
  .network((network) => network.policy(filteredPolicy))
  .replace()
  .create();

const allowlisted = {
  defaultEgress: "deny",
  defaultIngress: "allow",
  rules: [
    Rule.allowEgress(Destination.domain("api.openai.com")),
  ],
} satisfies NetworkPolicy;

await using web = await Sandbox.builder("ts-readme-web")
  .image("python")
  .network((network) => network.policy(allowlisted))
  .port(8080, 80)
  .create();
```

Domain blocking is network policy behavior. `DnsBuilder` configures DNS resolver behavior such as rebinding protection, nameservers, and query timeouts.

### Secrets

Secrets use placeholder substitution. The real value stays on the host and is substituted only for allowed network destinations.

```typescript
import { Sandbox } from "microsandbox";

await using sandbox = await Sandbox.builder("ts-readme-agent")
  .image("python")
  .secretEnv("OPENAI_API_KEY", process.env.OPENAI_API_KEY!, "api.openai.com")
  .replace()
  .create();
```

### Rootfs Patches

```typescript
await using sandbox = await Sandbox.builder("ts-readme-patched")
  .image("alpine")
  .patch((patch) =>
    patch
      .text("/etc/greeting.txt", "Hello!\n")
      .mkdir("/app", { mode: 0o755 })
      .text("/app/config.json", '{"debug": true}', { mode: 0o644 })
      .append("/etc/hosts", "127.0.0.1 myapp.local\n"),
  )
  .replace()
  .create();
```

### Detached Mode

```typescript
const sandbox = await Sandbox.builder("ts-readme-background")
  .image("python")
  .detached(true)
  .replace()
  .create();
await sandbox.detach();

const handle = await Sandbox.get("ts-readme-background");
const reconnected = await handle.connect();
const output = await reconnected.shell("echo reconnected");
console.log(output.stdout().trim());
```

### TLS Interception

```typescript
await using sandbox = await Sandbox.builder("tls-inspect")
  .image("python")
  .network((n) => n.tls((t) =>
    t.bypass("*.googleapis.com")
      .verifyUpstream(true)
      .interceptedPorts([443])
      .upstreamCaCert("/etc/ssl/corp-root.pem")
      .upstreamCaCertFor("api.internal", "./certs/api-ca.pem")
      .verifyUpstreamFor("*.preview.internal", false),
  ))
  .create();
```

### Metrics

```typescript
import { allSandboxMetrics } from "microsandbox";

const metrics = await sandbox.metrics();
console.log(`CPU: ${metrics.cpuPercent.toFixed(1)}%`);
console.log(`Memory: ${(metrics.memoryBytes / 1024 / 1024).toFixed(1)} MiB`);

for await (const sample of await sandbox.metricsStream(1000)) {
  console.log(sample.timestamp.toISOString(), sample.cpuPercent);
  break;
}

for (const [name, sample] of Object.entries(await allSandboxMetrics())) {
  console.log(`${name}: ${sample.cpuPercent.toFixed(1)}%`);
}
```

### Typed Errors

TypeScript exports typed errors for the common SDK categories and falls back to `MicrosandboxError` for unmapped runtime variants. Catch specific errors when you need category-specific handling, and catch `MicrosandboxError` as the broad SDK base class.

```typescript
import {
  MicrosandboxError,
  Sandbox,
  SandboxAlreadyExistsError,
} from "microsandbox";

try {
  await Sandbox.builder("worker").image("alpine").create();
} catch (error) {
  if (error instanceof SandboxAlreadyExistsError) {
    console.log("already exists; resume it or pass replace()");
  } else if (error instanceof MicrosandboxError) {
    console.log(`microsandbox error: ${error.message}`);
  } else {
    throw error;
  }
}
```

## Runtime Notes

- The `microsandbox` package depends on platform packages through optional dependencies.
- The platform package carries the native addon, `msb`, and `libkrunfw`.
- The `microsandbox` and `msb` bin shims forward to the resolved `msb` binary. They do not install runtime files.
- If no platform package is present, reinstall with optional dependencies enabled, install the matching `@superradcompany/microsandbox-<platform>` package, or set `MSB_PATH`.

## Inspect a runtime version

```typescript
import { resolveRuntimeVersion } from "microsandbox";

const version = await resolveRuntimeVersion("/path/to/msb");
```

This reads the embedded semantic version without executing the file or requiring firmware. It returns a version string, or `null` for older executables without the section. File access failures, malformed executable metadata and invalid version sections reject the promise. There is no implicit `--version` subprocess fallback, and normal sandbox launch does not perform this optional inspection.

## More Documentation

- [Sandbox lifecycle](https://docs.microsandbox.dev/sdk/typescript/sandbox)
- [Execution](https://docs.microsandbox.dev/sdk/typescript/execution)
- [Filesystem](https://docs.microsandbox.dev/sdk/typescript/filesystem)
- [Networking](https://docs.microsandbox.dev/sdk/typescript/networking)
- [Secrets](https://docs.microsandbox.dev/sdk/typescript/secrets)
- [Volumes](https://docs.microsandbox.dev/sdk/typescript/volumes)
- [Snapshots](https://docs.microsandbox.dev/sdk/typescript/snapshots)
- [SSH](https://docs.microsandbox.dev/sdk/typescript/ssh)
- [Agent client](https://docs.microsandbox.dev/sdk/typescript/agent-client)

## Development

From `sdk/node-ts`:

```bash
npm ci
npm run build
npm run typecheck
npm test
```

On a release branch whose native packages are not published yet, replace `npm ci` with `node ../../scripts/ci/build-unpublished-node-sdk.mjs`. This installs the locked build tools and builds TypeScript while restoring the package manifests afterward. Continue with `npm run build` to build the native addon locally. Standard `npm ci` works again after the post-release lockfile refresh.

Run repository examples from the specific example directory:

```bash
cd examples/typescript/root-oci
npm install
npm start
```

## License

Apache-2.0

The size helpers and `Mebibytes` type are re-exported from `@microsandbox/types/size`, so values can be passed directly between this SDK and the standalone control client. The local npm workspace links that shared package with a versioned dependency for publishing. `npm ci`, `npm run build:ts`, and `npm run typecheck` work from this directory; the build and typecheck scripts build the shared types first.

---

### Overview (`sdk/python/README.md`)

# microsandbox

Lightweight VM sandboxes for Python applications that need hardware-level isolation for AI agents, tools, tests, and untrusted code.

The `microsandbox` Python package provides async Python bindings to the [microsandbox](https://github.com/superradcompany/microsandbox) runtime. It creates microVM-backed sandboxes from OCI images or other rootfs sources, then exposes command execution, guest filesystem access, networking, secrets, volumes, metrics, logs, snapshots, and SSH/SFTP through Python-friendly classes and dataclasses.

For the full API reference and longer guides, use the docs site:

- [Python SDK guide](https://docs.microsandbox.dev/sdk/python/sandbox)
- [SDK overview](https://docs.microsandbox.dev/sdk/overview)
- [Repository examples](../../examples/python)

A complete runtime in the configured home (`MSB_HOME`, or `~/.microsandbox` by default) takes precedence over wheel binaries. Explicit binary paths still win. A partial home installation errors instead of falling back to the wheel. This also applies to the packaged CLI entry points.

## Features

- Hardware VM isolation with a guest Linux kernel
- Async sandbox lifecycle, execution, filesystem, metrics, and logs APIs
- OCI image, bind-rootfs, disk-image, and snapshot-based sandboxes
- Named volumes, bind mounts, tmpfs mounts, and disk-image mounts
- Network policies, DNS filtering, TLS interception, secrets, and port publishing
- Rootfs patches before boot
- Detached sandboxes that can outlive the Python process
- Typed Python surface with `StrEnum`s, frozen dataclasses, event objects, `.pyi` stubs, and `py.typed`

## Requirements

- Python 3.10+
- Linux with KVM, macOS with Apple Silicon, or Windows 11 with WHP enabled
- Windows support is currently preview; see the [Windows troubleshooting guide](https://docs.microsandbox.dev/troubleshooting/windows) for WHP and runtime setup notes.

## Supported Platforms

| Platform | Architecture | Notes |
| --- | --- | --- |
| macOS | ARM64 / Apple Silicon | Wheel bundles `msb` and `libkrunfw` |
| Linux | x86_64 | Wheel bundles `msb` and `libkrunfw` |
| Linux | ARM64 | Wheel bundles `msb` and `libkrunfw` |
| Windows | x86_64, ARM64 | Preview; requires WHP |

Python wheels bundle the matching `msb` runtime and `libkrunfw` library. Source checkouts and unreleased local builds can override runtime paths with `MSB_PATH`, `MSB_LIBKRUNFW_PATH`, or `microsandbox.set_libkrunfw_path(...)`.

## Installation

```bash
pip install microsandbox
```

## Quick Start

```python
import asyncio

from microsandbox import Sandbox


async def main() -> None:
    async with await Sandbox.create("python-readme", image="alpine", replace=True) as sandbox:
        output = await sandbox.shell("echo 'Hello from microsandbox!'")
        print(output.stdout_text.strip())


asyncio.run(main())
```

`async with` stops and removes the sandbox when the block exits. Use `Sandbox.create(...)` without a context manager when you want to control `stop()`, `kill()`, or `remove()` yourself.

### Reusable Lifecycle Convergence

Use `connect_or_create` when a stable name should converge on one persisted sandbox. Existing configuration wins; creation arguments are used only if creation is necessary. Handles retain a stable `id`, so lifecycle calls on stale receivers refuse to act on a replacement that reused the name.

```python
from microsandbox import SandboxStatus

sandbox = await Sandbox.connect_or_create("worker", image="python", memory=1024)

print(f"{await sandbox.name}: {await sandbox.id}")
running = await (await Sandbox.get("worker")).connect_or_start()
await running.request_stop()
stopped = await running.wait_for_status(SandboxStatus.STOPPED)
restarted = await stopped.restart()
await restarted.destroy()
```

## Common Examples

These snippets assume you already have a live `sandbox: Sandbox`.

### Fork a Live Sandbox

Forking copies a running or paused local sandbox's disk and execution state into an independent child. Memory uses copy-on-write automatically. The source keeps its previous running or paused state. Host resources require explicit bindings; see [forking and resource bindings](https://docs.microsandbox.dev/sandboxes/snapshots#forking).

```python
child = await sandbox.fork("experiment")
await child.stop()
```

Use `await sandbox.fork_many(["alice", "bob"])` to capture once for several children. Inspect every returned outcome: one child's startup failure does not remove successful siblings. See the [fork API reference](https://docs.microsandbox.dev/sdk/python/sandbox#forking).

Restoring starts from a saved snapshot instead. Use `cow_memory=True` to request copy-on-write memory for a full-snapshot restore. A generation describes snapshot-history progression; a branch describes a distinct path through that history. The former live branch APIs and old CoW restore names remain deprecated aliases. See [restore migration notes](https://docs.microsandbox.dev/sandboxes/snapshots#migrating-restore-options) for the old-to-new names and language-specific deprecation notices.

### Command Execution

```python
import sys

from microsandbox import ExecEventType

output = await sandbox.exec("python3", ["-c", "print(1 + 1)"])
print(output.stdout_text)
print(output.exit_code)

output = await sandbox.shell("echo hello && pwd")
print(output.stdout_text)

output = await sandbox.exec(
    "python3",
    ["script.py"],
    cwd="/app",
    env={"PYTHONPATH": "/app/lib"},
    timeout=30.0,
)

handle = await sandbox.exec_stream("tail", ["-f", "/var/log/app.log"])
async for event in handle:
    match event.event_type:
        case ExecEventType.STDOUT:
            sys.stdout.buffer.write(event.data)
        case ExecEventType.STDERR:
            sys.stderr.buffer.write(event.data)
        case ExecEventType.EXITED:
            break
```

### Filesystem Operations

```python
fs = sandbox.fs

await fs.write("/tmp/config.json", b'{"debug": true}')
print(await fs.read_text("/tmp/config.json"))

for entry in await fs.list("/etc"):
    print(f"{entry.path} ({entry.kind})")

await fs.copy_from_host("./local-file.txt", "/tmp/file.txt")
await fs.copy_to_host("/tmp/output.txt", "./output.txt")

if await fs.exists("/tmp/config.json"):
    meta = await fs.stat("/tmp/config.json")
    print(f"size: {meta.size}, kind: {meta.kind}")
```

### Named Volumes

```python
from microsandbox import Sandbox, Volume

data = await Volume.create("python-readme-data", quota_mib=100)

writer = await Sandbox.create(
    "python-readme-writer",
    image="alpine",
    volumes={"/data": Volume.named(data.name)},
    replace=True,
)
await writer.shell("echo 'hello' > /data/message.txt")
await writer.stop()

reader = await Sandbox.create(
    "python-readme-reader",
    image="alpine",
    volumes={"/data": Volume.named(data.name, readonly=True)},
    replace=True,
)
output = await reader.shell("cat /data/message.txt")
print(output.stdout_text.strip())
await reader.stop()
```

### Network, DNS, and Ports

```python
from microsandbox import Network, NetworkProfile, Sandbox
from microsandbox.types import DnsConfig

isolated = await Sandbox.create(
    "python-readme-isolated",
    image="alpine",
    network=Network.none(),
    replace=True,
)

filtered = await Sandbox.create(
    "python-readme-filtered",
    image="alpine",
    network=Network(
        deny_domains=("blocked.example.com",),
        deny_domain_suffixes=(".evil.com",),
        dns=DnsConfig(nameservers=("1.1.1.1:53",)),
    ),
    replace=True,
)

web = await Sandbox.create(
    "python-readme-web",
    image="python",
    ports={8080: 80},
    network=Network.from_profiles(NetworkProfile.PUBLIC),
    replace=True,
)
```

### Secrets

Secrets use placeholder substitution. The real value stays on the host and is substituted only for allowed network destinations.

```python
import os

from microsandbox import Sandbox, Secret

sandbox = await Sandbox.create(
    "python-readme-agent",
    image="python",
    secrets=[
        Secret.env(
            "OPENAI_API_KEY",
            value=os.environ["OPENAI_API_KEY"],
            allow=["api.openai.com"],
        ),
    ],
    replace=True,
)
```

### Rootfs Patches

```python
from microsandbox import Patch, Sandbox

sandbox = await Sandbox.create(
    "python-readme-patched",
    image="alpine",
    patches=[
        Patch.text("/etc/greeting.txt", "Hello!\n"),
        Patch.mkdir("/app", mode=0o755),
        Patch.text("/app/config.json", '{"debug": true}', mode=0o644),
        Patch.append("/etc/hosts", "127.0.0.1 myapp.local\n"),
    ],
    replace=True,
)
```

### Detached Mode

```python
sandbox = await Sandbox.create(
    "python-readme-background",
    image="python",
    detached=True,
    replace=True,
)

handle = await Sandbox.get("python-readme-background")
reconnected = await handle.connect()
output = await reconnected.shell("echo reconnected")
print(output.stdout_text.strip())
```

### TLS Interception

```python
from microsandbox import (
    Network,
    Sandbox,
    ScopedUpstreamCACert,
    ScopedVerifyUpstream,
    TlsConfig,
)

sandbox = await Sandbox.create(
    "tls-inspect",
    image="python",
    network=Network(
        tls=TlsConfig(
            bypass=("*.googleapis.com",),
            verify_upstream=True,
            intercepted_ports=(443,),
            upstream_ca_certs=("/etc/ssl/corp-root.pem",),
            scoped_upstream_ca_certs=(
                ScopedUpstreamCACert("api.internal", "./certs/api-ca.pem"),
            ),
            scoped_verify_upstream=(
                ScopedVerifyUpstream("*.preview.internal", False),
            ),
        ),
    ),
)
```

### Metrics

```python
from microsandbox import MiB, all_sandbox_metrics

metrics = await sandbox.metrics()
print(f"CPU: {metrics.cpu_percent:.1f}%")
print(f"Memory: {metrics.memory_bytes // MiB} MiB")

async for sample in sandbox.metrics_stream(interval=1.0):
    print(f"CPU: {sample.cpu_percent:.1f}%")
    break

for name, sample in (await all_sandbox_metrics()).items():
    print(f"{name}: {sample.cpu_percent:.1f}%")
```

### Storage Usage and Runtime Cache Cleanup

`Storage.usage()` reports the selected local backend's aggregate storage. Sandbox and snapshot handles also provide `storage_usage()` for their managed files. Reports contain raw integer byte counts; `None` means unknown. Logical sizes and allocated-block observations do not measure exclusive physical ownership on filesystems that share copy-on-write blocks.

```python
from microsandbox import Storage

usage = await Storage.usage()
print(usage.branch_memory.logical_bytes)

preview = await Storage.prune(dry_run=True, older_than_seconds=600)
for entry in preview.entries:
    print(entry.path, entry.state, entry.logical_bytes)

# Explicitly remove currently unused runtime RAM after rechecking ownership.
result = await Storage.prune(older_than_seconds=600)
print(result.logical_bytes_removed)
```

Pruning preserves durable snapshots, sandbox disks, named volumes, and stable lock files. Pending handoffs, live or paused VMs, and retained baselines protect their RAM. The report lists skipped entries and per-file errors, including partial success; `physical_bytes_reclaimed` remains `None`. Remote backend storage operations raise `UnsupportedError`. Static storage calls retain the backend selected when called, and handle methods retain the backend that created the handle.

### Typed Errors

Python exports typed errors for the common SDK categories and falls back to `MicrosandboxError` for unmapped runtime variants. Catch specific errors when you need category-specific handling, and catch `MicrosandboxError` as the broad SDK base class.

```python
from microsandbox import MicrosandboxError, Sandbox, SandboxAlreadyExistsError

try:
    await Sandbox.create("worker", image="alpine")
except SandboxAlreadyExistsError:
    print("already exists; resume it or pass replace=True")
except MicrosandboxError as exc:
    print(f"microsandbox error: {exc}")
```

## Runtime Setup

Installed wheels bundle the runtime files. The setup helpers are useful for source checkouts, shared runtime installs, and surfacing setup failures at process startup.

```python
from microsandbox import ensure_runtime

runtime = await ensure_runtime()
print(runtime.msb_path, runtime.libkrunfw_path)
```

## More Documentation

- [Sandbox lifecycle](https://docs.microsandbox.dev/sdk/python/sandbox)
- [Execution](https://docs.microsandbox.dev/sdk/python/execution)
- [Filesystem](https://docs.microsandbox.dev/sdk/python/filesystem)
- [Networking](https://docs.microsandbox.dev/sdk/python/networking)
- [Secrets](https://docs.microsandbox.dev/sdk/python/secrets)
- [Volumes](https://docs.microsandbox.dev/sdk/python/volumes)
- [Snapshots](https://docs.microsandbox.dev/sdk/python/snapshots)
- [Images](https://docs.microsandbox.dev/sdk/python/images)
- [SSH](https://docs.microsandbox.dev/sdk/python/ssh)
- [Agent client](https://docs.microsandbox.dev/sdk/python/agent-client)

## Development

From `sdk/python`:

```bash
uv sync --group dev
uv run maturin develop --release
uv run pytest tests
uv run ruff check .
```

From the repository root, run an example against the SDK project:

```bash
uv run --project sdk/python python examples/python/root-oci/main.py
```

Runtime integration tests require local virtualization support and runtime artifacts:

```bash
cd sdk/python
uv run pytest integration/test_create_kwargs.py integration/test_exec.py
```

## License

Apache-2.0

---

### Overview (`sdk/ruby/README.md`)

# Ruby SDK

The microsandbox Ruby SDK provides Ruby 3.3+ bindings for creating and
controlling local or cloud sandboxes.

## Installation

Install the gem:

```sh
gem install microsandbox
```

> [!NOTE]
> Platform gems are currently built and validated in CI but not yet published
> to RubyGems — every `gem install microsandbox` still compiles the source gem
> and needs a Rust toolchain. This section describes the state once the release
> wiring ships them.

Precompiled platform gems carry the native extension, so these combinations
install without a Rust toolchain:

| Gem platform        | Ruby     | Notes              |
| ------------------- | -------- | ------------------ |
| `x86_64-linux-gnu`  | 3.3–4.0  | glibc 2.35+        |
| `aarch64-linux-gnu` | 3.3–4.0  | glibc 2.35+        |
| `arm64-darwin`      | 3.3–4.0  | Apple Silicon      |
| `x64-mingw-ucrt`    | 3.3–4.0  | RubyInstaller 3.3+ |

The Linux gems require glibc 2.35 or newer (Ubuntu 22.04, Debian 12, and
later). The platform name carries no glibc version, so on an older glibc host
the platform gem still installs but fails to load — force the source gem there
(see below).

The Linux gems need `libcap-ng0` at runtime. Debian and Ubuntu ship it in the
base system — the stock `ruby:slim` images load the gem with no extra
packages, which CI verifies — so this only matters on stripped-down
environments such as distroless images.

Installing a platform gem requires RubyGems 3.3.11 or newer; earlier releases
mismatch `-linux` gems against glibc hosts. Run `gem update --system` first if
`gem --version` reports anything older.

musl (Alpine), Windows on ARM, and any other platform or Ruby version fall
back to the source gem automatically. The source gem compiles the extension
during install and therefore needs a Rust toolchain (1.85 or newer).

To compile from source even where a platform gem exists:

```sh
gem install microsandbox --platform ruby
```

With Bundler:

```ruby
gem "microsandbox", force_ruby_platform: true
```

To build a platform gem from a checkout, install every target Ruby, then run
from `sdk/ruby`:

```sh
rake cargo:patch_workspace version_check
rake gem:stage # Once each under Ruby 3.3, 3.4, and 4.0
GEM_PLATFORM=arm64-darwin rake gem:platform
```

`gem:platform` refuses to package unless all three ABIs are staged. Set
`RUBY_ABIS` (for example `RUBY_ABIS=3.4`) to relax that when testing against a
single local Ruby; CI never sets it.

`cargo:patch_workspace` builds against the Rust SDK in this checkout and saves
the standalone lockfile for restoration. Run it before `version_check` so you
can build an unpublished release. The check still requires matching gem and
extension versions and an exact Rust SDK pin, but skips the registry lockfile
check. When finished, run `rake cargo:unpatch_workspace` to restore the lockfile
and switch back to the published SDK.

To use the local backend, install the microsandbox runtime and firmware once:

```ruby
require "microsandbox"

Microsandbox.install unless Microsandbox.installed?
```

Local sandboxes require Apple Silicon virtualization on macOS or KVM on Linux. On Windows, use Windows 11 on x64 or ARM64 and enable WHP. Ruby CI currently covers Linux x86_64.

## Quick start

`Sandbox.with` stops the sandbox when the block exits, including when the block
raises an exception:

```ruby
require "microsandbox"

Microsandbox.install unless Microsandbox.installed?

Microsandbox::Sandbox.with(
  "my-sandbox",
  image: "python",
  cpus: 1,
  memory: 512
) do |sandbox|
  output = sandbox.exec("python", ["-c", "print('Hello from a microVM!')"])
  puts output.stdout
end
```

## Backends

The local backend is the default. Select a backend explicitly when needed:

```ruby
Microsandbox.use_local_backend! # Default
# Or:
Microsandbox.use_cloud_backend!(ENV.fetch("MSB_API_KEY"))
# Or:
Microsandbox.use_cloud_profile!("production")
```

## Lifecycle

A lifecycle-owning `Sandbox` stops when Ruby garbage-collects it. Prefer
`Sandbox.with` for scoped work. Call `sandbox.detach` when the VM must outlive
the Ruby object, then manage it through `Sandbox.get`.

If both a `Sandbox.with` block and its cleanup fail, the block's original
exception is preserved.

Blocking calls do not prevent other Ruby threads from running. Forked child
processes recreate the native runtime before use.

Use `connect_or_create` when a stable name should converge on one persisted sandbox. Existing configuration wins; options are used only if creation is necessary. Handles retain a stable `id`, so lifecycle calls on stale receivers refuse to act on a replacement that reused the name.

```ruby
sandbox = Microsandbox::Sandbox.connect_or_create(
  "worker",
  image: "python",
  memory: 1024
)

puts "#{sandbox.name}: #{sandbox.id}"
running = Microsandbox::Sandbox.get("worker").connect_or_start
running.request_stop
stopped = running.wait_for_status("stopped")
restarted = stopped.restart
restarted.destroy
```

Run `ruby examples/lifecycle_convergence.rb` from `sdk/ruby` to exercise the complete local lifecycle against a live microVM. The example verifies convergence, restart, destroy, and stale-handle identity safety, then emits machine-readable timing metrics.

## Networking and secrets

`network: :none` disables networking. An allowlist creates a default-deny
egress policy:

```ruby
sandbox = Microsandbox::Sandbox.create(
  "secure-sandbox",
  image: "python",
  network: { allowed_hosts: ["api.example.com"], allowed_ports: [443] },
  secrets: [{
    env: "API_KEY",
    value: ENV.fetch("API_KEY"),
    allowed_host: "api.example.com"
  }]
)
```

The guest receives a placeholder for each secret. The host proxy substitutes
the real value only for the allowed TLS hostname. Secret values persist in
host-side sandbox configuration, so load them from a secret manager, never log
them, and rotate them after suspected host compromise.

Route outbound TCP through an HTTP CONNECT proxy with
`proxy: Microsandbox::OutboundProxy.http_connect("127.0.0.1:3128")`. The
microsandbox host resolves and checks each destination before opening the
tunnel. HTTP CONNECT proxy authentication and UDP are not supported.

## Snapshots

Snapshot operations use the selected backend and preserve whether a snapshot
reference is an ID or a path. Existing static save calls remain available, and
opened snapshots and live handles can save through the backend they retain:

```ruby
snapshot = Microsandbox::Snapshot.open("after-pip-install")
snapshot.save_to("/tmp/after-pip-install.tar.zst", with_image: true)

handle = Microsandbox::Snapshot.get("after-pip-install")
handle.save_to("/tmp/after-pip-install.tar.zst")

Microsandbox::Snapshot.save(
  "after-pip-install",
  "/tmp/after-pip-install.tar.zst",
  plain_tar: false
)
```

Snapshot archive operations are currently local-only. With the cloud backend, `save`, `save_to`, direct directory enumeration, reindexing, archive loading, and payload verification raise an unsupported-operation error. Capture, lookup, listing, open, and removal remain backend-neutral. The Ruby SDK does not yet expose dedicated sandbox restoration; use another SDK or the CLI for that operation.

## Supported surface

The gem supports sandbox lifecycle operations, collected exec and shell
output, SSH exec, logs, metrics, guest filesystem operations, local image,
volume, and snapshot management, local or cloud backend selection, and typed
error classes (see [Errors](#errors)).

SSH exec inherits the global inactivity timeout by default. Override it for a
single command in seconds, or use `0` to disable it:

```ruby
output = sandbox.ssh_exec("long-running-agent", inactivity_timeout: 1_800)
persistent = sandbox.ssh_exec("long-running-agent", inactivity_timeout: 0)
```

Streaming exec, logs, metrics, and filesystem handles; interactive SSH/SFTP;
live modification plans; and the complete Rust network and mount builders are
not currently exposed. Use the Rust SDK when those APIs are required.

## Errors

Every error reported by a sandbox, image, volume, snapshot, or backend
operation is a `Microsandbox::Error`, so `rescue Microsandbox::Error` catches
all of them. The native layer raises the subclass matching the core error,
which lets callers branch on the failure without matching message text:

```ruby
begin
  sandbox.exec("sleep", ["30"], timeout: 1)
rescue Microsandbox::ExecTimeoutError => error
  puts "timed out: #{error.message}"
rescue Microsandbox::Error => error
  puts "#{error.code}: #{error.message}"
end
```

Class names and `#code` strings mirror the Python SDK; the snapshot,
exec-failed, and volume-already-exists classes follow the Go SDK's finer
coverage. All classes are direct subclasses of `Microsandbox::Error`
(code `microsandbox-error`):

| Group                 | Classes                                                                                                                                                                                              |
| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Runtime bootstrap     | `RuntimeNotInstalledError`, `RuntimeIncompleteError`                                                                                                                                                 |
| Configuration         | `InvalidConfigError`, `NoDefaultCommandError`                                                                                                                                                        |
| Lifecycle             | `SandboxNotFoundError`, `SandboxNotRunningError`, `SandboxAlreadyExistsError`, `SandboxReplacedError`, `SandboxStillRunningError`, `SandboxStopTimedOutError`, `StopTimeoutError`                    |
| Execution             | `ExecTimeoutError`, `ExecFailedError`                                                                                                                                                                |
| Filesystem            | `FilesystemError`, `PathNotFoundError`                                                                                                                                                               |
| Volumes and images    | `VolumeNotFoundError`, `VolumeAlreadyExistsError`, `ImageNotFoundError`, `ImageInUseError`, `ImagePullFailedError`                                                                                   |
| Snapshots             | `SnapshotNotFoundError`, `SnapshotAlreadyExistsError`, `SnapshotSandboxRunningError`, `SnapshotImageMissingError`, `SnapshotIntegrityError`, `SnapshotSourceRecoveryError`, `SnapshotMigrationError` |
| Networking            | `NetworkPolicyError`, `SecretViolationError`, `TlsError`                                                                                                                                             |
| I/O                   | `IoError`                                                                                                                                                                                            |
| Metrics               | `MetricsDisabledError`, `MetricsUnavailableError`                                                                                                                                                    |
| Runtime compatibility | `UnsupportedOperationError`                                                                                                                                                                          |
| Backend routing       | `CloudHttpError`, `UnsupportedError`                                                                                                                                                                 |

Each class exposes its stable, machine-readable code through `.code` and
`#code` (for example `Microsandbox::ExecTimeoutError.code == "exec-timeout"`).
Core errors without a dedicated class raise `Microsandbox::Error` itself.
`PathNotFoundError`, `ImagePullFailedError`, `SecretViolationError`,
`TlsError`, and `SandboxStopTimedOutError` are defined for parity with the
Python SDK but are not raised by the current core; an explicit
`stop_with_timeout` that runs out of time raises `StopTimeoutError`.

`UnsupportedError` is raised when the selected backend does not implement an
operation. Its message names the Ruby API and the remedy, both also available
as attributes:

```ruby
Microsandbox.use_cloud_backend!(ENV.fetch("MSB_API_KEY"))
begin
  Microsandbox::Sandbox.create("my-sandbox", image: "python", replace: true)
rescue Microsandbox::UnsupportedError => error
  error.message   # => "sandbox.create is not supported by this backend: the replace option is not accepted here"
  error.operation # => "sandbox.create"
  error.hint      # => "the replace option is not accepted here"
end
```

`SnapshotSourceRecoveryError` is raised when a snapshot was captured but the
source sandbox failed to recover its prior execution state. It carries the
recovery locator as attributes, so there is no need to parse the message:
`source_sandbox`, `checkpoint_id`, `checkpoint_root`, `checkpoint_path`,
`detail`, `publication_error`, and `artifact`. `artifact` is a Hash with
`"kind"` (`"installed"` or `"archive"`), `"path"`, `"snapshot_id"`, and
`"digest"` keys, set only when the requested snapshot was published;
otherwise `checkpoint_path` names the retained runtime-local checkpoint. The
error does not imply that the source is running or safe to resume.

Argument validation is not covered by that guarantee: unknown keywords and
wrongly typed values keep raising Ruby's `ArgumentError` and `TypeError`
before any operation runs.

## Development

The gem, native extension, and published Rust SDK must use the same version.
`rake version_check` verifies their versions, the exact SDK pin, and the
standalone lockfile's registry entry.

After publishing the Rust SDK, the release workflow opens a PR to refresh
`ext/microsandbox/Cargo.lock`. To refresh it manually, run
`cargo update -p microsandbox --precise <version>` from `ext/microsandbox`,
using the version pinned in `Cargo.toml` with no workspace patch active.

---

### Overview (`sdk/rust/README.md`)

# microsandbox

Lightweight VM sandboxes for Rust applications that need hardware-level isolation for AI agents, tools, tests, and untrusted code.

`microsandbox` is the Rust SDK for the [microsandbox](https://github.com/superradcompany/microsandbox) runtime. It exposes an async API for creating microVM-backed sandboxes, running commands, reading and writing guest files, managing volumes, configuring network policy, and working with secrets.

For full API documentation, use the docs site and generated Rust docs:

- [Rust SDK guide](https://docs.microsandbox.dev/sdk/rust/sandbox)
- [SDK overview](https://docs.microsandbox.dev/sdk/overview)
- [Repository examples](../../examples/rust)

## Features

- Hardware VM isolation with a guest Linux kernel
- OCI image, bind-rootfs, disk-image, and snapshot-based sandboxes
- Collected and streaming command execution
- Guest filesystem read, write, list, copy, stat, and stream operations
- Named volumes, bind mounts, tmpfs mounts, and disk-image mounts
- Network policies, DNS filtering, TLS interception, secrets, and port publishing
- Rootfs patches before boot
- Detached sandboxes that can outlive the Rust process
- Metrics, logs, snapshots, SSH/SFTP, image cache, and local/cloud backend helpers

## Requirements

- Rust toolchain with Rust 2024 edition support
- Linux with KVM, macOS with Apple Silicon, or Windows 11 (x64 or ARM64) with WHP enabled
- Windows support is currently preview; see the [Windows troubleshooting guide](https://docs.microsandbox.dev/troubleshooting/windows) for WHP and runtime setup notes.

## Installation

```bash
cargo add microsandbox
```

### Cargo Features

| Feature | Default | Description |
| --- | --- | --- |
| `local` | yes | Local runtime, setup, image cache, snapshots, metrics, and filesystem-backed volume APIs |
| `cloud` | yes | Cloud API backend and remote sandbox/volume operations |
| `keyring` | yes | Registry credential lookup through the platform keyring |
| `net` | yes | Network configuration, port publishing, policies, TLS interception, and secrets; the SDK uses the type/builder surface without compiling the host network engine |
| `download-binaries` | yes | Install a matching official `msb` + `libkrunfw` pair during Cargo builds; implies `local` |
| `embed-binaries` | no | Embed a compressed `msb` + `libkrunfw` archive for offline runtime installation; implies `local` |
| `ssh` | no | SSH, SFTP, and interactive SSH helpers |

```bash
# Cloud only
cargo add microsandbox --no-default-features --features cloud,net

# Local, with automatic runtime installation at build time
cargo add microsandbox --no-default-features --features local,net,download-binaries,keyring

# Local, without downloading or embedding runtime binaries
cargo add microsandbox --no-default-features --features local,net
```

For the last option, install the runtime with the [CLI installer](https://docs.microsandbox.dev/getting-started/quickstart) or call `setup::ensure_runtime()` at startup. See [Runtime setup](https://docs.microsandbox.dev/sdk/setup) for details.

## Quick Start

```rust
use microsandbox::Sandbox;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let sandbox = Sandbox::builder("rust-readme")
        .image("alpine")
        .cpus(1)
        .memory(512)
        .replace()
        .create()
        .await?;

    let output = sandbox.shell("echo 'Hello from microsandbox!'").await?;
    println!("{}", output.stdout()?.trim());

    sandbox.stop().await?;
    Sandbox::remove("rust-readme").await?;

    Ok(())
}
```

### Reusable Lifecycle Convergence

Use `connect_or_create` when a stable name should converge on one persisted sandbox. Existing configuration wins; the builder is used only if creation is necessary. Handles retain a stable `id`, so lifecycle calls on stale receivers refuse to act on a replacement that reused the name.

```rust
let sandbox = Sandbox::builder("worker")
    .image("python")
    .memory(1024)
    .connect_or_create()
    .await?;

println!("{}: {}", sandbox.name(), sandbox.id());
let running = Sandbox::get("worker").await?.connect_or_start().await?;
running.request_stop().await?;
let stopped = running
    .wait_for_status(microsandbox::sandbox::SandboxStatus::Stopped)
    .await?;
let restarted = stopped.restart().await?;
restarted.destroy().await?;
```

## Common Examples

These snippets assume you already have a live `sandbox: Sandbox`. See [examples/rust](../../examples/rust) for complete runnable crates.

### Fork a Live Sandbox

Forking copies a running or paused local sandbox's disk and execution state into an independent child. Memory uses copy-on-write automatically. The source keeps its previous running or paused state. Host resources require explicit bindings; see [forking and resource bindings](https://docs.microsandbox.dev/sandboxes/snapshots#forking).

```rust
let child = sandbox.fork("experiment").fork().await?;
child.stop().await?;
```

Use `fork_many(["alice", "bob"]).fork().await?` to capture once for several children. Inspect every returned outcome: one child's startup failure does not remove successful siblings. See the [fork API reference](https://docs.microsandbox.dev/sdk/rust/sandbox#forking).

Restoring starts from a saved snapshot instead. Use `.cow_memory()` to request copy-on-write memory for a full-snapshot restore. A generation describes snapshot-history progression; a branch describes a distinct path through that history. The former live branch APIs and old CoW restore names remain deprecated aliases. See [restore migration notes](https://docs.microsandbox.dev/sandboxes/snapshots#migrating-restore-options) for the old-to-new names and language-specific deprecation notices.

### Command Execution

```rust
use microsandbox::ExecEvent;

let output = sandbox.exec("python3", ["-c", "print(1 + 1)"]).await?;
println!("stdout: {}", output.stdout()?);
println!("exit code: {}", output.status().code);

let mut handle = sandbox.exec_stream("tail", ["-f", "/var/log/app.log"]).await?;
while let Some(event) = handle.recv().await {
    match event {
        ExecEvent::Stdout(data) => print!("{}", String::from_utf8_lossy(&data)),
        ExecEvent::Stderr(data) => eprint!("{}", String::from_utf8_lossy(&data)),
        ExecEvent::Exited { code } => {
            println!("exited with {code}");
            break;
        }
        ExecEvent::Started { .. } => {}
        ExecEvent::Failed(err) => {
            eprintln!("exec failed: {err:?}");
            break;
        }
        ExecEvent::StdinError(err) => eprintln!("stdin error: {err:?}"),
    }
}
```

### Filesystem Operations

```rust
let fs = sandbox.fs();

fs.write("/tmp/config.json", br#"{"debug": true}"#).await?;

let data = fs.read("/tmp/config.json").await?;
println!("{}", String::from_utf8_lossy(&data));

for entry in fs.list("/etc").await? {
    println!("{} ({:?})", entry.path, entry.kind);
}
```

### Named Volumes

```rust
use microsandbox::{Sandbox, Volume, size::SizeExt};

let data = Volume::builder("readme-data").quota(100.mib()).create().await?;

let writer = Sandbox::builder("readme-writer")
    .image("alpine")
    .volume("/data", |v| v.named(data.name()))
    .replace()
    .create()
    .await?;

writer.shell("echo 'hello' > /data/message.txt").await?;
writer.stop().await?;

let reader = Sandbox::builder("readme-reader")
    .image("alpine")
    .volume("/data", |v| v.named(data.name()).readonly())
    .replace()
    .create()
    .await?;

let output = reader.shell("cat /data/message.txt").await?;
println!("{}", output.stdout()?.trim());
```

### Network Policies

```rust
use microsandbox::{NetworkPolicy, Sandbox};

let isolated = Sandbox::builder("readme-isolated")
    .image("alpine")
    .network(|n| n.policy(NetworkPolicy::none()))
    .replace()
    .create()
    .await?;

let filtered_policy = NetworkPolicy::builder()
    .default_allow()
    .rule(|r| r.egress().deny().domain("blocked.example.com"))
    .rule(|r| r.egress().deny().domain_suffix(".evil.com"))
    .build()?;

let filtered = Sandbox::builder("readme-filtered")
    .image("alpine")
    .network(|n| n.policy(filtered_policy))
    .replace()
    .create()
    .await?;
```

Use `.disable_network()` when the sandbox should not receive a network interface at all. Use `NetworkPolicy::none()` when the interface should exist but policy should deny traffic.

### Port Publishing

```rust
let sandbox = Sandbox::builder("readme-web")
    .image("python")
    .port(8080, 80)
    .replace()
    .create()
    .await?;
```

### Secrets

Secrets use placeholder substitution. The real value stays on the host and is substituted only for allowed network destinations.

```rust
let sandbox = Sandbox::builder("readme-agent")
    .image("python")
    .secret_env("OPENAI_API_KEY", "sk-real-secret-123", "api.openai.com")
    .replace()
    .create()
    .await?;
```

### Rootfs Patches

```rust
let sandbox = Sandbox::builder("readme-patched")
    .image("alpine")
    .patch(|p| {
        p.text("/etc/greeting.txt", "Hello!\n", None, false)
            .mkdir("/app", Some(0o755))
            .append("/etc/hosts", "127.0.0.1 myapp.local\n")
    })
    .replace()
    .create()
    .await?;
```

### Detached Mode

```rust
let sandbox = Sandbox::builder("readme-background")
    .image("python")
    .detached(true)
    .replace()
    .create()
    .await?;

sandbox.detach().await;

let handle = Sandbox::get("readme-background").await?;
let reconnected = handle.connect().await?;
let output = reconnected.shell("echo reconnected").await?;
println!("{}", output.stdout()?.trim());
```

## More Documentation

- [Sandbox lifecycle](https://docs.microsandbox.dev/sdk/rust/sandbox)
- [Execution](https://docs.microsandbox.dev/sdk/rust/execution)
- [Filesystem](https://docs.microsandbox.dev/sdk/rust/filesystem)
- [Networking](https://docs.microsandbox.dev/sdk/rust/networking)
- [Secrets](https://docs.microsandbox.dev/sdk/rust/secrets)
- [Volumes](https://docs.microsandbox.dev/sdk/rust/volumes)
- [Snapshots](https://docs.microsandbox.dev/sdk/rust/snapshots)
- [SSH](https://docs.microsandbox.dev/sdk/rust/ssh)

## License

Apache-2.0

---

### Overview (`packages/agent-client/rust/README.md`)

# microsandbox-agent-client

`AgentClient` is `Client<AgentProtocol>` from `microsandbox-protocol-client`. The agent package supplies relay setup and generation metadata; the shared engine supplies one reader, writer, allocator, and ownership registry. SDK services such as execution collection and filesystem handles remain in `microsandbox`.

```text
TypedMessage / EncodedMessage / raw envelope / exact packet
                           |
                 Client<AgentProtocol>
                           |
          current or legacy relay handshake
                           |
                       agent relay
```

## Native, encoded, and raw messages

```rust,no_run
use microsandbox_agent_client::{AgentClient, TypedMessage, EncodedMessage};
use microsandbox_protocol::{fs::{FsOp, FsRequest, FsResponse}, message::MessageType};

async fn inspect(client: &AgentClient) -> Result<(), Box<dyn std::error::Error>> {
    let request = FsRequest {
        bulk: None,
        op: FsOp::Stat { path: "/etc/os-release".into(), follow_symlink: true },
    };
    let reply = client.request(TypedMessage::new(MessageType::FsRequest, &request)).await?;
    let response: FsResponse = reply.payload()?;
    println!("{}", response.ok);

    // The caller's payload bytes are not parsed or normalized by this path.
    let payload = vec![0xa0];
    let _reply = client.request(EncodedMessage::new(MessageType::Ping, payload)).await?;
    Ok(())
}
```

`Message.t` is the actual wire string. Unknown names and unknown envelope fields remain inspectable: `Message::raw()` borrows the original frame and `into_raw()` returns it. Ordinary requests surface peer error messages as messages. Optional `Request<AgentProtocol>` implementations add checked unary decoding without narrowing the generic API.

## Owned streams and explicit IDs

```rust,no_run
use microsandbox_agent_client::{AgentClient, TypedMessage};
use microsandbox_protocol::{exec::{ExecRequest, ExecStdin}, message::MessageType};

async fn exchange(client: &AgentClient, request: &ExecRequest) -> Result<(), Box<dyn std::error::Error>> {
    let stream = client.stream(TypedMessage::new(MessageType::ExecRequest, request)).await?;
    let id = stream.id();
    let (sender, mut receiver) = stream.into_parts();
    client.send(id, TypedMessage::new(MessageType::ExecStdin, ExecStdin { data: vec![] })).await?;
    sender.send(TypedMessage::new(MessageType::ExecStdin, ExecStdin { data: b"input".to_vec() })).await?;
    while let Some(message) = receiver.recv().await? {
        println!("{}", message.t);
    }
    Ok(())
}
```

Both split parts retain the connection and ID lease; sender handles may be cloned and there is one consuming receiver. Terminal receipt disables sends and delivers the terminal frame once. Closing or dropping the receiver disables sends and retains bounded drain state until terminal completion. It sends no process signal or filesystem EOF. Transport loss before a terminal frame is an error, distinct from clean exhaustion.

`request_raw(flags, body)`, `stream_raw(flags, body)`, and `send_raw(id, flags, body)` exchange opaque envelopes without CBOR decoding. `write_unchecked(packet_bytes)` serializes exact caller bytes without allocating an ID or subscribing for replies. `TransportPacket` remains an optional standalone framing helper; `packet.into_bytes()` feeds the exact write API. Standalone packet readers require exclusive transport ownership.

## Setup and metadata

Enable `uds` for native Unix sockets or `named-pipe` for Windows pipes. `connect_stream` accepts an owned `AsyncRead + AsyncWrite + Unpin + Send` transport, including `UdsTransport` and `NamedPipeTransport`. The `stream` feature name remains accepted for existing manifests; generic owned transports are available without additional dependencies.

```rust,no_run
use microsandbox_agent_client::AgentClient;
use std::time::Duration;

async fn connect(path: &std::path::Path) -> Result<(), Box<dyn std::error::Error>> {
    let client = AgentClient::connect_with(path, |o| o.setup_timeout(Duration::from_secs(5))).await?;
    println!("{}", client.ready().agent_version());
    println!("{}", client.ready().negotiated_version);
    println!("{} ready bytes", client.ready().ready_bytes().len());
    client.close().await;
    Ok(())
}
```

Setup has one deadline across dial and handshake. The current `[id_min,id_max]` relay prologue and the supported pre-0.5 `[id_offset]` prologue retain their existing parsing, ID ranges, and ready payload behavior. Known operations use the smaller host/peer generation as their availability gate. Current connections continue emitting the host's existing envelope generation; legacy connections emit generation one. Source-level API changes do not retire the legacy wire path.

## Migration from the previous active client

| Previous API | Shared-client API |
| --- | --- |
| `connect(path)` / `connect_stream(stream)` | Same entry points; owned streams no longer require the `stream` feature |
| `request(type, &payload)` | `request(TypedMessage::new(type, &payload))` |
| `stream(type, &payload) -> (id, receiver)` | `stream(TypedMessage::new(type, &payload))`, then `id()` / `into_parts()` |
| `send(id, type, &payload)` | `send(id, TypedMessage::new(type, &payload))` |
| `request_raw(flags, body)` | Same call; retains drain state after a nonterminal first reply |
| `stream_raw(flags, body) -> (id, receiver)` | Owned raw stream, then `id()` / `into_parts()` |
| `send_raw(id, flags, body)` | Same call, requiring a live owned ID |
| `connect_with_timeout(path, duration)` | `connect_with(path, |o| o.setup_timeout(duration))` |
| `connect_stream_with_timeout(stream, duration)` | `connect_stream_with(stream, |o| o.setup_timeout(duration))` |
| `connect_with_deadline(path, deadline)` | `connect_with(path, |o| o.setup_timeout(deadline.saturating_duration_since(Instant::now())))` |
| `connect_stream_with_deadline(stream, deadline)` | `connect_stream_with(stream, |o| o.setup_timeout(deadline.saturating_duration_since(Instant::now())))` |
| `ready()` | `ready().agent` |
| `ready_bytes()` | `ready().ready_bytes()` |
| `negotiated_version()` / `supports(type)` | `ready().negotiated_version` / `ready().supports(type)` |
| `protocol()` / `is_legacy_protocol()` | `ready().wire_format` / `ready().is_legacy_protocol()` |
| `agent_version()` | `ready().agent_version()` |
| `ensure_version_compat(type)` | `AgentProtocol::ensure_version_compat_for(type, client.ready().negotiated_version)` |
| `AgentClient::ensure_version_compat_for(type, generation)` | `AgentProtocol::ensure_version_compat_for(type, generation)` |
| Unwired `AgentStream<T>` / packet-only `AgentTransport` | Active `AgentStream` / owned byte transports accepted directly by `connect_stream` |
| Connection close by ownership transfer | Shared `close(&self)`; last client/stream owner also closes the transport |

`ClientError.delivery` distinguishes `NotSent` from `Unknown` after writer admission. Requests are never automatically replayed. Raw stream access, known-ID sends, custom transports, ready bytes, dynamic names, and encoded payloads remain available without adopting SDK domain objects.

Deadline conversions use `tokio::time::Instant`, matching the original absolute-deadline API. Keep the conversion inside the options closure so it uses the remaining duration when setup begins. Known native and encoded sends perform the same availability check automatically; the public `AgentProtocol` helper supports callers retaining only the generation. Unsupported operations now use the shared `ClientError` category rather than the previous agent-specific error fields.

Validation: `cargo test -p microsandbox-agent-client --all-features --locked`. Unix-socket tests require permission to bind temporary local sockets. The migrated historical handshake cases are source-level regression tests; they do not replace live historical runtime/SDK validation.

The SDK retains an internal optimized client for generation-eight bulk transfers and Unix shared arenas. Its existing bulk APIs remain available; the generic client is the public transport-independent framed API.

---

### Overview (`packages/agent-client/typescript/README.md`)

# @microsandbox/agent-client

Low-level TypeScript access to the microsandbox agent protocol. `AgentClient` is `Client<AgentProtocol>` from `@microsandbox/protocol-client`; the agent package supplies relay setup, its existing encoder, and generation gates. The shared engine owns framing, IDs, queues, cancellation, and transport lifetime. Sandbox lifecycle and process/filesystem convenience APIs belong to the SDK.

```text
AgentProtocol (setup, encoder, gates)
                 |
          Client<AgentProtocol>
            /          \
 native / encoded     raw / exact packets
                 |
       Unix socket / named pipe / WebSocket
```

## Installation and entry points

Install with `npm install @microsandbox/agent-client`. Node.js 22 or newer is required for native transports. The root entry is browser-safe. `@microsandbox/agent-client/node` imports Node's `net` module and accepts native endpoint paths verbatim, including Windows named-pipe names.

## Native messages

```ts
import { connectUnix } from "@microsandbox/agent-client/node";
import { typedMessage } from "@microsandbox/agent-client";

const client = await connectUnix("/tmp/msb-agent.sock", { setupTimeoutMs: 10_000 });
try {
  const response = await client.request(typedMessage("core.fs.request", {
    op: { Stat: { path: "/etc/os-release", follow_symlink: true } },
  }), { requestTimeoutMs: 5_000 });
  console.log(response.type, response.decodePayload());
  console.log(client.ready.agentVersion, client.ready.negotiatedVersion);
} finally {
  await client.close();
}
```

`typedMessage(name, value)` asks the agent codec to encode a native payload. `encodedMessage(name, bytes)` supplies only the application payload; it does not supply an envelope or framed packet. Both paths apply known message gates and flags. Names are open strings, so extensions remain accessible without editing an enum. These methods do not validate an application schema. `requestTyped()` accepts an optional checked request with its own result decoder and requires a terminal response.

The current TypeScript agent encoder continues to emit generation-five envelopes, including its existing CBOR map and byte-string representations. The ready generation controls feature gates. The supported pre-0.5 relay prologue selects generation-one envelopes and rejects unavailable filesystem/TCP operations before sending. The captured ready frame and unknown fields remain available through `client.ready.frame` and `client.ready.readyBytes`.

## Browser streams

```ts
import { AgentClient, WebSocketConnector, typedMessage } from "@microsandbox/agent-client";

const client = await AgentClient.connectConnector(
  new WebSocketConnector("wss://relay.example.com/agent"),
  { setupTimeoutMs: 10_000 },
);
try {
  const stream = await client.openStream(typedMessage("core.exec.request", {
    cmd: "sh", args: ["-lc", "echo hello"],
  }));
  try {
    for await (const frame of stream) {
      console.log(frame.type, frame.payload);
    }
  } finally {
    stream.close();
  }
} finally {
  await client.close();
}
```

The relay must forward the agent's binary byte stream, including its prologue. `WebSocketTransport` also wraps a caller-constructed socket for custom authentication. Its incoming queue defaults to 8 MiB and 4096 messages; overflow closes the connection because browser WebSockets cannot pause incoming messages. Outgoing writes wait for the browser's send queue to drain. The connector and handshake share one setup deadline.

## Raw and exact access

```ts
import { type AgentClient, TransportPacket } from "@microsandbox/agent-client";

async function exchangeOpaque(client: AgentClient, envelope: Uint8Array) {
  const stream = await client.openStreamRaw(0, envelope);
  const { sender, receiver } = stream.split();
  try {
    await sender.send(0, envelope);
    await client.sendRaw(sender.id, 0, envelope);
    return await receiver.next({ requestTimeoutMs: 5_000 });
  } finally {
    receiver.close();
    sender.close();
  }
}

async function forwardExactPacket(client: AgentClient, bytes: Uint8Array) {
  await client.writeUnchecked(TransportPacket.fromBytes(bytes));
}
```

Raw APIs leave envelope and payload bytes opaque. `requestRaw(flags, body)` returns the first raw reply. `openStreamRaw()` receives through the terminal reply. `sendRaw(id, flags, body)` requires an active ID owned by this connection. A split sender retains its original lease and cannot regain permission if the numeric ID is reused. `writeUnchecked(bytes)` can write even deliberately malformed transport bytes; `TransportPacket.fromBytes()` optionally validates a single packet first.

Native replies retain the complete original raw frame as `frame.raw`, including unknown envelope fields. `core.error` is an ordinary terminal agent response; interpreting it belongs to the caller or a checked request decoder.

## Ownership and failures

Streams own their send and receive halves. `split()` transfers those halves and invalidates the original stream's send/receive methods. Closing a receiver or leaving async iteration stops local delivery; it sends no remote cancel, signal, or EOF. An abandoned ID remains draining until its terminal frame arrives. A timed-out `next()` does not consume the next arriving frame.

`client.clone()` shares the connection. `client.close()` closes every shared handle and wakes pending operations. Explicitly close clients and streams: JavaScript garbage collection only offers best-effort cleanup. A request deadline covers writer admission and the local response wait; it does not cancel remote execution. `ClientError.delivery` is `not_sent` before admission and `unknown` afterward. Nothing automatically retries an admitted operation.

## Migration from the previous package API

| Previous surface | Shared-engine surface |
| --- | --- |
| Independent `AgentClient` class/router | `AgentClient = Client<AgentProtocol>` plus convenience constructors |
| `handshakeTimeoutMs` | `setupTimeoutMs`, covering dial and handshake together |
| `client.negotiatedVersion()` | `client.ready.negotiatedVersion` |
| Closed `MessageType` parameter | Open string names; `MessageType` still lists known names |
| Packet-oriented custom `AgentTransport` | Ordered `read(maxBytes)`, `write(bytes)`, `close()` byte transport |
| `InboundFrame.fromRawFrame(frame)` | `new AgentEnvelopeCodec().decode(frame)` |
| `new AgentStream(...)` | Obtain owned streams from `client.openStream()` or `openStreamRaw()` |
| Request, encoded payload, stream, exact packet methods | Retained on the generic client, with additional raw and split interfaces |

These are public source API changes. Existing agent wire behavior is preserved; source and byte tests do not replace the required historical runtime/SDK launch tests.

## Development

From the repository's `packages` directory, run `npm ci`, `npm run build`, `npm run typecheck`, and `npm test`. The private workspace links the shared package locally; published dependency metadata uses the release version. The README's TypeScript examples are checked with the public package declarations.

---

### Overview (`packages/control-client/rust/README.md`)

# Rust control client

`ControlConnection` discovers whether an existing control endpoint supports framed CBOR or legacy JSON. `ControlClient` is the explicitly framed `Client<ControlProtocol>` entry point, and `JsonControlClient` is an explicit legacy unary adapter. Their checked helpers share the same operation records.

```text
ControlConnection -- JSON capabilities --+-- advertised CBOR: fresh stream + hello
                                        |                      |
                                        |               ControlClient
                                        |
                                        +-- legacy: JsonControlClient
```

Automatic discovery has one deadline across the probe, redial, and hello. It selects CBOR only after a valid affirmative capability response. Malformed replies, arbitrary peer errors, empty EOF, and failed framed setup are errors. There is no error-string downgrade or mutation replay. `ControlClient` sends its hello directly when callers already know the peer supports framed control.

```rust,no_run
use microsandbox_control_client::{
    ControlConnection, ControlMessageType, ControlReply, Empty, GetMemoryState, TypedMessage,
};

async fn inspect(path: &std::path::Path) -> Result<(), Box<dyn std::error::Error>> {
    let connection = ControlConnection::connect(path).await?;
    let reply = connection.request(TypedMessage::new(ControlMessageType::MemoryState, Empty {})).await?;
    match reply {
        ControlReply::Framed(message) => println!("{}", message.t),
        ControlReply::Json(reply) => println!("{} original JSON bytes", reply.raw().len()),
    }
    println!("{} MiB", connection.request_typed(&GetMemoryState).await?.target_mib);
    connection.close().await;
    Ok(())
}
```

In framed mode, clones reuse one connection. Automatic JSON mode rediscovers before each fresh exchange when its connector cannot prove runtime identity. An owner implementing `VerifiedControlConnector` verifies the connected peer and the runtime's OS birth token on every dial, then rechecks the active run before each operation; `connect_verified_connector` can reuse that owner's JSON selection. A path or PID alone is insufficient. A detected change invalidates the handle before sending instead of silently retargeting the request. The runtime owner, such as an SDK backend, supplies that identity implementation; the protocol package does not claim to verify it itself.

`JsonControlClient::new(path)` and `from_connector(...)` are inert constructors for callers explicitly choosing legacy mode. Its `request` translates only known native requests and returns the actual `JsonReply`, including `ok:false` replies. Checked helpers report `LegacyRemote` with the original reply and unknown batch progress. JSON numbers retain their original tokens; checked memory fields use `u64` without a floating-point intermediate. `connection.framed()` fails locally in JSON mode, and encoded-payload input is rejected there before dialing. Raw streams and exact packets remain available on the explicitly framed client.

`connection.capabilities()` borrows the released generation-1 discovery projection without network I/O, while `connection.runtime_capabilities()` exposes the complete discovered facility set. `GetCapabilities` remains available for a fresh generation-1-compatible observation and `GetRuntimeCapabilities` requests the complete record on generation 2. `closed().await` observes shared closure, including an idle framed disconnect; a successful JSON exchange ending in EOF does not close the session.

Generation 1 remains the released capabilities, memory, CPU, and secrets surface. Generation 2 adds `CreateCheckpoint`, `CreateDiskCheckpoint`, `CreateBranch`, `PauseRuntime`, `ResumeRuntime`, `GetPauseState`, `GrowRootDisk`, and `CompactDisks`. `ControlConnection` talking to a framed generation-1 runtime selects the historical JSON representation for these checked operations before sending anything; it never retries a mutation after a framed failure. The explicit `ControlClient` instead returns a not-sent unsupported-operation error. Linux descriptor-backed branch creation remains an SDK-level JSON/`SCM_RIGHTS` exception because ordinary framed messages cannot transfer file descriptors.

Enable `uds` for Unix endpoint connections or `named-pipe` for Windows endpoint connections. Caller-owned transports and connectors can be used without either native feature. Endpoint paths are accepted verbatim.

```rust,no_run
use microsandbox_control_client::{
    ControlClient, GetMemoryState, SetMemoryTarget, size::SizeExt,
};

async fn resize(path: &std::path::Path) -> Result<(), Box<dyn std::error::Error>> {
    let client = ControlClient::connect(path).await?;
    let before = client.request_typed(&GetMemoryState).await?;
    let accepted = client.request_typed(&SetMemoryTarget::new(2048.mib())).await?;
    println!("{} -> {} MiB", before.target_mib, accepted.target_mib);
    client.close().await;
    Ok(())
}
```

Memory wire records retain `u64` values. The existing size helpers retain their conversion rules; direct `SetMemoryTarget { total_mib }` construction provides full-width wire input. Target responses report acceptance and current observations, not guest convergence. Ordered secret batches preserve partial completion and the index of the first failed entry.

The client keeps `request`, `stream`, `request_raw`, `stream_raw`, explicit-ID `send`/`send_raw`, exact `write_unchecked`, owned `into_parts`, and checked `request_typed` operations. Generic requests return peer error frames as messages; checked helpers interpret them and retain the original response. No sandbox, execution, filesystem, or convergence service is required to use any of these paths.

```rust,no_run
use microsandbox_control_client::{ControlClient, EncodedMessage};

async fn inspect(client: &ControlClient, payload: Vec<u8>) -> Result<(), Box<dyn std::error::Error>> {
    let response = client.request(EncodedMessage::new("extension.inspect", payload)).await?;
    println!("{}: {} payload bytes", response.t, response.p.len());
    Ok(())
}
```

Setup defaults to one ten-second deadline and requests to a thirty-second local wait. Request expiry does not cancel remote work or trigger replay. `ClientError` retains `NotSent` versus `Unknown` delivery. Clones and owned streams share a connection; `close` closes it for all owners.

Run `cargo test -p microsandbox-control-client --all-features --locked`. For live tests, set `MSB_CONTROL_TEST_SOCKET` and `MSB_CONTROL_TEST_MODE=cbor` or `json`. The explicitly framed test requires CBOR; run only `live_automatic_discovery_and_explicit_json -- --ignored --exact` against a JSON-only runtime. Use bounded disposable fixtures (512 MiB and two CPUs), and run live tests serially. The automatic test changes targets and restores them through explicit JSON. Optional `MSB_CONTROL_TEST_SECRET` names a dummy fixture initially set to `before` and allowed for `example.invalid`; the test exercises legacy partial-failure reporting and restores that fixture. Skipped live tests are not runtime or platform validation.

---

### Overview (`packages/control-client/typescript/README.md`)

# TypeScript control client

`ControlClient` is `Client<ControlProtocol>` over the shared framed engine. It sends a bounded `control.hello` directly, negotiates generation and limits, and exposes the original welcome frame. Use it when the peer is known to support framed control. It does not probe JSON or downgrade after failure.

The root entry is browser-safe and accepts a caller-owned byte transport or reconnectable connector. The `/node` entry accepts an existing Unix socket or Windows named-pipe path without renaming it.

```ts
import { GetMemoryState, MiB, SetMemoryTarget, typedMessage } from "@microsandbox/control-client";
import { connectFramedControl } from "@microsandbox/control-client/node";

async function inspectAndResize(path: string) {
  const client = await connectFramedControl(path, { setupTimeoutMs: 10_000 });
  try {
    const response = await client.request(typedMessage("control.memory.state", {}));
    console.log(response.type, response.raw.body);
    const before = await client.requestTyped(new GetMemoryState());
    const accepted = await client.requestTyped(new SetMemoryTarget(MiB(2048)));
    console.log(before.target_mib, accepted.target_mib); // bigint, even for small values
  } finally {
    await client.close();
  }
}
```

Generation-1 checked helpers cover capabilities, memory state/target, CPU state/target, and ordered secret updates. Generation 2 adds `GetRuntimeCapabilities`, `CreateCheckpoint`, `CreateDiskCheckpoint`, `CreateBranch`, `PauseRuntime`, `ResumeRuntime`, `GetPauseState`, `GrowRootDisk`, and `CompactDisks`. They validate replies and retain the actual response on `ControlClientError`. Generic message requests return peer error frames directly. A target reply reports acceptance and observation; it does not establish guest convergence. Secret batches stop at the first failure and retain earlier completed changes.

Use `connectControl` for automatic discovery on the same endpoint. It sends the existing read-only JSON capabilities request, closes that exchange, and opens a fresh framed connection only after an affirmative CBOR advertisement. Setup has one total deadline across both connections. Malformed discovery, timeout, EOF without a reply, or a failed welcome never trigger fallback or replay.

```text
connectControl(path)
    |
    +-- JSON capabilities --> valid legacy reply --> fresh JSON exchange per operation
    |
    +-- JSON capabilities --> advertises CBOR -----> fresh hello/welcome --> shared framed client
```

```ts
import { GetMemoryState, MiB, SetMemoryTarget, typedMessage } from "@microsandbox/control-client";
import { connectControl } from "@microsandbox/control-client/node";

async function compatibleResize(path: string) {
  const connection = await connectControl(path);
  try {
    const reply = await connection.request(typedMessage("control.memory.state", {}));
    if (reply.kind === "cbor") console.log(reply.frame.raw.body);
    else console.log(reply.reply.raw, reply.reply.value.get("memory"));
    const before = await connection.requestTyped(new GetMemoryState());
    const accepted = await connection.requestTyped(new SetMemoryTarget(MiB(2048)));
    return { before, accepted };
  } finally {
    await connection.close();
  }
}
```

`ControlConnection.connectConnector` accepts a repeatable caller connector from the browser-safe root. The connection exposes the read-only generation-1 `capabilities` projection, the complete `runtimeCapabilities` discovery snapshot, `mode`, `clone()`, `isClosed()`, `close()`, `request()`, and `requestTyped()`. `framed()` returns the complete generic surface in CBOR mode and fails locally with `unsupported_mode` in JSON mode. Encoded messages likewise fail locally in JSON mode; the adapter translates only known, validated native operations.

When a framed peer selects generation 1, `ControlConnection.requestTyped()` sends generation-2 checked operations through their historical JSON form before any mutation is admitted. An unverified connector rediscovers the endpoint format first. This path is never a retry after a CBOR failure. The explicit `ControlClient` rejects those operations locally against a generation-1 welcome. Linux descriptor-backed branch creation remains outside ordinary CBOR because it requires `SCM_RIGHTS`; generation 2 covers direct local branching without descriptor transfer.

`JsonReply` contains the original response line, including its delimiter and whitespace, and a lossless `Map` of fields. `JsonNumber.token` retains each original numeric token; checked memory observations become `bigint` without passing through a JavaScript number. Ordinary requests return `ok:false` replies. Checked helpers raise `legacy_remote` with the original reply and unknown batch progress. Neither arbitrary diagnostic strings nor legacy errors are converted into structured CBOR errors.

For explicit legacy access, construction is inert and performs no discovery:

```ts
import { GetCpuState } from "@microsandbox/control-client";
import { jsonControl } from "@microsandbox/control-client/node";

async function inspectLegacy(path: string) {
  const client = jsonControl(path);
  try {
    return await client.requestTyped(new GetCpuState());
  } finally {
    await client.close();
  }
}
```

The equivalent root API is `JsonControlClient.fromConnector(connector, options?)`. Ordinary legacy request and response lines keep their existing size contract; only automatic discovery replies have a 64 KiB limit. Preparing a secret batch snapshots its entries without imposing the framed 4 MiB ceiling on JSON. JavaScript strings remain subject to garbage collection; explicit buffer clearing is not a guarantee that all copies have been erased.

A standalone automatic JSON connection rediscovers before each fresh operation because an endpoint path cannot establish process identity. `ControlConnection.connectVerifiedConnector` permits discovery reuse when the caller supplies a `VerifiedControlConnector`: `connect` must verify every connected peer against the saved OS process birth identity, and `verifySession` must check the active run and process before each operation. Both receive a deadline and cancellation context. Runtime replacement raises `runtime_changed` before mutation admission and closes the shared handle. This extension point does not implement an SDK database or OS identity verifier by itself.

`MiB`, `GiB`, `KiB`, `TiB`, and `Mebibytes` are exported from `@microsandbox/types/size`. Their arithmetic matches the SDK helpers. `SetMemoryTarget` validates integer precision and range before converting to the wire quantity; it also accepts `bigint` for full-width values.

All generic operations remain available:

| Need | Method |
|---|---|
| Native or encoded payload | `request(typedMessage(...))`, `request(encodedMessage(...))` |
| Exact opaque envelope | `requestRaw(flags, body)` |
| Message or raw subscription | `openStream(...)`, `openStreamRaw(...)` |
| Owned sender and receiver | `stream.split()` |
| Follow-up using an owned ID | `sendOnStream(id, ...)`, `sendRaw(id, ...)` |
| Exact packet without a subscription | `writeUnchecked(bytes)` |
| Optional checked unary operation | `requestTyped(request)` |
| Shared connection ownership | `clone()`, `close()` |

```ts
import { ControlClient, encodedMessage, type ByteTransport } from "@microsandbox/control-client";

async function inspectExtension(transport: ByteTransport, payload: Uint8Array) {
  const client = await ControlClient.connectTransport(transport);
  try {
    const reply = await client.request(encodedMessage("extension.inspect", payload));
    return { name: reply.type, payload: reply.payload, original: reply.raw };
  } finally {
    await client.close();
  }
}
```

The default control request deadline is 30 seconds. Expiry abandons the local wait; it does not cancel remote work or retry it. `ClientError.delivery` distinguishes requests that were not admitted from unknown outcomes. Complete idle connections do not expire automatically. Explicit close closes every clone; JavaScript finalization is only best-effort cleanup.

From `packages`, run `npm run build`, `npm run typecheck`, and `npm test`. Live validation is deliberately opt-in: set `MSB_CONTROL_TEST_SOCKET` to a disposable framed runtime's socket and run `npm test -w @microsandbox/control-client`. Add `MSB_CONTROL_TEST_MODE=cbor` to run automatic discovery as well. The package serializes test files because live files mutate and restore the same VM's targets. A skipped live test is not platform or runtime compatibility evidence.

For a historical JSON-only runtime, set `MSB_CONTROL_TEST_MODE=json` and run only the compatibility live file with `npm exec -w @microsandbox/control-client -- vitest run tests/compatibility-live.test.ts`. It checks automatic and explicit JSON access, concurrent reads, full-width result types, and accepted memory/CPU targets, then restores the original targets. These running-runtime tests do not establish SDK launch compatibility.

The additional secret test requires `MSB_CONTROL_TEST_SECRET` naming a dummy fixture initially set to `before` and allowed for `example.invalid`. It exercises partial completion, malformed batches, and legacy JSON failures, then restores those fixture values.

---

### Overview (`packages/microsandbox-types/rust/README.md`)

# microsandbox-types

Shared task and wire contract types for microsandbox.

This crate is the source of truth for the backend-neutral shapes that describe a sandbox and the cloud HTTP bodies that carry them. The Rust SDK, the CLI, and the cloud backend all import these types so they agree on one definition instead of duplicating wire shapes. The generated `@microsandbox/types` TypeScript package is derived from this crate.

It is a leaf dependency by design. It pulls in `serde`, `serde_json`, `chrono`, `sha2`, and `thiserror`, and nothing from the local VM machinery (no runtime, image, network, or database crates). That keeps it cheap enough for front-end generation, cloud API models, and SDK wrappers to all depend on.

## What This Crate Owns

The crate models durable user and wire intent: what the user wants to exist, not how a backend fulfills it.

- **Sandbox spec** (`domain` module): `SandboxSpec`, `SandboxResources`, `SandboxRuntimeOptions`, `EnvVar`, `SandboxPolicy`.
- **Rootfs sources**: `RootfsSource` (bind, OCI, disk image), `OciRootfsSource`, `DiskImageFormat`, `PullPolicy`.
- **Mounts and patches**: `VolumeMount`, `MountOptions`, `StatVirtualization`, `HostPermissions`, `Patch`, `SecurityProfile`.
- **Volumes and snapshots**: `VolumeSpec`, `VolumeKind`, `NamedVolumeCreate`, `NamedVolumeMode`, `SnapshotSpec`, and the canonical `SnapshotManifest` schema.
- **Networking**: `NetworkSpec`, `PublishedPortSpec`, `PortProtocol`.
- **Exec and logs**: `Rlimit`, `RlimitResource`, `SandboxLogLevel`, `LogSource`, `HandoffInit`.
- **Cloud wire contracts** (`cloud` module): sandbox, snapshot, pagination, message, and error request/response types.
- **Validation** (`validation` module): `validate_sandbox_name`, `validate_hostname`, `hostname_from_sandbox_name`, and the `MAX_SANDBOX_NAME_BYTES` / `MAX_HOSTNAME_BYTES` limits.

Backend-private materialized state stays out: registry credentials, local CA paths, replace flags, pull-discovered manifest digests, snapshot upper paths, process handles, and DB rows belong to the SDK and backends, not the contract.

## Usage

```toml
[dependencies]
microsandbox-types = "0.7.0"
```

```rust
use microsandbox_types::{RootfsSource, SandboxResources, SandboxSpec};

let spec = SandboxSpec {
    name: "worker".into(),
    image: RootfsSource::oci("python"),
    resources: SandboxResources { cpus: 2, memory_mib: 1024 },
    ..Default::default()
};
```

The Rust SDK re-exports the contract types it accepts, so most SDK users get these through `microsandbox::*` and do not depend on this crate directly.

## Serialization Notes

- `SandboxSpec`, `SandboxResources`, `SandboxRuntimeOptions`, `NetworkSpec`, and `MountOptions` use `#[serde(default)]`, so partial JSON fills missing fields from static defaults.
- Lowercase-on-the-wire enums (`StatVirtualization`, `HostPermissions`, `SecurityProfile`, `SandboxLogLevel`, `LogSource`, `PortProtocol`) match the CLI grammar and the TypeScript string unions.
- `VolumeMount` has hand-written `Serialize`/`Deserialize`. It tags variants with a `type` field and accepts a legacy top-level `readonly` flag, folding it into `MountOptions` on read.
- Static defaults only. Nothing here reads process-global or profile config; the SDK and backends apply environment defaults before execution.

## TypeScript Generation

The `ts` feature derives `ts_rs::TS` on exported wire types and builds the `microsandbox-types-generate` binary, which writes `domain.ts`, `snapshot.ts`, and `cloud.ts` beneath `../typescript/src`.

```bash
# Regenerate the checked-in bindings.
cargo run -p microsandbox-types --features ts --bin microsandbox-types-generate

# Verify they are current (used in CI; exits non-zero when stale).
cargo run -p microsandbox-types --features ts --bin microsandbox-types-generate -- --check
```

A unit test (`checked_in_bindings_match_generated_output`) also fails when any generated TypeScript file drifts, so `cargo test --features ts` catches stale bindings too.

## Testing

```bash
cargo test -p microsandbox-types
cargo test -p microsandbox-types --features ts
```

---

### Overview (`packages/microsandbox-types/typescript/README.md`)

# @microsandbox/types

Shared task and wire contract types for microsandbox, as TypeScript.

This package is the type-only mirror of the cloud contract in the `microsandbox-types` Rust crate. It gives TypeScript consumers (the cloud front end and anything talking to the cloud API) the exact cloud wire shapes microsandbox uses, so a `CloudSandboxSpec` means the same thing on both sides of the wire. Only the domain types the cloud contract references are generated; the broader SDK/domain surface is not.

It contains no runtime code. Every export is a type; importing it adds nothing to your bundle.

## Generated, Not Hand-Written

Three files are generated from the Rust crate with [`ts-rs`](https://github.com/Aleph-Alpha/ts-rs), each carrying a `// @generated` header. Do not edit them:

- `src/cloud.ts` — the package entry: the cloud wire types, which import and re-export their domain and snapshot dependencies.
- `src/domain.ts` — only the domain types the cloud twins transitively reference (`EnvVar`, `Rlimit`, `NetworkPolicy`, …).
- `src/snapshot.ts` — the canonical snapshot manifest schema embedded by cloud snapshot resources.

To change a shape, edit the Rust type and regenerate from the repo root:

```bash
cargo run -p microsandbox-types --features ts --bin microsandbox-types-generate
```

## Install

```bash
npm install @microsandbox/types
```

## Usage

Import the shapes you need with `import type`:

```ts
import type {
  CloudCreateSandboxRequest,
  CloudSnapshot,
  CloudSnapshotOperation,
} from "@microsandbox/types";

function createSandbox(request: CloudCreateSandboxRequest) {
  // POST the source-tagged request to the cloud API
}
```

`CloudCreateSnapshotRequest` and `CloudSnapshot` are unions discriminated by
`kind`; the current public variant is `"disk"`. `CloudSnapshotOperation` carries
that same kind and exposes only the public statuses `queued`, `in_progress`,
`succeeded`, and `failed`. Both snapshot creation results and disk-snapshot
restore requests use `CloudSnapshotLocation` to distinguish a managed artifact
ID from a host-volume path.

## Generated Shape Notes

The bindings follow `ts-rs` conventions, which mirror the Rust serde representation:

- Cloud enums are internally tagged with a `type` field: `CloudRootfsSource` is `{ type: "bind"; … } | { type: "oci"; reference: string } | { type: "disk_image"; … }`, and `CloudVolumeMount` / `CloudHostPattern` / `CloudViolationAction` follow the same shape.
- `CloudCreateSandboxRequest` is a flat union discriminated by `source: "oci" | "bind" | "disk_image" | "disk_snapshot"`. Common sandbox fields stay flat on every variant; source-specific fields such as `reference`, `path`, and `disk_snapshot_ref` appear only where they apply. Servers also accept the legacy `image` request shape without `source` during migration.
- `CloudCreateSnapshotRequest` and `CloudSnapshot` are flat unions discriminated by the current `kind: "disk"`; common snapshot fields stay flat beside the discriminator.
- Lowercase domain enums like `StatVirtualization` are string-literal unions (`"strict" | "relaxed" | "off"`).
- Optional Rust fields are `T | null`; fields skipped when absent are `?:` optional.
- Referenced domain and snapshot types live in `domain.ts` and `snapshot.ts` and are re-exported from the package entry, so a single import from `@microsandbox/types` sees the whole cloud contract.

## Build And Typecheck

```bash
npm run build       # tsc -> dist/
npm run typecheck   # tsc --noEmit
```

---

### Overview (`packages/protocol-client/rust/README.md`)

# Generic framed client

`Client<P>` owns one byte connection, its correlation IDs, and all routed subscriptions. `P: Protocol` establishes the transport and selects message metadata and an `EnvelopeCodec`. Protocol consumers can implement both traits outside this crate. `tests/engine.rs` exercises external protocol setup and routing, while `tests/external_codec.rs` supplies an external connector and a non-CBOR envelope codec across native, encoded, raw, checked, stream, explicit-ID, and exact-packet calls. Rust native payload serialization remains CBOR; the custom codec controls its enclosing envelope, and encoded/raw paths retain caller-supplied bytes.

```text
native payload ---- TypedMessage ---+
encoded payload --- EncodedMessage -+--> codec --> shared writer
raw envelope -----------------------+-----------> shared writer
exact packet -----------------------------------> shared writer

shared reader --> ID routing --> RawStream --> Stream (decode on recv)
```

`request` returns the first response. If it is not terminal, the router retains the ID and discards later replies until terminal completion. `request_typed` additionally requires a terminal response and invokes a borrowed `Request<P>` implementation's checked decoder. Application errors remain that implementation's responsibility; the generic engine does not equate a terminal response with success.

`stream` and `stream_raw` return owned subscriptions. `into_parts()` moves them into a cloneable sender and one receiver. Both retain the connection. Dropping or closing the receiver disables sends and drains any admitted operation until terminal completion; it does not send a process signal, file EOF, TCP close, or cancellation packet. Old sender handles cannot gain permission when an ID is reused.

`send` and `send_raw` support explicit IDs owned by the connection. `write_unchecked` serializes caller-owned packet bytes without creating an ID or response subscription. Native and encoded-payload paths apply protocol availability gates; raw paths preserve opaque bytes. A decoded `Message` retains its original `RawFrame`, including unknown envelope fields.

Connections are shared handles. Explicit `close(&self)` closes every handle and wakes waiters. `closed().await` observes closure, including idle peer disconnection, without polling; canceling that wait leaves the connection alone. Last-owner drop closes the transport; the reader and writer do not keep the owner alive. A stream remains an owner after a temporary client handle is dropped.

Queues have item and combined byte limits. A retained slow receiver can backpressure the whole connection; fair per-stream flow control is not provided by the wire protocol. Dropping that receiver releases its queued frames and wakes blocked routing. Active and draining IDs both consume in-flight capacity.

`ClientError.delivery` distinguishes `NotSent` before writer admission from `Unknown` afterward. Timeouts and dropped futures stop local waiting, without retrying or undoing peer work. An EOF before terminal completion is an error; truncation inside a frame is reported separately. Optional incomplete-frame deadlines start at the first byte and do not expire idle connections.

The `uds` and `named-pipe` features enable `LocalConnector` on their platforms. It uses the endpoint supplied by the caller without inventing names. A custom `Connector` receives an absolute setup deadline and returns an exclusively owned byte stream. `connect_stream` accepts a transport that the caller has already dialed and authenticated.

Validation: `cargo test -p microsandbox-protocol-client --all-features --locked`. The Unix connector test binds a temporary socket and therefore needs an execution environment that permits local socket creation.

Protocols choose whether completed correlation IDs may be reused. Agent connections retire IDs for the lifetime of the connection, matching the relay; control connections may reuse an ID after its terminal response. Exhaustion is a local error and does not reconnect or replay a request.

---

### Overview (`packages/protocol-client/typescript/README.md`)

# @microsandbox/protocol-client

A protocol-neutral framed client for TypeScript. `Client<P>` owns one byte transport, correlation IDs, bounded queues, a serialized writer, and response routing. `P` owns setup, its envelope codec, and message gates. Agent and control operations stay in their protocol packages; sandbox lifecycle stays in the SDK.

```text
                 Client<P>
          /          |           \
 native payload   encoded      raw frame / exact packet
          \          |           /
          protocol codec + shared router
                       |
                ByteTransport
```

## Implementing a protocol

An external package can implement setup and metadata using only public declarations. This example protocol reads one ready byte and uses the shared CBOR envelope. Its server must implement that same handshake.

```ts
import {
  Client, CborEnvelopeCodec, readExactly,
  type Protocol, type ByteTransport, type EstablishContext, type Established, type SendMetadata,
} from "@microsandbox/protocol-client";

class ExampleProtocol implements Protocol<number> {
  async establish(transport: ByteTransport, context: EstablishContext): Promise<Established<number>> {
    const ready = (await readExactly(transport, 1, context.signal))[0]!;
    return {
      transport, ready, codec: new CborEnvelopeCodec(), limits: context.limits,
      ids: { start: 1, endExclusive: 2 ** 32 },
    };
  }
  prepare(ready: number, _wireName: string): SendMetadata {
    return { generation: ready, flags: 0 };
  }
}

async function connect(transport: ByteTransport) {
  return Client.connectTransport(transport, new ExampleProtocol(), { setupTimeoutMs: 10_000 });
}
```

`ByteTransport.read(maxBytes)` returns between one and `maxBytes` ordered bytes, or `null` for clean EOF. The engine makes one read at a time and independently serializes writes. `close()` must wake both directions. Passing a transport transfers ownership, including when setup fails. `Connector.connect(context)` supplies repeatable dialing with a deadline and abort signal; it does no discovery itself. `LocalConnector` and `NodeTransport` live at `@microsandbox/protocol-client/node`. Browser-safe `WebSocketConnector` and `WebSocketTransport` are root exports.

`EnvelopeCodec.encodePayload()` supports protocol-specific native encoders. `encode()` wraps supplied payload bytes; `decode()` returns an inspectable `InboundFrame` retaining its `raw` frame. The standard codec uses `{ v, t, p }`, with `p` an actual CBOR byte string, strict unsigned integers, and open message names. Raw subscriptions never invoke that codec.

## Public access levels

| API | Input and result |
| --- | --- |
| `request(typedMessage(name, value))` | Native payload; first `InboundFrame` reply |
| `request(encodedMessage(name, bytes))` | Pre-encoded application payload; first message reply |
| `requestTyped(request)` | Optional checked request and result decoder; requires terminal reply |
| `openStream(message)` | Owned native stream through terminal |
| `requestRaw(flags, body)` / `openStreamRaw(flags, body)` | Opaque envelope bodies and raw replies |
| `sendOnStream(id, message)` / `sendRaw(id, flags, body)` | Explicit ID owned by this connection |
| `writeUnchecked(bytes)` | Exact transport bytes without framing or semantic checks |
| `stream.split()` | Separate owned sender and single receiver |
| `client.ready` / `clone()` / `close()` | Protocol metadata and shared connection lifetime |

Checked helpers are optional; neither native messages nor raw responses are narrowed to a generated message enum. Application errors remain distinct from `ClientError`. Unknown message names, payload bytes, and envelope fields remain inspectable.

## Limits, ownership, and cancellation

Default engine limits are 4 MiB per frame, 1024 in-flight exchanges, 256 queued writes, 1024 queued responses per exchange, and 8 MiB of combined engine-owned packet buffering. Protocol setup can select tighter limits. Counts exclude caller-owned inputs, returned frames, and operating-system or browser socket buffers. Native transports read in paused mode; the WebSocket adapter separately caps its input queue and closes on overflow.

A setup deadline covers dialing and protocol establishment. Optional request deadlines cover writer admission and local response waiting; incomplete-frame deadlines start only when a frame begins. Idle connections have no implicit incomplete-frame deadline. `AbortSignal` cancels local waiting. `delivery: "not_sent"` means an attempt never entered the writer; `"unknown"` means it may have reached the peer. The engine never retries an admitted operation.

Closing a stream only stops local delivery. Nonterminal abandoned exchanges retain their IDs until terminal; stale split senders remain invalid after ID reuse. A receive timeout leaves future frames available. Shared `client.close()` wakes every handle and closes the transport. Close explicitly in `finally`: JavaScript finalization is best-effort, while explicit close is deterministic.

## Development

From `packages`, run `npm ci`, `npm run build`, `npm run typecheck`, and `npm test`. Tests exercise an externally defined protocol, full-width IDs, byte preservation, routing, cancellation and admission, ownership, backpressure, transport truncation, WebSocket bounds, and public README examples. Live runtime and historical SDK compatibility tests are additional release requirements.

To check package exports independently of workspace resolution, install locally packed client packages into a separate consumer directory, then run `node protocol-fixtures/check-packed-protocol-consumer.mjs /path/to/consumer` from `packages`. The consumer lockfile must identify the protocol-client tarball and integrity, and the installed package must not be a workspace symlink. This compiles and executes a custom connector, non-CBOR envelope codec, native/encoded/raw/checked calls, full-width IDs, split streams, explicit sends, and exact packet writes against the installed declarations.

The command prints a retained output directory. With the packed agent and control packages also installed in that consumer, pass that output directory to `node protocol-fixtures/check-browser-package-roots.mjs /path/to/consumer/protocol-consumer-output`. This bundles all three root exports for the browser, rejects Node builtin imports, and executes the same protocol consumer without Node globals. It verifies browser package loading; actual browser/WebSocket integration remains a separate check.

Protocols choose whether completed correlation IDs may be reused. Agent connections retire IDs for the lifetime of the connection, matching the relay; control connections may reuse an ID after its terminal response. Exhaustion is a local error and does not reconnect or replay a request.

---

### Overview (`sdk/go/examples/README.md`)

# Go Examples

Run from `sdk/go`:

```sh
go run ./examples/basic
```

## Examples

| Example | Description |
|---------|-------------|
| `basic` | Create a sandbox, run commands, use filesystem and metrics |
| `cloud-backend` | Cloud backend lifecycle and live logs |
| `detached` | Detached lifecycle, reattach, stop, and remove |
| `disk` | Build and mount a raw ext4 disk image |
| `errors` | Typed error handling with `IsKind` and `errors.As` |
| `filesystem` | Filesystem read/write/list/stat/copy/streaming operations |
| `image-cache` | List, get, inspect, and garbage-collect cached OCI images |
| `lifecycle-convergence` | Live convergence and stale-handle identity-safety checks with timing metrics |
| `metrics` | Point-in-time and streaming metrics |
| `network` | Presets, DNS, TLS, and custom network settings |
| `patches` | Pre-boot rootfs patches |
| `ports` | Publish guest TCP ports on host ports |
| `secrets` | Secret placeholder injection |
| `snapshot-fork` | Create a stopped-sandbox snapshot and restore a fresh sandbox from it |
| `streaming` | Streaming exec, signals, and cancellation |
| `tls` | TLS interception configuration |
| `volumes` | Named volume lifecycle |

The `snapshot-fork` example keeps its historical directory name; it demonstrates restoring a saved disk snapshot. Live forking uses the `Fork` API.

---

### Api Reference Overview (`docs/api-reference/overview.mdx`)

---
title: Overview
"og:title": Microsandbox cloud REST API reference
description: Authenticate cloud REST API requests with API keys or personal tokens, and handle errors and pagination.
keywords: ["cloud API", "REST API", "API keys", "personal access tokens", "endpoint reference"]
icon: "book-open"
---

<Note>
  microsandbox cloud is in [private beta](/cloud/overview); the API is available to organizations with access.
</Note>

The microsandbox cloud REST API manages sandboxes and the accounts around them. Use it directly when an SDK does not cover your workflow, or when you need account, billing, or audit endpoints.

For running code in sandboxes (exec, streaming, PTY, file transfer, SSH), use the [SDKs](/sdk/overview) or [CLI](/cli/overview). They speak the sandbox command channel, which is not part of this REST surface.

## Credentials

The sidebar separates endpoints by who the request represents:

| Credential | Format | Represents | Use for |
| --- | --- | --- | --- |
| API key | `msb_...` | An organization | Services, SDKs, CLI automation, and organization resources |
| Personal token | `msb_pat_...` | A user | Account and organization administration |

API keys and personal tokens are both sent as bearer tokens:

<CodeGroup>
```bash Shell
curl https://api.microsandbox.dev/v1/sandboxes \
  -H "Authorization: Bearer $MSB_API_KEY"
```
</CodeGroup>

API keys are created in the [dashboard](https://dashboard.microsandbox.dev/access/api-keys). API-key paths infer the organization from the key. Personal-token paths include the organization slug when the request needs an organization.

## Base URL

```text
https://api.microsandbox.dev
```

## Errors

Errors return an `error` object with a stable machine-readable `code`, a human-readable `message`, and optional structured `details`:

<CodeGroup>
```json JSON
{
  "error": {
    "code": "invalid_api_key",
    "message": "unauthorized",
    "details": null
  }
}
```
</CodeGroup>

## Pagination

List endpoints use cursor pagination. Pass `limit` to size the page and `cursor` to continue from a previous response:

<CodeGroup>
```json JSON
{
  "data": [ ... ],
  "has_more": true,
  "next_cursor": "eyJv..."
}
```
</CodeGroup>

Request the next page with `?cursor=<next_cursor>`; `has_more: false` means you have everything.

## Usage and billing

The usage endpoints report metered consumption for the current period. Reported cost figures are estimates; the [dashboard](https://dashboard.microsandbox.dev/billing) is the source of truth for billing.

---

### Cli Overview (`docs/cli/overview.mdx`)

---
title: CLI overview
"og:title": Microsandbox CLI installation and command reference
sidebarTitle: "Overview"
description: Install the microsandbox CLI and manage local or cloud microVM sandboxes from your terminal.
icon: "book"
---

Interactive sessions are detected automatically; no `-it` flags are needed.

Set an API key to use [microsandbox cloud](/cloud/overview). Each reference page marks local-only commands and cloud limitations.

## Install

<CodeGroup>
```bash CLI (Linux & macOS)
curl -fsSL https://install.microsandbox.dev | sh
```

```bash Homebrew (macOS)
brew install superradcompany/tap/microsandbox
```

```powershell CLI (Windows)
irm https://install.microsandbox.dev/windows | iex
```
</CodeGroup>

On Linux and macOS, the install script writes the runtime to `~/.microsandbox/` and creates command links in `~/.local/bin/`. It does not edit shell startup files. If `~/.local/bin` is not on your `PATH`, the installer prints the command to add it. On macOS, Homebrew provides the same CLI through the official tap. On Windows, use PowerShell. Windows support is currently in preview.

<Note>
  Local sandboxes require Linux with glibc 2.28 or newer and KVM, an Apple Silicon Mac, or an x64 or ARM64 machine running Windows 10 or later with Windows Hypervisor Platform enabled. See [Linux troubleshooting](/troubleshooting/linux), [macOS troubleshooting](/troubleshooting/macos), or [Windows troubleshooting](/troubleshooting/windows) for setup help.
</Note>

Check your local setup with:

<CodeGroup>
```bash Shell
msb doctor
```
</CodeGroup>

## Quick start

<CodeGroup>
```bash Shell
msb create python --name worker
msb exec worker -- python -c "print('hello')"
msb stop worker
msb rm worker
```
</CodeGroup>

`msb run` without `--name` creates an ephemeral sandbox, removed when its command finishes. Named sandboxes persist. Names must contain 1–128 UTF-8 bytes.

## Command forms

Use short commands for everyday work. These forms are equivalent:

<CodeGroup>
```bash Shell
msb run alpine -- echo hello
msb sandbox run alpine -- echo hello
msb sbx run alpine -- echo hello
```
</CodeGroup>

Aliases such as `ls`, `rm`, `cp`, and `mod` also work inside `sandbox` and `sbx`.

Saved-state operations are grouped under `msb snap`. The equivalent long form, `msb snapshot`, is also supported; this documentation uses `msb snap` consistently:

<CodeGroup>
```bash Shell
msb snap create ready --sandbox devbox --full
msb snap restore devbox:ready --name worker
msb snap ls --group devbox
msb snap export devbox:ready --output ready.msb
msb snap import ready.msb --group received
```
</CodeGroup>

<Accordion title="Snapshot behavior, aliases, and backend support">

Omit `--full` to save disk state for a fresh boot. The group defaults to the source sandbox's name, so the first command creates `devbox:ready`. Full captures, live forks, groups, and archive operations are local-only. Use `msb fork devbox --names worker-a worker-b` to create children directly from the same live moment. See [Snapshots](/sandboxes/snapshots) for backend limits and resource bindings. Existing top-level `restore` and snapshot `save`/`load` commands remain supported.

</Accordion>

## Quick reference

| Commands | Purpose |
| --- | --- |
| [`run`, `create`, `start`, `stop`, `restart`](/cli/sandbox-commands) | Manage sandbox lifecycles |
| [`exec`, `copy`, `logs`](/cli/sandbox-commands#msb-exec) | Run commands and access files or output |
| [`ls`, `ps`, `inspect`, `metrics`](/cli/sandbox-commands#msb-ls) | Inspect sandboxes |
| [`modify`, `pause`, `resume`, `fork`, `rm`](/cli/sandbox-commands#msb-modify) | Change, fork, or remove sandboxes |
| [`snap`](/cli/snapshot-commands) | Save and restore state |
| [`ssh`](/cli/ssh-commands) | SSH sessions, keys, and listeners |
| [`volume`, `volumes`](/cli/volume-commands) | Manage named storage |
| [`df`, `prune`](/cli/storage-commands) | Inspect local storage and reclaim unused runtime RAM |
| [`pull`, `load`, `save`, `images`, `rmi`, `image`](/cli/image-commands) | Manage cached images |
| [`registry`, `registries`](/cli/registry-commands) | Manage registry credentials |
| [`context`](/cli/management#msb-context) | Show the active backend |
| [`install`, `uninstall`](/cli/sandbox-commands#msb-install) | Turn sandboxes into shell commands |
| [`doctor`, `update`, `downgrade`, `self`](/cli/management#msb-self) | Manage the CLI installation |
| [`completion`](#shell-completion) | Generate shell completions |

## Global options

| Flag | Description |
|------|-------------|
| `--tree` | Display the complete command tree with descriptions |
| `-L, --levels <N>` | Limit tree depth, counting the selected root as level 0 (requires `--tree`) |
| `-C, --commands` | Hide flags and arguments in the command tree (requires `--tree`) |
| `-b, --brief` | Omit descriptions from the command tree (requires `--tree`) |
| `--error` | Show only errors |
| `--warn` | Show warnings and errors |
| `--info` | Show info, warnings, and errors |
| `--debug` | Show debug output |
| `--trace` | Show all output including trace |

## Output formatting

Command results normally go to stdout; progress and diagnostics go to stderr. Use JSON output when a command supports it for scripting.

<Accordion title="Streams, units, and output exceptions">

Progress, styled action confirmations, warnings, and errors go to stderr. Tables, inspection results, and JSON go to stdout. Empty-list messages retain their command-specific behavior: the registry empty-list message goes to stdout, while listing installed aliases produces no output when none exist. The plain `snapshot reindex` completion message also remains on stdout.

Indented output uses two spaces, with nested content at four spaces. Human-readable image, snapshot, and volume sizes use binary units (KiB, MiB, GiB, TiB); JSON retains its documented numeric fields.

</Accordion>

## Shell completion

`msb completion <shell>` prints a completion script for `bash`, `zsh`, `fish`, `elvish`, or `powershell`. Write it to the location your shell loads completions from:

<CodeGroup>
```bash bash
# Requires the bash-completion package. Without it, add this line to
# ~/.bashrc instead:  source <(msb completion bash)
mkdir -p ~/.local/share/bash-completion/completions
msb completion bash > ~/.local/share/bash-completion/completions/msb
```

```zsh zsh
mkdir -p ~/.zsh/completions
msb completion zsh > ~/.zsh/completions/_msb
# Add this line to ~/.zshrc before compinit runs:
#   fpath=(~/.zsh/completions $fpath)
```

```fish fish
mkdir -p ~/.config/fish/completions
msb completion fish > ~/.config/fish/completions/msb.fish
```

```elvish elvish
mkdir -p ~/.config/elvish/completions
msb completion elvish > ~/.config/elvish/completions/msb.elv
# Add this line to ~/.config/elvish/rc.elv:
#   eval (slurp < ~/.config/elvish/completions/msb.elv)
```

```powershell PowerShell
$completions = Join-Path (Split-Path $PROFILE) "completions"
New-Item -Path $completions -ItemType Directory -Force | Out-Null
msb completion powershell > (Join-Path $completions "msb.ps1")
# Add this line to your profile ($PROFILE):
#   . "$PSScriptRoot/completions/msb.ps1"
```
</CodeGroup>

Restart your shell. Regenerate saved completions after upgrading.

## Command tree

Use `--tree` as an alternative to `--help` to see every command, subcommand, and flag at once:

<CodeGroup>
```bash Shell
msb --tree
```
</CodeGroup>

You can scope it to a specific subcommand:

<CodeGroup>
```bash Shell
msb image --tree    # Show only image commands
msb volume --tree   # Show only volume commands
msb run --tree      # Show all flags for run
msb sandbox --tree  # Show the canonical sandbox command group
msb sbx run --tree  # Show run flags through the group alias
```
</CodeGroup>

Limit the depth or hide flags and arguments for a compact command overview:

<CodeGroup>
```bash Shell
msb --tree -L 2
msb --tree --commands
msb image --tree --commands -L 1
msb --tree --brief --commands -L 2
msb --tree -Cb -L 2
```
</CodeGroup>

<Accordion title="Tree depth and display rules">

`-L N` (or `--levels N`) counts the selected root as level 0, so `-L 1` shows its immediate children and `-L 0` shows only the root. Omitting the limit shows all levels. At the depth limit, an inline marker such as `… 4 subcommands, 2 arguments` counts the immediate children that were collapsed; arguments include both flags and positional arguments. Hidden commands and options are excluded, and `--commands` excludes arguments from both the tree and these counts.

`--brief` omits command and argument descriptions and their alignment padding, while retaining names, aliases, value placeholders, and collapse counts. It works with both `-L` and `--commands`.

</Accordion>

---

### Cloud Overview (`docs/cloud/overview.mdx`)

---
title: Cloud
description: Run the same sandboxes on hosted infrastructure with an API key
keywords: ["microsandbox cloud", "hosted sandboxes", "cloud API keys"]
icon: "cloud"
---

<Note>
  microsandbox cloud is in **private beta**, and currently requires you to explicitly [request access](https://dashboard.microsandbox.dev/signup). If you already have access, you can get your API key from the [dashboard](https://dashboard.microsandbox.dev/access/api-keys).
</Note>

microsandbox cloud runs your sandboxes on hosted infrastructure. The SDKs and the `msb` CLI are the same ones you use locally: the same builders, the same commands, the same code. Select the cloud backend and provide an API key; there is no daemon or separate client.

## Connect

<CodeGroup>
```bash Shell
export MSB_BACKEND=cloud
export MSB_API_KEY="msb_..."
msb context
```
</CodeGroup>

`msb context` confirms that subsequent commands will run on cloud and shows what selected it without exposing the API key. For profiles, SDK selection, and precedence, see [Local or cloud](/operations/backends).

## Sandbox identity

Looking up a sandbox by name selects the sandbox currently using that name.
The returned object retains that sandbox's ID for lifecycle operations, commands,
and file access. If the sandbox is deleted and its name reused, operations on
an old object fail instead of reaching the replacement. Fetch the sandbox by
name again to select the replacement; you do not need to manage IDs yourself.

## Same code, different backend

Everything from the [quickstart](/getting-started/quickstart) works unchanged. With cloud selected and the API key exported, this creates a sandbox on cloud instead of your machine:

<CodeGroup>
```typescript TypeScript
import { Sandbox } from "microsandbox";

await using sb = await Sandbox.builder("hello")
    .image("python")
    .memory(512)
    .create();

const output = await sb.exec("python", ["-c", "print('Hello from the cloud!')"]);
console.log(output.stdout());
```

```rust Rust
use microsandbox::Sandbox;

let sb = Sandbox::builder("hello")
    .image("python")
    .memory(512)
    .create()
    .await?;

let output = sb.exec("python", ["-c", "print('Hello from the cloud!')"]).await?;
println!("{}", output.stdout()?);

sb.stop().await?;
```

```python Python
from microsandbox import Sandbox

sb = await Sandbox.create("hello", image="python", memory=512)

output = await sb.exec("python", ["-c", "print('Hello from the cloud!')"])
print(output.stdout_text)

await sb.stop()
```

```go Go
sb, err := m.CreateSandbox(ctx, "hello",
    m.WithImage("python"),
    m.WithMemory(512),
)
if err != nil {
    return err
}
defer sb.Stop(ctx)

output, err := sb.Exec(ctx, "python", []string{"-c", "print('Hello from the cloud!')"})
fmt.Println(output.Stdout())
```

```ruby Ruby
require "microsandbox"

Microsandbox::Sandbox.with("hello", image: "python", memory: 512) do |sb|
  output = sb.exec("python", ["-c", "print('Hello from the cloud!')"])
  puts output.stdout
end
```

```bash CLI
msb run python -- python3 -c "print('Hello from the cloud!')"
```
</CodeGroup>

Hardware virtualization support on your machine is not needed for cloud sandboxes; `msb doctor` checks apply to the local runtime only.

## What works on cloud

<CardGroup cols={2}>
  <Card title="Sandbox lifecycle" icon="box">
    Create, inspect, list, reconnect, start, stop, and remove sandboxes.
  </Card>
  <Card title="Commands and files" icon="terminal">
    Run buffered, streaming, and interactive commands, then move files in or out.
  </Card>
  <Card title="Persistent storage" icon="hard-drive">
    Create managed volumes and access their files without starting a sandbox.
  </Card>
  <Card title="Network controls" icon="shield-halved">
    Apply egress policy and hostname-scoped secrets from the same SDK surface.
  </Card>
</CardGroup>

## Compatibility

The everyday workflow is shared across local and cloud: sandbox lifecycle, command execution, common guest-filesystem operations, network policy, secrets, SSH sessions, and managed storage. The differences below are the ones to plan for.

Across the docs, no badge means a feature works everywhere. Exceptions use only three badges: **Local-only**, **Limited on cloud**, and **Cloud-only**.

| Area | Cloud status | Difference or alternative |
|---|---|---|
| Sandbox lifecycle and execution | **Works everywhere** | Create, start, inspect, list, stop, remove, exec, shell, streaming, and interactive PTY workflows use the same SDK and CLI surface. |
| Guest filesystem | **Limited on cloud** | Common path operations and host-to-guest copies work. Low-level open-handle operations are local-only. |
| Managed volumes | **Limited on cloud** | Default and named directory volumes support lifecycle and filesystem access. Create a named volume before mounting it; disk-kind volumes and create-on-mount are local-only. |
| Volume and host paths | **Limited on cloud** | Bind and disk-image source paths resolve against the organization's host volume, not the machine running the client. |
| Network policy and secrets | **Works everywhere** | Egress policies and hostname-scoped secret substitution carry across backends. |
| Custom networking | **Local-only** | Published ports, custom nameservers, interface overrides, rate limiters, TLS interception, and host-CA trust are not accepted by cloud create. |
| Logs | **Limited on cloud** | SDK live-follow and `msb logs -f` work. Bounded or historical reads are local-only; persist followed output externally when retention matters. |
| SSH | **Limited on cloud** | Native SDK sessions and `msb ssh` work. `msb ssh serve` is local-only; reusable SDK servers require explicit key material. |
| Images | **Limited on cloud** | OCI image references and registry credentials work. Local cache commands, host-directory or disk-image roots, plain-HTTP registries, and custom registry CAs are local-only. |
| Reconfiguration | **Local-only** | Recreate the sandbox with the new CPU, memory, mounts, or other settings instead of using `modify`. |
| Snapshots | **Limited on cloud** | Capture disk snapshots from stopped persistent sandboxes and restore them into new sandboxes. Live capture, full checkpoints, forking, groups, and local artifact archives remain local-only. |
| Resource metrics | **Local-only** | Instrument the workload with an external monitoring system. |
| Process controls | **Limited on cloud** | Graceful stop plus maximum-duration and idle-timeout policies work. Force kill, drain, ping, and touch are local-only. |

## REST API

The same key also authenticates the [REST API](/api-reference/overview) directly, so you can manage sandboxes, volumes, and usage over plain HTTPS without an SDK. Command execution, file transfer, and SSH ride the sandbox command channel, which the SDKs and CLI handle for you.

## What to know

- **Local or cloud.** Cloud is explicit: set `MSB_BACKEND=cloud`, select a cloud profile, or choose it in code. See [Local or cloud](/operations/backends).
- **Networking.** Cloud sandboxes get a platform-assigned hostname. SSH works through the same `msb ssh` and SDK SSH clients you use locally.
- **Images.** Specify an OCI image when you create the sandbox; the cloud pulls it for you.
- **Managed settings.** [Device policy](/enterprise/managed-configuration) applies to create requests and CLI/SDK SSH sessions. Unsupported create settings fail before the request is sent. Existing cloud sandboxes and the hosted SSH gateway keep their settings.
- **Keys and billing.** API keys, usage, and invoices live in the [dashboard](https://dashboard.microsandbox.dev).

## Next steps

<CardGroup cols={2}>
  <Card title="API reference" icon="brackets-curly" href="/api-reference/overview">
    The REST API behind the SDKs, authenticated with your API key.
  </Card>

  <Card title="Local or cloud" icon="cloud-arrow-up" href="/operations/backends">
    Force local, select in code, or switch with profiles.
  </Card>

  <Card title="Quickstart" icon="bolt" href="/getting-started/quickstart">
    New to microsandbox? Start here; everything carries over.
  </Card>
</CardGroup>

---

### Getting Started Quickstart (`docs/getting-started/quickstart.mdx`)

---
title: Quickstart
description: Get a sandbox running in under 5 minutes.
icon: "bolt"
---

## Run your first sandbox

<CodeGroup>
```bash npx
npx microsandbox run debian
```

```bash macOS & Linux
curl -fsSL https://install.microsandbox.dev | sh
msb run debian
```

```powershell Windows
irm https://install.microsandbox.dev/windows | iex
msb run debian
```
</CodeGroup>

<Tip>
  Prefer short commands such as `msb run debian` for everyday use. `msb sandbox run debian` and `msb sbx run debian` do the same thing, with the same flags. Explore the full group with `msb sandbox --help`.
</Tip>

Having trouble with a local installation? Choose your platform for setup checks and fixes.

<div className="msb-platform-strip msb-platform-strip-three" role="list" aria-label="Installation troubleshooting">
  <a className="msb-platform-item msb-platform-link" role="listitem" href="/troubleshooting/linux">
    <span className="msb-platform-icon"><Icon icon="linux" size={18} /></span>
    <span className="msb-platform-copy"><strong>Linux</strong><span>glibc 2.28+ and KVM</span></span>
  </a>
  <a className="msb-platform-item msb-platform-link" role="listitem" href="/troubleshooting/macos">
    <span className="msb-platform-icon"><Icon icon="apple" size={18} /></span>
    <span className="msb-platform-copy"><strong>macOS</strong><span>Apple Silicon</span></span>
  </a>
  <a className="msb-platform-item msb-platform-link" role="listitem" href="/troubleshooting/windows">
    <span className="msb-platform-icon"><Icon icon="windows" size={18} /></span>
    <span className="msb-platform-copy"><strong>Windows</strong><span>10+ · WHP</span></span>
  </a>
</div>

<Steps>
  <Step title="Install microsandbox">
    For application code, install the SDK for your language. For terminal workflows, use one of the CLI options above. Both run microsandbox locally by default; there is no separate server or daemon to set up. The same installation also drives [microsandbox cloud](/cloud/overview) when an API key is set.

    <CodeGroup>
    ```bash TypeScript
    npm install microsandbox
    ```

    ```bash Rust
    cargo add microsandbox
    ```

    ```bash Python
    pip install microsandbox
    ```

    ```bash Go
    go get github.com/superradcompany/microsandbox/sdk/go
    ```

    ```bash Ruby
    gem install microsandbox
    ```

    </CodeGroup>

    You can also provision the runtime with the CLI installer above or explicit SDK setup helpers. See [Runtime setup](/sdk/setup#bring-your-own-runtime) for Node's bundled binaries and Rust's opt-out options.

    Check local virtualization support with:

    <CodeGroup>
    ```bash Shell
    msb doctor
    ```
    </CodeGroup>

    For platform-specific setup notes, see [Linux troubleshooting](/troubleshooting/linux), [macOS troubleshooting](/troubleshooting/macos), or [Windows troubleshooting](/troubleshooting/windows).
  </Step>

  <Step title="Run code in a sandbox">
    Create a sandbox, execute code inside it, and get the result back.

    <CodeGroup>
    ```typescript TypeScript
    import { Sandbox } from "microsandbox";

    await using sb = await Sandbox.builder("hello")
        .image("python")
        .memory(512)
        .create();

    const output = await sb.exec("python", ["-c", "print('Hello from a microVM!')"]);
    console.log(output.stdout()); // Hello from a microVM!
    ```

    ```rust Rust
    use std::error::Error;

    use microsandbox::Sandbox;

    #[tokio::main]
    async fn main() -> Result<(), Box<dyn Error>> {
        let sb = Sandbox::builder("hello")
            .image("python")
            .memory(512)
            .create()
            .await?;

        let output = sb.exec("python", ["-c", "print('Hello from a microVM!')"]).await?;
        println!("{}", output.stdout()?); // Hello from a microVM!

        sb.stop().await?;
        Ok(())
    }
    ```

    ```python Python
    import asyncio
    from microsandbox import Sandbox

    async def main():
        sb = await Sandbox.create(
            "hello",
            image="python",
            memory=512,
        )

        output = await sb.exec("python", ["-c", "print('Hello from a microVM!')"])
        print(output.stdout_text)  # Hello from a microVM!

        await sb.stop()

    asyncio.run(main())
    ```

    ```go Go
    sb, err := m.CreateSandbox(ctx, "hello",
        m.WithImage("python"),
        m.WithMemory(512),
    )
    if err != nil {
        return err
    }
    defer sb.Stop(ctx)

    output, err := sb.Exec(ctx, "python", []string{"-c", "print('Hello from a microVM!')"})
    if err != nil {
        return err
    }
    fmt.Println(output.Stdout()) // Hello from a microVM!
    ```

    ```ruby Ruby
    require "microsandbox"

    Microsandbox.install unless Microsandbox.installed?

    Microsandbox::Sandbox.with("hello", image: "python", memory: 512) do |sandbox|
      output = sandbox.exec("python", ["-c", "print('Hello from a microVM!')"])
      puts output.stdout
    end
    ```

    ```bash CLI
    msb run python -- python3 -c "print('Hello from a microVM!')"
    ```

    </CodeGroup>
  </Step>
</Steps>

## What just happened?

Here's what happened behind that `Sandbox.builder(...).create()` call:

1. **Pulled the image** from Docker Hub, unless it was already cached.
2. **Assembled a copy-on-write filesystem** so changes inside the sandbox do not modify the base image.
3. **Booted a microVM** as a child process with the resource limits you configured.
4. **Started the guest agent** so the SDK can run commands and move data in and out.

The `exec` call uses the host-guest command channel, not SSH and not the sandbox network.

<Tip>
  Want the same sandbox on hosted infrastructure? Set `MSB_BACKEND=cloud` and export `MSB_API_KEY`; the code above then runs on [microsandbox cloud](/cloud/overview) unchanged.
</Tip>

## Next steps

- [SDK references](/sdk/overview#reference): choose TypeScript, Rust, Python, Go, or Ruby for sandbox lifecycle and API details
- [Protect API credentials](/sandboxes/secrets): let sandboxed code use an API without receiving the credential value
- [Browse guides](/how-to/overview): set up agents, CI/CD, Docker, and more
- [Run on microsandbox cloud](/cloud/overview): the same code on hosted infrastructure
- [Modify live sandbox settings](/sandboxes/tuning): resize headroom, labels, secrets, and storage
- [Troubleshoot Linux setup](/troubleshooting/linux): KVM, `/dev/kvm`, and permissions
- [Troubleshoot macOS setup](/troubleshooting/macos): Apple Silicon and local runtime checks
- [Troubleshoot Windows setup](/troubleshooting/windows): WHP, `msb doctor`, and runtime setup
- [Run commands and stream output](/sandboxes/commands): `exec`, `shell`, `attach`, and streaming
- [Control network access](/networking/overview): policies, DNS interception, and secret protection
- [Manage sandboxes with the CLI](/cli/overview): create, inspect, and manage sandboxes from the terminal

---

### How To Overview (`docs/how-to/overview.mdx`)

---
title: How-to guides
sidebarTitle: Overview
description: Practical steps for running your tools and workloads in microsandbox
icon: "book-open"
---

<div className="msb-guides-overview">
<div className="msb-guides-grid">

<section className="msb-guide-group">
  <h2 id="running-agents"><span className="msb-guide-heading"><Icon icon="robot" size={16} /><span>Agents</span></span></h2>
  <ul className="not-prose">
    <li><a href="/how-to/agents/claude-code"><span>Run Claude Code</span><Icon icon="arrow-right" size={12} /></a></li>
    <li><a href="/how-to/agents/codex"><span>Run Codex CLI</span><Icon icon="arrow-right" size={12} /></a></li>
    <li><a href="/how-to/agents/opencode"><span>Run OpenCode</span><Icon icon="arrow-right" size={12} /></a></li>
    <li><a href="/how-to/agents/openclaw"><span>Set up OpenClaw</span><Icon icon="arrow-right" size={12} /></a></li>
    <li><a href="/how-to/agents/goose"><span>Run Goose</span><Icon icon="arrow-right" size={12} /></a></li>
    <li><a href="/how-to/agents/gemini-cli"><span>Run Gemini CLI</span><Icon icon="arrow-right" size={12} /></a></li>
    <li><a href="/how-to/agents/hermes-agent"><span>Set up Hermes Agent</span><Icon icon="arrow-right" size={12} /></a></li>
    <li><a href="/how-to/agents/pi"><span>Run Pi</span><Icon icon="arrow-right" size={12} /></a></li>
  </ul>
</section>

<section className="msb-guide-group">
  <h2 id="development-environments"><span className="msb-guide-heading"><Icon icon="code" size={16} /><span>Development</span></span></h2>
  <ul className="not-prose">
    <li><a href="/how-to/development/vscode"><span>Connect VS Code</span><Icon icon="arrow-right" size={12} /></a></li>
    <li><a href="/how-to/development/jetbrains-acp"><span>Connect JetBrains via ACP</span><Icon icon="arrow-right" size={12} /></a></li>
    <li><a href="/how-to/development/code-server"><span>Run code-server</span><Icon icon="arrow-right" size={12} /></a></li>
    <li><a href="/how-to/development/jupyterlab"><span>Run JupyterLab</span><Icon icon="arrow-right" size={12} /></a></li>
    <li><a href="/how-to/development/vnc-desktop"><span>Open a VNC desktop</span><Icon icon="arrow-right" size={12} /></a></li>
  </ul>
</section>

<section className="msb-guide-group">
  <h2 id="browser-automation"><span className="msb-guide-heading"><Icon icon="globe" size={16} /><span>Browser automation</span></span></h2>
  <ul className="not-prose">
    <li><a href="/how-to/browser-automation/browser-use"><span>Run a browser agent</span><Icon icon="arrow-right" size={12} /></a></li>
    <li><a href="/how-to/browser-automation/playwright"><span>Automate a browser</span><Icon icon="arrow-right" size={12} /></a></li>
  </ul>
</section>

<section className="msb-guide-group">
  <h2 id="working-with-workloads"><span className="msb-guide-heading"><Icon icon="gears" size={16} /><span>Workloads</span></span></h2>
  <ul className="not-prose">
    <li><a href="/how-to/file-processing/ffmpeg"><span>Transcode media with FFmpeg</span><Icon icon="arrow-right" size={12} /></a></li>
    <li><a href="/how-to/file-processing/libreoffice-pdf"><span>Convert documents to PDF</span><Icon icon="arrow-right" size={12} /></a></li>
    <li><a href="/how-to/plugins/terraform"><span>Vet a Terraform provider offline</span><Icon icon="arrow-right" size={12} /></a></li>
    <li><a href="/how-to/data/migration-rehearsal"><span>Rehearse a database migration</span><Icon icon="arrow-right" size={12} /></a></li>
    <li><a href="/how-to/web-automation/scrapy"><span>Crawl a site with Scrapy</span><Icon icon="arrow-right" size={12} /></a></li>
    <li><a href="/how-to/automation/parallel-batch-jobs"><span>Process a batch in parallel</span><Icon icon="arrow-right" size={12} /></a></li>
  </ul>
</section>

<section className="msb-guide-group">
  <h2 id="sandbox-setup"><span className="msb-guide-heading"><Icon icon="box" size={16} /><span>Sandbox setup</span></span></h2>
  <ul className="not-prose">
    <li><a href="/how-to/docker/docker-in-sandbox"><span>Run Docker in a sandbox</span><Icon icon="arrow-right" size={12} /></a></li>
    <li><a href="/how-to/docker/local-images"><span>Use local images</span><Icon icon="arrow-right" size={12} /></a></li>
    <li><a href="/how-to/guest/systemd-services"><span>Boot with systemd</span><Icon icon="arrow-right" size={12} /></a></li>
    <li><a href="/how-to/sandboxing/warm-workers"><span>Start warm workers from snapshots</span><Icon icon="arrow-right" size={12} /></a></li>
  </ul>
</section>

<section className="msb-guide-group">
  <h2 id="hosting"><span className="msb-guide-heading"><Icon icon="server" size={16} /><span>Deployment</span></span></h2>
  <ul className="not-prose">
    <li><a href="/how-to/docker/docker"><span>Run microsandbox in Docker</span><Icon icon="arrow-right" size={12} /></a></li>
    <li><a href="/how-to/sandboxing/kubernetes"><span>Run microsandbox in Kubernetes</span><Icon icon="arrow-right" size={12} /></a></li>
  </ul>
</section>

<section className="msb-guide-group">
  <h2 id="storage"><span className="msb-guide-heading"><Icon icon="database" size={16} /><span>Storage</span></span></h2>
  <ul className="not-prose">
    <li><a href="/how-to/data/postgresql"><span>Run PostgreSQL</span><Icon icon="arrow-right" size={12} /></a></li>
    <li><a href="/how-to/data/redis"><span>Run Redis</span><Icon icon="arrow-right" size={12} /></a></li>
    <li><a href="/how-to/data/juicefs-s3"><span>Mount S3 with JuiceFS</span><Icon icon="arrow-right" size={12} /></a></li>
  </ul>
</section>

<section className="msb-guide-group">
  <h2 id="ci-automation"><span className="msb-guide-heading"><Icon icon="code-pull-request" size={16} /><span>CI/CD</span></span></h2>
  <ul className="not-prose">
    <li><a href="/how-to/ci-cd/github-actions-runner"><span>Run a GitHub Actions job</span><Icon icon="arrow-right" size={12} /></a></li>
    <li><a href="/how-to/ci-cd/pr-checks"><span>Run isolated PR checks</span><Icon icon="arrow-right" size={12} /></a></li>
    <li><a href="/how-to/ci-cd/compose-tests"><span>Run Docker Compose tests</span><Icon icon="arrow-right" size={12} /></a></li>
    <li><a href="/how-to/ci-cd/preview-deploys"><span>Serve a preview deployment</span><Icon icon="arrow-right" size={12} /></a></li>
    <li><a href="/how-to/automation/dependency-audits"><span>Schedule dependency audits</span><Icon icon="arrow-right" size={12} /></a></li>
  </ul>
</section>

<section className="msb-guide-group">
  <h2 id="observability"><span className="msb-guide-heading"><Icon icon="chart-line" size={16} /><span>Observability</span></span></h2>
  <ul className="not-prose">
    <li><a href="/how-to/metrics-backends/grafana-cloud"><span>Send metrics to Grafana Cloud</span><Icon icon="arrow-right" size={12} /></a></li>
    <li><a href="/how-to/metrics-backends/grafana-alloy"><span>Forward metrics with Grafana Alloy</span><Icon icon="arrow-right" size={12} /></a></li>
    <li><a href="/how-to/metrics-backends/otel-collector"><span>Inspect metrics with OpenTelemetry</span><Icon icon="arrow-right" size={12} /></a></li>
    <li><a href="/how-to/metrics-backends/prometheus"><span>Send metrics to Prometheus</span><Icon icon="arrow-right" size={12} /></a></li>
    <li><a href="/how-to/metrics-backends/datadog"><span>Send metrics to Datadog</span><Icon icon="arrow-right" size={12} /></a></li>
  </ul>
</section>

</div>

</div>

---

### Images Overview (`docs/images/overview.mdx`)

---
title: Images
description: Use OCI container images or boot from disk image files
icon: "layer-group"
---

## OCI images

microsandbox uses standard OCI container images as root filesystems. Docker Hub, GHCR, ECR, GCR, any OCI-compatible registry works. Existing images run as-is.

When you specify an image like `python`, microsandbox pulls the manifest, downloads the layers in parallel, and stacks them as a copy-on-write filesystem. Changes inside the sandbox don't modify the base image. Two sandboxes using the same image share the same cached layers on disk.

### Pull policies

By default, microsandbox pulls an image only if it isn't already cached. You can change this behavior.

| Policy | Behavior |
|--------|----------|
| `"if-missing"` | Pull only if not cached (default) |
| `"always"` | Always check the registry for updates |
| `"never"` | Use local cache only, fail if missing |

<CodeGroup>
```typescript TypeScript
import { Sandbox } from "microsandbox";

await using sb = await Sandbox.builder("worker")
    .image("python")
    .pullPolicy("always")
    .create();
```

```rust Rust
let sb = Sandbox::builder("worker")
    .image("python")
    .pull_policy(PullPolicy::Always)
    .create()
    .await?;
```

```python Python
from microsandbox import PullPolicy, Sandbox

sb = await Sandbox.create("worker", image="python", pull_policy=PullPolicy.ALWAYS)
```

```go Go
sb, err := m.CreateSandbox(ctx, "worker",
    m.WithImage("python"),
    m.WithPullPolicy(m.PullPolicyAlways),
)
```

```ruby Ruby
sb = Microsandbox::Sandbox.create("worker", image: "python", pull_policy: "always")
```
</CodeGroup>

<Tip>
  Once microsandbox resolves an image reference, it pins the exact layers. At first pull, `python` resolves to a specific set of immutable layers. Subsequent `start()` calls use the pinned layers without re-resolving the mutable tag, so your sandbox is reproducible even if the upstream tag moves.
</Tip>

### Private registries

Authenticate to private registries by passing credentials.

<CodeGroup>
```typescript TypeScript
await using sb = await Sandbox.builder("worker")
    .image("registry.corp.io/team/app:latest")
    .registry((r) => r.auth({
        kind: "basic",
        username: "deploy",
        password: process.env.REGISTRY_TOKEN!,
    }))
    .pullPolicy("always")
    .create();
```

```rust Rust
let sb = Sandbox::builder("worker")
    .image("registry.corp.io/team/app:latest")
    .registry(|r| r.auth(RegistryAuth::Basic {
        username: "deploy".into(),
        password: std::env::var("REGISTRY_PASSWORD")?,
    }))
    .pull_policy(PullPolicy::Always)
    .create()
    .await?;
```

```python Python
import os
from microsandbox import PullPolicy, RegistryAuth, Sandbox

sb = await Sandbox.create(
    "worker",
    image="registry.corp.io/team/app:latest",
    registry_auth=RegistryAuth.basic("deploy", os.environ["REGISTRY_PASSWORD"]),
    pull_policy=PullPolicy.ALWAYS,
)
```

```go Go
sb, err := m.CreateSandbox(ctx, "worker",
    m.WithImage("registry.corp.io/team/app:latest"),
    m.WithRegistryAuth(m.RegistryAuth{
        Username: "deploy",
        Password: os.Getenv("REGISTRY_PASSWORD"),
    }),
    m.WithPullPolicy(m.PullPolicyAlways),
)
```
</CodeGroup>

### Registry TLS

<Tooltip tip="Plain-HTTP registries and custom CA certificates are not supported on microsandbox cloud; images are pulled from HTTPS registries only."><span className="msb-badge-local">Local-only <Icon icon="circle-info" size={11} /></span></Tooltip>

By default, microsandbox connects to registries over HTTPS using system CA roots. You can customize this per-registry in `~/.microsandbox/config.json`.

#### Plain HTTP registries

Local registries often run without TLS. Mark them as insecure to connect over plain HTTP:

<CodeGroup>
```json JSON
{
  "registries": {
    "hosts": {
      "localhost:5050": {
        "insecure": true
      }
    }
  }
}
```
</CodeGroup>

#### Custom CA certificates

For registries using self-signed or internal CA certificates, point `ca_certs` to a PEM file. This applies globally to all registry connections:

<CodeGroup>
```json JSON
{
  "registries": {
    "ca_certs": "/path/to/ca-bundle.pem"
  }
}
```
</CodeGroup>

microsandbox adds these certificates to the default system roots, so public registries continue to work normally. You can also supply the same roots per-sandbox through the SDK: `caCerts` in TypeScript, `ca_certs` in Rust, `registry_ca_certs` in Python, and `WithRegistryCACerts` / `WithRegistryCACertsPath` in Go.

#### Combined configuration

<CodeGroup>
```json JSON
{
  "registries": {
    "ca_certs": "/path/to/corporate-ca.pem",
    "hosts": {
      "localhost:5050": {
        "insecure": true,
        "auth": { "username": "deploy", "password_env": "REGISTRY_TOKEN" }
      },
      "ghcr.io": {
        "auth": { "username": "user", "store": "keyring" }
      }
    }
  }
}
```
</CodeGroup>

Per-sandbox SDK options override user registry settings. [Managed registry settings](/configuration#auth-resolution-order) take precedence over both. Managed `insecure: false` requires TLS without changing credentials; managed `auth: null` forces anonymous access, and managed CA settings replace per-call custom certificates.

<CodeGroup>
```typescript TypeScript
await using sb = await Sandbox.builder("worker")
    .image("localhost:5050/my-app:latest")
    .registry((r) => r.insecure())
    .create();
```

```rust Rust
let sb = Sandbox::builder("worker")
    .image("localhost:5050/my-app:latest")
    .registry(|r| r.insecure())
    .create()
    .await?;
```

```python Python
from microsandbox import Sandbox

sb = await Sandbox.create(
    "worker",
    image="localhost:5050/my-app:latest",
    registry_insecure=True,
)
```

```go Go
sb, err := m.CreateSandbox(ctx, "worker",
    m.WithImage("localhost:5050/my-app:latest"),
    m.WithRegistryInsecure(),
)
```
</CodeGroup>

### Image storage

<Tooltip tip="Image-cache commands are local-only. On microsandbox cloud, specify an OCI image on create and it is pulled for you."><span className="msb-badge-local">Local-only <Icon icon="circle-info" size={11} /></span></Tooltip>

Images are cached in the global microsandbox home directory:

| Path | Description |
|------|-------------|
| `~/.microsandbox/cache/layers/` | Materialized EROFS image layers |
| `~/.microsandbox/cache/fsmeta/` | Merged metadata images for cached manifests |
| `~/.microsandbox/cache/vmdk/` | VMDK descriptors used to boot cached images |
| `~/.microsandbox/db/` | Database tracking image metadata and digests |

Layers are content-addressable and deduplicated. If `python:3.12` and `python:3.11` share a base layer, it's stored once.

<Tip>
  Use `msb pull` from the CLI to pre-pull images before creating sandboxes. This avoids blocking on a download during `Sandbox.create`.
</Tip>

Use `msb image prune` to remove cached images that are not used by any sandbox or indexed snapshot and reclaim dangling image artifacts. Prune keeps images referenced by existing sandboxes or snapshots. It cleans up image metadata, unreachable manifests, orphaned layers, layer EROFS artifacts, fsmeta EROFS artifacts, and VMDK descriptor artifacts.

`msb load` accepts Docker image archives and OCI Image Layout archives. By default, `msb save` exports from the materialized EROFS cache as a Docker archive. Use `--format oci` to export as an OCI Image Layout archive instead. The expanded forms are `msb image load` and `msb image save`. The exported image should run the same way after `msb load`, but it is a regenerated archive: manifest digest and layer digests can differ from the original registry image.

## Disk images

<Tooltip tip="Booting from a disk image references a file on the caller host and is local-only. On microsandbox cloud, publish the filesystem as an OCI image instead."><span className="msb-badge-local">Local-only <Icon icon="circle-info" size={11} /></span></Tooltip>

In addition to OCI container images, microsandbox can boot a sandbox directly from a disk image file. The guest gets raw block device access, and its kernel mounts the filesystem from the device directly.

This is a fundamentally different path from OCI images. With OCI, microsandbox stacks image layers as a copy-on-write filesystem. With disk images, the guest owns the block device. No overlay, no copy-on-write between sandboxes.

Use disk images when you need a pre-built VM template, a custom kernel configuration, or an OS that isn't available as a container image.

### Supported formats

| Format | Description |
|--------|-------------|
| **QCOW2** | QEMU Copy-On-Write v2. Supports thin provisioning and backing files. The most common format. |
| **Raw** | Uncompressed raw disk image. No overhead, but no thin provisioning. |
| **VMDK** | VMware virtual disk format. |

### Usage

When you pass a file path ending in `.qcow2`, `.raw`, or `.vmdk`, microsandbox auto-detects the format. For disk-image roots, rename ambiguous files with one of those extensions and set the filesystem type when auto-detection needs a hint.

<CodeGroup>
```typescript TypeScript
import { Sandbox } from "microsandbox";

// Auto-detect format from the file extension
await using sb = await Sandbox.builder("custom-vm")
    .image("./ubuntu-22.04.qcow2")
    .cpus(2)
    .memory(2048)
    .create();

// Explicit filesystem type via the ImageBuilder
await using sb2 = await Sandbox.builder("custom-vm")
    .imageWith((i) => i.disk("./alpine.raw").fstype("ext4"))
    .cpus(1)
    .memory(512)
    .create();
```

```rust Rust
use microsandbox::size::SizeExt;

// Auto-detect
let sb = Sandbox::builder("custom-vm")
    .image("./ubuntu-22.04.qcow2")
    .cpus(2)
    .memory(2.gib())
    .create()
    .await?;

// Explicit filesystem type
let sb2 = Sandbox::builder("custom-vm")
    .image_with(|i| i.disk("./alpine.raw").fstype("ext4"))
    .cpus(1)
    .memory(512)
    .create()
    .await?;
```

```python Python
from microsandbox import Image, Sandbox

# Auto-detect format from file extension
sb = await Sandbox.create("custom-vm", image="./ubuntu-22.04.qcow2", cpus=2, memory=2048)

# Explicit filesystem type
sb2 = await Sandbox.create("custom-vm", image=Image.disk("./alpine.raw", fstype="ext4"), cpus=1, memory=512)
```

```go Go
// Auto-detect format from the file extension
sb, err := m.CreateSandbox(ctx, "custom-vm",
    m.WithImage("./ubuntu-22.04.qcow2"),
    m.WithCPUs(2),
    m.WithMemory(2048),
)

// Explicit filesystem type
sb2, err := m.CreateSandbox(ctx, "custom-vm-2",
    m.WithImageDisk("./alpine.raw", "ext4"),
    m.WithCPUs(1),
    m.WithMemory(512),
)
```
```ruby Ruby
# Auto-detect the format from the file extension.
sb = Microsandbox::Sandbox.create(
  "custom-vm", image: "./ubuntu-22.04.qcow2", cpus: 2, memory: 2048
)
# Ruby accepts image paths but does not expose a filesystem-type override.
```

</CodeGroup>

<Note>
  The filesystem type (`ext4`, `xfs`, etc.) must match what's actually on the disk image. The guest kernel mounts it using the specified filesystem driver.
</Note>

## Reference

For exact image APIs, see [TypeScript](/sdk/typescript/images), [Rust](/sdk/rust/images), [Python](/sdk/python/images), [Go](/sdk/go/images), or [Ruby](/sdk/ruby/images). For local image management, see [Image commands](/cli/image-commands).

---

### Networking Overview (`docs/networking/overview.mdx`)

---
title: Overview
description: Control network access and isolation
keywords: ["sandbox networking", "network policy", "port forwarding", "egress"]
icon: "network-wired"
---

Microsandbox checks sandbox traffic against a host-controlled network policy. Start with the defaults, then allow only the destinations and ports your workload needs.

## Defaults

Sandboxes can reach the public internet by default. Private networks, the host, loopback, link-local addresses, and cloud metadata endpoints are blocked. Inbound traffic needs a published port.

To isolate a sandbox from the network:

<CodeGroup>
```typescript TypeScript
import { Sandbox } from "microsandbox";

await using sb = await Sandbox.builder("isolated")
    .image("python")
    .disableNetwork()
    .create();
```

```rust Rust
use microsandbox::Sandbox;

let sb = Sandbox::builder("isolated")
    .image("python")
    .network(|n| n.enabled(false))
    .create()
    .await?;
```

```python Python
from microsandbox import Network, Sandbox

sb = await Sandbox.create("isolated", image="python", network=Network.none())
```

```go Go
sb, err := m.CreateSandbox(ctx, "isolated",
    m.WithImage("python"),
    m.WithNetwork(m.NetworkPolicy.None()),
)
```

```ruby Ruby
sb = Microsandbox::Sandbox.create("isolated", image: "python", network: :none)
```

```bash CLI
msb create python --name isolated --no-net
```
</CodeGroup>

The TypeScript and Rust examples disable the network device. Python `Network.none()`, Go `NetworkPolicy.None()`, and CLI `--no-net` retain the device but deny traffic in both directions through policy.

Ruby exposes network disablement, host/port allowlists, proxies, and basic TLS interception. Deployment profiles, policy profiles, NAT64, port publishing, and rate limits require TypeScript, Rust, Python, Go, or the CLI; see the [Ruby networking reference](/sdk/ruby/networking) for its available options.

## Deployment profiles

Local sandboxes use `single-tenant` by default. Use `multi-tenant` for shared hosts: it enforces public-network access, DNS rebinding protection, and stricter limits. Sandbox policies can restrict access further. Cloud chooses its own profile.

<CodeGroup>
```typescript TypeScript
import { Sandbox } from "microsandbox";

await using sb = await Sandbox.builder("shared-worker")
    .image("python")
    .deploymentProfile("multi-tenant")
    .create();
```

```rust Rust
use microsandbox::sandbox::DeploymentProfile;
use microsandbox::Sandbox;

let sb = Sandbox::builder("shared-worker")
    .image("python")
    .deployment_profile(DeploymentProfile::MultiTenant)
    .create()
    .await?;
```

```python Python
from microsandbox import DeploymentProfile, Sandbox

sb = await Sandbox.create(
    "shared-worker",
    image="python",
    deployment_profile=DeploymentProfile.MULTI_TENANT,
)
```

```go Go
sb, err := m.CreateSandbox(ctx, "shared-worker",
    m.WithImage("python"),
    m.WithDeploymentProfile(m.DeploymentProfileMultiTenant),
)
```

```bash CLI
msb create python --name shared-worker --deployment-profile multi-tenant
```
</CodeGroup>

<Accordion title="Deployment restrictions and operator settings">

In `multi-tenant` mode, traffic must pass both the platform's public-network policy and the sandbox's policy. Custom DNS servers and interface overrides are removed; host CA import and published ports are disabled. The sandbox cannot relax these restrictions.

An operator can set `deployment_profile` in `~/.microsandbox/config.json` or on `LocalBackend`. This overrides the sandbox's choice on create and restart. Managed cloud requests do not carry a deployment profile; the hosting driver selects it.

</Accordion>

<span id="connection-limits" />

<Accordion title="Connection limits and compatibility">

The per-sandbox `maxTcpConnections` setting optionally limits concurrent guest-initiated TCP connections. `maxConnections` remains a deprecated TCP-only alias. When omitted, `single-tenant` has no connection-count cap and `multi-tenant` defaults to 1,024. An explicit positive value overrides either default; zero explicitly selects unlimited. These are runtime defaults, not operator-enforced ceilings. Services accepting tenant configuration must enforce their own policy on overrides.

Legacy v0.6.x launch contracts keep their historical default of 256 TCP connections when the limit is omitted. The current SDK rejects explicit unlimited TCP connections, values above 4,096, or multi-tenant values above 256 when launching an older runtime because those peers cannot preserve the requested meaning. A current runtime also rejects legacy zero TCP limits: old zero meant deny all connections, not unlimited. Current-contract defaults and explicit limits are unchanged.

`maxUdpConnections` independently limits host-side UDP relay sessions. Omission is unlimited in `single-tenant` mode and defaults to 1,024 in `multi-tenant` mode; zero explicitly selects unlimited. At a finite limit, a new session evicts the least recently active session. Sessions expire after 60 seconds of inactivity. Historical runtime launch contracts do not support an explicit UDP limit, including zero; the SDK rejects that override with an upgrade-required error instead of silently ignoring it.

`maxInboundTcpConnections` limits active inbound TCP connections across published ports; `0` means unlimited. Omission uses the runtime default: unlimited in **0.7.9+**, or **256** in older versions. Explicit overrides require runtime support or fail with an upgrade-required error. This local-only limit is separate from the listener queue (`tcpAcceptQueueSize`).

On 0.7.9+, finished connections leave the active budget after response delivery. The runtime retains up to 256 finished sockets in TIME-WAIT, evicting the oldest under pressure and shortening protection against delayed packets. Host writes stalled for 30 seconds are closed; successful writes restart the timer.

Each TCP socket allocates 128 KiB of buffers. An outbound cap of 1,024 allows up to 128 MiB; an inbound cap of 256 plus 256 retained sockets allows up to 64 MiB. The retention pool can use 32 MiB even with a small active cap. These figures exclude relay channels, host socket buffers, and other allocations. Unlimited connections still face memory, file-descriptor, and source-port limits.

</Accordion>

## High-level profiles

Combine `public`, `private`, and `host` to allow those destination groups. Use `none` to deny all traffic or `all` to allow all traffic. This example allows public and private networks:

<CodeGroup>
```typescript TypeScript
import { NetworkPolicy, Sandbox } from "microsandbox";

await using sb = await Sandbox.builder("worker")
    .image("python")
    .network((n) => n.policy(NetworkPolicy.fromProfiles(["public", "private"])))
    .create();
```

```rust Rust
use microsandbox::{NetworkPolicy, NetworkProfile, Sandbox};

let policy = NetworkPolicy::from_profiles([NetworkProfile::Public, NetworkProfile::Private]);
let sb = Sandbox::builder("worker")
    .image("python")
    .network(|n| n.policy(policy))
    .create()
    .await?;
```

```python Python
from microsandbox import Network, NetworkProfile, Sandbox

sb = await Sandbox.create(
    "worker",
    image="python",
    network=Network.from_profiles(NetworkProfile.PUBLIC, NetworkProfile.PRIVATE),
)
```

```go Go
sb, err := m.CreateSandbox(ctx, "worker",
    m.WithImage("python"),
    m.WithNetwork(m.NetworkPolicy.FromProfiles(m.NetworkProfilePublic, m.NetworkProfilePrivate)),
)
```

```bash CLI
msb create python --name worker --net "public,private"
```
</CodeGroup>

Add `host` for services on your machine; see [Reaching the host](#reaching-the-host). Duplicate profiles are ignored. Explicit rules placed before profile rules take precedence.

## Low-level custom policies

For finer control, set default actions and add rules. The first matching rule wins. This example allows public HTTPS and gateway DNS while denying other outbound traffic:

<CodeGroup>
```typescript TypeScript
import { NetworkPolicy, Sandbox } from "microsandbox";

await using sb = await Sandbox.builder("restricted-worker")
    .image("alpine")
    .network((n) => n.policy(
        NetworkPolicy.builder()
            .defaultDeny()
            .egress((e) => e.tcp().port(443).allowPublic())
            .egress((e) => e.udp().tcp().port(53).allowHost())
            .build(),
    ))
    .create();
```

```rust Rust
use microsandbox::{NetworkPolicy, Sandbox};

let policy = NetworkPolicy::builder()
    .default_deny()
    .egress(|e| e.tcp().port(443).allow_public())
    .egress(|e| e.udp().tcp().port(53).allow_host())
    .build()?;

let sb = Sandbox::builder("restricted-worker")
    .image("alpine")
    .network(|n| n.policy(policy))
    .create()
    .await?;
```

```python Python
from microsandbox import Action, DestGroup, Destination, Network, NetworkPolicy, Protocol, Rule, Sandbox

sb = await Sandbox.create(
    "restricted-worker",
    image="alpine",
    network=Network(policy=NetworkPolicy(
        default_egress=Action.DENY,
        rules=(
            Rule.allow(destination=Destination.group(DestGroup.PUBLIC), protocol=Protocol.TCP, port=443),
            *Rule.allow_dns(),
        ),
    )),
)
```

```go Go
sb, err := m.CreateSandbox(ctx, "restricted-worker",
    m.WithImage("alpine"),
    m.WithNetwork(&m.NetworkConfig{
        DefaultEgress: m.PolicyActionDeny,
        Rules: []m.PolicyRule{
            {Action: m.PolicyActionAllow, Direction: m.PolicyDirectionEgress, Destination: "public", Protocol: m.PolicyProtocolTCP, Port: "443"},
            m.Rule.AllowDNS(),
        },
    }),
)
```

```bash CLI
msb create alpine --name restricted-worker \
  --net-default-egress deny \
  --net-rule "allow@public:tcp:443,allow@dns"
```
</CodeGroup>

Rules can match destination groups, IPs, CIDRs, domains, and ports. See the [CLI rule syntax](/cli/sandbox-commands#network-rule-syntax) or an [SDK reference](#reference) for exact options.

## NAT64 prefixes

NAT64 lets IPv6 networks reach IPv4 destinations. Microsandbox checks both addresses against policy, so translated private and metadata addresses stay blocked. The standard `64:ff9b::/96` prefix is included by default.

If your local network routes another `/96` prefix, add it like this:

<CodeGroup>
```typescript TypeScript
import { Sandbox } from "microsandbox";

await using sb = await Sandbox.builder("translated")
    .image("python")
    .network((n) => n.nat64Prefix("64:ff9b:1::/96"))
    .create();
```

```rust Rust
use microsandbox::Sandbox;

let prefix = "64:ff9b:1::/96".parse()?;
let sb = Sandbox::builder("translated")
    .image("python")
    .network(|n| n.nat64_prefix(prefix))
    .create()
    .await?;
```

```python Python
from microsandbox import Network, Sandbox

sb = await Sandbox.create(
    "translated",
    image="python",
    network=Network(nat64_prefixes=("64:ff9b::/96", "64:ff9b:1::/96")),
)
```

```go Go
sb, err := m.CreateSandbox(ctx, "translated",
    m.WithImage("python"),
    m.WithNetwork(&m.NetworkConfig{
        NAT64Prefixes: []string{"64:ff9b::/96", "64:ff9b:1::/96"},
    }),
)
```

```bash CLI
msb create python --name translated --net-nat64-prefix 64:ff9b:1::/96
```
</CodeGroup>

This setting classifies traffic; it does not set up NAT64 routing. Cloud rejects custom or empty prefix lists. See [CLI options](/cli/sandbox-commands#network-profiles) for details.

## Hostname rules and HTTPS

Allow specific domains with hostname rules. By default, HTTPS allowed only by a hostname rule requires [TLS inspection](/networking/tls), so microsandbox can check the hostname inside the request:

<CodeGroup>
```typescript TypeScript
import { NetworkPolicy, Sandbox } from "microsandbox";

const policy = NetworkPolicy.builder()
    .defaultDeny()
    .egress((e) => e.tcp().port(443).allowDomain("github.com"))
    .egress((e) => e.udp().tcp().port(53).allowHost())
    .build();

await using sb = await Sandbox.builder("github-only")
    .image("python")
    .network((n) => n.policy(policy))
    .interceptTls()
    .create();
```

```rust Rust
use microsandbox::{NetworkPolicy, Sandbox};

let policy = NetworkPolicy::builder()
    .default_deny()
    .egress(|e| e.tcp().port(443).allow_domains(["github.com"]))
    .egress(|e| e.udp().tcp().port(53).allow_host())
    .build()?;

let sb = Sandbox::builder("github-only")
    .image("python")
    .network(|n| n.policy(policy))
    .intercept_tls()
    .create()
    .await?;
```

```python Python
from microsandbox import Action, Destination, Network, NetworkPolicy, Protocol, Rule, Sandbox

sb = await Sandbox.create(
    "github-only",
    image="python",
    network=Network(policy=NetworkPolicy(
        default_egress=Action.DENY,
        rules=(
            Rule.allow(destination=Destination.domain("github.com"), protocol=Protocol.TCP, port=443),
            *Rule.allow_dns(),
        ),
    )),
    intercept_tls=True,
)
```

```go Go
sb, err := m.CreateSandbox(ctx, "github-only",
    m.WithImage("python"),
    m.WithNetwork(&m.NetworkConfig{
        DefaultEgress: m.PolicyActionDeny,
        Rules: []m.PolicyRule{
            {Action: m.PolicyActionAllow, Direction: m.PolicyDirectionEgress, Destination: "github.com", Protocol: m.PolicyProtocolTCP, Port: "443"},
            m.Rule.AllowDNS(),
        },
    }),
    m.WithInterceptTLS(),
)
```

```ruby Ruby
sb = Microsandbox::Sandbox.create(
  "github-only",
  image: "python",
  network: { allowed_hosts: ["github.com"], allowed_ports: [443] },
  intercept_tls: true
)
```

```bash CLI
msb create python --name github-only \
  --net-default-egress deny \
  --net-rule "allow@github.com:tcp:443,allow@dns" \
  --tls-intercept
```
</CodeGroup>

<Accordion title="How strict hostname checks work">

Domain and domain-suffix rules start with the hostname microsandbox can observe. DNS rules see the query name, and TLS connections usually expose SNI before the encrypted session starts. That is enough to decide whether a connection may be opened, but it is not the same as seeing the HTTP request authority.

For plaintext HTTP, microsandbox can inspect the `Host` header and require it to match the hostname that policy allowed. For HTTPS without TLS interception, or for a host that is configured to bypass interception, the HTTP `Host` header and HTTP/2 `:authority` value are encrypted. In that case microsandbox cannot prove which application hostname the request is using after the TLS session starts.

Strict hostname policy mode is enabled by default: hostname allowlists fail closed unless microsandbox can inspect the request authority. In strict mode, hostname-based allow rules still work for plaintext HTTP and TLS-intercepted HTTPS. Non-intercepted HTTPS is denied when it would otherwise be allowed only by a hostname rule. IP, CIDR, group, and default allow rules are not treated as hostname-based allows. Set `--net-strict=false` or `network.strict: false` in sandbox configuration to opt out.

</Accordion>

## Port mapping

Publish a guest port to your host machine. This example forwards host port `8080` to guest port `80`, listening on `127.0.0.1` by default. Port publishing is local-only.

<CodeGroup>
```typescript TypeScript
import { Sandbox } from "microsandbox";

await using sb = await Sandbox.builder("api")
    .image("python")
    .port(8080, 80)
    .create();
```

```rust Rust
use microsandbox::Sandbox;

let sb = Sandbox::builder("api")
    .image("python")
    .port(8080, 80)
    .create()
    .await?;
```

```python Python
from microsandbox import PortBinding, Sandbox

sb = await Sandbox.create("api", image="python", ports=[PortBinding.tcp(8080, 80)])
```

```go Go
sb, err := m.CreateSandbox(ctx, "api",
    m.WithImage("python"),
    m.WithPorts(map[uint16]uint16{8080: 80}),
)
```

```bash CLI
msb create python --name api -p 8080:80
```
</CodeGroup>

<Accordion title="Bind addresses, port ranges, and Windows">

Use an explicit bind address, such as `0.0.0.0`, only when you intentionally want to listen beyond localhost. `-p 8080:80` and SDK helpers like `.port(8080, 80)` bind to `127.0.0.1`; `-p 127.0.0.1:8080:80` is the same loopback-only shape. `-p 0.0.0.0:8080:80` or a specific LAN interface address makes the host listener reachable outside the machine, subject to your OS firewall and network policy.

If the host starts without IPv4 or IPv6 routes, microsandbox assigns private guest addresses for published ports unless guest addresses are already configured. A specific host bind address must still exist at startup.

CLI `-p` and YAML `ports` lists also accept inclusive, equal-length port ranges: `-p 8000-8002:80-82` forwards three TCP ports, while `-p 0.0.0.0:10240-11264:10240-11264/udp` publishes 1,025 UDP ports. Ranges expand into the same individual mappings used by the SDKs, with one host socket per port. See [Published ports](/cli/sandbox-commands#published-ports) for validation and host resource requirements.

On Windows, the first published port may trigger a Windows Defender Firewall prompt for `msb.exe` because the runtime opens a host listening socket. For local development, keep the bind address on `127.0.0.1`. Only allow private/public network access in the firewall prompt when you intentionally bind a published port beyond loopback.

</Accordion>

## Rate limits

Limit inbound or outbound bandwidth and packet rate on local sandboxes. Omitted limits leave traffic unthrottled; changes take effect on the next start. Cloud does not support this setting.

This example allows 1 MiB/s outbound, a one-time 512 KiB burst, and 1,000 packets per second:

<CodeGroup>
```typescript TypeScript
import { Sandbox } from "microsandbox";

await using sb = await Sandbox.builder("throttled")
    .image("python")
    .network((n) => n.rateLimiter((r) => r.egress((r) => r
        .bandwidth(1_048_576, 1_000)
        .bandwidthBurst(524_288)
        .ops(1_000, 1_000))))
    .create();
```

```rust Rust
use std::time::Duration;

use microsandbox::size::SizeExt;
use microsandbox::Sandbox;

let sb = Sandbox::builder("throttled")
    .image("python")
    .network(|n| n.rate_limiter(|r| r.egress(|r| r
        .bandwidth(1.mib(), Duration::from_secs(1))
        .bandwidth_burst(512.kib())
        .ops(1_000, Duration::from_secs(1)))))
    .create()
    .await?;
```

```python Python
from microsandbox import Network, NetworkRateLimiter, RateLimiter, Sandbox, TokenBucket

sb = await Sandbox.create(
    "throttled",
    image="python",
    network=Network(
        rate_limiter=NetworkRateLimiter(
            egress=RateLimiter(
                bandwidth=TokenBucket(size=1_048_576, refill_time_ms=1_000, one_time_burst=524_288),
                ops=TokenBucket(size=1_000, refill_time_ms=1_000),
            ),
        ),
    ),
)
```

```go Go
sb, err := m.CreateSandbox(ctx, "throttled",
    m.WithImage("python"),
    m.WithNetwork(&m.NetworkConfig{
        RateLimiter: &m.NetworkRateLimiterConfig{
            Egress: &m.RateLimiterConfig{
                Bandwidth: &m.TokenBucketConfig{Size: 1 << 20, RefillTime: time.Second, OneTimeBurst: 512 << 10},
                Ops:       &m.TokenBucketConfig{Size: 1000, RefillTime: time.Second},
            },
        },
    }),
)
```

```bash CLI
msb create python --name throttled \
  --net-egress-bandwidth 1M/1s \
  --net-egress-bandwidth-burst 512K \
  --net-egress-ops 1000/1s
```
</CodeGroup>

<Accordion title="How limits and bursts are applied">

Network rate limits apply only to traffic crossing a sandbox's virtual network device. They do not limit CPU, memory, disk I/O, API requests, or how many sandboxes can run. If you do not configure a limiter, microsandbox does not throttle the sandbox's network bandwidth or packet rate. Once configured, the limiter is enforced independently for outbound (egress) and inbound (ingress) traffic.

For each direction, you can configure a bandwidth bucket measured in bytes per refill interval, a packet-rate bucket measured in packets per interval, or both. An omitted direction or bucket remains unthrottled. Buckets start full, refill continuously, and can include a one-time startup burst that does not refill. Limits are set at creation and take effect on the next sandbox start.

The `--net-ingress-*` flags mirror the egress flags for inbound traffic (`--net-ingress-bandwidth`, `--net-ingress-bandwidth-burst`, `--net-ingress-ops`, `--net-ingress-ops-burst`). Sizes accept raw bytes plus `K`, `M`, and `G` suffixes; the interval defaults to one second when omitted. A frame larger than the bandwidth bucket is delivered once and the limiter then pauses long enough to pay it off, so oversized packets are throttled instead of stuck.

</Accordion>

<span id="what-a-denied-http-request-sees" />

## HTTP denial responses

New local sandboxes return JSON `403 Forbidden` responses for supported denied requests by default, with `code`, `message`, and `domain` fields. Set `deny_response: false` to close denied connections without a response. This requires a supporting runtime; cloud behavior is unchanged.

In JSON mode, `network_deny_message` and `secret_deny_message` customize the respective messages. Messages are used as-is; the destination is in `domain`. Changing a message or format does not override an explicit `deny_response: false`.

Select `deny_response_format: text` for legacy behavior: network denials return plain text, `deny_message` substitutes `{host}`, and secret violations close without a response. The old message setting remains supported but is deprecated for new integrations. Each mode uses only its own message settings. Omitted settings use the runtime defaults: responses enabled with JSON format. Explicit settings take precedence. Cloud accepts the default settings without applying them; enabled custom settings are local-only.

<CodeGroup>
```typescript TypeScript
await using sb = await Sandbox.builder("agent")
    .image("python")
    .network((n) => n
        .policy(policy)
        .tls((t) => t)
        .http((h) => h
            .denyResponse(true)
            .denyResponseFormat("json")
            .networkDenyMessage("Ask the user for network access.")
            .secretDenyMessage("Ask the user to review secret access.")))
    .create();
```

```rust Rust
use microsandbox::sandbox::HttpDenyResponseFormat;

let sb = Sandbox::builder("agent")
    .image("python")
    .network(|n| n
        .policy(policy)
        .tls(|t| t.enabled(true))
        .http(|h| h
            .deny_response(true)
            .deny_response_format(HttpDenyResponseFormat::Json)
            .network_deny_message("Ask the user for network access.")
            .secret_deny_message("Ask the user to review secret access.")))
    .create()
    .await?;
```

```python Python
from microsandbox import HttpConfig, Network, Sandbox, TlsConfig

sb = await Sandbox.create(
    "agent",
    image="python",
    network=Network(
        policy=policy,
        tls=TlsConfig(),
        http=HttpConfig(
            deny_response=True,
            deny_response_format="json",
            network_deny_message="Ask the user for network access.",
            secret_deny_message="Ask the user to review secret access.",
        ),
    ),
)
```

```go Go
sb, err := m.CreateSandbox(ctx, "agent",
    m.WithImage("python"),
    m.WithNetwork(&m.NetworkConfig{
        Rules:           rules,
        TLS:             &m.TLSConfig{},
        HTTP: &m.HTTPConfig{
            DenyResponse: true,
            DenyResponseFormat: m.HTTPDenyResponseJSON,
            NetworkDenyMessage: "Ask the user for network access.",
            SecretDenyMessage: "Ask the user to review secret access.",
        },
    }),
)
```

```ruby Ruby
sb = Microsandbox::Sandbox.create(
  "agent",
  image: "python",
  network: { allowed_hosts: ["example.com"], allowed_ports: [80, 443] },
  http: {
    deny_response: true,
    deny_response_format: "json",
    network_deny_message: "Ask the user for network access.",
    secret_deny_message: "Ask the user to review secret access."
  }
)
```

```yaml sandbox.yml
network:
  policy: none
  allow: [github.com]
  http:
    deny_response: true
    deny_response_format: json
    network_deny_message: "Ask the user for network access."
    secret_deny_message: "Ask the user to review secret access."
```
</CodeGroup>

Check the response status, or use `curl --fail` to make a `403` return a nonzero exit code.

<Accordion title="Which requests receive a response?">

Responses work for HTTP/1.0 and HTTP/1.1. HTTPS requires [TLS interception](/networking/tls) on the destination port, with no bypass for the host. Network-policy denials never reach the upstream server.

In JSON mode, secret-policy denials can also return a response for the first request on a connection, before any upstream response bytes have reached the guest. The connection may already be open when a placeholder is detected; forwarding stops at the violation.

Secret-policy violations still close the connection for later or pipelined requests, after an upstream response (including `100 Continue`), and for `HEAD`, `CONNECT`, `block-and-terminate`, or unsupported body substitution.

DNS denies still return `NXDOMAIN`. HTTP/2, other protocols, malformed or incomplete requests, and silent clients close without an HTTP response.

</Accordion>

<Accordion title="Response format">

JSON responses use `Content-Type: application/json`. Match `code` to identify the error and display `message` to the user. `domain` is the requested hostname, an IP address fallback for network denials, or `null` when unknown.

Network-policy denial:

```json
{
  "code": "network_policy_denied",
  "message": "Request blocked by network policy.",
  "domain": "example.com"
}
```

Secret-policy denial:

```json
{
  "code": "secret_policy_denied",
  "message": "Request blocked by secret policy.",
  "domain": "example.com"
}
```

Both custom messages are JSON-escaped. An omitted message uses the default; an empty string leaves `message` empty (Go treats an empty string as omitted). Text mode works with older runtimes that support denial responses. JSON mode requires the new runtime capability; the SDK returns an upgrade-required error if it is missing.

</Accordion>

## Reaching the host

`host.microsandbox.internal` points to the host machine from inside a local sandbox. The default policy blocks it, so allow the `host` group when the guest needs a local service:

<CodeGroup>
```typescript TypeScript
import { NetworkPolicy, Sandbox } from "microsandbox";

await using sb = await Sandbox.builder("devbox")
    .image("python")
    .network((n) => n.policy(NetworkPolicy.fromProfiles(["public", "host"])))
    .create();
```

```rust Rust
use microsandbox::{NetworkPolicy, NetworkProfile, Sandbox};

let policy = NetworkPolicy::from_profiles([NetworkProfile::Public, NetworkProfile::Host]);
let sb = Sandbox::builder("devbox")
    .image("python")
    .network(|n| n.policy(policy))
    .create()
    .await?;
```

```python Python
from microsandbox import Network, NetworkProfile, Sandbox

sb = await Sandbox.create(
    "devbox",
    image="python",
    network=Network.from_profiles(NetworkProfile.PUBLIC, NetworkProfile.HOST),
)
```

```go Go
sb, err := m.CreateSandbox(ctx, "devbox",
    m.WithImage("python"),
    m.WithNetwork(m.NetworkPolicy.FromProfiles(m.NetworkProfilePublic, m.NetworkProfileHost)),
)
```

```bash CLI
msb create python --name devbox --net "public,host"
```
</CodeGroup>

Guest `127.0.0.1` is inside the sandbox, not the host. On cloud, the `host` group refers to the cloud worker, not the machine running your SDK.

## Reference

For exact APIs, see [TypeScript](/sdk/typescript/networking), [Rust](/sdk/rust/networking), [Python](/sdk/python/networking), [Go](/sdk/go/networking), or [Ruby](/sdk/ruby/networking). For flags and configuration, see [Sandbox commands](/cli/sandbox-commands#network-profiles) and [Sandbox configuration](/cli/configuration#network).

## Next

- [DNS](/networking/dns): nameservers and domain blocking
- [Outbound proxies](/networking/outbound-proxy): route traffic through a proxy
- [TLS inspection](/networking/tls): inspect HTTPS traffic
- [Host sockets](/networking/host-sockets): connect to host IPC without a TCP port
- [Network security](/security/network): understand the policy boundary and defenses

---

### Sandboxes Overview (`docs/sandboxes/overview.mdx`)

---
title: Overview
description: What sandboxes are and how to configure them
keywords: ["microVM sandboxes", "sandbox configuration", "sandbox lifecycle"]
icon: "circle-info"
---

A sandbox is a microVM with its own Linux kernel, filesystem, and network stack. Your application or the `msb` CLI starts it as a child process on your machine or on [microsandbox cloud](/cloud/overview), then talks to the guest agent to run commands, move files, and control lifecycle.

The security boundary is hardware virtualization, not Linux namespaces. That makes sandboxes a good fit for untrusted workloads: user-submitted code, AI agent actions, plugins, dependency installs, CI jobs, scrapers, and tools that should not inherit the host process's full privileges.

## Create a sandbox

At minimum, a sandbox needs a name and an image. Everything else has defaults: 1 vCPU, 512 MiB memory, public-only networking, and `/bin/sh` as the default shell.

<CodeGroup>
```typescript TypeScript
await using sb = await Sandbox.builder("worker")
    .image("python")
    .create();
```

```rust Rust
let sb = Sandbox::builder("worker")
    .image("python")
    .create()
    .await?;
```

```python Python
sb = await Sandbox.create("worker", image="python")
```

```go Go
sb, err := m.CreateSandbox(ctx, "worker", m.WithImage("python"))
```

```ruby Ruby
sb = Microsandbox::Sandbox.create("worker", image: "python")
```

```bash CLI
msb create python --name worker
```
</CodeGroup>

Sandbox names must be non-empty and no longer than 128 UTF-8 bytes.

## Common configuration

<Tooltip tip="max_cpus and max_memory are not carried on microsandbox cloud; set the CPU and memory you need at creation and recreate the sandbox to resize."><span className="msb-badge-limited">Limited on cloud <Icon icon="circle-info" size={11} /></span></Tooltip>

<CodeGroup>
```typescript TypeScript
await using sb = await Sandbox.builder("worker")
    .image("python")
    .memory(1024)
    .cpus(2)
    .env("DEBUG", "true")
    .label("user.id", "alice")
    .volume("/tmp/scratch", (m) => m.tmpfs().size(100))
    .create();
```

```rust Rust
let sb = Sandbox::builder("worker")
    .image("python")
    .memory(1024)
    .cpus(2)
    .env("DEBUG", "true")
    .label("user.id", "alice")
    .volume("/tmp/scratch", |v| v.tmpfs().size(100))
    .create()
    .await?;
```

```python Python
sb = await Sandbox.create(
    "worker",
    image="python",
    memory=1024,
    cpus=2,
    env={"DEBUG": "true"},
    labels={"user.id": "alice"},
    volumes={"/tmp/scratch": Volume.tmpfs(size_mib=100)},
)
```

```go Go
sb, err := m.CreateSandbox(ctx, "worker",
    m.WithImage("python"),
    m.WithMemory(1024),
    m.WithCPUs(2),
    m.WithEnv(map[string]string{"DEBUG": "true"}),
    m.WithLabels(map[string]string{"user.id": "alice"}),
    m.WithMounts(map[string]m.MountConfig{
        "/tmp/scratch": m.Mount.Tmpfs(m.TmpfsOptions{SizeMiB: 100}),
    }),
)
```

```ruby Ruby
sb = Microsandbox::Sandbox.create(
  "worker",
  image: "python",
  memory: 1024,
  cpus: 2,
  env: { "DEBUG" => "true" },
  labels: { "user.id" => "alice" }
)
# Ruby does not expose the tmpfs mount option shown in the other tabs.
```

```bash CLI
msb create python --name worker \
  -c 2 \
  -m 1G \
  -e DEBUG=true \
  --label user.id=alice \
  --tmpfs /tmp/scratch:100
```
</CodeGroup>

| Option | Default | Description |
|--------|---------|-------------|
| `image` | required | OCI image, local rootfs path, or disk image |
| `cpus` | `1` | Virtual CPU limit |
| `max_cpus` | same as `cpus` | Boot-time maximum possible virtual CPUs |
| `memory` | `512` | Guest memory limit in MiB |
| `max_memory` | same as `memory` | Boot-time maximum hotpluggable memory in MiB |
| `thp` | `madvise` | Guest transparent huge-page policy selected at boot |
| `guest_clock` | `sync` | Follow host time (`sync`) or stop host clock updates after boot (`off`). Local Rust SDK and CLI only |
| `workdir` | image default | Default working directory for commands |
| `shell` | `/bin/sh` | Shell used by `shell()` calls |
| `env` | empty | Environment variables |
| `labels` | empty | Key/value metadata for metric attribution |
| `volumes` | empty | Bind, named, tmpfs, or disk-image mounts |
| `network` | public-only | Network policy and published ports |
| `scripts` | empty | Named scripts mounted at `/.msb/scripts/` |

`max_cpus` and `max_memory` reserve live resize headroom. They default to the
starting `cpus` and `memory` values, so set them higher when a sandbox may need
to grow later. See [Live Modify](/sandboxes/tuning) for the full change
model.

Labels are arbitrary `key=value` metadata attached to a sandbox. They can be set
at create time or changed while the sandbox is running, and they also select
sandboxes in bulk. See [Labels](/sandboxes/labels) for naming guidance, bulk
actions, metric attribution, and OCI image labels.

<Tip>
  `cpus` and `memory` are limits, not reservations. Guest memory is allocated as the VM touches pages.
</Tip>

## Image sources

Most sandboxes start from an OCI image. You can also use a host directory as the root filesystem, or boot from a disk image. Select one source:

<Tooltip tip="Host-directory and disk-image root filesystems are local-only; microsandbox cloud supports OCI root filesystems."><span className="msb-badge-local">Local-only <Icon icon="circle-info" size={11} /></span></Tooltip>

<CodeGroup>
```typescript TypeScript
const image = "python"; // Or "./my-rootfs" or "./alpine.qcow2".
await using sb = await Sandbox.builder("worker").image(image).create();
```

```rust Rust
use microsandbox::Sandbox;

let image = "python"; // Or "./my-rootfs" or "./alpine.qcow2".
let sb = Sandbox::builder("worker").image(image).create().await?;
```

```python Python
from microsandbox import Sandbox

image = "python"  # Or "./my-rootfs" or "./alpine.qcow2".
sb = await Sandbox.create("worker", image=image)
```

```go Go
image := "python" // Or "./my-rootfs" or "./alpine.qcow2".
sb, err := m.CreateSandbox(ctx, "worker", m.WithImage(image))
if err != nil {
    return err
}
```

```ruby Ruby
image = "python" # Or "./my-rootfs" or "./alpine.qcow2".
sb = Microsandbox::Sandbox.create("worker", image: image)
```

```bash CLI
# Choose one source:
msb create python --name worker
msb create ./my-rootfs --name worker
msb create ./alpine.qcow2 --name worker
```
</CodeGroup>

OCI images use a copy-on-write overlay so sandboxes can share cached base layers. Disk images are attached as block devices, so each sandbox should use its own disk image copy unless the image format handles its own snapshotting.

See [Images](/images/overview) for the full OCI and disk image model.

## Naming conflicts

<Tooltip tip="Replace-on-create is not available on microsandbox cloud; remove the existing sandbox first, then create the replacement."><span className="msb-badge-local">Local-only <Icon icon="circle-info" size={11} /></span></Tooltip>

Creating a sandbox fails if another sandbox already has the same name. Use [`connect_or_create`](/sandboxes/lifecycle#reuse-or-create-a-named-sandbox) when you want to reuse the current persisted identity and its configuration. Use replace when you explicitly want a fresh sandbox with that name:

<CodeGroup>
```typescript TypeScript
await using sb = await Sandbox.builder("worker")
    .image("python")
    .replace()
    .create();
```

```rust Rust
let sb = Sandbox::builder("worker")
    .image("python")
    .replace()
    .create()
    .await?;
```

```python Python
sb = await Sandbox.create("worker", image="python", replace=True)
```

```go Go
sb, err := m.CreateSandbox(ctx, "worker",
    m.WithImage("python"),
    m.WithReplace(),
)
```

```ruby Ruby
sb = Microsandbox::Sandbox.create("worker", image: "python", replace: true)
```

```bash CLI
msb create --replace python --name worker
```
</CodeGroup>

When replacing a running sandbox, microsandbox attempts graceful shutdown before force-killing it. Use `replace_with_timeout` or `--replace-with-timeout` when the workload needs a longer grace period.

## Reference

For the complete sandbox API, see [TypeScript](/sdk/typescript/sandbox), [Rust](/sdk/rust/sandbox), [Python](/sdk/python/sandbox), [Go](/sdk/go/sandbox), or [Ruby](/sdk/ruby/sandbox). For terminal and REST workflows, see [Sandbox commands](/cli/sandbox-commands) and the [Cloud API](/api-reference/overview).

## Where to go next

- [Lifecycle](/sandboxes/lifecycle): start, stop, detach, wait, and remove sandboxes
- [Commands](/sandboxes/commands): run commands and stream output
- [Filesystem](/sandboxes/filesystem): read, write, and transfer files
- [Secrets](/sandboxes/secrets): inject credentials without exposing values
- [Images](/images/overview): choose OCI, directory, or disk-image roots
- [Volumes](/sandboxes/volumes): persist or share data
- [SSH](/sandboxes/ssh): connect with SSH and SFTP clients
- [Snapshots](/sandboxes/snapshots): capture and reuse writable sandbox state
- [Live Modify](/sandboxes/tuning): change settings after create
- [Labels](/sandboxes/labels): organize, select, and attribute sandboxes
- [Logs](/sandboxes/logs): inspect workload and runtime output
- [Metrics](/sandboxes/metrics): monitor sandbox resource usage
- [Bootstrap](/sandboxes/bootstrap): prepare scripts, patches, and PID 1
- [Networking](/networking/overview): control egress, ingress, DNS, and ports

---

### Sdk Overview (`docs/sdk/overview.mdx`)

---
title: Overview
"og:title": Microsandbox SDKs for TypeScript, Rust, Python, Go, and Ruby
description: Install a microsandbox SDK, create your first microVM sandbox, and find API references for TypeScript, Rust, Python, Go, and Ruby.
keywords: ["microsandbox SDK", "Python SDK", "TypeScript SDK", "Rust SDK", "Go SDK"]
icon: "play"
---

The SDK runs microsandbox directly from your application. Running locally, creating a sandbox spawns a VM as a child process; there is no daemon to install and no server to connect to. With an API key set, the same SDK runs sandboxes on [microsandbox cloud](/cloud/overview) instead.

## Installation

<CodeGroup>
```bash TypeScript
npm install microsandbox
```

```bash Rust
cargo add microsandbox
```

```bash Python
pip install microsandbox
```

```bash Go
go get github.com/superradcompany/microsandbox/sdk/go
```

```bash Ruby
gem install microsandbox
```
</CodeGroup>

<Accordion title="Provisioning the runtime separately">

All five SDKs can use a separately provisioned runtime. See [Runtime setup](/sdk/setup#bring-your-own-runtime) for code tabs covering each SDK’s packaging and setup requirements.

</Accordion>

## Small example

Create a sandbox from a container image, run a command, print the output, and stop it.

<CodeGroup>
```typescript TypeScript
import { Sandbox } from "microsandbox";

await using sb = await Sandbox.builder("hello")
    .image("python")
    .create();

const output = await sb.exec("python", ["-c", "print('Hello, World!')"]);
console.log("Output:", output.stdout());
```

```rust Rust
use std::error::Error;

use microsandbox::Sandbox;

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    let sb = Sandbox::builder("hello")
        .image("python")
        .create()
        .await?;

    let output = sb.exec("python", ["-c", "print('Hello, World!')"]).await?;
    println!("{}", output.stdout()?);

    sb.stop().await?;
    Ok(())
}
```

```python Python
import asyncio
from microsandbox import Sandbox

async def main():
    sb = await Sandbox.create(
        "hello",
        image="python",
    )

    output = await sb.exec("python", ["-c", "print('Hello, World!')"])
    print("Output:", output.stdout_text)

    await sb.stop()

asyncio.run(main())
```

```go Go
package main

import (
    "context"
    "fmt"
    "log"

    m "github.com/superradcompany/microsandbox/sdk/go"
)

func main() {
    ctx := context.Background()

    if _, err := m.EnsureRuntime(ctx, m.RuntimeConfig{}, m.InstallOptions{}); err != nil {
        log.Fatal(err)
    }

    sb, err := m.CreateSandbox(ctx, "hello", m.WithImage("python"))
    if err != nil {
        log.Fatal(err)
    }
    defer func() {
        _ = sb.Stop(context.Background())
        _ = sb.Close()
    }()

    out, err := sb.Exec(ctx, "python", []string{"-c", "print('Hello, World!')"})
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("Output:", out.Stdout())
}
```

```ruby Ruby
require "microsandbox"

Microsandbox.install unless Microsandbox.installed?

Microsandbox::Sandbox.with("hello", image: "python") do |sandbox|
  output = sandbox.exec("python", ["-c", "print('Hello, World!')"])
  puts "Output: #{output.stdout}"
end
```

</CodeGroup>

## Reference

Choose the SDK for your language:

- [TypeScript SDK](/sdk/typescript/sandbox)
- [Rust SDK](/sdk/rust/sandbox)
- [Python SDK](/sdk/python/sandbox)
- [Go SDK](/sdk/go/sandbox)
- [Ruby SDK](/sdk/ruby/sandbox)

For low-level protocol integrations, see the Agent Client references for [TypeScript](/sdk/typescript/agent-client), [Rust](/sdk/rust/agent-client), [Python](/sdk/python/agent-client), and [Go](/sdk/go/agent-client). See also [error handling](/sdk/errors).

---

### Security Overview (`docs/security/overview.mdx`)

---
title: "Security model"
description: "The trust boundary microsandbox enforces, what it protects, and what it leaves to you"
icon: "shield-halved"
---

microsandbox runs code you don't trust, like AI agents, user submissions, plugins, scrapers, and build jobs, without handing any of it your host. This page describes the security model end to end: the boundary it draws, the guarantees that boundary gives you by default, and the things it deliberately leaves to you.

The whole model rests on one idea. **The guest is untrusted, the host is trusted, and a hardware hypervisor sits between them.** Every interaction a workload has with the outside world, whether files, network, secrets, or the control channel, is brokered by host-side code on the trusted side of that boundary.

## The trust boundary

A sandbox is a microVM: a real virtual machine with its own Linux kernel, scheduled by a hardware hypervisor (KVM on Linux, Apple's Hypervisor.framework on macOS). It is **not** a container sharing your host kernel.

<Frame>
  <img className="block dark:hidden" src="/images/security/trust-boundary-light.svg" alt="An untrusted guest reaches the trusted host only through hypervisor-mediated virtio channels: console, network, files, and disks." />
  <img className="hidden dark:block" src="/images/security/trust-boundary-dark.svg" alt="An untrusted guest reaches the trusted host only through hypervisor-mediated virtio channels: console, network, files, and disks." />
</Frame>

Two actors sit on either side of one boundary:

| Side | What lives there | How it's treated |
|---|---|---|
| **Guest** | Your workload and its processes, the guest Linux kernel, and `agentd` (the in-guest agent) | Fully untrusted. Assume it is adversarial. |
| **Host** | Your application or the `msb` CLI, plus the per-sandbox process that embeds the VMM, network stack, filesystem broker, and secret injection | Trusted. It enforces the boundary. |

The guest reaches the host only through a fixed, small set of paravirtual (virtio) devices. It cannot make host syscalls, read host memory, or open host connections on its own. Everything it asks for is mediated by the trusted side.

## What you get by default

Before any configuration, a sandbox already gives you:

- **Hardware-isolated execution.** Each sandbox has its own kernel and memory, scheduled by the hypervisor. A guest kernel compromise stays in the guest.
- **A private filesystem.** The guest sees only its image plus whatever you explicitly mount. Your host filesystem stays invisible.
- **An isolated writable root.** Writes land in a per-sandbox layer that never touches the shared image cache or another sandbox.
- **A network that can't pivot inward.** The public internet is reachable, but private ranges, loopback, link-local, cloud metadata, and your host are all denied. Inbound traffic only reaches the ports you publish.
- **Secrets that stay on the host.** Credentials you bind never enter the VM. The guest only ever sees a meaningless placeholder.

<Note>
One default surprises people: workloads run as **root inside the guest**. That is in-guest root, contained by the VM boundary. It is not a privilege on your host, and you can drop it. See [Isolation boundary](/security/isolation#in-guest-privilege) and [Hardening](/security/hardening).
</Note>

## Scope: what the boundary covers

Being candid about the edges is more useful than a long list of features. Here is where the model defends you, where it does not, and what it expects you to own.

### Defended by the model

- **Guest-to-host escape.** The VM and hypervisor boundary is what keeps a compromised guest off your host.
- **Sandbox-to-sandbox isolation.** Separate VMs, separate processes, separate writable layers, separate network gateways.
- **Egress filtering and SSRF.** Workloads cannot reach your private network, host, or cloud metadata service by default.
- **Secret leakage to unintended hosts.** A bound credential is only revealed at the host you allow it for.
- **Host filesystem privacy.** The guest sees nothing of your host disk unless you mount it.

### Out of scope, by design

- **A compromised host.** If an attacker already controls your host, or the process that launches sandboxes, they are on the trusted side. Secret values live in host memory, and host configuration defines the policy. microsandbox protects the host from the guest, not a guest from a hostile host.
- **The hypervisor and CPU.** The model trusts KVM, Hypervisor.framework, and the silicon to enforce VM isolation. A hypervisor or hardware vulnerability sits below this boundary.
- **What an allowed destination does with your data.** If you allow egress to `api.example.com` and inject a secret there, that endpoint receives the real credential. The model controls *where* data can go, not what the far side does with it.
- **Image provenance.** Pulled content is verified against its declared digest, but signatures and attestations are not checked. The registry you pull from is part of your trusted base. See [Filesystem & images](/security/filesystem#image-supply-chain).
- **Self-inflicted denial of service.** A workload can exhaust its own CPU and memory within the limits you set. That doesn't affect other sandboxes or the host.

### Your responsibility

- Pick a network policy that matches your threat model. The default is sensible, but high-stakes workloads often want deny-by-default.
- Drop in-guest privileges when the workload doesn't need root.
- Pin images you trust, and mount only what's needed, read-only where possible.
- Keep the host that runs sandboxes secure. It is the trusted anchor for everything above.

## How the model is enforced

Each subsystem carries part of the boundary. The pages below go deep on each, candidly including the limits.

<CardGroup cols={2}>
  <Card title="Isolation boundary" icon="microchip" href="/security/isolation">
    The microVM, the host-guest channel, and in-guest privilege.
  </Card>
  <Card title="Filesystem & images" icon="hard-drive" href="/security/filesystem">
    Private root, mounts, snapshots, and image supply chain.
  </Card>
  <Card title="Network defenses" icon="network-wired" href="/security/network">
    Egress filtering, SSRF, DNS rebinding, and metadata.
  </Card>
  <Card title="Secret handling" icon="key" href="/security/secrets">
    How credentials stay on the host, and the exact guarantee.
  </Card>
  <Card title="Hardening" icon="user-shield" href="/security/hardening">
    Dial the controls to match your threat level.
  </Card>
</CardGroup>

## Reporting a vulnerability

If you find a way across any of these boundaries, like a guest-to-host escape, a cross-sandbox leak, an egress-filter bypass, or a secret revealed outside its intended host, we want to hear about it. Use [GitHub's private vulnerability reporting](https://github.com/superradcompany/microsandbox/security/advisories/new) or email `security@superrad.company`. The full process and scope are in the project's [security policy](https://github.com/superradcompany/microsandbox/blob/main/SECURITY.md).

---

### Configuration (`docs/configuration.mdx`)

---
title: Global configuration
"og:title": Microsandbox global configuration reference
description: Configure microsandbox defaults for the CLI and SDKs using the shared config.json file.
icon: "gear"
---

For guidance on choosing between global configuration, sandbox YAML, CLI
arguments, and SDK inputs, start with [Configuration](/operations/configuration).

microsandbox reads user configuration from `~/.microsandbox/config.json`, or `MSB_CONFIG_PATH` when set. All fields are optional. A missing file or empty JSON object adds no user overrides; defaults and managed settings still apply.

## Reference example

Set only the values you want to override. For example, give new sandboxes two CPUs and 1 GiB of memory:

<CodeGroup>
```json JSON
{
  "sandbox_defaults": {
    "cpus": 2,
    "memory_mib": 1024
  }
}
```
</CodeGroup>

<Accordion title="How relative paths are resolved">

Relative host paths stored in this file resolve from the file's directory, including `home`, `paths.*`, and `registries.ca_certs`. For example, `/etc/microsandbox/config.json` containing `"ca_certs": "./company.pem"` selects `/etc/microsandbox/company.pem`, regardless of the caller's working directory. This intentionally replaces the previous working-directory-relative interpretation.

Use an absolute path to keep a location elsewhere. Programmatic SDK paths and environment defaults are captured relative to the caller's working directory when the backend is constructed. Guest paths, such as `sandbox_defaults.workdir`, are unaffected.

</Accordion>

<Accordion title="Full example">
<CodeGroup>
```
/* Detailed source-code truncated for AI context efficiency. */
```
</CodeGroup>
</Accordion>

<Accordion title="Schema version">

Newly saved user files include `"version": 1`. An omitted version defaults to `1`; invalid or unsupported versions are rejected.

For reading and saving configuration programmatically, see the [Rust configuration reference](/sdk/rust/configuration#globalconfigpatch).

</Accordion>

## Managed configuration

Administrators can enforce these same fields using a protected `managed.json` file with the shape `{ "version": 1, "overrides": { ... } }`. Supplied managed values take precedence over ordinary user settings and CLI/SDK options. The user and managed files have independent schema versions, each defaulting to `1` when omitted. Managed values are not automatically saved into the user file.

See [Managed deployment](/enterprise/managed-configuration) for deployment, precedence, clearing, and updates. This page remains the reference for individual fields and accepted values.

## Top-level fields

| Field | Default | Description |
|-------|---------|-------------|
| `home` | `~/.microsandbox` | Root directory for all microsandbox data |
| `log_level` | `null` (silent) | Log level for sandbox processes: `error`, `warn`, `info`, `debug`, `trace` |
| `deployment_profile` | `null` | Authoritative local host-runtime isolation profile: `single-tenant` or `multi-tenant` |
| `database` | [reference](#database) | Database connection settings |
| `paths` | [reference](#paths) | Path overrides for binaries and directories |
| `sandbox_defaults` | [reference](#sandbox_defaults) | Defaults applied to new sandboxes, subject to backend support |
| `runtime` | [reference](#runtime) | Host runtime performance policy |
| `registries` | [reference](#registries) | Container registry authentication |
| `ssh` | [reference](#ssh) | Host-side SSH session defaults |
| `metrics` | [reference](#metrics) | Live metrics shared-memory registry settings |
| `snapshots` | [reference](#snapshots) | Full snapshot capture and restore limits |
| `active_profile` | `null` | Default [backend profile](#profiles) name |
| `profiles` | [reference](#profiles) | Named [backend profiles](/operations/backends#profiles) |

## `deployment_profile`

<Tooltip tip="Local runtime placement only; microsandbox cloud applies its platform deployment profile."><span className="msb-badge-local">Local-only <Icon icon="circle-info" size={11} /></span></Tooltip>

Set `deployment_profile` when the local host must enforce one deployment policy for every sandbox:

<CodeGroup>
```json JSON
{
  "deployment_profile": "multi-tenant"
}
```
</CodeGroup>

`single-tenant` preserves the network configuration requested by each sandbox. `multi-tenant` applies the host-runtime isolation floor for shared infrastructure. The configured value is authoritative on create and restart, so a per-sandbox CLI or SDK option cannot weaken or replace it. A [programmatic backend override](/sdk/rust/configuration#deployment_profile) takes precedence over the user file. When the field is absent or `null`, each sandbox selects its own profile and defaults to `single-tenant`.

The parser also accepts the internal wire spellings `single_tenant` and `multi_tenant` for compatibility, but the kebab-case values above are canonical in this human-edited file.

## `database`

<Tooltip tip="Configures the local runtime database; microsandbox cloud stores state in the control plane."><span className="msb-badge-local">Local-only <Icon icon="circle-info" size={11} /></span></Tooltip>

| Field | Default | Description |
|-------|---------|-------------|
| `url` | `null` | Database URL. Uses SQLite under `home` when null |
| `max_connections` | `5` | Maximum connection pool size |
| `connect_timeout_secs` | `30` | Timeout when acquiring a database connection from the pool |
| `busy_timeout_secs` | `30` | SQLite busy timeout before a contended write surfaces as an error |

## `paths`

<Tooltip tip="Configures files used by the local runtime; these paths do not apply to microsandbox cloud."><span className="msb-badge-local">Local-only <Icon icon="circle-info" size={11} /></span></Tooltip>

All path fields are optional. Runtime pairs resolve from environment overrides, SDK-provided paths, explicit configuration, then the install under `home`. Other unset paths resolve relative to `home`; runtime resolution does not search `PATH` or debug build directories.

On Windows, the default `home` is `%USERPROFILE%\.microsandbox`. JSON strings can use escaped backslashes such as `"C:\\Users\\you\\.microsandbox\\lib\\libkrunfw.dll"` or forward slashes such as `"C:/Users/you/.microsandbox/lib/libkrunfw.dll"`.

| Field | Default | Description |
|-------|---------|-------------|
| `msb` | `{home}/bin/msb` | `msb` binary. Configure a matching `libkrunfw` path; when omitted, resolution can use a library adjacent to this executable. An incomplete pair fails closed. |
| `libkrunfw` | `{home}/lib/libkrunfw` | Matching libkrunfw shared library (`.so` on Linux, `.dylib` on macOS, `.dll` on Windows). Set this together with `msb`. |
| `agentd` | embedded when enabled | Linux guest Agentd executable. Normal release builds embed it in `msb`; custom builds without `embed-binaries` must provide this field or `MSB_AGENTD_PATH`. The environment variable takes precedence, and an explicitly configured missing or invalid file fails closed. |
| `cache` | `{home}/cache` | Image layer cache |
| `sandboxes` | `{home}/sandboxes` | Per-sandbox state |
| `volumes` | `{home}/volumes` | Named volumes |
| `snapshots` | `{home}/snapshots` | Snapshot artifacts |
| `logs` | `{home}/logs` | Sandbox logs |
| `secrets` | `{home}/secrets` | Secrets. Registry secrets live under `secrets/registries/` |

Runtime path settings are captured during local backend construction, with managed paths taking precedence. Existing backend handles keep their settings until replaced.

<span id="rust-sdk-path-helpers" />

For programmatic access, see the [Rust configuration reference](/sdk/rust/configuration#runtime-paths).

## `ssh`

| Field | Default | Description |
|-------|---------|-------------|
| `inactivity_timeout_secs` | `600` | Disconnect an SSH session after this many seconds without SSH traffic. Set to `0` to disable. |

The timeout applies to host-side SSH sessions created by `msb ssh`, `msb ssh serve`, and the SDK, for both local and cloud sandboxes. SSH traffic resets the timer. This setting is separate from a sandbox lifecycle idle timeout: disconnecting SSH does not stop the sandbox, and sandbox activity policy does not change the SSH timeout.

<Accordion title="SSH precedence and configuration lifetime">

Explicit CLI or SDK options override the user-configured value; a managed timeout takes precedence over both. The setting is resolved when a client or server endpoint is prepared using the backend's retained configuration, so file changes do not alter existing connections. Programmatic local backends can [override the persisted timeout](/sdk/rust/configuration#ssh_inactivity_timeout_secs).

</Accordion>

## `sandbox_defaults`

<Tooltip tip="Sandbox settings are applied to local and cloud create requests. Cloud rejects unsupported options such as outbound proxies and custom metrics sampling."><span className="msb-badge-limited">Limited on cloud <Icon icon="circle-info" size={11} /></span></Tooltip>

Defaults applied to sandboxes unless overridden per-sandbox.

For workload effects, host requirements, filesystem expectations, and verification steps for these settings, see [Performance](/sandboxes/optimization).

| Field | Default | Description |
|-------|---------|-------------|
| `cpus` | `1` | Number of vCPUs |
| `memory_mib` | `512` | Guest memory in MiB |
| `cpu_placement` | `"inherit"` | Host vCPU placement: `inherit`, `auto`, `spread`, or `compact` |
| `placement_profile` | `null` | Name of a host-defined entry in `runtime.placement_profiles` |
| `thp` | `"madvise"` | Guest transparent huge-page policy: `always`, `madvise`, or `never` |
| `oci` | [reference](#sandbox_defaults-oci) | Defaults for OCI-rooted sandboxes |
| `shell` | `"/bin/sh"` | Shell for interactive sessions and scripts |
| `workdir` | `null` | Working directory inside the sandbox |
| `outbound_proxy` | `null` | Default outbound SOCKS4/SOCKS5 proxy for local sandboxes. Uses the existing proxy object with `protocol`, `address` (`IP:port`), and optional authentication. See [managed proxy configuration](/enterprise/corporate-networking#route-sandbox-traffic). |
| `metrics_sample_interval_ms` | `1000` | Runtime metrics sampling interval in milliseconds. Set to `0` to disable sampling. |
| `disable_metrics_sample` | `false` | Force-disable metrics sampling regardless of `metrics_sample_interval_ms`. |

<Accordion title="Precedence, explicit clears, and backend differences">

Explicit CLI or SDK options win over `config.json`, and managed overrides take precedence over both. microsandbox resolves the effective values before creating a local sandbox or sending a cloud create request. Cloud creation rejects sandbox options it cannot represent. File changes apply after constructing a new backend or starting a new CLI invocation; they do not rewrite existing sandboxes.

Local creation resolves built-in defaults < image defaults < `config.json` < CLI/SDK patches < managed overrides after image metadata is available. Cloud creation applies user settings, request options, and managed overrides on the client; the cloud worker resolves image metadata.

For local `workdir`, omission inherits the lower layer and explicit `null` clears it, including an image working directory. Local CPU and memory maxima remain explicit when supplied; otherwise they follow the final size. Cloud supports no separate hotplug maximum: a supplied maximum equal to the requested size follows the final size. A maximum that differs from the final size is rejected.

</Accordion>

<Accordion title="Proxy credentials and managed overrides">

An ordinary `outbound_proxy` default can be overridden per sandbox. A managed value takes precedence over CLI/SDK proxy options; managed `null` clears them.

The whole proxy object is replaced, including credentials. SOCKS4 accepts an optional `user_id`; SOCKS5 accepts `credentials` containing `username` and `password: { "kind": "env", "var": "PROXY_PASSWORD" }`. Passwords are resolved at sandbox startup.

Proxy defaults do not enable disabled networking or route host downloads. Cloud creation fails if the final configuration contains a proxy, including one supplied by managed policy.

</Accordion>

### `sandbox_defaults.oci`

Defaults applied only when the sandbox rootfs is an OCI image.

User OCI defaults apply to local creation. Managed root-disk settings also apply to cloud create requests, which accept only `kind: "managed"`.

| Field | Default | Description |
|-------|---------|-------------|
| `root_disk` | `null` | Default OCI root disk using the same tagged shape as the SDK: `managed`, `tmpfs`, or `flat`. A missing value preserves the managed layered root. User-owned `disk-image` defaults are rejected because they would share one writable image across sandboxes. |
| `upper_size_mib` | `null` | Deprecated managed-layered size shorthand. When both this field and `root_disk` are absent, the resolved managed size is 4096 MiB. |

`root_disk` and `upper_size_mib` are mutually exclusive. For a portable flat default, use `clone: "auto"`; it attempts a native reflink and safely falls back to a sparse copy. Use `clone: "reflink"` only when failure is preferable to copying on a host filesystem without reflink support.

When the root-disk default is flat, plain `msb pull IMAGE` also prepares the reusable flat artifact. An explicit `msb pull IMAGE --materialize layered|flat|all` always wins.

## `runtime`

<Tooltip tip="Host runtime placement and block-device writeback settings do not apply to microsandbox cloud."><span className="msb-badge-local">Local-only <Icon icon="circle-info" size={11} /></span></Tooltip>

| Field | Default | Description |
|-------|---------|-------------|
| `block_writeback` | `{ "mode": "auto" }` | Buffered host writeback containment and live pressure sharing on Linux. Pool pressure never rejects sandbox creation; unconfigured `auto` is a no-op on other hosts. |
| `placement_profiles` | `{}` | Host-owned named CPU and NUMA placement profiles selectable by sandboxes |

### `runtime.placement_profiles`

Placement profiles let an operator define safe host topology policy once while callers select it by name. `numa.mode` is `prefer_single`, `strict_single`, or `inherit`; `memory.mode` is `follow_cpu` or `inherit`.

This release does not enable multi-node guest placement. `prefer_single` falls back to inherited host NUMA behavior when one node cannot fit. `strict_single` fails clearly because it is an explicit guarantee.

<Accordion title="Platform behavior and placement fallbacks">

`follow_cpu` requires [managed CPU placement](/sandboxes/optimization#policies); it cannot use inherited placement. Linux prefers the selected node for ordinary profiles and allows memory to spill elsewhere under pressure; `strict_single` uses a required binding instead. If CPUs span nodes, capacity is already insufficient, or the kernel rejects a best-effort affinity or memory-policy syscall, an ordinary profile starts with inherited memory placement.

Windows keeps ordinary memory inherited because its preferred-node allocation cannot be undone if a later vCPU affinity attempt falls back; `strict_single` can still request required preferred-node allocation. Windows checks current boot-time availability, but does not expose equivalent per-node total capacity for a hard future-growth promise.

macOS inherits ordinary CPU and memory scheduling for non-strict profiles because it has no equivalent hard-affinity API.

</Accordion>

### `runtime.block_writeback`

| Field | Modes | Default | Description |
|-------|-------|---------|-------------|
| `mode` | all | `"auto"` | `auto` gives each eligible disk a measured 1536 MiB maximum and shares the aggregate pool when the host is busy, `fixed` requires an explicit maximum, and `off` disables both the VMM bound and host-global pressure coordination. |
| `per_disk_mib` | `fixed` | required | Explicit maximum for each eligible writable raw disk. The boot-time maximum is at least 128 MiB; the live fair share may fall below it under aggregate pressure. This field is rejected in `auto` and `off` modes. |
| `pool_mib` | `auto`, `fixed` | `null` | Optional aggregate dirty-credit pressure-pool override. `null` derives the pool as the lower of 10% of physical RAM and a conservative estimate of Linux's dirty-background threshold, using the exact byte threshold when configured or the ratio applied to current `MemAvailable`. This field is rejected in `off` mode. |

Use a fixed policy only when representative measurements justify a different per-disk window:

<CodeGroup>
```json JSON
{
  "runtime": {
    "block_writeback": {
      "mode": "fixed",
      "per_disk_mib": 2048,
      "pool_mib": 12288
    }
  }
}
```
</CodeGroup>

<Accordion title="Pool sharing and live writeback behavior">

`pool_mib` is a live pressure budget, not eagerly allocated RAM. Every eligible writable disk in the same `MSB_HOME` receives a weighted max-min fair share. Disks with a smaller configured maximum keep that smaller value, and the remaining pool is divided equally across the rest. Creating another sandbox never fails merely because this pool is full.

Existing VMMs observe membership changes within 250 ms. If a disk already owns more dirty data than its new share, libkrun retires accounted ranges and pauses later writes until it converges below the target. A guest write already reserved before the target changed completes safely. `fixed` and an explicitly pooled `auto` policy are unsupported on other hosts.

</Accordion>

See the [performance guide](/sandboxes/optimization) for choosing and measuring runtime settings.

## `registries`

<Tooltip tip="These user and managed registry settings govern local image pulls. Cloud uses explicitly supplied credentials or credentials stored in the cloud."><span className="msb-badge-local">Local-only <Icon icon="circle-info" size={11} /></span></Tooltip>

| Field | Default | Description |
|-------|---------|-------------|
| `ca_certs` | `null` | Path to a PEM file with additional CA root certificates trusted for registry pulls |
| `hosts` | `{}` | Per-registry settings keyed by registry hostname |

### `registries.hosts`

A map of registry hostnames to settings. Each host entry can mark the registry as insecure (plain HTTP) and can include an `auth` entry. Each auth entry specifies a username and exactly **one** credential source.

<CodeGroup>
```json JSON
{
  "registries": {
    "hosts": {
      "ghcr.io": {
        "auth": {
          "username": "octocat",
          "store": "keyring"
        }
      },
      "localhost:5050": {
        "insecure": true,
        "auth": {
          "username": "dev",
          "password_env": "LOCAL_REGISTRY_TOKEN"
        }
      }
    }
  }
}
```
</CodeGroup>

#### Host entry fields

| Field | Required | Description |
|-------|----------|-------------|
| `insecure` | No | Use plain HTTP instead of HTTPS for this registry |
| `auth` | No | Authentication entry for this registry |

#### Auth entry fields

| Field | Required | Description |
|-------|----------|-------------|
| `username` | Yes | Registry username |
| `store` | No | Credential store. Only `"keyring"` is supported (macOS Keychain, Windows Credential Manager, Linux Secret Service) |
| `password_env` | No | Environment variable containing the password or token |
| `secret_name` | No | Filename under `{home}/secrets/registries/` containing the password or token |

<Note>
  Exactly one of `store`, `password_env`, or `secret_name` must be set per entry. Setting none or more than one is an error.
</Note>

### Auth resolution order

<Accordion title="Managed registry credentials and certificates">

Managed registry hosts merge by field. Setting `insecure: false` enforces TLS while preserving employee credentials. Omitted `auth` keeps the normal credential lookup and CLI/SDK credentials; explicit `auth: null` forces anonymous access. A managed `auth` object replaces credentials, including explicit CLI/SDK credentials. A managed `registries.ca_certs` path replaces user and per-call certificates; `null` clears them. Without a managed certificate setting, per-call certificates append to user certificates.

</Accordion>

For local pulls without managed `auth`, microsandbox resolves credentials in this order, including when a managed host entry only sets TLS options:

1. **Explicit SDK auth** via `.registry(|r| r.auth(...))` on the sandbox builder
2. **OS keyring** entries created by `msb registry login`
3. **Config file** `registries.hosts.<host>.auth` entries in `config.json`
4. **Docker config** `~/.docker/config.json` credential helpers
5. **Anonymous** (no authentication)

## `metrics`

<Tooltip tip="Host-side SDK and CLI resource metrics are not available on microsandbox cloud."><span className="msb-badge-local">Local-only <Icon icon="circle-info" size={11} /></span></Tooltip>

| Field | Default | Description |
|-------|---------|-------------|
| `capacity` | `0` | Number of slots reserved in the live metrics shared-memory registry. `0` uses the built-in default. Stop all sandboxes for the same home before changing this value. |

## `snapshots`

| Field | Default | Description |
|-------|---------|-------------|
| `max_filesystem_state_mib` | `4` | Largest virtio-fs backend state, in MiB, that one filesystem device can carry through a full snapshot. Valid values are `1` through `4095`. |

This limit applies per filesystem device during capture, verification, export, import, restore, and live forks.

After changing it, restart running sandboxes to update their capture limit. Host snapshot checks use the new limit immediately, so lowering it can reject captures from sandboxes still using a larger limit.

When reading snapshots, allow enough space for each device's saved state. The reader does not need to match the capture host's maximum. A smaller limit rejects oversized state before making changes.

Before raising it, consider memory use, especially when restoring imported snapshots. Raise it only for snapshots you trust.

## `profiles`

Named backend profiles keyed by profile name; `active_profile` selects the default. How profiles participate in local or cloud selection, including the full precedence order, is documented in [Local or cloud](/operations/backends).

| Field | Default | Description |
|-------|---------|-------------|
| `backend` | required | `local` or `cloud` |
| `api_key_ref` | none | Credential reference; required for `cloud` profiles |
| `url` | `https://api.microsandbox.dev` | Cloud endpoint override for development, self-hosted, or on-prem control planes |

Supported credential references for `api_key_ref`:

| Prefix | Description |
|--------|-------------|
| `env:<VAR_NAME>` | Read the API key from an environment variable |
| `inline:<API_KEY>` | Store the API key directly in `config.json`; use only for development or CI |

---

### Docs.Json (`docs/docs.json`)

{
  "$schema": "https://mintlify.com/docs.json",
  "theme": "maple",
  "name": "microsandbox",
  "logo": {
    "light": "/images/microsandbox-banner-light.svg",
    "dark": "/images/microsandbox-banner-dark.svg",
    "href": "https://microsandbox.dev"
  },
  "favicon": "/logo/favicon.png",
  "colors": {
    "primary": "#BF84FE",
    "light": "#BF84FE",
    "dark": "#BF84FE"
  },
  "thumbnails": {
    "appearance": "dark",
    "background": "/images/og-background.png"
  },
  "navbar": {
    "primary": {
      "type": "github",
      "href": "https://github.com/superradcompany/microsandbox"
    }
  },
  "navigation": {
    "tabs": [
      {
        "tab": "Documentation",
        "groups": [
          {
            "group": "Getting Started",
            "icon": "rocket",
            "pages": [
              "getting-started/introduction",
              "getting-started/quickstart",
              "cloud/overview",
              "getting-started/agents"
            ]
          },
          {
            "group": "Sandboxes",
            "icon": "box",
            "pages": [
              "sandboxes/overview",
              "sandboxes/lifecycle",
              "sandboxes/commands",
              "sandboxes/filesystem",
              "sandboxes/secrets",
              "images/overview",
              "sandboxes/volumes",
              "sandboxes/ssh",
              "sandboxes/snapshots",
              "sandboxes/tuning",
              "sandboxes/labels",
              "sandboxes/logs",
              "sandboxes/metrics",
              "sandboxes/bootstrap"
            ]
          },
          {
            "group": "Networking",
            "icon": "network-wired",
            "pages": [
              "networking/overview",
              "networking/tls",
              "networking/dns",
              "networking/outbound-proxy",
              "networking/host-sockets"
            ]
          },
          {
            "group": "Deep dive",
            "icon": "gauge",
            "pages": [
              "operations/backends",
              "operations/configuration",
              "observability/msb-metrics",
              "sandboxes/optimization"
            ]
          },
          {
            "group": "Troubleshooting",
            "icon": "screwdriver-wrench",
            "pages": [
              "troubleshooting/linux",
              "troubleshooting/macos",
              "troubleshooting/windows"
            ]
          }
        ]
      },
      {
        "tab": "SDKs & CLI",
        "groups": [
          {
            "group": "Getting Started",
            "icon": "flag",
            "pages": [
              "sdk/overview",
              "sdk/setup",
              "sdk/errors",
              "configuration"
            ]
          },
          {
            "group": "SDKs",
            "icon": "code",
            "pages": [
              {
                "group": "TypeScript",
                "icon": "js",
                "expanded": false,
                "pages": [
                  "sdk/typescript/sandbox",
                  "sdk/typescript/execution",
                  "sdk/typescript/ssh",
                  "sdk/typescript/filesystem",
                  "sdk/typescript/volumes",
                  "sdk/typescript/networking",
                  "sdk/typescript/proxies",
                  "sdk/typescript/vsock",
                  "sdk/typescript/secrets",
                  "sdk/typescript/snapshots",
                  "sdk/typescript/images",
                  "sdk/typescript/agent-client"
                ]
              },
              {
                "group": "Rust",
                "icon": "rust",
                "expanded": false,
                "pages": [
                  "sdk/rust/sandbox",
                  "sdk/rust/configuration",
                  "sdk/rust/execution",
                  "sdk/rust/ssh",
                  "sdk/rust/filesystem",
                  "sdk/rust/volumes",
                  "sdk/rust/networking",
                  "sdk/rust/proxies",
                  "sdk/rust/vsock",
                  "sdk/rust/secrets",
                  "sdk/rust/snapshots",
                  "sdk/rust/images",
                  "sdk/rust/agent-client"
                ]
              },
              {
                "group": "Python",
                "icon": "python",
                "expanded": false,
                "pages": [
                  "sdk/python/sandbox",
                  "sdk/python/execution",
                  "sdk/python/ssh",
                  "sdk/python/filesystem",
                  "sdk/python/volumes",
                  "sdk/python/networking",
                  "sdk/python/proxies",
                  "sdk/python/vsock",
                  "sdk/python/secrets",
                  "sdk/python/snapshots",
                  "sdk/python/images",
                  "sdk/python/agent-client"
                ]
              },
              {
                "group": "Go",
                "icon": "golang",
                "expanded": false,
                "pages": [
                  "sdk/go/sandbox",
                  "sdk/go/execution",
                  "sdk/go/ssh",
                  "sdk/go/filesystem",
                  "sdk/go/volumes",
                  "sdk/go/images",
                  "sdk/go/networking",
                  "sdk/go/proxies",
                  "sdk/go/vsock",
                  "sdk/go/secrets",
                  "sdk/go/snapshots",
                  "sdk/go/agent-client"
                ]
              },
              {
                "group": "Ruby",
                "icon": "gem",
                "expanded": false,
                "pages": [
                  "sdk/ruby/sandbox",
                  "sdk/ruby/configuration",
                  "sdk/ruby/execution",
                  "sdk/ruby/ssh",
                  "sdk/ruby/filesystem",
                  "sdk/ruby/volumes",
                  "sdk/ruby/networking",
                  "sdk/ruby/vsock",
                  "sdk/ruby/snapshots",
                  "sdk/ruby/images"
                ]
              }
            ]
          },
          {
            "group": "CLI",
            "icon": "terminal",
            "pages": [
              "cli/overview",
              "cli/configuration",
              "cli/sandbox-commands",
              "cli/snapshot-commands",
              "cli/ssh-commands",
              "cli/volume-commands",
              "cli/storage-commands",
              "cli/image-commands",
              "cli/registry-commands",
              "cli/management"
            ]
          }
        ]
      },
      {
        "tab": "API Reference",
        "groups": [
          {
            "group": "Getting Started",
            "icon": "book-open",
            "pages": [
              "api-reference/overview"
            ]
          },
          {
            "group": "API keys",
            "icon": "key",
            "openapi": {
              "source": "/api-reference/openapi.json",
              "directory": "api-reference/endpoints"
            }
          },
          {
            "group": "Personal tokens",
            "icon": "user",
            "openapi": {
              "source": "/api-reference/openapi.personal.json",
              "directory": "api-reference/personal"
            }
          }
        ]
      },
      {
        "tab": "How-to guides",
        "groups": [
          {
            "group": "How-to guides",
            "pages": [
              "how-to/overview",
              {
                "group": "Agents",
                "expanded": false,
                "icon": "robot",
                "pages": [
                  "how-to/agents/claude-code",
                  "how-to/agents/codex",
                  "how-to/agents/opencode",
                  "how-to/agents/openclaw",
                  "how-to/agents/goose",
                  "how-to/agents/gemini-cli",
                  "how-to/agents/hermes-agent",
                  "how-to/agents/pi"
                ]
              },
              {
                "group": "Development",
                "expanded": false,
                "icon": "code",
                "pages": [
                  "how-to/development/vscode",
                  "how-to/development/jetbrains-acp",
                  "how-to/development/code-server",
                  "how-to/development/jupyterlab",
                  "how-to/development/vnc-desktop"
                ]
              },
              {
                "group": "Browser automation",
                "expanded": false,
                "icon": "globe",
                "pages": [
                  "how-to/browser-automation/browser-use",
                  "how-to/browser-automation/playwright"
                ]
              },
              {
                "group": "Workloads",
                "expanded": false,
                "icon": "gears",
                "pages": [
                  "how-to/file-processing/ffmpeg",
                  "how-to/file-processing/libreoffice-pdf",
                  "how-to/plugins/terraform",
                  "how-to/data/migration-rehearsal",
                  "how-to/web-automation/scrapy",
                  "how-to/automation/parallel-batch-jobs"
                ]
              },
              {
                "group": "Sandbox setup",
                "expanded": false,
                "icon": "box",
                "pages": [
                  "how-to/docker/docker-in-sandbox",
                  "how-to/docker/local-images",
                  "how-to/guest/systemd-services",
                  "how-to/sandboxing/warm-workers"
                ]
              },
              {
                "group": "Deployment",
                "expanded": false,
                "icon": "server",
                "pages": [
                  "how-to/docker/docker",
                  "how-to/sandboxing/kubernetes"
                ]
              },
              {
                "group": "Storage",
                "expanded": false,
                "icon": "database",
                "pages": [
                  "how-to/data/postgresql",
                  "how-to/data/redis",
                  "how-to/data/juicefs-s3"
                ]
              },
              {
                "group": "CI/CD",
                "expanded": false,
                "icon": "code-pull-request",
                "pages": [
                  "how-to/ci-cd/github-actions-runner",
                  "how-to/ci-cd/pr-checks",
                  "how-to/ci-cd/compose-tests",
                  "how-to/ci-cd/preview-deploys",
                  "how-to/automation/dependency-audits"
                ]
              },
              {
                "group": "Observability",
                "expanded": false,
                "icon": "chart-line",
                "pages": [
                  "how-to/metrics-backends/grafana-cloud",
                  "how-to/metrics-backends/grafana-alloy",
                  "how-to/metrics-backends/otel-collector",
                  "how-to/metrics-backends/prometheus",
                  "how-to/metrics-backends/datadog"
                ]
              }
            ]
          }
        ]
      },
      {
        "tab": "Enterprise",
        "groups": [
          {
            "group": "Enterprise",
            "pages": ["enterprise"]
          },
          {
            "group": "Employee devices",
            "pages": [
              "enterprise/prepare-environment",
              "enterprise/corporate-networking",
              "enterprise/managed-configuration",
              "enterprise/deploy-and-verify"
            ]
          },
          {
            "group": "Talk to us",
            "pages": ["enterprise/contact"]
          }
        ]
      },
      {
        "tab": "Security",
        "pages": [
          "security/overview",
          "security/isolation",
          "security/filesystem",
          "security/network",
          "security/secrets",
          "security/hardening"
        ]
      },
      {
        "tab": "Changelog",
        "groups": [
          {
            "group": "Migration guides",
            "icon": "arrow-right-arrow-left",
            "pages": [
              "migrations/v0.7"
            ]
          },
          {
            "group": "Releases",
            "icon": "tag",
            "pages": [
              "changelog/v0.7.8",
              "changelog/v0.7.7",
              "changelog/v0.7.6",
              "changelog/v0.7.5",
              "changelog/v0.7.4",
              "changelog/v0.7.3",
              "changelog/v0.7.2",
              "changelog/v0.7.1",
              "changelog/v0.7.0"
            ]
          },
          {
            "group": "Past updates",
            "icon": "clock-rotate-left",
            "pages": [
              {
                "group": "September 2026",
                "expanded": false,
                "pages": [
                  "changelog/2026-09-25",
                  "changelog/2026-09-11",
                  "changelog/2026-09-04"
                ]
              },
              {
                "group": "August 2026",
                "expanded": false,
                "pages": [
                  "changelog/2026-08-28",
                  "changelog/2026-08-26",
                  "changelog/2026-08-21",
                  "changelog/2026-08-14",
                  "changelog/2026-08-07"
                ]
              },
              {
                "group": "July 2026",
                "expanded": false,
                "pages": [
                  "changelog/2026-07-31",
                  "changelog/2026-07-24",
                  "changelog/2026-07-17",
                  "changelog/2026-07-10",
                  "changelog/2026-07-05"
                ]
              },
              {
                "group": "June 2026",
                "expanded": false,
                "pages": [
                  "changelog/2026-06-26",
                  "changelog/2026-06-19",
                  "changelog/2026-06-12",
                  "changelog/2026-06-05"
                ]
              },
              {
                "group": "May 2026",
                "expanded": false,
                "pages": [
                  "changelog/2026-05-29",
                  "changelog/2026-05-22",
                  "changelog/2026-05-15",
                  "changelog/2026-05-01"
                ]
              },
              {
                "group": "April 2026",
                "expanded": false,
                "pages": [
                  "changelog/2026-04-24",
                  "changelog/2026-04-17",
                  "changelog/2026-04-10",
                  "changelog/2026-04-03"
                ]
              }
            ],
            "expanded": false
          }
        ]
      }
    ]
  },
  "redirects": [
    {
      "source": "/networking/security-model",
      "destination": "/security/network"
    },
    {
      "source": "/getting-started/windows-troubleshooting",
      "destination": "/troubleshooting/windows"
    },
    {
      "source": "/examples/kubernetes",
      "destination": "/how-to/sandboxing/kubernetes"
    },
    {
      "source": "/examples/automation/parallel-batch-jobs",
      "destination": "/how-to/automation/parallel-batch-jobs"
    },
    {
      "source": "/examples/browser-automation/browser-use",
      "destination": "/how-to/browser-automation/browser-use"
    },
    {
      "source": "/examples/web-automation/scrapy",
      "destination": "/how-to/web-automation/scrapy"
    },
    {
      "source": "/examples/overview",
      "destination": "/how-to/overview"
    },
    {
      "source": "/getting-started/backends",
      "destination": "/operations/backends"
    },
    {
      "source": "/operations/backend-selection",
      "destination": "/operations/backends"
    },
    {
      "source": "/observability/deep-dive",
      "destination": "/observability/msb-metrics#metrics"
    },
    {
      "source": "/optimization/overview",
      "destination": "/sandboxes/optimization"
    },
    {
      "source": "/sandboxes/cpu-placement",
      "destination": "/sandboxes/optimization#cpu"
    },
    {
      "source": "/images/disk-images",
      "destination": "/images/overview#disk-images"
    },
    {
      "source": "/networking/vsock",
      "destination": "/networking/host-sockets"
    },
    {
      "source": "/sandbox-config",
      "destination": "/cli/configuration"
    },
    {
      "source": "/recipes/use-cases/ai-coding-agent",
      "destination": "/getting-started/agents"
    },
    {
      "source": "/examples/use-cases/ai-coding-agent",
      "destination": "/getting-started/agents"
    },
    {
      "source": "/recipes",
      "destination": "/how-to/overview"
    },
    {
      "source": "/examples/data/juicefs-r2",
      "destination": "/how-to/data/juicefs-s3"
    },
    {
      "source": "/cloud/compatibility",
      "destination": "/cloud/overview#compatibility"
    },
    {
      "source": "/enterprise/offline-deployment",
      "destination": "/enterprise/prepare-environment#prepare-offline-devices"
    },
    {
      "source": "/recipes/overview",
      "destination": "/how-to/overview"
    },
    {
      "source": "/examples/agents/claude-code",
      "destination": "/how-to/agents/claude-code"
    },
    {
      "source": "/examples/agents/codex",
      "destination": "/how-to/agents/codex"
    },
    {
      "source": "/examples/agents/opencode",
      "destination": "/how-to/agents/opencode"
    },
    {
      "source": "/examples/agents/openclaw",
      "destination": "/how-to/agents/openclaw"
    },
    {
      "source": "/examples/agents/goose",
      "destination": "/how-to/agents/goose"
    },
    {
      "source": "/examples/agents/gemini-cli",
      "destination": "/how-to/agents/gemini-cli"
    },
    {
      "source": "/examples/agents/hermes-agent",
      "destination": "/how-to/agents/hermes-agent"
    },
    {
      "source": "/examples/agents/pi",
      "destination": "/how-to/agents/pi"
    },
    {
      "source": "/examples/development/vscode",
      "destination": "/how-to/development/vscode"
    },
    {
      "source": "/examples/development/jetbrains-acp",
      "destination": "/how-to/development/jetbrains-acp"
    },
    {
      "source": "/examples/development/code-server",
      "destination": "/how-to/development/code-server"
    },
    {
      "source": "/examples/development/jupyterlab",
      "destination": "/how-to/development/jupyterlab"
    },
    {
      "source": "/examples/development/vnc-desktop",
      "destination": "/how-to/development/vnc-desktop"
    },
    {
      "source": "/examples/ci-cd/github-actions-runner",
      "destination": "/how-to/ci-cd/github-actions-runner"
    },
    {
      "source": "/examples/ci-cd/pr-checks",
      "destination": "/how-to/ci-cd/pr-checks"
    },
    {
      "source": "/examples/ci-cd/compose-tests",
      "destination": "/how-to/ci-cd/compose-tests"
    },
    {
      "source": "/examples/ci-cd/preview-deploys",
      "destination": "/how-to/ci-cd/preview-deploys"
    },
    {
      "source": "/examples/automation/dependency-audits",
      "destination": "/how-to/automation/dependency-audits"
    },
    {
      "source": "/examples/sandboxing/warm-workers",
      "destination": "/how-to/sandboxing/warm-workers"
    },
    {
      "source": "/examples/browser-automation/playwright",
      "destination": "/how-to/browser-automation/playwright"
    },
    {
      "source": "/examples/file-processing/libreoffice-pdf",
      "destination": "/how-to/file-processing/libreoffice-pdf"
    },
    {
      "source": "/examples/file-processing/ffmpeg",
      "destination": "/how-to/file-processing/ffmpeg"
    },
    {
      "source": "/examples/docker/docker-in-sandbox",
      "destination": "/how-to/docker/docker-in-sandbox"
    },
    {
      "source": "/examples/docker/docker",
      "destination": "/how-to/docker/docker"
    },
    {
      "source": "/examples/docker/local-images",
      "destination": "/how-to/docker/local-images"
    },
    {
      "source": "/examples/guest/systemd-services",
      "destination": "/how-to/guest/systemd-services"
    },
    {
      "source": "/examples/plugins/terraform",
      "destination": "/how-to/plugins/terraform"
    },
    {
      "source": "/examples/data/juicefs-s3",
      "destination": "/how-to/data/juicefs-s3"
    },
    {
      "source": "/examples/data/postgresql",
      "destination": "/how-to/data/postgresql"
    },
    {
      "source": "/examples/data/migration-rehearsal",
      "destination": "/how-to/data/migration-rehearsal"
    },
    {
      "source": "/examples/data/redis",
      "destination": "/how-to/data/redis"
    },
    {
      "source": "/examples/metrics-backends/grafana-cloud",
      "destination": "/how-to/metrics-backends/grafana-cloud"
    },
    {
      "source": "/examples/metrics-backends/grafana-alloy",
      "destination": "/how-to/metrics-backends/grafana-alloy"
    },
    {
      "source": "/examples/metrics-backends/otel-collector",
      "destination": "/how-to/metrics-backends/otel-collector"
    },
    {
      "source": "/examples/metrics-backends/prometheus",
      "destination": "/how-to/metrics-backends/prometheus"
    },
    {
      "source": "/examples/metrics-backends/datadog",
      "destination": "/how-to/metrics-backends/datadog"
    },
    {
      "source": "/recipes/:slug*",
      "destination": "/examples/:slug*"
    }
  ],
  "integrations": {
    "posthog": {
      "apiKey": "phc_mMJPTEQbarHl4JMTdFBcgw5v612dmQrvKv5mcK3KJ3O",
      "apiHost": "https://g.microsandbox.dev"
    }
  },
  "appearance": {
    "default": "system"
  },
  "description": "Easy, fast microVMs for untrusted workloads, on your machine or in the cloud",
  "seo": {
    "organization": {
      "id": "https://microsandbox.dev/#organization",
      "name": "microsandbox",
      "url": "https://microsandbox.dev",
      "sameAs": [
        "https://github.com/superradcompany/microsandbox",
        "https://www.linkedin.com/company/superradcompany",
        "https://x.com/microsandbox"
      ]
    }
  },
  "footer": {
    "socials": {
      "github": "https://github.com/superradcompany/microsandbox",
      "discord": "https://discord.gg/T95Y3XnEAK",
      "linkedin": "https://www.linkedin.com/company/superradcompany",
      "x": "https://x.com/microsandbox"
    }
  }
}

---

### Enterprise (`docs/enterprise.mdx`)

---
title: Enterprise deployment
sidebarTitle: Overview
description: Choose a deployment approach for employee devices or your own infrastructure
icon: "building"
---

Use microsandbox on employee devices or in your own infrastructure. Choose the deployment approach that fits your workloads and operational requirements.

## Choose your deployment

| Deployment | Purpose | Next step |
| --- | --- | --- |
| Employee devices | Standardize local development and team settings | [Rollout guides](#roll-out-to-employee-devices) |
| Runtime on your infrastructure | Run sandboxes on your servers or clusters | [Discuss your deployment](/enterprise/contact) |
| Private cloud deployment | Run the microsandbox cloud platform in your infrastructure | [Discuss requirements](/enterprise/contact) |

For our hosted service, see [microsandbox Cloud](/cloud/overview).

## Roll out to employee devices

Start with one working installation, connect it to company services, then apply policy and roll it out to a pilot group.

| Step | Outcome |
| --- | --- |
| [Prepare your environment](/enterprise/prepare-environment) | Approved SDKs, runtime files, and images work on the target devices, including offline installations. |
| [Configure company networking](/enterprise/corporate-networking) | Sandboxes can reach approved services through company routing, DNS, and certificate trust. |
| [Enforce team settings](/enterprise/managed-configuration) | A protected policy defines which supported settings employees cannot override. |
| [Deploy and verify](/enterprise/deploy-and-verify) | A tested pilot, repeatable device deployment, and an update and rollback process. |

### Deployment responsibilities

| microsandbox provides | Your organization manages |
| --- | --- |
| Local sandbox isolation and network controls | Device security, host permissions, and approved workloads |
| Managed overrides for supported CLI and SDK settings | Policy distribution, file protection, and supported client versions |
| Runtime and image installation tools | Package approval, distribution, and updates |
| Configuration for company network access | VPN/proxy enrollment, credentials, DNS, and corporate certificates |

Managed settings apply through supported CLI and SDK clients. They are not a replacement for device security: local administrators can remove policy or replace the runtime.

These guides focus on local sandboxes on company-managed devices. Some managed settings also apply to outgoing cloud requests, as described in [Enforce team settings](/enterprise/managed-configuration). They do not reconfigure the cloud service.

## Discuss your deployment

Planning a runtime rollout or private cloud deployment? [Contact our team](/enterprise/contact) with a short description of your workloads and deployment needs.

---

### Snapshot Groups Explained (`docs/snapshot-groups-explained.md`)

# Microsandbox snapshots: groups, checkpoints, and the head

This describes the snapshot-group implementation on the development stack, not an already released CLI.

## Start with the snapshot

A snapshot is a saved point you can use to create another sandbox:

```text
Running sandbox
      |
      +-- disk snapshot ----> new VM boots from the saved disk
      |
      +-- full snapshot ----> new VM resumes saved RAM, CPUs, devices, and disk
```

Disk snapshots also work when the source is paused or stopped. Full snapshots require resident execution state: a running or user-paused VM. Each capture produces a new immutable snapshot, even when unchanged disk layers or RAM objects are reused. Exporting a snapshot packages it as a `.msb` archive; loading an archive installs it without starting a VM.

## A group gives those saved points a local home

A **group** is a namespace containing snapshots and a selected **head**. Member names such as `cp01` are meaningful inside their group. Each snapshot also keeps its portable `snap_...` ID. A new group imported from competing branches can temporarily have no selected head; choose one explicitly before restoring by the bare group name.

```text
~/.microsandbox/snapshots/
|
+-- worker/
|   +-- group.json                 head = snap_B
|   +-- snap_A/
|   |   +-- snapshot.json          ID, parent, disk/state references
|   |   +-- group-member.json      name = cp01
|   |   +-- layers/...             disk-only payload
|   +-- snap_B/
|       +-- snapshot.json          parent = snap_A
|       +-- group-member.json      name = cp02
|       +-- checkpoint/...         full checkpoint payload, when captured full
|
+-- imported/
    +-- group.json
    +-- snap_A/...                 a separate local copy of the same snapshot
```

IDs above are shortened for readability. A disk-only member uses `layers/`; a full member uses `checkpoint/` with its disk layers, RAM objects, and execution/device state. Optional `metadata.json` stores labels.

Groups do not magically make random IDs collision-proof. They keep local addresses separate. Within one group, the same ID with the same descriptor is reusable; the same ID with different descriptor bytes is rejected. A name already used by another member is also rejected. Nothing is silently overwritten. If a global ID resolves to multiple local copies, use the group-qualified address instead.

## Create and restore

```bash
msb create alpine --name worker --memory 512M

# Group defaults to the source sandbox's name: worker.
msb snap create cp01 --sandbox worker --full
msb snap create cp02 --sandbox worker --full

# A bare group selects its head, currently cp02.
msb snap restore worker --name latest --cow-mem

# A qualified name selects an exact checkpoint.
msb snap restore worker:cp01 --name earlier --cow-mem

# You can choose a different group, or let a member name be generated.
msb snap create --sandbox worker --group experiments --full
```

`--cow-mem` shares clean restored RAM pages using copy-on-write; child writes remain private. It does not change which snapshot is selected. Omit `--full` at capture for a disk-only snapshot, and omit `--cow-mem` when cold-booting disk state.

## The head moves forward, not sideways by surprise

Snapshots record their actual source ancestry. Neither timestamps, import order, nor an export's `--since` base defines that ancestry.

```text
worker:cp01 ---- worker:cp02 ---- worker:cp03  <- head
                     \
                      +-------- worker:experiment
```

The rules are small:

- Empty group: a single capture or import initializes its head. A batch selects its one provably newest tip, if there is one.
- Known descendant of the current head: advance automatically.
- Same member, older member, sibling, unrelated history, or missing ancestry: keep the current head. The capture/import still succeeds.
- Explicit selection: choose any complete installed member, including an older one.

```bash
msb snap head worker             # Read the current head ID
msb snap head worker:experiment  # Explicitly choose the other branch
msb snap head worker:cp01        # Explicitly rewind
```

There is no special `main` branch. The head is a selected snapshot, not a rule for guessing which future branch is preferred.

### What if two separate operations publish siblings concurrently?

```text
                    +---- snapshot A
head: cp02 ---------+
                    +---- snapshot B

A publishes first:  head cp02 -> A
B publishes next:   B is A's sibling, so head stays A

Result: both snapshots exist. Only the first head update wins.
```

Publication checks and head replacement share a per-group lock. The losing sibling is not discarded or reported as a failed capture. If you want B, select it explicitly. Two captures of the *same* source are serialized and record a parent chain; they are not treated as sibling captures.

A **single batch containing both siblings** is different: neither argument order nor which file finishes first chooses the head. An existing group retains its head; a new group imports both members with no selected head. Then use `msb snap head worker:<member>` to choose.

## Move a history to another machine

```bash
# On the source machine:
mkdir -p checkpoints
msb snap export worker:cp01 --output checkpoints/cp01.msb
msb snap export worker:cp02 --output checkpoints/cp02.msb --since worker:cp01

# On the destination machine:
msb snap import checkpoints/*.msb --group received
msb snap restore received --name restored --cow-mem
```

The shell expands `*.msb` into archive paths. Their order and filenames do not determine ancestry or load order. You can also list them explicitly, in any order:

```bash
msb snap import checkpoints/cp02.msb checkpoints/cp01.msb --group received
```

Loading unpacks each supplied archive once, matches omitted disk layers and RAM objects to the available payloads, and validates the reconstructed snapshots before publishing members. It looks in the supplied batch first, then the explicitly selected destination group. This works for disk-only and full incremental archives. No intermediate VM runs.

The same automatic lookup works when archives arrive separately:

```bash
msb snap import checkpoints/cp01.msb --group received
msb snap import checkpoints/cp02.msb --group received
```

`--base` is only needed when the missing data is elsewhere, such as `--base another-group:cp01` or `--base /path/to/baseline.msb`. It supplies data; it does not define ancestry or select the group head. An external archive supplied as `--base` must be standalone; include dependent archives in the batch instead. Missing dependencies and conflicting IDs, names, or duplicate labels fail before publishing any incoming members.

Current development limitation: disk-only captures reassign layer IDs, so `--since` between successive disk-only captures can reject the base. Use standalone disk-only exports for that workflow until capture identity preservation is fixed. Full-checkpoint incremental imports were live-tested successfully; the batch loader also handles dependency-correct disk-only archives.

`--since` omits disk layers and reusable RAM objects supplied by the explicit base. Loading reconstructs a complete owned snapshot; the target does not depend on replaying earlier VMs. Each archive still includes the target's complete memory map and CPU/device state. The `.msb` archive does not contain a local group's mutable head file: its declared archive head is the import candidate, and the receiving group applies the rules above.

Loading without `--group` creates one fresh generated group for the whole batch. The CLI prints a digest and installed artifact **path** for each input archive head, in input order. With one archive, the final line remains its installed path. With several archives, the final path is not necessarily the selected group head; use the group selector or `msb snap head received` instead. Repeating the same snapshot installs it only once.

The destination directory is now an explicit `--dest DIR` option, leaving positional arguments for archive paths. Existing snapshot/archive formats are unchanged, including legacy readers.

Importing an old checkpoint does not rewind an existing group. To deliberately select the imported archive's head:

```bash
msb snap import checkpoints/cp01.msb --group received --set-head
```

For a batch, `--set-head` requires one unambiguous tip; it refuses competing tips rather than picking the last argument. Load those members without `--set-head`, then select the one you want.

Missing historical checkpoints are okay when payload dependencies are complete. But a missing parent may prevent proving a fast-forward. Filling a history hole does not retrospectively select some other retained tip; select that tip explicitly or import it again once its ancestry is known.

Direct archive capture (`msb snap create --output PATH`, or `-o PATH`) and direct archive restore still skip installed snapshot directories. `msb fork` still creates a local child without publishing a durable snapshot. Neither operation implicitly moves a group's head; a later explicit capture can join a group using the child's recorded ancestry.

---

### Api Reference Openapi.Json (`docs/api-reference/openapi.json`)

{
  "openapi": "3.1.0",
  "info": {
    "title": "microsandbox cloud API",
    "description": "REST API for microsandbox cloud operations authenticated with an organization API key.",
    "version": "0.1.0"
  },
  "servers": [
    {
      "url": "https://api.microsandbox.dev"
    }
  ],
  "security": [
    {
      "api_key": []
    }
  ],
  "paths": {
    "/v1/sandboxes": {
      "get": {
        "tags": [
          "Sandboxes"
        ],
        "summary": "List sandboxes",
        "description": "List the organization's sandboxes with cursor pagination and optional label filtering.",
        "operationId": "list_sandboxes",
        "parameters": [
          {
            "name": "cursor",
            "in": "query",
            "description": "Pagination cursor",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Page size (default 20, max 100)",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "labels",
            "in": "query",
            "description": "JSON object of labels to AND-match",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of sandboxes",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedSandboxResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      },
      "post": {
        "tags": [
          "Sandboxes"
        ],
        "summary": "Create a sandbox",
        "description": "Create a sandbox, optionally start it immediately, and optionally wait until it is ready.",
        "operationId": "create_sandbox",
        "parameters": [
          {
            "name": "start",
            "in": "query",
            "description": "If `true`, durably queue the sandbox for immediate orchestrator dispatch.",
            "required": false,
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "wait_for",
            "in": "query",
            "description": "Optional lifecycle state to wait for after durable start admission.",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "null"
                },
                {
                  "$ref": "#/components/schemas/SandboxWaitFor"
                }
              ]
            }
          },
          {
            "name": "wait_timeout",
            "in": "query",
            "description": "Server-side wait budget in seconds; valid only with `wait_for`.",
            "required": false,
            "schema": {
              "type": [
                "integer",
                "null"
              ],
              "format": "int64",
              "minimum": 0
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateSandboxRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Sandbox created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SandboxResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "invalid request",
                    "details": null
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          },
          "409": {
            "description": "Name already exists",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "name_already_exists",
                    "message": "'name' already exists",
                    "details": null
                  }
                }
              }
            }
          },
          "429": {
            "description": "Readiness wait capacity exhausted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "too many requests",
                    "details": null
                  }
                }
              }
            }
          },
          "502": {
            "description": "Orchestrator unreachable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "orchestrator_unreachable",
                    "message": "orchestrator unreachable",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/v1/sandboxes/by-name/{name}": {
      "get": {
        "tags": [
          "Sandboxes"
        ],
        "summary": "Get a sandbox by name",
        "description": "Retrieve one sandbox by its organization-scoped name.",
        "operationId": "get_sandbox_by_name",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "description": "Sandbox name (unique within the org)",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Sandbox details",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SandboxResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          },
          "404": {
            "description": "Sandbox not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "sandbox_not_found",
                    "message": "resource not found",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      },
      "delete": {
        "tags": [
          "Sandboxes"
        ],
        "summary": "Delete a sandbox by name",
        "description": "Delete a sandbox selected by its organization-scoped name. Resource cleanup continues asynchronously.",
        "operationId": "delete_sandbox_by_name",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "description": "Sandbox name (unique within the org)",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Sandbox scheduled for deletion",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          },
          "404": {
            "description": "Sandbox not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "sandbox_not_found",
                    "message": "resource not found",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/v1/sandboxes/by-name/{name}/start": {
      "post": {
        "tags": [
          "Sandboxes"
        ],
        "summary": "Start a sandbox by name",
        "description": "Start a sandbox selected by its organization-scoped name.",
        "operationId": "start_by_name",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "description": "Sandbox name (unique within the org)",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "wait_for",
            "in": "query",
            "description": "Optional lifecycle state to wait for after durable start admission.",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "null"
                },
                {
                  "$ref": "#/components/schemas/SandboxWaitFor"
                }
              ]
            }
          },
          {
            "name": "wait_timeout",
            "in": "query",
            "description": "Server-side wait budget in seconds; valid only with `wait_for`.",
            "required": false,
            "schema": {
              "type": [
                "integer",
                "null"
              ],
              "format": "int64",
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Sandbox start admitted or requested wait completed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SandboxResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          },
          "404": {
            "description": "Sandbox not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "sandbox_not_found",
                    "message": "resource not found",
                    "details": null
                  }
                }
              }
            }
          },
          "409": {
            "description": "Invalid state transition",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_state_transition",
                    "message": "invalid state transition",
                    "details": null
                  }
                }
              }
            }
          },
          "429": {
            "description": "Readiness wait capacity exhausted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "too many requests",
                    "details": null
                  }
                }
              }
            }
          },
          "502": {
            "description": "Orchestrator unreachable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "orchestrator_unreachable",
                    "message": "orchestrator unreachable",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/v1/sandboxes/by-name/{name}/stop": {
      "post": {
        "tags": [
          "Sandboxes"
        ],
        "summary": "Stop a sandbox by name",
        "description": "Stop a running sandbox selected by its organization-scoped name.",
        "operationId": "stop_by_name",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "description": "Sandbox name (unique within the org)",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Sandbox stopping",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SandboxResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          },
          "404": {
            "description": "Sandbox not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "sandbox_not_found",
                    "message": "resource not found",
                    "details": null
                  }
                }
              }
            }
          },
          "409": {
            "description": "Invalid state transition",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_state_transition",
                    "message": "invalid state transition",
                    "details": null
                  }
                }
              }
            }
          },
          "502": {
            "description": "Orchestrator unreachable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "orchestrator_unreachable",
                    "message": "orchestrator unreachable",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/v1/sandboxes/{sandbox_id}": {
      "get": {
        "tags": [
          "Sandboxes"
        ],
        "summary": "Get a sandbox",
        "description": "Retrieve one sandbox by its unique identifier.",
        "operationId": "get_sandbox",
        "parameters": [
          {
            "name": "sandbox_id",
            "in": "path",
            "description": "Sandbox ID",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Sandbox details",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SandboxResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          },
          "404": {
            "description": "Sandbox not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "sandbox_not_found",
                    "message": "resource not found",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      },
      "delete": {
        "tags": [
          "Sandboxes"
        ],
        "summary": "Delete a sandbox",
        "description": "Delete a sandbox. Resource cleanup continues asynchronously.",
        "operationId": "delete_sandbox",
        "parameters": [
          {
            "name": "sandbox_id",
            "in": "path",
            "description": "Sandbox ID",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Sandbox scheduled for deletion",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          },
          "404": {
            "description": "Sandbox not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "sandbox_not_found",
                    "message": "resource not found",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      },
      "patch": {
        "tags": [
          "Sandboxes"
        ],
        "summary": "Update a sandbox",
        "description": "Rename a sandbox by updating its display name or slug.",
        "operationId": "update_sandbox",
        "parameters": [
          {
            "name": "sandbox_id",
            "in": "path",
            "description": "Sandbox ID",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateSandboxRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Updated sandbox",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SandboxResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid name/slug",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "invalid request",
                    "details": null
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          },
          "404": {
            "description": "Sandbox not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "sandbox_not_found",
                    "message": "resource not found",
                    "details": null
                  }
                }
              }
            }
          },
          "409": {
            "description": "Name or slug already taken",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "name_already_exists",
                    "message": "'name' already exists",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/v1/sandboxes/{sandbox_id}/metrics": {
      "get": {
        "tags": [
          "Sandboxes"
        ],
        "summary": "Get sandbox metrics",
        "operationId": "get_sandbox_metrics",
        "parameters": [
          {
            "name": "sandbox_id",
            "in": "path",
            "description": "Sandbox ID",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "range_secs",
            "in": "query",
            "description": "Look-back window in seconds (default 3600 = 1h, max 604800 = 7d).",
            "required": false,
            "schema": {
              "type": [
                "integer",
                "null"
              ],
              "format": "int64",
              "minimum": 0
            }
          },
          {
            "name": "step_secs",
            "in": "query",
            "description": "Sample resolution in seconds (default 30, min 5).",
            "required": false,
            "schema": {
              "type": [
                "integer",
                "null"
              ],
              "format": "int64",
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Sandbox metrics",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SandboxMetricsResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid range/step",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "invalid request",
                    "details": null
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          },
          "404": {
            "description": "Sandbox not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "sandbox_not_found",
                    "message": "resource not found",
                    "details": null
                  }
                }
              }
            }
          },
          "502": {
            "description": "Metrics backend unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "orchestrator_unreachable",
                    "message": "orchestrator unreachable",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/v1/sandboxes/{sandbox_id}/start": {
      "post": {
        "tags": [
          "Sandboxes"
        ],
        "summary": "Start a sandbox",
        "description": "Start a sandbox and optionally wait until it is running.",
        "operationId": "start_sandbox",
        "parameters": [
          {
            "name": "sandbox_id",
            "in": "path",
            "description": "Sandbox ID",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "wait_for",
            "in": "query",
            "description": "Optional lifecycle state to wait for after durable start admission.",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "null"
                },
                {
                  "$ref": "#/components/schemas/SandboxWaitFor"
                }
              ]
            }
          },
          {
            "name": "wait_timeout",
            "in": "query",
            "description": "Server-side wait budget in seconds; valid only with `wait_for`.",
            "required": false,
            "schema": {
              "type": [
                "integer",
                "null"
              ],
              "format": "int64",
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Sandbox start admitted or requested wait completed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SandboxResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          },
          "404": {
            "description": "Sandbox not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "sandbox_not_found",
                    "message": "resource not found",
                    "details": null
                  }
                }
              }
            }
          },
          "409": {
            "description": "Invalid state transition",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_state_transition",
                    "message": "invalid state transition",
                    "details": null
                  }
                }
              }
            }
          },
          "429": {
            "description": "Readiness wait capacity exhausted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "too many requests",
                    "details": null
                  }
                }
              }
            }
          },
          "502": {
            "description": "Orchestrator unreachable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "orchestrator_unreachable",
                    "message": "orchestrator unreachable",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/v1/sandboxes/{sandbox_id}/stop": {
      "post": {
        "tags": [
          "Sandboxes"
        ],
        "summary": "Stop a sandbox",
        "description": "Stop a running sandbox.",
        "operationId": "stop_sandbox",
        "parameters": [
          {
            "name": "sandbox_id",
            "in": "path",
            "description": "Sandbox ID",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Sandbox stopping",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SandboxResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          },
          "404": {
            "description": "Sandbox not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "sandbox_not_found",
                    "message": "resource not found",
                    "details": null
                  }
                }
              }
            }
          },
          "409": {
            "description": "Invalid state transition",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_state_transition",
                    "message": "invalid state transition",
                    "details": null
                  }
                }
              }
            }
          },
          "502": {
            "description": "Sandbox orchestrator unreachable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "orchestrator_unreachable",
                    "message": "orchestrator unreachable",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/v1/sandboxes/{sandbox_id}/volumes": {
      "get": {
        "tags": [
          "Sandboxes"
        ],
        "summary": "List mounted volumes",
        "operationId": "list_sandbox_volumes",
        "parameters": [
          {
            "name": "sandbox_id",
            "in": "path",
            "description": "Sandbox ID",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Mounted volumes",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/SandboxVolumeMount"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          },
          "404": {
            "description": "Sandbox not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "sandbox_not_found",
                    "message": "resource not found",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/v1/sandboxes/snapshot-candidates": {
      "get": {
        "tags": [
          "Snapshots"
        ],
        "summary": "List snapshot candidates",
        "description": "List persistent, stopped sandboxes supported by the current disk snapshot policy.",
        "operationId": "list_snapshot_candidates",
        "parameters": [
          {
            "name": "cursor",
            "in": "query",
            "description": "Pagination cursor",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Page size (default 20, max 100)",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated snapshot candidates",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedSnapshotCandidateResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/v1/snapshot-operations": {
      "get": {
        "tags": [
          "Snapshots"
        ],
        "summary": "List snapshot operations",
        "description": "List asynchronous snapshot operations requested by this organization.",
        "operationId": "list_operations",
        "parameters": [
          {
            "name": "cursor",
            "in": "query",
            "description": "Pagination cursor",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Page size (default 20, max 100)",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "exclude_redundant",
            "in": "query",
            "description": "Hide successful managed operations and superseded host-volume operations represented in snapshot inventory",
            "required": false,
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Explicit snapshot operations",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedSnapshotOperationResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/v1/snapshot-operations/{operation_id}": {
      "get": {
        "tags": [
          "Snapshots"
        ],
        "summary": "Get a snapshot operation",
        "description": "Poll an asynchronous snapshot operation.",
        "operationId": "get_operation",
        "parameters": [
          {
            "name": "operation_id",
            "in": "path",
            "description": "Snapshot operation id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Snapshot operation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloudSnapshotOperation"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          },
          "404": {
            "description": "Snapshot operation not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "sandbox_not_found",
                    "message": "resource not found",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/v1/snapshots": {
      "get": {
        "tags": [
          "Snapshots"
        ],
        "summary": "List snapshots",
        "description": "List the organization's managed disk snapshots.",
        "operationId": "list_snapshots",
        "parameters": [
          {
            "name": "cursor",
            "in": "query",
            "description": "Pagination cursor",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Page size (default 20, max 100)",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Managed snapshots",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedSnapshotResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      },
      "post": {
        "tags": [
          "Snapshots"
        ],
        "summary": "Create a snapshot",
        "description": "Create a managed or host-volume disk snapshot asynchronously.",
        "operationId": "create",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CloudCreateSnapshotRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "description": "Snapshot capture queued",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloudSnapshotOperation"
                }
              }
            }
          },
          "400": {
            "description": "Invalid snapshot request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "invalid request",
                    "details": null
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          },
          "404": {
            "description": "Source sandbox not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "sandbox_not_found",
                    "message": "resource not found",
                    "details": null
                  }
                }
              }
            }
          },
          "409": {
            "description": "Snapshot destination already exists",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "name_already_exists",
                    "message": "'name' already exists",
                    "details": null
                  }
                }
              }
            }
          },
          "422": {
            "description": "Unsupported or ineligible snapshot request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/v1/snapshots/by-name/{name}": {
      "get": {
        "tags": [
          "Snapshots"
        ],
        "summary": "Get a snapshot by name",
        "description": "Retrieve a managed disk snapshot by its organization-unique name.",
        "operationId": "get_snapshot_by_name",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "description": "Managed snapshot name",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Managed snapshot",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloudSnapshot"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          },
          "404": {
            "description": "Snapshot not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "sandbox_not_found",
                    "message": "resource not found",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      },
      "delete": {
        "tags": [
          "Snapshots"
        ],
        "summary": "Delete a snapshot by name",
        "description": "Delete a managed disk snapshot by its organization-unique name.",
        "operationId": "delete_snapshot_by_name",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "description": "Managed snapshot name",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Snapshot deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          },
          "404": {
            "description": "Snapshot not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "sandbox_not_found",
                    "message": "resource not found",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/v1/snapshots/{snapshot_id}": {
      "get": {
        "tags": [
          "Snapshots"
        ],
        "summary": "Get a snapshot",
        "description": "Retrieve a managed disk snapshot by its immutable identifier.",
        "operationId": "get",
        "parameters": [
          {
            "name": "snapshot_id",
            "in": "path",
            "description": "Managed snapshot id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Managed snapshot",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloudSnapshot"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          },
          "404": {
            "description": "Snapshot not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "sandbox_not_found",
                    "message": "resource not found",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      },
      "delete": {
        "tags": [
          "Snapshots"
        ],
        "summary": "Delete a snapshot",
        "description": "Delete a managed disk snapshot by its immutable identifier.",
        "operationId": "delete",
        "parameters": [
          {
            "name": "snapshot_id",
            "in": "path",
            "description": "Managed snapshot id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Snapshot deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          },
          "404": {
            "description": "Snapshot not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "sandbox_not_found",
                    "message": "resource not found",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/v1/volumes": {
      "get": {
        "tags": [
          "Volumes"
        ],
        "summary": "List volumes",
        "description": "List persistent volumes available to the organization, including current usage.",
        "operationId": "list_volumes",
        "responses": {
          "200": {
            "description": "The org's volumes",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/VolumeResponse"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      },
      "post": {
        "tags": [
          "Volumes"
        ],
        "summary": "Create a volume",
        "description": "Create a named persistent volume with optional capacity and labels.",
        "operationId": "create_volume",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateVolumeRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Volume created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VolumeResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "invalid request",
                    "details": null
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          },
          "409": {
            "description": "Volume name already in use",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "name_already_exists",
                    "message": "'name' already exists",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/v1/volumes/default": {
      "get": {
        "tags": [
          "Volumes"
        ],
        "summary": "Get the default volume",
        "operationId": "get_default",
        "responses": {
          "200": {
            "description": "Get the default volume",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VolumeResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          },
          "403": {
            "description": "Insufficient permissions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/v1/volumes/{id}": {
      "delete": {
        "tags": [
          "Volumes"
        ],
        "summary": "Delete a volume",
        "description": "Schedule an unused named volume for deletion.",
        "operationId": "delete_volume",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Volume id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Volume scheduled for deletion",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          },
          "404": {
            "description": "Volume not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "sandbox_not_found",
                    "message": "resource not found",
                    "details": null
                  }
                }
              }
            }
          },
          "409": {
            "description": "The default volume cannot be deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "the host volume cannot be deleted",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      },
      "patch": {
        "tags": [
          "Volumes"
        ],
        "summary": "Update a volume",
        "description": "Update a named volume's capacity limit or labels.",
        "operationId": "update_volume",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Volume id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateVolumeRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Volume updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VolumeResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request (e.g. cap exceeds the org cap)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "invalid request",
                    "details": null
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          },
          "404": {
            "description": "Volume not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "sandbox_not_found",
                    "message": "resource not found",
                    "details": null
                  }
                }
              }
            }
          },
          "409": {
            "description": "The host volume cannot be capped",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "the host volume cannot be capped",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/v1/volumes/{id}/files": {
      "get": {
        "tags": [
          "Volumes"
        ],
        "summary": "List directory contents",
        "operationId": "list_directory_contents",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Volume UUID",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "x-msb-file-path",
            "in": "header",
            "description": "URL-safe base64 encoded volume-relative UTF-8 path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List directory contents",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VolumeFileListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "invalid request",
                    "details": null
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          },
          "403": {
            "description": "Insufficient permissions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Volume or path not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "sandbox_not_found",
                    "message": "resource not found",
                    "details": null
                  }
                }
              }
            }
          },
          "502": {
            "description": "Volume filesystem unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "orchestrator_unreachable",
                    "message": "orchestrator unreachable",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      },
      "delete": {
        "tags": [
          "Volumes"
        ],
        "summary": "Delete a file or directory",
        "operationId": "remove",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Volume UUID",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "x-msb-file-path",
            "in": "header",
            "description": "URL-safe base64 encoded volume-relative UTF-8 path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-msb-file-recursive",
            "in": "header",
            "description": "Recursively remove a directory; defaults to false",
            "required": false,
            "schema": {
              "type": [
                "boolean",
                "null"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Delete a file or directory",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VolumeFileMutationResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "invalid request",
                    "details": null
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          },
          "403": {
            "description": "Insufficient permissions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Volume or path not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "sandbox_not_found",
                    "message": "resource not found",
                    "details": null
                  }
                }
              }
            }
          },
          "502": {
            "description": "Volume filesystem unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "orchestrator_unreachable",
                    "message": "orchestrator unreachable",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/v1/volumes/{id}/files/content": {
      "get": {
        "tags": [
          "Volumes"
        ],
        "summary": "Read a file",
        "operationId": "read",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Volume UUID",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "x-msb-file-path",
            "in": "header",
            "description": "URL-safe base64 encoded volume-relative UTF-8 path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Read a file",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "$ref": "#/components/schemas/VolumeFileContent"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "invalid request",
                    "details": null
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          },
          "403": {
            "description": "Insufficient permissions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Volume or path not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "sandbox_not_found",
                    "message": "resource not found",
                    "details": null
                  }
                }
              }
            }
          },
          "502": {
            "description": "Volume filesystem unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "orchestrator_unreachable",
                    "message": "orchestrator unreachable",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      },
      "put": {
        "tags": [
          "Volumes"
        ],
        "summary": "Write a file",
        "operationId": "write",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Volume UUID",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "x-msb-file-path",
            "in": "header",
            "description": "URL-safe base64 encoded volume-relative UTF-8 path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/octet-stream": {
              "schema": {
                "$ref": "#/components/schemas/VolumeFileContent"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Write a file",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VolumeFileMutationResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "invalid request",
                    "details": null
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          },
          "403": {
            "description": "Insufficient permissions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Volume or path not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "sandbox_not_found",
                    "message": "resource not found",
                    "details": null
                  }
                }
              }
            }
          },
          "502": {
            "description": "Volume filesystem unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "orchestrator_unreachable",
                    "message": "orchestrator unreachable",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/v1/volumes/{id}/files/copy": {
      "post": {
        "tags": [
          "Volumes"
        ],
        "summary": "Copy a file or directory",
        "operationId": "copy",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Volume UUID",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FileMoveRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Copy a file or directory",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VolumeFileMutationResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "invalid request",
                    "details": null
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          },
          "403": {
            "description": "Insufficient permissions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Volume or path not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "sandbox_not_found",
                    "message": "resource not found",
                    "details": null
                  }
                }
              }
            }
          },
          "502": {
            "description": "Volume filesystem unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "orchestrator_unreachable",
                    "message": "orchestrator unreachable",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/v1/volumes/{id}/files/exists": {
      "get": {
        "tags": [
          "Volumes"
        ],
        "summary": "Check if a file exists",
        "operationId": "exists",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Volume UUID",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "x-msb-file-path",
            "in": "header",
            "description": "URL-safe base64 encoded volume-relative UTF-8 path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Check if a file exists",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VolumeFileExistsResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "invalid request",
                    "details": null
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          },
          "403": {
            "description": "Insufficient permissions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Volume or path not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "sandbox_not_found",
                    "message": "resource not found",
                    "details": null
                  }
                }
              }
            }
          },
          "502": {
            "description": "Volume filesystem unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "orchestrator_unreachable",
                    "message": "orchestrator unreachable",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/v1/volumes/{id}/files/mkdir": {
      "post": {
        "tags": [
          "Volumes"
        ],
        "summary": "Create a directory",
        "operationId": "mkdir",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Volume UUID",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FilePathRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Create a directory",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VolumeFileMutationResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "invalid request",
                    "details": null
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          },
          "403": {
            "description": "Insufficient permissions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Volume or path not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "sandbox_not_found",
                    "message": "resource not found",
                    "details": null
                  }
                }
              }
            }
          },
          "502": {
            "description": "Volume filesystem unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "orchestrator_unreachable",
                    "message": "orchestrator unreachable",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/v1/volumes/{id}/files/rename": {
      "post": {
        "tags": [
          "Volumes"
        ],
        "summary": "Move a file or directory",
        "operationId": "rename",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Volume UUID",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FileMoveRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Move a file or directory",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VolumeFileMutationResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "invalid request",
                    "details": null
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          },
          "403": {
            "description": "Insufficient permissions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Volume or path not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "sandbox_not_found",
                    "message": "resource not found",
                    "details": null
                  }
                }
              }
            }
          },
          "502": {
            "description": "Volume filesystem unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "orchestrator_unreachable",
                    "message": "orchestrator unreachable",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/v1/volumes/{id}/files/stat": {
      "get": {
        "tags": [
          "Volumes"
        ],
        "summary": "Get file details",
        "operationId": "stat",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Volume UUID",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "x-msb-file-path",
            "in": "header",
            "description": "URL-safe base64 encoded volume-relative UTF-8 path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Get file details",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VolumeFileStatResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "invalid request",
                    "details": null
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          },
          "403": {
            "description": "Insufficient permissions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Volume or path not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "sandbox_not_found",
                    "message": "resource not found",
                    "details": null
                  }
                }
              }
            }
          },
          "502": {
            "description": "Volume filesystem unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "orchestrator_unreachable",
                    "message": "orchestrator unreachable",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/v1/volumes/{id}/metrics": {
      "get": {
        "tags": [
          "Volumes"
        ],
        "summary": "Get volume metrics",
        "operationId": "get_volume_metrics",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Volume id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "range_secs",
            "in": "query",
            "description": "Look-back window in seconds (default 3600 = 1h, max 604800 = 7d).",
            "required": false,
            "schema": {
              "type": [
                "integer",
                "null"
              ],
              "format": "int64",
              "minimum": 0
            }
          },
          {
            "name": "step_secs",
            "in": "query",
            "description": "Sample resolution in seconds (default 30, min 5).",
            "required": false,
            "schema": {
              "type": [
                "integer",
                "null"
              ],
              "format": "int64",
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Volume performance metrics",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VolumeMetricsResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid time range or resolution",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "invalid request",
                    "details": null
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          },
          "404": {
            "description": "Volume not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "sandbox_not_found",
                    "message": "resource not found",
                    "details": null
                  }
                }
              }
            }
          },
          "502": {
            "description": "Metrics backend unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "orchestrator_unreachable",
                    "message": "orchestrator unreachable",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/v1/quotas": {
      "get": {
        "tags": [
          "Quotas"
        ],
        "summary": "Get quota usage",
        "description": "Return current resource usage, plan limits, and organization-specific quota overrides.",
        "operationId": "get_quotas",
        "responses": {
          "200": {
            "description": "Quota usage",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QuotaUsage"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          },
          "404": {
            "description": "Org not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "org_not_found",
                    "message": "org not found",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/v1/billing": {
      "get": {
        "tags": [
          "Billing"
        ],
        "summary": "Get billing summary",
        "operationId": "get_billing",
        "responses": {
          "200": {
            "description": "Billing summary",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingSummaryResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/v1/billing/invoices": {
      "get": {
        "tags": [
          "Billing"
        ],
        "summary": "List invoices",
        "operationId": "list_invoices",
        "responses": {
          "200": {
            "description": "Invoice list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Invoice"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/v1/billing/invoices/{invoice_id}": {
      "get": {
        "tags": [
          "Billing"
        ],
        "summary": "Get an invoice",
        "operationId": "get_invoice",
        "parameters": [
          {
            "name": "invoice_id",
            "in": "path",
            "description": "Invoice ID",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Invoice detail",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Invoice"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          },
          "404": {
            "description": "Invoice not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "sandbox_not_found",
                    "message": "resource not found",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/v1/billing/plans": {
      "get": {
        "tags": [
          "Billing"
        ],
        "summary": "List billing plans",
        "operationId": "list_plans",
        "responses": {
          "200": {
            "description": "Available plans",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/BillingPlan"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/v1/billing/usage": {
      "get": {
        "tags": [
          "Billing"
        ],
        "summary": "Get a usage summary",
        "description": "Return billable usage for the organization.",
        "operationId": "get_usage",
        "responses": {
          "200": {
            "description": "Usage summary",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UsageSummary"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/v1/billing/usage/sandbox": {
      "get": {
        "tags": [
          "Billing"
        ],
        "summary": "Get per-sandbox usage",
        "description": "Return billable usage grouped by sandbox for a selected UTC time range.",
        "operationId": "get_sandbox_breakdown",
        "responses": {
          "200": {
            "description": "Per-sandbox / per-day usage",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UsageBreakdown"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/v1/billing/usage/storage": {
      "get": {
        "tags": [
          "Billing"
        ],
        "summary": "Get per-volume storage usage",
        "description": "Return persistent storage usage grouped by volume for a selected UTC time range.",
        "operationId": "get_storage_breakdown",
        "responses": {
          "200": {
            "description": "Per-volume / per-day storage usage",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StorageBreakdown"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/v1/events": {
      "get": {
        "tags": [
          "Audit events"
        ],
        "summary": "List audit events",
        "description": "List audit events for the organization with cursor pagination and optional filters.",
        "operationId": "list_events",
        "parameters": [
          {
            "name": "event_type",
            "in": "query",
            "description": "Filter by event type (e.g. \"api_key_created\").",
            "required": false,
            "schema": {
              "type": [
                "string",
                "null"
              ]
            }
          },
          {
            "name": "since",
            "in": "query",
            "description": "Only return events at or after this timestamp.",
            "required": false,
            "schema": {
              "type": [
                "string",
                "null"
              ],
              "format": "date-time"
            }
          },
          {
            "name": "until",
            "in": "query",
            "description": "Only return events at or before this timestamp.",
            "required": false,
            "schema": {
              "type": [
                "string",
                "null"
              ],
              "format": "date-time"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "Opaque cursor from a previous response.",
            "required": false,
            "schema": {
              "type": [
                "string",
                "null"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Number of items to return (default: 20, max: 100).",
            "required": false,
            "schema": {
              "type": [
                "integer",
                "null"
              ],
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of audit events",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedEventResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/v1/events/{event_id}": {
      "get": {
        "tags": [
          "Audit events"
        ],
        "summary": "Get an audit event",
        "description": "Retrieve one audit event by its identifier.",
        "operationId": "get_event",
        "parameters": [
          {
            "name": "event_id",
            "in": "path",
            "description": "Event ID",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Event details",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EventResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          },
          "404": {
            "description": "Event not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "sandbox_not_found",
                    "message": "resource not found",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/v1/me/org": {
      "get": {
        "tags": [
          "Organization"
        ],
        "summary": "Get the current organization",
        "description": "Returns the organization associated with the current credential.",
        "operationId": "get_org",
        "responses": {
          "200": {
            "description": "Current organization",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrgWithRole"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    },
    "/v1/members": {
      "get": {
        "tags": [
          "Members"
        ],
        "summary": "List organization members",
        "description": "List organization members and their roles with cursor pagination.",
        "operationId": "list_members",
        "parameters": [
          {
            "name": "cursor",
            "in": "query",
            "description": "Pagination cursor",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Page size (default 20, max 100)",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of members",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedMemberResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "unauthorized",
                    "details": null
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          }
        ]
      }
    }
  },
  "components": {
    "schemas": {
      "Action": {
        "type": "string",
        "description": "Action to take on traffic matched by a [`Rule`] (or a policy default).",
        "enum": [
          "allow",
          "deny"
        ]
      },
      "BillingEffectiveState": {
        "type": "string",
        "description": "Effective billing state derived from plan, period end, and suspension.",
        "enum": [
          "free",
          "active",
          "past_due",
          "suspended"
        ]
      },
      "BillingPlan": {
        "type": "object",
        "description": "Billing plan definition with resource quotas.\n\n`name` is the plan's identity, an arbitrary catalog string (no fixed enum) -\n\"free\"/\"builder\"/… are just the names we ship. Every quota is `Option`:\n`None` = unlimited (counts/budgets) or the platform default (per-sandbox\nvCPU/memory/disk). There is no magic 0.",
        "required": [
          "id",
          "name",
          "display_name",
          "self_serve",
          "is_default",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "display_name": {
            "type": "string"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "included_disk_gib_hours": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32"
          },
          "included_memory_gib_hours": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32"
          },
          "included_storage_gib": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32"
          },
          "included_vcpu_hours": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32",
            "description": "Monthly included allowances bundled into the flat fee (pool of vCPU-hours /\nmemory GiB-hours / writable-disk GiB-hours / durable-storage GiB). Usage\nbeyond the pool is metered overage on priced plans; on an unpriced (free)\nplan the compute pool is a hard wall."
          },
          "is_default": {
            "type": "boolean",
            "description": "The plan new orgs are assigned. Exactly one row is the default."
          },
          "max_concurrent_ephemeral_running": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32",
            "description": "Max simultaneously-running *ephemeral* sandboxes (cheap, self-cleaning)."
          },
          "max_concurrent_persistent_running": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32",
            "description": "Max simultaneously-running *persistent* sandboxes (snapshot/restore cost)."
          },
          "max_disk_size_mib_per_sandbox": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32",
            "description": "Maximum writable disk size (MiB) per sandbox."
          },
          "max_memory_mib_per_sandbox": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32"
          },
          "max_sandboxes": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32"
          },
          "max_snapshot_storage_gib": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32",
            "description": "Per-org managed-snapshot storage hard cap (GiB), compared against the\nsummed compressed archive size of the org's managed snapshots."
          },
          "max_total_storage_gib": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32",
            "description": "Per-org durable-storage hard cap (GiB). Used storage is billed at a flat\nper-GB-month rate (no free allotment)."
          },
          "max_uptime_minutes_per_sandbox": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32"
          },
          "max_vcpus_per_sandbox": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32"
          },
          "monthly_sandbox_minutes": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32",
            "description": "Deprecated wall-clock minutes budget (NULL). The compute wall is now\n`included_vcpu_hours` on unpriced plans."
          },
          "name": {
            "type": "string"
          },
          "self_serve": {
            "type": "boolean",
            "description": "May a user self-subscribe to this plan via the API? Fail-closed; the\nchange-plan check keys on this flag, not the plan name."
          },
          "subscription_price_cents": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "description": "Flat monthly subscription price (cents); `None` = unpriced (free). Source\nof upgrade/downgrade ordering - compare prices, not a fixed tier rank."
          },
          "trial_duration_days": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "usage_disk_gib_hour_usd": {
            "type": [
              "number",
              "null"
            ],
            "format": "double"
          },
          "usage_memory_gib_hour_usd": {
            "type": [
              "number",
              "null"
            ],
            "format": "double"
          },
          "usage_snapshot_storage_gib_month_usd": {
            "type": [
              "number",
              "null"
            ],
            "format": "double",
            "description": "Managed-snapshot storage rate (USD/GiB-month), separate from volumes."
          },
          "usage_storage_gib_month_usd": {
            "type": [
              "number",
              "null"
            ],
            "format": "double"
          },
          "usage_vcpu_hour_usd": {
            "type": [
              "number",
              "null"
            ],
            "format": "double",
            "description": "Per-unit overage rates (USD) charged on usage beyond the included pool;\n`None` on the free/unpriced plan (no overage path). Drive the live cost\nestimate `subscription + Σ overage`; Autumn's invoice stays authoritative."
          }
        }
      },
      "BillingPlanSummary": {
        "type": "object",
        "description": "Summary of a billing plan (subset of full plan).",
        "required": [
          "name",
          "display_name",
          "has_custom_billing_terms"
        ],
        "properties": {
          "display_name": {
            "type": "string"
          },
          "has_custom_billing_terms": {
            "type": "boolean"
          },
          "name": {
            "type": "string"
          }
        }
      },
      "BillingPricingTerms": {
        "type": "object",
        "description": "Pricing fields which may be inherited from a plan or negotiated per customer.",
        "properties": {
          "included_disk_gib_hours": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32"
          },
          "included_memory_gib_hours": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32"
          },
          "included_storage_gib": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32"
          },
          "included_vcpu_hours": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32"
          },
          "subscription_price_cents": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64"
          },
          "usage_disk_gib_hour_usd": {
            "type": [
              "number",
              "null"
            ],
            "format": "double"
          },
          "usage_memory_gib_hour_usd": {
            "type": [
              "number",
              "null"
            ],
            "format": "double"
          },
          "usage_snapshot_storage_gib_month_usd": {
            "type": [
              "number",
              "null"
            ],
            "format": "double"
          },
          "usage_storage_gib_month_usd": {
            "type": [
              "number",
              "null"
            ],
            "format": "double"
          },
          "usage_vcpu_hour_usd": {
            "type": [
              "number",
              "null"
            ],
            "format": "double"
          }
        }
      },
      "BillingSummaryResponse": {
        "type": "object",
        "description": "Billing summary response for an organization.",
        "required": [
          "plan",
          "effective_pricing",
          "effective_state",
          "is_suspended"
        ],
        "properties": {
          "available_credit_cents": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "description": "Positive USD credit Stripe will apply automatically to future invoices.",
            "minimum": 0
          },
          "billing_terms_override": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/BillingTermsOverrideSummary",
                "description": "Current agreement metadata, including setup and payment-pending states."
              }
            ]
          },
          "checkout_url": {
            "type": [
              "string",
              "null"
            ],
            "description": "Hosted-checkout URL to complete payment for a just-changed plan (Autumn).\nSet only on a plan change that needs payment; the client redirects to it."
          },
          "current_period_ends_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "effective_pricing": {
            "$ref": "#/components/schemas/BillingPricingTerms",
            "description": "Effective commercial terms after applying any org-specific agreement."
          },
          "effective_state": {
            "$ref": "#/components/schemas/BillingEffectiveState"
          },
          "is_suspended": {
            "type": "boolean"
          },
          "pending_plan": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/BillingPlanSummary",
                "description": "Plan the org will switch to at `current_period_ends_at`, if a downgrade is scheduled."
              }
            ]
          },
          "plan": {
            "$ref": "#/components/schemas/BillingPlanSummary"
          }
        }
      },
      "BillingTermsOverrideSummary": {
        "type": "object",
        "description": "Effective window for the org's active negotiated billing terms.",
        "required": [
          "status",
          "plan",
          "effective_from"
        ],
        "properties": {
          "effective_from": {
            "type": "string",
            "format": "date-time"
          },
          "effective_until": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "plan": {
            "$ref": "#/components/schemas/BillingPlanSummary"
          },
          "post_expiry_plan": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/BillingPlanSummary",
                "description": "Explicit plan assigned at expiry; absent means target-plan retail pricing."
              }
            ]
          },
          "status": {
            "$ref": "#/components/schemas/OrgBillingTermsOverrideStatus"
          }
        }
      },
      "CertCacheConfig": {
        "type": "object",
        "description": "Per-domain certificate cache configuration.",
        "properties": {
          "capacity": {
            "type": "integer",
            "description": "Maximum number of cached certificates. Default: 1000.",
            "minimum": 0
          },
          "validity_hours": {
            "type": "integer",
            "format": "int64",
            "description": "Certificate validity duration in hours. Default: 24.",
            "minimum": 0
          }
        }
      },
      "CheckpointSnapshotState": {
        "type": "object",
        "description": "Immutable checkpoint-manifest-backed snapshot state.",
        "required": [
          "checkpoint_id",
          "manifest"
        ],
        "properties": {
          "checkpoint_id": {
            "type": "string",
            "description": "Stable identifier for the captured cut."
          },
          "manifest": {
            "type": "string",
            "description": "SHA-256 identity of the disk or composite checkpoint manifest."
          }
        },
        "additionalProperties": false
      },
      "CloudCreateSandboxRequest": {
        "oneOf": [
          {
            "allOf": [
              {
                "$ref": "#/components/schemas/CloudSandboxSpec",
                "description": "Settings shared by every sandbox source."
              },
              {
                "type": "object",
                "required": [
                  "reference"
                ],
                "properties": {
                  "patches": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/CloudPatch"
                    },
                    "description": "Rootfs patches applied before VM start."
                  },
                  "pull_policy": {
                    "$ref": "#/components/schemas/CloudPullPolicy",
                    "description": "OCI image pull policy."
                  },
                  "reference": {
                    "type": "string",
                    "description": "OCI image reference."
                  },
                  "resources": {
                    "$ref": "#/components/schemas/CloudSandboxResources",
                    "description": "CPU, memory, and writable-disk resources."
                  }
                }
              },
              {
                "type": "object",
                "description": "Create a sandbox from an OCI image.",
                "required": [
                  "source"
                ],
                "properties": {
                  "source": {
                    "type": "string",
                    "enum": [
                      "oci"
                    ]
                  }
                }
              }
            ],
            "description": "Create a sandbox from an OCI image."
          },
          {
            "allOf": [
              {
                "$ref": "#/components/schemas/CloudSandboxSpec",
                "description": "Settings shared by every sandbox source."
              },
              {
                "type": "object",
                "required": [
                  "path"
                ],
                "properties": {
                  "patches": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/CloudPatch"
                    },
                    "description": "Rootfs patches applied before VM start."
                  },
                  "path": {
                    "type": "string",
                    "description": "Host directory used as the root filesystem."
                  },
                  "resources": {
                    "$ref": "#/components/schemas/CloudSandboxComputeResources",
                    "description": "CPU and memory resources."
                  }
                }
              },
              {
                "type": "object",
                "description": "Create a sandbox from a host directory.",
                "required": [
                  "source"
                ],
                "properties": {
                  "source": {
                    "type": "string",
                    "enum": [
                      "bind"
                    ]
                  }
                }
              }
            ],
            "description": "Create a sandbox from a host directory."
          },
          {
            "allOf": [
              {
                "$ref": "#/components/schemas/CloudSandboxSpec",
                "description": "Settings shared by every sandbox source."
              },
              {
                "type": "object",
                "required": [
                  "path",
                  "format"
                ],
                "properties": {
                  "format": {
                    "$ref": "#/components/schemas/CloudDiskImageFormat",
                    "description": "Disk image format."
                  },
                  "fstype": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Inner filesystem type, when it cannot be detected automatically."
                  },
                  "patches": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/CloudPatch"
                    },
                    "description": "Rootfs patches applied before VM start."
                  },
                  "path": {
                    "type": "string",
                    "description": "Host path to the disk image."
                  },
                  "resources": {
                    "$ref": "#/components/schemas/CloudSandboxComputeResources",
                    "description": "CPU and memory resources."
                  }
                }
              },
              {
                "type": "object",
                "description": "Create a sandbox from a disk image file.",
                "required": [
                  "source"
                ],
                "properties": {
                  "source": {
                    "type": "string",
                    "enum": [
                      "disk_image"
                    ]
                  }
                }
              }
            ],
            "description": "Create a sandbox from a disk image file."
          },
          {
            "allOf": [
              {
                "$ref": "#/components/schemas/CloudSandboxSpec",
                "description": "Settings shared by every sandbox source."
              },
              {
                "type": "object",
                "required": [
                  "disk_snapshot_ref"
                ],
                "properties": {
                  "disk_snapshot_ref": {
                    "$ref": "#/components/schemas/CloudSnapshotLocation",
                    "description": "Disk snapshot to restore."
                  },
                  "pull_policy": {
                    "$ref": "#/components/schemas/CloudPullPolicy",
                    "description": "Pull policy used if the snapshot's pinned base image must be fetched."
                  },
                  "resources": {
                    "$ref": "#/components/schemas/CloudSandboxComputeResources",
                    "description": "CPU and memory resources."
                  }
                }
              },
              {
                "type": "object",
                "description": "Create a fresh-booted sandbox from a disk snapshot.",
                "required": [
                  "source"
                ],
                "properties": {
                  "source": {
                    "type": "string",
                    "enum": [
                      "disk_snapshot"
                    ]
                  }
                }
              }
            ],
            "description": "Create a fresh-booted sandbox from a disk snapshot."
          }
        ],
        "description": "Wire shape of a cloud sandbox create request body.\n\nEach root filesystem origin is a distinct source variant. The common\nsandbox settings remain flat beside the source-specific fields. Legacy\nrequests carrying an `image` object are accepted during migration."
      },
      "CloudCreateSnapshotRequest": {
        "oneOf": [
          {
            "allOf": [
              {
                "$ref": "#/components/schemas/CloudSnapshotSpec",
                "description": "Settings shared by every snapshot kind."
              },
              {
                "type": "object",
                "description": "Capture disk state only.",
                "required": [
                  "kind"
                ],
                "properties": {
                  "kind": {
                    "type": "string",
                    "enum": [
                      "disk"
                    ]
                  }
                }
              }
            ],
            "description": "Capture disk state only."
          }
        ],
        "description": "Wire shape of a cloud snapshot create request body."
      },
      "CloudDiskImageFormat": {
        "type": "string",
        "description": "Disk image format for cloud disk-image sources. Twin of [`DiskImageFormat`]\nwith a snake_case wire.",
        "enum": [
          "qcow2",
          "raw",
          "vmdk"
        ]
      },
      "CloudErrorDetails": {
        "type": "object",
        "description": "Nested cloud API error details.",
        "properties": {
          "code": {
            "type": [
              "string",
              "null"
            ],
            "description": "Machine-readable error code."
          },
          "message": {
            "type": [
              "string",
              "null"
            ],
            "description": "Human-readable error message."
          }
        }
      },
      "CloudHostPattern": {
        "oneOf": [
          {
            "type": "object",
            "description": "Exact hostname match.",
            "required": [
              "value",
              "type"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "exact"
                ]
              },
              "value": {
                "type": "string",
                "description": "Hostname to match exactly."
              }
            }
          },
          {
            "type": "object",
            "description": "Wildcard match (e.g. `*.openai.com`).",
            "required": [
              "value",
              "type"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "wildcard"
                ]
              },
              "value": {
                "type": "string",
                "description": "Wildcard pattern."
              }
            }
          },
          {
            "type": "object",
            "description": "Any host (dangerous - the secret can be exfiltrated).",
            "required": [
              "type"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "any"
                ]
              }
            }
          }
        ],
        "description": "Host allowlist pattern for cloud secrets. Twin of [`HostPattern`], with the\ndomain's scalar variants normalized to `{ value }` for a uniform union."
      },
      "CloudNetworkSpec": {
        "type": "object",
        "description": "Cloud network specification: a subset of the domain [`crate::domain::NetworkSpec`].\nInterface overrides, host port mapping, DNS, TLS interception, rate limits,\nand host-CA trust are not part of this type. Unknown fields are ignored for\ncompatibility with newer clients; accepting them does not enable their behavior.",
        "properties": {
          "enabled": {
            "type": "boolean",
            "description": "Whether networking is enabled for this sandbox.",
            "default": true
          },
          "max_connections": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Max concurrent guest connections.",
            "default": null,
            "minimum": 0
          },
          "policy": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/NetworkPolicy",
                "description": "Egress/ingress policy."
              }
            ],
            "default": null
          },
          "secrets": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/CloudSecretsConfig",
                "description": "Secret-injection config."
              }
            ],
            "default": null
          },
          "strict": {
            "type": "boolean",
            "description": "Require hostname-based policy allows to use inspectable application authority.",
            "default": true
          }
        }
      },
      "CloudPatch": {
        "oneOf": [
          {
            "type": "object",
            "description": "Write text content to a file.",
            "required": [
              "path",
              "content",
              "replace",
              "type"
            ],
            "properties": {
              "content": {
                "type": "string",
                "description": "Text content to write."
              },
              "mode": {
                "type": [
                  "integer",
                  "null"
                ],
                "format": "int32",
                "description": "File permissions, such as `0o644`. `None` uses the default.",
                "minimum": 0
              },
              "path": {
                "type": "string",
                "description": "Absolute guest path, such as `/etc/app.conf`."
              },
              "replace": {
                "type": "boolean",
                "description": "Allow replacing a file that already exists in the rootfs."
              },
              "type": {
                "type": "string",
                "enum": [
                  "text"
                ]
              }
            }
          },
          {
            "type": "object",
            "description": "Write raw bytes to a file.",
            "required": [
              "path",
              "content",
              "replace",
              "type"
            ],
            "properties": {
              "content": {
                "type": "array",
                "items": {
                  "type": "integer",
                  "format": "int32",
                  "minimum": 0
                },
                "description": "Raw byte content to write."
              },
              "mode": {
                "type": [
                  "integer",
                  "null"
                ],
                "format": "int32",
                "description": "File permissions, such as `0o644`. `None` uses the default.",
                "minimum": 0
              },
              "path": {
                "type": "string",
                "description": "Absolute guest path."
              },
              "replace": {
                "type": "boolean",
                "description": "Allow replacing a file that already exists in the rootfs."
              },
              "type": {
                "type": "string",
                "enum": [
                  "file"
                ]
              }
            }
          },
          {
            "type": "object",
            "description": "Copy a file from the host into the rootfs.",
            "required": [
              "src",
              "dst",
              "replace",
              "type"
            ],
            "properties": {
              "dst": {
                "type": "string",
                "description": "Absolute guest destination path."
              },
              "mode": {
                "type": [
                  "integer",
                  "null"
                ],
                "format": "int32",
                "description": "File permissions. `None` preserves source permissions.",
                "minimum": 0
              },
              "replace": {
                "type": "boolean",
                "description": "Allow replacing a file that already exists in the rootfs."
              },
              "src": {
                "type": "string",
                "description": "Host path to copy from."
              },
              "type": {
                "type": "string",
                "enum": [
                  "copy_file"
                ]
              }
            }
          },
          {
            "type": "object",
            "description": "Copy a directory from the host into the rootfs.",
            "required": [
              "src",
              "dst",
              "replace",
              "type"
            ],
            "properties": {
              "dst": {
                "type": "string",
                "description": "Absolute guest destination path."
              },
              "replace": {
                "type": "boolean",
                "description": "Allow replacing files that already exist in the rootfs."
              },
              "src": {
                "type": "string",
                "description": "Host directory to copy from."
              },
              "type": {
                "type": "string",
                "enum": [
                  "copy_dir"
                ]
              }
            }
          },
          {
            "type": "object",
            "description": "Create a symlink.",
            "required": [
              "target",
              "link",
              "replace",
              "type"
            ],
            "properties": {
              "link": {
                "type": "string",
                "description": "Absolute guest path where the symlink is created."
              },
              "replace": {
                "type": "boolean",
                "description": "Allow replacing a path that already exists in the rootfs."
              },
              "target": {
                "type": "string",
                "description": "Symlink target path."
              },
              "type": {
                "type": "string",
                "enum": [
                  "symlink"
                ]
              }
            }
          },
          {
            "type": "object",
            "description": "Create a directory.",
            "required": [
              "path",
              "type"
            ],
            "properties": {
              "mode": {
                "type": [
                  "integer",
                  "null"
                ],
                "format": "int32",
                "description": "Directory permissions, such as `0o755`. `None` uses the default.",
                "minimum": 0
              },
              "path": {
                "type": "string",
                "description": "Absolute guest path."
              },
              "type": {
                "type": "string",
                "enum": [
                  "mkdir"
                ]
              }
            }
          },
          {
            "type": "object",
            "description": "Remove a file or directory.",
            "required": [
              "path",
              "type"
            ],
            "properties": {
              "path": {
                "type": "string",
                "description": "Absolute guest path to remove."
              },
              "type": {
                "type": "string",
                "enum": [
                  "remove"
                ]
              }
            }
          },
          {
            "type": "object",
            "description": "Append content to an existing file.",
            "required": [
              "path",
              "content",
              "type"
            ],
            "properties": {
              "content": {
                "type": "string",
                "description": "Content to append."
              },
              "path": {
                "type": "string",
                "description": "Absolute guest path of the file to append to."
              },
              "type": {
                "type": "string",
                "enum": [
                  "append"
                ]
              }
            }
          }
        ],
        "description": "Filesystem change applied before the sandbox starts. The type field selects the operation."
      },
      "CloudPullPolicy": {
        "type": "string",
        "description": "Cloud pull policy. Twin of domain [`PullPolicy`] with a snake_case wire.",
        "enum": [
          "if_missing",
          "always",
          "never"
        ]
      },
      "CloudRlimit": {
        "type": "object",
        "description": "A POSIX resource limit. Twin of [`Rlimit`] using [`CloudRlimitResource`].",
        "required": [
          "resource",
          "soft",
          "hard"
        ],
        "properties": {
          "hard": {
            "type": "integer",
            "format": "int64",
            "description": "Hard limit (ceiling, requires privileges to raise).",
            "minimum": 0
          },
          "resource": {
            "$ref": "#/components/schemas/CloudRlimitResource",
            "description": "Resource type."
          },
          "soft": {
            "type": "integer",
            "format": "int64",
            "description": "Soft limit (can be raised up to the hard limit by the process).",
            "minimum": 0
          }
        }
      },
      "CloudRlimitResource": {
        "type": "string",
        "description": "POSIX resource-limit identifiers. Twin of [`RlimitResource`] with a\nsnake_case wire.",
        "enum": [
          "cpu",
          "fsize",
          "data",
          "stack",
          "core",
          "rss",
          "nproc",
          "nofile",
          "memlock",
          "as",
          "locks",
          "sigpending",
          "msgqueue",
          "nice",
          "rtprio",
          "rttime"
        ]
      },
      "CloudSandboxComputeResources": {
        "type": "object",
        "description": "CPU and memory request shared by sources without a managed writable disk.",
        "properties": {
          "memory_mib": {
            "type": "integer",
            "format": "int32",
            "description": "Guest memory in MiB.",
            "default": 512,
            "minimum": 0
          },
          "vcpus": {
            "type": "integer",
            "format": "int32",
            "description": "Number of virtual CPUs.",
            "default": 1,
            "minimum": 0
          }
        }
      },
      "CloudSandboxResources": {
        "type": "object",
        "description": "Cloud resource request.",
        "properties": {
          "disk_size_mib": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32",
            "description": "Writable disk size in MiB. Applies only to OCI root filesystems.",
            "default": null,
            "minimum": 0
          },
          "memory_mib": {
            "type": "integer",
            "format": "int32",
            "description": "Guest memory in MiB.",
            "default": 512,
            "minimum": 0
          },
          "vcpus": {
            "type": "integer",
            "format": "int32",
            "description": "Number of virtual CPUs.",
            "default": 1,
            "minimum": 0
          }
        }
      },
      "CloudSandboxRuntimeOptions": {
        "type": "object",
        "description": "Cloud guest runtime options: a subset of [`crate::domain::SandboxRuntimeOptions`]. The\nhostname and the metrics-sampling knobs are not part of this type.\nUnknown fields are ignored for compatibility with newer clients.",
        "properties": {
          "cmd": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            },
            "description": "Command override.",
            "default": null
          },
          "entrypoint": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            },
            "description": "Entrypoint override.",
            "default": null
          },
          "log_level": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/SandboxLogLevel",
                "description": "Runtime log level."
              }
            ],
            "default": null
          },
          "scripts": {
            "type": "object",
            "description": "Named in-guest scripts.",
            "default": {},
            "additionalProperties": {
              "type": "string"
            },
            "propertyNames": {
              "type": "string"
            }
          },
          "shell": {
            "type": [
              "string",
              "null"
            ],
            "description": "Default shell.",
            "default": null
          },
          "user": {
            "type": [
              "string",
              "null"
            ],
            "description": "Guest user.",
            "default": null
          },
          "workdir": {
            "type": [
              "string",
              "null"
            ],
            "description": "Working directory for guest commands.",
            "default": null
          }
        }
      },
      "CloudSandboxSpec": {
        "type": "object",
        "description": "Settings shared by every cloud sandbox creation source.",
        "required": [
          "name"
        ],
        "properties": {
          "env": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EnvVar"
            },
            "description": "Environment variables visible to commands in the sandbox.",
            "default": []
          },
          "init": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/HandoffInit",
                "description": "Hand off PID 1 to a guest init binary after agentd setup."
              }
            ],
            "default": null
          },
          "labels": {
            "type": "object",
            "description": "User-defined labels attached to the sandbox.",
            "default": {},
            "additionalProperties": {
              "type": "string"
            },
            "propertyNames": {
              "type": "string"
            }
          },
          "lifecycle": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/SandboxPolicy",
                "description": "Sandbox lifecycle policy."
              }
            ],
            "default": {
              "ephemeral": false,
              "idle_timeout_secs": null,
              "max_duration_secs": null
            }
          },
          "mounts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CloudVolumeMount"
            },
            "description": "Volume mounts.",
            "default": []
          },
          "name": {
            "type": "string",
            "description": "Unique sandbox name.",
            "default": ""
          },
          "network": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/CloudNetworkSpec",
                "description": "Network specification."
              }
            ],
            "default": {
              "enabled": true,
              "strict": true
            }
          },
          "rlimits": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CloudRlimit"
            },
            "description": "Sandbox-wide resource limits inherited by guest processes.",
            "default": []
          },
          "runtime": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/CloudSandboxRuntimeOptions",
                "description": "Guest runtime options."
              }
            ],
            "default": {
              "cmd": null,
              "entrypoint": null,
              "log_level": null,
              "scripts": {},
              "shell": null,
              "user": null,
              "workdir": null
            }
          },
          "security_profile": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/SecurityProfile",
                "description": "In-guest security profile."
              }
            ],
            "default": "default"
          }
        }
      },
      "CloudSecretEntry": {
        "type": "object",
        "description": "A single cloud secret entry. Twin of domain [`SecretEntry`].",
        "required": [
          "env_var",
          "placeholder"
        ],
        "properties": {
          "allowed_hosts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CloudHostPattern"
            },
            "description": "Hosts allowed to receive this secret."
          },
          "env_var": {
            "type": "string",
            "description": "Environment variable name exposed to the sandbox."
          },
          "injection": {
            "$ref": "#/components/schemas/SecretInjection",
            "description": "Where the secret may be injected."
          },
          "on_violation": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/CloudViolationAction",
                "description": "Per-secret violation action overriding the config default."
              }
            ]
          },
          "placeholder": {
            "type": "string",
            "description": "Placeholder the sandbox sees instead of the real value."
          },
          "require_tls_identity": {
            "type": "boolean",
            "description": "Require verified TLS identity before substituting (default: true)."
          },
          "source": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/CloudSecretSource",
                "description": "Host-side source resolved into `value` at spawn time."
              }
            ]
          },
          "value": {
            "type": "string",
            "description": "The secret value (empty when `source` carries a reference instead)."
          }
        }
      },
      "CloudSecretSource": {
        "oneOf": [
          {
            "type": "object",
            "description": "Read from a host environment variable at apply time.",
            "required": [
              "var",
              "type"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "env"
                ]
              },
              "var": {
                "type": "string",
                "description": "Host environment variable name."
              }
            }
          },
          {
            "type": "object",
            "description": "Read from a host-side secret store reference.",
            "required": [
              "reference",
              "type"
            ],
            "properties": {
              "reference": {
                "type": "string",
                "description": "Store-specific secret reference."
              },
              "type": {
                "type": "string",
                "enum": [
                  "store"
                ]
              }
            }
          }
        ],
        "description": "Host-side source for a cloud secret. Twin of [`SecretSource`]."
      },
      "CloudSecretsConfig": {
        "type": "object",
        "description": "Secret-injection config for the cloud API. Twin of domain [`SecretsConfig`].",
        "properties": {
          "entries": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CloudSecretEntry"
            },
            "description": "Secrets to inject."
          },
          "on_violation": {
            "$ref": "#/components/schemas/CloudViolationAction",
            "description": "Default action when a placeholder leaks to a disallowed host."
          }
        }
      },
      "CloudSnapshot": {
        "oneOf": [
          {
            "allOf": [
              {
                "$ref": "#/components/schemas/CloudSnapshotDetails",
                "description": "Fields shared by every snapshot kind."
              },
              {
                "type": "object",
                "description": "A disk-only snapshot.",
                "required": [
                  "kind"
                ],
                "properties": {
                  "kind": {
                    "type": "string",
                    "enum": [
                      "disk"
                    ]
                  }
                }
              }
            ],
            "description": "A disk-only snapshot."
          }
        ],
        "description": "Wire shape of a completed cloud snapshot."
      },
      "CloudSnapshotDetails": {
        "type": "object",
        "description": "Fields shared by every completed cloud snapshot kind.",
        "required": [
          "name",
          "location",
          "digest",
          "size_bytes",
          "manifest",
          "labels",
          "created_at"
        ],
        "properties": {
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Creation timestamp."
          },
          "digest": {
            "type": "string",
            "description": "Snapshot identity: the `sha256:` digest of the canonical descriptor."
          },
          "labels": {
            "type": "object",
            "description": "User-defined labels stored on the snapshot.",
            "additionalProperties": {
              "type": "string"
            },
            "propertyNames": {
              "type": "string"
            }
          },
          "location": {
            "$ref": "#/components/schemas/CloudSnapshotLocation",
            "description": "Where the snapshot artifact resides."
          },
          "manifest": {
            "$ref": "#/components/schemas/SnapshotManifest",
            "description": "Canonical snapshot descriptor."
          },
          "name": {
            "type": "string",
            "description": "Snapshot name."
          },
          "sandbox_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Identifier of the sandbox the snapshot was captured from, when known."
          },
          "size_bytes": {
            "type": "integer",
            "format": "int64",
            "description": "Stored payload size in bytes.",
            "minimum": 0
          }
        }
      },
      "CloudSnapshotKind": {
        "type": "string",
        "description": "Kind of cloud snapshot artifact or capture operation.",
        "enum": [
          "disk"
        ]
      },
      "CloudSnapshotLocation": {
        "oneOf": [
          {
            "type": "object",
            "description": "Held in managed snapshot storage.",
            "required": [
              "id",
              "type"
            ],
            "properties": {
              "id": {
                "type": "string",
                "description": "Identifier of the stored artifact."
              },
              "type": {
                "type": "string",
                "enum": [
                  "managed"
                ]
              }
            }
          },
          {
            "type": "object",
            "description": "Stored in a directory on a mounted host volume.",
            "required": [
              "path",
              "type"
            ],
            "properties": {
              "path": {
                "type": "string",
                "description": "Artifact directory path on the host volume."
              },
              "type": {
                "type": "string",
                "enum": [
                  "host_volume"
                ]
              }
            }
          }
        ],
        "description": "Public locator for a managed or host-volume cloud snapshot."
      },
      "CloudSnapshotOperation": {
        "type": "object",
        "description": "Wire shape of the asynchronous snapshot operation returned by snapshot\ncapture endpoints.",
        "required": [
          "id",
          "kind",
          "status",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Timestamp of the terminal status, when the operation has finished."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Creation timestamp."
          },
          "error": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/CloudErrorDetails",
                "description": "Error details for a failed operation."
              }
            ]
          },
          "id": {
            "type": "string",
            "description": "Server-side operation identifier."
          },
          "kind": {
            "$ref": "#/components/schemas/CloudSnapshotKind",
            "description": "Kind of snapshot being captured."
          },
          "result": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/CloudSnapshot",
                "description": "The resulting snapshot, present once the operation succeeds."
              }
            ]
          },
          "status": {
            "$ref": "#/components/schemas/CloudSnapshotOperationStatus",
            "description": "Current operation status."
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "Timestamp of the most recent status change."
          }
        }
      },
      "CloudSnapshotOperationStatus": {
        "type": "string",
        "description": "Status of an asynchronous cloud snapshot operation.",
        "enum": [
          "queued",
          "in_progress",
          "succeeded",
          "failed"
        ]
      },
      "CloudSnapshotSpec": {
        "type": "object",
        "description": "Settings shared by every cloud snapshot capture kind.",
        "required": [
          "sandbox_id",
          "name"
        ],
        "properties": {
          "dest_dir": {
            "type": [
              "string",
              "null"
            ],
            "description": "Directory on a mounted host volume to write the artifact into. `None`\nstores the snapshot in managed snapshot storage."
          },
          "force": {
            "type": "boolean",
            "description": "Replace an existing snapshot with the same name."
          },
          "labels": {
            "type": "object",
            "description": "User-defined labels stored on the snapshot.",
            "additionalProperties": {
              "type": "string"
            },
            "propertyNames": {
              "type": "string"
            }
          },
          "name": {
            "type": "string",
            "description": "Snapshot name."
          },
          "record_integrity": {
            "type": "boolean",
            "description": "Record payload integrity metadata during capture."
          },
          "sandbox_id": {
            "type": "string",
            "description": "Immutable identifier of the sandbox to capture."
          }
        }
      },
      "CloudViolationAction": {
        "oneOf": [
          {
            "type": "object",
            "description": "Block the request silently.",
            "required": [
              "type"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "block"
                ]
              }
            }
          },
          {
            "type": "object",
            "description": "Block and log (default).",
            "required": [
              "type"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "block_and_log"
                ]
              }
            }
          },
          {
            "type": "object",
            "description": "Block and terminate the sandbox.",
            "required": [
              "type"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "block_and_terminate"
                ]
              }
            }
          },
          {
            "type": "object",
            "description": "Forward the request with the placeholder unchanged for matching hosts.",
            "required": [
              "hosts",
              "type"
            ],
            "properties": {
              "hosts": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/CloudHostPattern"
                },
                "description": "Hosts for which the placeholder passes through unchanged."
              },
              "type": {
                "type": "string",
                "enum": [
                  "passthrough"
                ]
              }
            }
          }
        ],
        "description": "Action on a cloud secret violation. Twin of [`ViolationAction`], with\n`Passthrough`'s host list normalized to a `hosts` field."
      },
      "CloudVolumeMount": {
        "oneOf": [
          {
            "type": "object",
            "description": "Bind mount a host directory into the guest.",
            "required": [
              "host",
              "guest",
              "type"
            ],
            "properties": {
              "guest": {
                "type": "string",
                "description": "Guest path to mount at."
              },
              "host": {
                "type": "string",
                "description": "Host directory to bind into the guest."
              },
              "host_permissions": {
                "$ref": "#/components/schemas/HostPermissions",
                "description": "Host permission policy applied to the mount."
              },
              "options": {
                "$ref": "#/components/schemas/MountOptions",
                "description": "Mount options (read-only, no-exec, …)."
              },
              "quota_mib": {
                "type": [
                  "integer",
                  "null"
                ],
                "format": "int32",
                "description": "Optional guest-write quota in MiB.",
                "minimum": 0
              },
              "stat_virtualization": {
                "$ref": "#/components/schemas/StatVirtualization",
                "description": "How guest `stat()` results are virtualized."
              },
              "type": {
                "type": "string",
                "enum": [
                  "bind"
                ]
              }
            }
          },
          {
            "type": "object",
            "description": "Mount a named volume into the guest.",
            "required": [
              "name",
              "guest",
              "type"
            ],
            "properties": {
              "guest": {
                "type": "string",
                "description": "Guest path to mount at."
              },
              "host_permissions": {
                "$ref": "#/components/schemas/HostPermissions",
                "description": "Host permission policy applied to the mount."
              },
              "name": {
                "type": "string",
                "description": "Named volume to mount."
              },
              "options": {
                "$ref": "#/components/schemas/MountOptions",
                "description": "Mount options (read-only, no-exec, …)."
              },
              "stat_virtualization": {
                "$ref": "#/components/schemas/StatVirtualization",
                "description": "How guest `stat()` results are virtualized."
              },
              "type": {
                "type": "string",
                "enum": [
                  "named"
                ]
              }
            }
          },
          {
            "type": "object",
            "description": "Temporary filesystem backed by guest memory.",
            "required": [
              "guest",
              "type"
            ],
            "properties": {
              "guest": {
                "type": "string",
                "description": "Guest path to mount at."
              },
              "options": {
                "$ref": "#/components/schemas/MountOptions",
                "description": "Mount options (read-only, no-exec, …)."
              },
              "size_mib": {
                "type": [
                  "integer",
                  "null"
                ],
                "format": "int32",
                "description": "Optional size cap in MiB.",
                "minimum": 0
              },
              "type": {
                "type": "string",
                "enum": [
                  "tmpfs"
                ]
              }
            }
          },
          {
            "type": "object",
            "description": "Mount a disk image file as a virtio-blk device at a guest path.",
            "required": [
              "host",
              "guest",
              "format",
              "type"
            ],
            "properties": {
              "format": {
                "$ref": "#/components/schemas/CloudDiskImageFormat",
                "description": "Disk image format."
              },
              "fstype": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Inner filesystem type (auto-detected if absent)."
              },
              "guest": {
                "type": "string",
                "description": "Guest path to mount at."
              },
              "host": {
                "type": "string",
                "description": "Host path to the disk image file."
              },
              "options": {
                "$ref": "#/components/schemas/MountOptions",
                "description": "Mount options (read-only, no-exec, …)."
              },
              "type": {
                "type": "string",
                "enum": [
                  "disk_image"
                ]
              }
            }
          }
        ],
        "description": "Persistent volume mounted at a path inside the sandbox."
      },
      "CreateSandboxRequest": {
        "allOf": [
          {
            "$ref": "#/components/schemas/CloudCreateSandboxRequest",
            "description": "The microsandbox create request - source, resources, runtime, env,\nmounts, network, and lifecycle - flattened onto the request body."
          },
          {
            "type": "object",
            "properties": {
              "registry": {
                "$ref": "#/components/schemas/RegistrySelection",
                "description": "Registry-credential selection for the image pull. Absent ⇒ `Auto`\n(infer the credential from the image's registry host). Credential-only:\nthis never changes *where* the image is pulled from - the image string\ncontrols that - only *which* stored credential, if any, is presented."
              },
              "slug": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Optional globally-unique slug - the SSH username token. Lowercase\nletters, digits, and single hyphens; 3-63 chars. Omitted ⇒ a dictionary\nslug is generated. Must be globally unique (409 on conflict)."
              }
            }
          }
        ],
        "description": "What the user sends in `POST /v1/sandboxes`.\n\nThe body carries the shared microsandbox [`CloudCreateSandboxRequest`] union.\nIts source-specific and common [`CloudSandboxSpec`] fields stay flat on the\nwire beside the cloud-only SSH `slug` and registry-credential selection."
      },
      "CreateVolumeRequest": {
        "type": "object",
        "description": "POST /v1/orgs/{slug}/volumes",
        "required": [
          "name"
        ],
        "properties": {
          "capacity_gib": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32",
            "description": "Optional per-volume storage cap in GiB (#496). Omitted/`null` = no\nper-volume limit (the org's plan cap still governs). Must be `>= 1` and\n`<= the org's storage cap`.",
            "minimum": 0
          },
          "labels": {
            "type": "object",
            "description": "User-defined labels (free-form string map). Omitted = none. Keys using a\nplatform-reserved prefix are rejected.",
            "additionalProperties": {
              "type": "string"
            },
            "propertyNames": {
              "type": "string"
            }
          },
          "name": {
            "type": "string",
            "description": "Volume name, unique within the org. Lowercase alphanumeric with single\ninternal hyphens (e.g. `team-ml`). Creates a `managed` volume; the host\ndisk is system-provisioned and has no name."
          },
          "storage": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/VolumeType",
                "description": "Physical backing for the volume. Omitted = `block` (the default and only\nvalue today)."
              }
            ]
          }
        }
      },
      "Destination": {
        "oneOf": [
          {
            "type": "string",
            "description": "Match any destination.",
            "enum": [
              "any"
            ]
          },
          {
            "type": "object",
            "description": "IP address or CIDR block (e.g. `\"1.2.3.4\"`, `\"10.0.0.0/8\"`).",
            "required": [
              "cidr"
            ],
            "properties": {
              "cidr": {
                "type": "string",
                "description": "IP address or CIDR block (e.g. `\"1.2.3.4\"`, `\"10.0.0.0/8\"`)."
              }
            }
          },
          {
            "type": "object",
            "description": "Exact domain name (e.g. `\"example.com\"`).",
            "required": [
              "domain"
            ],
            "properties": {
              "domain": {
                "type": "string",
                "description": "Exact domain name (e.g. `\"example.com\"`)."
              }
            }
          },
          {
            "type": "object",
            "description": "Domain suffix - the apex and any subdomain of it.",
            "required": [
              "domain_suffix"
            ],
            "properties": {
              "domain_suffix": {
                "type": "string",
                "description": "Domain suffix - the apex and any subdomain of it."
              }
            }
          },
          {
            "type": "object",
            "description": "A pre-defined destination group.",
            "required": [
              "group"
            ],
            "properties": {
              "group": {
                "$ref": "#/components/schemas/DestinationGroup",
                "description": "A pre-defined destination group."
              }
            }
          }
        ],
        "description": "Traffic destination matched by a network rule, such as an IP range or domain name."
      },
      "DestinationGroup": {
        "type": "string",
        "description": "Pre-defined destination category for a [`Destination::Group`] match.",
        "enum": [
          "public",
          "loopback",
          "private",
          "link_local",
          "metadata",
          "multicast",
          "host"
        ]
      },
      "Direction": {
        "type": "string",
        "description": "Direction a [`Rule`] applies to.",
        "enum": [
          "egress",
          "ingress",
          "any"
        ]
      },
      "DiskImageFormat": {
        "type": "string",
        "description": "Disk image format for virtio-blk root filesystems and volume mounts.",
        "enum": [
          "Qcow2",
          "Raw",
          "Vmdk"
        ]
      },
      "DnsConfig": {
        "type": "object",
        "description": "DNS interception and filtering settings. Carried in [`NetworkSpec::dns`].",
        "properties": {
          "nameservers": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Upstream nameservers as `IP`, `IP:PORT`, `HOST`, or `HOST:PORT`\nstrings. Empty falls back to the host's `/etc/resolv.conf`.",
            "default": []
          },
          "query_timeout_ms": {
            "type": "integer",
            "format": "int64",
            "description": "Per-query timeout in milliseconds. Default: 5000.",
            "default": 5000,
            "minimum": 0
          },
          "rebind_protection": {
            "type": "boolean",
            "description": "Whether DNS-rebinding protection is enabled. Default: true.",
            "default": true
          }
        }
      },
      "EnvVar": {
        "type": "object",
        "description": "Environment variable entry.",
        "required": [
          "key",
          "value"
        ],
        "properties": {
          "key": {
            "type": "string",
            "description": "Environment variable name."
          },
          "value": {
            "type": "string",
            "description": "Environment variable value."
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "description": "Error envelope returned by every non-2xx response.",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "description": "Stable machine-readable error code, e.g. `invalid_api_key`."
              },
              "message": {
                "type": "string",
                "description": "Human-readable description of the error."
              },
              "details": {
                "type": [
                  "object",
                  "null"
                ],
                "description": "Optional structured context for the error."
              }
            }
          }
        }
      },
      "EventActor": {
        "oneOf": [
          {
            "type": "object",
            "required": [
              "user_id",
              "label",
              "redacted_email",
              "kind"
            ],
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "user"
                ]
              },
              "label": {
                "type": "string"
              },
              "redacted_email": {
                "type": "string"
              },
              "user_id": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "type": "object",
            "required": [
              "api_key_id",
              "name",
              "kind"
            ],
            "properties": {
              "api_key_id": {
                "type": "string",
                "format": "uuid"
              },
              "kind": {
                "type": "string",
                "enum": [
                  "api_key"
                ]
              },
              "name": {
                "type": "string"
              }
            }
          },
          {
            "type": "object",
            "required": [
              "code",
              "label",
              "kind"
            ],
            "properties": {
              "code": {
                "type": "string"
              },
              "kind": {
                "type": "string",
                "enum": [
                  "system"
                ]
              },
              "label": {
                "type": "string"
              }
            }
          },
          {
            "type": "object",
            "description": "The authorized SSH key that authenticated a front-door session. The\nprincipal SSH actually proves at connect is the *key*, not a person -\nthe scope-only model (D8) doesn't bind a key to a user (a global key may\nbe shared), so the session is attributed to the key (label + fingerprint),\nwhile whoever registered it rides the event payload as `added_by`.",
            "required": [
              "key_id",
              "fingerprint",
              "kind"
            ],
            "properties": {
              "fingerprint": {
                "type": "string",
                "description": "`SHA256:…` fingerprint."
              },
              "key_id": {
                "type": "string",
                "format": "uuid"
              },
              "kind": {
                "type": "string",
                "enum": [
                  "ssh_key"
                ]
              },
              "label": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Human label set at registration (e.g. `alice-laptop`), if any."
              }
            }
          }
        ],
        "description": "Maximum total length, in characters, of the truncated `command` field in\nImmutable actor snapshot stored alongside an event."
      },
      "EventResponse": {
        "type": "object",
        "description": "A single audit/lifecycle event returned by the API.",
        "required": [
          "id",
          "org_id",
          "category",
          "event_type",
          "actor",
          "payload",
          "timestamp"
        ],
        "properties": {
          "actor": {
            "$ref": "#/components/schemas/EventActor"
          },
          "category": {
            "type": "string"
          },
          "event_type": {
            "type": "string"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "org_id": {
            "type": "string",
            "format": "uuid"
          },
          "payload": {},
          "timestamp": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "FileMoveRequest": {
        "type": "object",
        "required": [
          "from",
          "to"
        ],
        "properties": {
          "from": {
            "type": "string"
          },
          "to": {
            "type": "string"
          }
        }
      },
      "FilePathRequest": {
        "type": "object",
        "required": [
          "path"
        ],
        "properties": {
          "path": {
            "type": "string"
          }
        }
      },
      "FileSnapshotState": {
        "type": "object",
        "description": "Concrete file-backed snapshot state.",
        "required": [
          "format",
          "fstype",
          "upper"
        ],
        "properties": {
          "format": {
            "$ref": "#/components/schemas/SnapshotFormat",
            "description": "On-disk payload format."
          },
          "fstype": {
            "type": "string",
            "description": "Filesystem type inside the payload."
          },
          "upper": {
            "$ref": "#/components/schemas/UpperLayer",
            "description": "File-backed upper-layer binding."
          }
        },
        "additionalProperties": false
      },
      "HandoffInit": {
        "type": "object",
        "description": "Fully-assembled handoff-init specification.",
        "required": [
          "cmd"
        ],
        "properties": {
          "args": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Supplemental argv. `argv[0]` is implicitly `cmd`."
          },
          "cmd": {
            "type": "string",
            "description": "Init binary: absolute path inside the guest rootfs, or the literal `auto`.\n\nAlways a Linux-style `/`-separated path - never build it with host OS path APIs, whose semantics diverge on Windows (`\\` separators, `/sbin/init` treated as relative)."
          },
          "env": {
            "type": "array",
            "items": {
              "type": "array",
              "items": false,
              "prefixItems": [
                {
                  "type": "string"
                },
                {
                  "type": "string"
                }
              ]
            },
            "description": "Extra env vars merged on top of the inherited env."
          }
        }
      },
      "HostPermissions": {
        "type": "string",
        "description": "Host permission propagation policy for a virtiofs-backed volume mount.\n\nSerializes/deserializes as the lowercase variant name (`\"private\"`, `\"mirror\"`) to align with the CLI and NAPI spellings.",
        "enum": [
          "private",
          "mirror"
        ]
      },
      "ImageRef": {
        "type": "object",
        "description": "Reference to the pinned OCI image used by the snapshot.",
        "required": [
          "ref",
          "manifest_digest"
        ],
        "properties": {
          "manifest_digest": {
            "type": "string",
            "description": "Pinned OCI manifest digest."
          },
          "ref": {
            "type": "string",
            "description": "Human-readable image reference."
          }
        },
        "additionalProperties": false
      },
      "InterceptCaConfig": {
        "type": "object",
        "description": "Certificate authority configuration for TLS interception.",
        "properties": {
          "cert_path": {
            "type": [
              "string",
              "null"
            ],
            "description": "Path to an existing CA certificate PEM file. If `None`, a CA is\nauto-generated and persisted."
          },
          "key_path": {
            "type": [
              "string",
              "null"
            ],
            "description": "Path to an existing CA private key PEM file. If `None`, a key is\nauto-generated and persisted."
          }
        }
      },
      "InterfaceOverrides": {
        "type": "object",
        "description": "Optional guest interface overrides. Unset fields are derived from the\nsandbox slot by the local network engine. Carried in\n[`NetworkSpec::interface`].",
        "properties": {
          "ipv4_address": {
            "type": [
              "string",
              "null"
            ],
            "description": "Guest IPv4 address (e.g. `172.16.0.2`). Default: derived from slot.",
            "default": null
          },
          "ipv4_pool": {
            "type": [
              "string",
              "null"
            ],
            "description": "Guest IPv4 pool CIDR (e.g. `\"172.16.0.0/12\"`). Default: derived from slot.",
            "default": null
          },
          "ipv6_address": {
            "type": [
              "string",
              "null"
            ],
            "description": "Guest IPv6 address. Default: derived from slot.",
            "default": null
          },
          "ipv6_pool": {
            "type": [
              "string",
              "null"
            ],
            "description": "Guest IPv6 pool CIDR. Default: derived from slot.",
            "default": null
          },
          "mac": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "integer",
              "format": "int32",
              "minimum": 0
            },
            "description": "Guest MAC address as six octets. Default: derived from slot.",
            "default": null
          },
          "mtu": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32",
            "description": "Interface MTU. Default: 1500.",
            "default": null,
            "minimum": 0
          }
        }
      },
      "Invoice": {
        "type": "object",
        "description": "Invoice from the billing provider (Autumn/Stripe).",
        "required": [
          "id",
          "status",
          "total_cents",
          "currency",
          "period_start",
          "period_end",
          "created_at"
        ],
        "properties": {
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "currency": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "invoice_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "period_end": {
            "type": "string",
            "format": "date-time"
          },
          "period_start": {
            "type": "string",
            "format": "date-time"
          },
          "status": {
            "type": "string"
          },
          "total_cents": {
            "type": "integer",
            "format": "int64"
          }
        }
      },
      "MessageResponse": {
        "type": "object",
        "description": "Simple message response for operations without a body.",
        "required": [
          "message"
        ],
        "properties": {
          "message": {
            "type": "string"
          }
        }
      },
      "MetricSeries": {
        "type": "object",
        "description": "One labelled series for a metric.",
        "required": [
          "labels",
          "samples"
        ],
        "properties": {
          "labels": {
            "type": "object",
            "description": "Series labels (e.g. `sandbox_name`), minus internal Prometheus names.",
            "additionalProperties": {
              "type": "string"
            },
            "propertyNames": {
              "type": "string"
            }
          },
          "samples": {
            "type": "array",
            "items": {
              "type": "array",
              "items": {
                "type": "number",
                "format": "double"
              }
            },
            "description": "`[epoch_millis, value]` pairs in ascending time order."
          }
        }
      },
      "MetricsRange": {
        "type": "object",
        "description": "The resolved time window a metrics response covers.",
        "required": [
          "start",
          "end",
          "step_secs"
        ],
        "properties": {
          "end": {
            "type": "integer",
            "format": "int64",
            "description": "Window end, epoch milliseconds."
          },
          "start": {
            "type": "integer",
            "format": "int64",
            "description": "Window start, epoch milliseconds."
          },
          "step_secs": {
            "type": "integer",
            "format": "int64",
            "description": "Sample resolution in seconds.",
            "minimum": 0
          }
        }
      },
      "MountOptions": {
        "type": "object",
        "description": "Guest mount behavior shared by every volume mount kind.",
        "properties": {
          "nodev": {
            "type": "boolean",
            "description": "Whether device files on the mount are ignored.",
            "default": false
          },
          "noexec": {
            "type": "boolean",
            "description": "Whether direct execution from the mount is disabled.\n\nThis prevents `execve` of binaries or scripts located on the mount. Interpreters can still read files from the mount, for example `sh /mnt/script.sh`, because the interpreter itself executes from a different filesystem.",
            "default": false
          },
          "nosuid": {
            "type": "boolean",
            "description": "Whether setuid and setgid privilege elevation from files on the mount is ignored.",
            "default": false
          },
          "override_gid": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32",
            "description": "Guest gid presented for host files under this mount that carry no\nper-file stat override. See [`override_uid`](Self::override_uid); the two\nmust be set together.",
            "default": null,
            "minimum": 0
          },
          "override_uid": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32",
            "description": "Guest uid presented for host files under this mount that carry no\nper-file stat override.\n\nHost-created files (written outside the guest) have no override, so\nwithout this they surface with the runtime's fallback owner. When set,\nsuch files are presented as this uid instead. Must be set together with\n[`override_gid`](Self::override_gid). `None` keeps the fallback.",
            "default": null,
            "minimum": 0
          },
          "readonly": {
            "type": "boolean",
            "description": "Whether the mount is read-only.\n\nGuest writes fail with the kernel's read-only filesystem behavior. Virtiofs-backed mounts also reject writes on the host-side filesystem server as defense in depth.",
            "default": false
          }
        }
      },
      "NetworkPolicy": {
        "type": "object",
        "description": "Egress/ingress network policy: an ordered [`Rule`] list plus a\nper-direction default [`Action`]. Carried in [`NetworkSpec::policy`].",
        "properties": {
          "default_egress": {
            "$ref": "#/components/schemas/Action",
            "description": "Default action for egress traffic matching no rule. Default: `Deny`."
          },
          "default_ingress": {
            "$ref": "#/components/schemas/Action",
            "description": "Default action for ingress traffic matching no rule. Default: `Deny`."
          },
          "rules": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Rule"
            },
            "description": "Ordered rules, evaluated first-match-wins per direction."
          }
        }
      },
      "OrgBillingTermsOverrideStatus": {
        "type": "string",
        "description": "Operational state of one immutable negotiated-pricing version.",
        "enum": [
          "applying",
          "pending_payment",
          "scheduled",
          "active",
          "failed",
          "expired"
        ]
      },
      "OrgMemberResponse": {
        "type": "object",
        "description": "Member with joined user profile data - repository read view.",
        "required": [
          "id",
          "user_id",
          "email",
          "role",
          "joined_at"
        ],
        "properties": {
          "avatar_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "display_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "email": {
            "type": "string"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "joined_at": {
            "type": "string",
            "format": "date-time"
          },
          "role": {
            "$ref": "#/components/schemas/OrgRole"
          },
          "user_id": {
            "type": "string",
            "format": "uuid"
          }
        }
      },
      "OrgRole": {
        "type": "string",
        "description": "Organization membership role.",
        "enum": [
          "owner",
          "admin",
          "member",
          "viewer"
        ]
      },
      "OrgWithRole": {
        "type": "object",
        "description": "Organization with the requesting user's role included.",
        "required": [
          "id",
          "name",
          "slug",
          "mfa_required",
          "role",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "mfa_required": {
            "type": "boolean"
          },
          "name": {
            "type": "string"
          },
          "role": {
            "$ref": "#/components/schemas/OrgRole"
          },
          "slug": {
            "type": "string"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "PaginatedEventResponse": {
        "type": "object",
        "description": "Wrapper for paginated event responses (needed for OpenAPI schema generation).",
        "required": [
          "data",
          "has_more"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EventResponse"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "PaginatedMemberResponse": {
        "type": "object",
        "description": "Paginated list of organization members.",
        "required": [
          "data",
          "has_more"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OrgMemberResponse"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "PaginatedSandboxResponse": {
        "type": "object",
        "description": "Paginated list of sandboxes.",
        "required": [
          "data",
          "has_more"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SandboxResponse"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "PaginatedSnapshotCandidateResponse": {
        "type": "object",
        "description": "Paginated list of sandboxes eligible for snapshots.",
        "required": [
          "data",
          "has_more"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SandboxResponse"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "PaginatedSnapshotOperationResponse": {
        "type": "object",
        "description": "Paginated list of snapshot operations.",
        "required": [
          "data",
          "has_more"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SnapshotOperationListItem"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "PaginatedSnapshotResponse": {
        "type": "object",
        "description": "Paginated list of managed snapshots.",
        "required": [
          "data",
          "has_more"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CloudSnapshot"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "PoolUsage": {
        "type": "object",
        "description": "An org's monthly pool usage against its hard-cap pools - the shared home of\nthe exhaustion predicates, whether read per-org (embedded in `QuotaUsage`)\nor in the quota monitor's batched sweep (`OrgPoolUsage`).",
        "required": [
          "hard_cap",
          "vcpu_hours_used",
          "memory_gib_hours_used"
        ],
        "properties": {
          "hard_cap": {
            "type": "boolean",
            "description": "Whether the plan hard-stops at its included pools (no overflow).\nDERIVED (not a stored column): true iff the resolved plan is unpriced -\nan unpriced plan has no overage path, so its pools become walls."
          },
          "included_memory_gib_hours": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32",
            "description": "Included memory GiB-hours/month; `None` = unlimited."
          },
          "included_vcpu_hours": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32",
            "description": "Included vCPU-hours/month; `None` = unlimited. On `hard_cap` plans this\nis the compute wall; on priced plans, usage beyond it is metered overage."
          },
          "memory_gib_hours_used": {
            "type": "number",
            "format": "double",
            "description": "Memory GiB-hours consumed this billing month (compute-on-read from `sandbox_runs`)."
          },
          "vcpu_hours_used": {
            "type": "number",
            "format": "double",
            "description": "vCPU-hours consumed this billing month (compute-on-read from `sandbox_runs`)."
          }
        }
      },
      "PortProtocol": {
        "type": "string",
        "description": "Transport protocol for a published port.",
        "enum": [
          "tcp",
          "udp"
        ]
      },
      "PortRange": {
        "type": "object",
        "description": "Inclusive guest-side port range for a [`Rule`] match.",
        "required": [
          "start",
          "end"
        ],
        "properties": {
          "end": {
            "type": "integer",
            "format": "int32",
            "description": "End port (inclusive).",
            "minimum": 0
          },
          "start": {
            "type": "integer",
            "format": "int32",
            "description": "Start port (inclusive).",
            "minimum": 0
          }
        }
      },
      "Protocol": {
        "type": "string",
        "description": "Protocol filter for a [`Rule`].",
        "enum": [
          "tcp",
          "udp",
          "icmpv4",
          "icmpv6"
        ]
      },
      "PublishedPortSpec": {
        "type": "object",
        "description": "A published port mapping between host and guest.",
        "required": [
          "host_port",
          "guest_port",
          "host_bind"
        ],
        "properties": {
          "guest_port": {
            "type": "integer",
            "format": "int32",
            "description": "Guest-side port to forward to.",
            "minimum": 0
          },
          "host_bind": {
            "type": "string",
            "description": "Host address to bind. Defaults to loopback."
          },
          "host_port": {
            "type": "integer",
            "format": "int32",
            "description": "Host-side port to bind.",
            "minimum": 0
          },
          "protocol": {
            "$ref": "#/components/schemas/PortProtocol",
            "description": "Transport protocol."
          }
        }
      },
      "QuotaUsage": {
        "allOf": [
          {
            "$ref": "#/components/schemas/PoolUsage"
          },
          {
            "type": "object",
            "required": [
              "pool_exhausted",
              "total_sandboxes",
              "concurrent_ephemeral_running",
              "concurrent_persistent_running",
              "snapshot_storage_bytes",
              "is_suspended"
            ],
            "properties": {
              "concurrent_ephemeral_running": {
                "type": "integer",
                "format": "int64",
                "description": "Currently running/starting ephemeral sandboxes."
              },
              "concurrent_persistent_running": {
                "type": "integer",
                "format": "int64",
                "description": "Currently running/starting persistent sandboxes."
              },
              "included_disk_gib_hours": {
                "type": [
                  "integer",
                  "null"
                ],
                "format": "int32",
                "description": "Included writable-disk GiB-hours/month (display/billing only - never a wall)."
              },
              "included_storage_gib": {
                "type": [
                  "integer",
                  "null"
                ],
                "format": "int32",
                "description": "Included durable-storage GiB/month."
              },
              "is_suspended": {
                "type": "boolean",
                "description": "Whether sandbox usage has been manually suspended for the organization."
              },
              "max_concurrent_ephemeral_running": {
                "type": [
                  "integer",
                  "null"
                ],
                "format": "int32",
                "description": "Maximum simultaneously-running ephemeral sandboxes."
              },
              "max_concurrent_persistent_running": {
                "type": [
                  "integer",
                  "null"
                ],
                "format": "int32",
                "description": "Maximum simultaneously-running persistent sandboxes."
              },
              "max_disk_size_mib_per_sandbox": {
                "type": [
                  "integer",
                  "null"
                ],
                "format": "int32",
                "description": "Maximum writable disk size (MiB) per sandbox."
              },
              "max_memory_mib_per_sandbox": {
                "type": [
                  "integer",
                  "null"
                ],
                "format": "int32",
                "description": "Maximum memory (MiB) per sandbox."
              },
              "max_sandboxes": {
                "type": [
                  "integer",
                  "null"
                ],
                "format": "int32",
                "description": "Maximum total sandboxes the org can have."
              },
              "max_snapshot_storage_gib": {
                "type": [
                  "integer",
                  "null"
                ],
                "format": "int32",
                "description": "Per-org managed-snapshot storage hard cap (GiB)."
              },
              "max_total_storage_gib": {
                "type": [
                  "integer",
                  "null"
                ],
                "format": "int32",
                "description": "Per-org durable-storage hard cap (GiB)."
              },
              "max_uptime_minutes_per_sandbox": {
                "type": [
                  "integer",
                  "null"
                ],
                "format": "int32",
                "description": "Maximum uptime per sandbox (minutes), if set."
              },
              "max_vcpus_per_sandbox": {
                "type": [
                  "integer",
                  "null"
                ],
                "format": "int32",
                "description": "Maximum CPU cores per sandbox."
              },
              "pool_exhausted": {
                "type": "boolean",
                "description": "Snapshot of [`PoolUsage::pool_exhausted`] at read time, for API\nconsumers - true when this org's sandboxes are being stopped and starts\nrefused."
              },
              "snapshot_storage_bytes": {
                "type": "integer",
                "format": "int64",
                "description": "Exact summed compressed archive size (bytes) of the org's managed\nsnapshots - the value snapshot quota enforcement compares."
              },
              "total_sandboxes": {
                "type": "integer",
                "format": "int64",
                "description": "Total sandboxes (all states) for the org."
              }
            }
          }
        ],
        "description": "Resolved quota limits and current usage for an organization.\n\nLimits are resolved via COALESCE: per-org override > plan. Every limit is\n`Option`: `None` = unlimited (counts/budgets) or the platform default\n(per-sandbox vCPU/memory/disk). The API materializes resource defaults in\nresponses. There is no magic 0."
      },
      "RegistrySelection": {
        "oneOf": [
          {
            "type": "object",
            "description": "Infer a stored credential from the image registry host.",
            "required": [
              "mode"
            ],
            "properties": {
              "mode": {
                "type": "string",
                "enum": [
                  "auto"
                ]
              }
            }
          },
          {
            "type": "object",
            "description": "Force an anonymous pull.",
            "required": [
              "mode"
            ],
            "properties": {
              "mode": {
                "type": "string",
                "enum": [
                  "anonymous"
                ]
              }
            }
          },
          {
            "type": "object",
            "description": "Use an already-stored organization credential.",
            "required": [
              "credential_id",
              "mode"
            ],
            "properties": {
              "credential_id": {
                "type": "string",
                "format": "uuid",
                "description": "Id of a configured `OrgRegistryCredential` belonging to the org."
              },
              "mode": {
                "type": "string",
                "enum": [
                  "specific"
                ]
              }
            }
          },
          {
            "type": "object",
            "description": "Use plaintext credentials for this create-and-start operation only.",
            "required": [
              "username",
              "password",
              "mode"
            ],
            "properties": {
              "mode": {
                "type": "string",
                "enum": [
                  "inline"
                ]
              },
              "password": {
                "type": "string",
                "format": "password",
                "description": "Registry password or access token. Always redacted from `Debug`."
              },
              "username": {
                "type": "string",
                "description": "Registry username."
              }
            }
          }
        ],
        "description": "Registry credential selection accepted on sandbox-create requests.\n\n`Inline` is deliberately request-only: its plaintext fields must be vaulted\nbefore persistence and must never enter the sandbox row, a worker launch\ndefinition, a log, or an API response."
      },
      "Rlimit": {
        "type": "object",
        "description": "A POSIX resource limit.",
        "required": [
          "resource",
          "soft",
          "hard"
        ],
        "properties": {
          "hard": {
            "type": "integer",
            "format": "int64",
            "description": "Hard limit (ceiling, requires privileges to raise).",
            "minimum": 0
          },
          "resource": {
            "$ref": "#/components/schemas/RlimitResource",
            "description": "Resource type."
          },
          "soft": {
            "type": "integer",
            "format": "int64",
            "description": "Soft limit (can be raised up to hard limit by the process).",
            "minimum": 0
          }
        }
      },
      "RlimitResource": {
        "type": "string",
        "description": "POSIX resource limit identifiers.",
        "enum": [
          "Cpu",
          "Fsize",
          "Data",
          "Stack",
          "Core",
          "Rss",
          "Nproc",
          "Nofile",
          "Memlock",
          "As",
          "Locks",
          "Sigpending",
          "Msgqueue",
          "Nice",
          "Rtprio",
          "Rttime"
        ]
      },
      "Rule": {
        "type": "object",
        "description": "A single egress/ingress policy rule. Evaluated first-match-wins per\ndirection.",
        "required": [
          "direction",
          "destination",
          "action"
        ],
        "properties": {
          "action": {
            "$ref": "#/components/schemas/Action",
            "description": "Action to take on a match."
          },
          "destination": {
            "$ref": "#/components/schemas/Destination",
            "description": "Destination filter (direction-dependent interpretation)."
          },
          "direction": {
            "$ref": "#/components/schemas/Direction",
            "description": "Direction this rule applies to."
          },
          "ports": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PortRange"
            },
            "description": "Guest-side port-range set; empty matches any port."
          },
          "protocols": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Protocol"
            },
            "description": "Protocol set; empty matches any protocol."
          }
        }
      },
      "SandboxDailyUsage": {
        "type": "object",
        "description": "Compute usage for one sandbox on one UTC day, across both billed dimensions.\n\nComputed on read from `sandbox_runs` (not from the billing provider) - open runs accrue\nagainst `now()`, so a *running* sandbox shows live usage. Quantities are\nseconds-based (vCPU-seconds, MiB-seconds), the same units the metering events\ncarry; display converts to vCPU-hours / GiB-hours. Sum over days for a\nsandbox's period total; sum over sandboxes for the daily series.",
        "required": [
          "sandbox_id",
          "day",
          "vcpu_seconds",
          "mem_mib_seconds",
          "disk_mib_seconds"
        ],
        "properties": {
          "day": {
            "type": "string",
            "format": "date-time",
            "description": "UTC midnight of the day this usage falls in."
          },
          "disk_mib_seconds": {
            "type": "number",
            "format": "double",
            "description": "Σ(overlap_secs × disk_mib) for this sandbox on this day - provisioned\nwritable disk billed on the same GiB-seconds basis while the sandbox runs."
          },
          "mem_mib_seconds": {
            "type": "number",
            "format": "double",
            "description": "Σ(overlap_secs × mem_mib) for this sandbox on this day."
          },
          "sandbox_id": {
            "type": "string",
            "format": "uuid"
          },
          "vcpu_seconds": {
            "type": "number",
            "format": "double",
            "description": "Σ(overlap_secs × vcpus) for this sandbox on this day."
          }
        }
      },
      "SandboxIdentity": {
        "type": "object",
        "description": "Minimal sandbox display info - the directory that turns the `sandbox_id`s in\na [`UsageBreakdown`]'s rows into human labels without a second round-trip.",
        "required": [
          "id",
          "name",
          "slug"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          }
        }
      },
      "SandboxLogLevel": {
        "type": "string",
        "description": "Runtime log verbosity for sandbox specs.",
        "enum": [
          "error",
          "warn",
          "info",
          "debug",
          "trace"
        ]
      },
      "SandboxMetricsResponse": {
        "type": "object",
        "description": "Per-sandbox metrics over a time range, keyed by metric (`cpu`,\n`memory_bytes`, `net_rx_bytes_rate`, …). See `plans/monitoring-plan.md`\n§ B for the catalog.",
        "required": [
          "sandbox_id",
          "range",
          "metrics"
        ],
        "properties": {
          "metrics": {
            "type": "object",
            "description": "Metric key → series. An empty series means \"no data in range\".",
            "additionalProperties": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/MetricSeries"
              }
            },
            "propertyNames": {
              "type": "string"
            }
          },
          "range": {
            "$ref": "#/components/schemas/MetricsRange",
            "description": "The resolved query window."
          },
          "sandbox_id": {
            "type": "string",
            "format": "uuid",
            "description": "The sandbox these metrics belong to."
          }
        }
      },
      "SandboxMountResponse": {
        "oneOf": [
          {
            "type": "object",
            "description": "Host-subpath bind mount.",
            "required": [
              "guest",
              "options",
              "stat_virtualization",
              "host_permissions",
              "type"
            ],
            "properties": {
              "guest": {
                "type": "string"
              },
              "host_permissions": {
                "$ref": "#/components/schemas/HostPermissions"
              },
              "options": {
                "$ref": "#/components/schemas/MountOptions"
              },
              "quota_mib": {
                "type": [
                  "integer",
                  "null"
                ],
                "format": "int32",
                "minimum": 0
              },
              "stat_virtualization": {
                "$ref": "#/components/schemas/StatVirtualization"
              },
              "type": {
                "type": "string",
                "enum": [
                  "Bind"
                ]
              }
            }
          },
          {
            "type": "object",
            "description": "Named-volume mount.",
            "required": [
              "guest",
              "options",
              "stat_virtualization",
              "host_permissions",
              "type"
            ],
            "properties": {
              "guest": {
                "type": "string"
              },
              "host_permissions": {
                "$ref": "#/components/schemas/HostPermissions"
              },
              "options": {
                "$ref": "#/components/schemas/MountOptions"
              },
              "stat_virtualization": {
                "$ref": "#/components/schemas/StatVirtualization"
              },
              "type": {
                "type": "string",
                "enum": [
                  "Named"
                ]
              }
            }
          },
          {
            "type": "object",
            "description": "Guest-memory tmpfs.",
            "required": [
              "guest",
              "options",
              "type"
            ],
            "properties": {
              "guest": {
                "type": "string"
              },
              "options": {
                "$ref": "#/components/schemas/MountOptions"
              },
              "size_mib": {
                "type": [
                  "integer",
                  "null"
                ],
                "format": "int32",
                "minimum": 0
              },
              "type": {
                "type": "string",
                "enum": [
                  "Tmpfs"
                ]
              }
            }
          },
          {
            "type": "object",
            "description": "Disk-image mount.",
            "required": [
              "guest",
              "format",
              "options",
              "type"
            ],
            "properties": {
              "format": {
                "$ref": "#/components/schemas/DiskImageFormat"
              },
              "fstype": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "guest": {
                "type": "string"
              },
              "options": {
                "$ref": "#/components/schemas/MountOptions"
              },
              "type": {
                "type": "string",
                "enum": [
                  "DiskImage"
                ]
              }
            }
          }
        ],
        "description": "A mount, projected for the user: guest path + options, never the resolved\nvolume id / source."
      },
      "SandboxNetworkResponse": {
        "type": "object",
        "description": "Network config, projected for the user. Secrets drop their vault path.",
        "required": [
          "enabled",
          "ports",
          "trust_host_cas"
        ],
        "properties": {
          "dns": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/DnsConfig"
              }
            ]
          },
          "enabled": {
            "type": "boolean"
          },
          "interface": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/InterfaceOverrides"
              }
            ]
          },
          "max_connections": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0
          },
          "policy": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/NetworkPolicy"
              }
            ]
          },
          "ports": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PublishedPortSpec"
            }
          },
          "secrets": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SandboxSecretResponse"
            }
          },
          "tls": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/TlsConfig"
              }
            ]
          },
          "trust_host_cas": {
            "type": "boolean"
          }
        }
      },
      "SandboxPolicy": {
        "type": "object",
        "description": "Sandbox lifecycle policy.",
        "properties": {
          "ephemeral": {
            "type": "boolean",
            "description": "Whether the sandbox is ephemeral.\n\nEphemeral sandboxes are one-off: the host runtime that owns the\nprocess removes the persisted DB row and on-disk state when the VM\nreaches a terminal status, and other host runtimes opportunistically\nclean up ephemeral leftovers from runtimes that died before they\ncould self-clean. Defaults to `false` (persistent); named and created\nsandboxes stay inspectable and restartable after they stop."
          },
          "idle_timeout_secs": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "description": "Idle timeout in seconds. `None` = no idle detection.",
            "minimum": 0
          },
          "max_duration_secs": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "description": "Hard cap on total sandbox lifetime in seconds. `None` = run forever.",
            "minimum": 0
          }
        }
      },
      "SandboxResponse": {
        "type": "object",
        "description": "Sandbox details and resolved configuration.",
        "required": [
          "id",
          "org_id",
          "name",
          "slug",
          "status",
          "ephemeral",
          "created_at"
        ],
        "properties": {
          "automatic_recovery_ends_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When an explicit recovery choice becomes necessary."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "ephemeral": {
            "type": "boolean"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "last_failure_message": {
            "type": [
              "string",
              "null"
            ],
            "description": "Human-readable reason for the last failure."
          },
          "name": {
            "type": "string"
          },
          "org_id": {
            "type": "string",
            "format": "uuid"
          },
          "slug": {
            "type": "string"
          },
          "spec": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/SandboxSpecResponse",
                "description": "Curated projection of the resolved spec; absent until resolved."
              }
            ]
          },
          "started_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Latest run's start time; `null` if the sandbox has never run."
          },
          "status": {
            "$ref": "#/components/schemas/SandboxStatus"
          },
          "status_reason": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/SandboxStatusReason",
                "description": "Scheduler condition while `status` is `starting`."
              }
            ]
          },
          "stopped_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Latest run's stop time; `null` while open or never run."
          },
          "temporarily_unavailable_since": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the current worker outage became user-visible."
          }
        }
      },
      "SandboxRuntimeOptions": {
        "type": "object",
        "description": "Guest runtime options for a sandbox.",
        "properties": {
          "cmd": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            },
            "description": "Image command override.",
            "default": null
          },
          "disable_metrics_sample": {
            "type": "boolean",
            "description": "Force-disable metrics sampling regardless of `metrics_sample_interval_ms`.",
            "default": false
          },
          "entrypoint": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            },
            "description": "Image entrypoint override.",
            "default": null
          },
          "hostname": {
            "type": [
              "string",
              "null"
            ],
            "description": "Guest hostname override.",
            "default": null
          },
          "log_level": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/SandboxLogLevel",
                "description": "Runtime log verbosity."
              }
            ],
            "default": null
          },
          "metrics_sample_interval_ms": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "description": "Metrics sampling interval in milliseconds. `None` disables sampling.",
            "default": 1000,
            "minimum": 0
          },
          "scripts": {
            "type": "object",
            "description": "Named scripts available inside the guest.",
            "default": {},
            "additionalProperties": {
              "type": "string"
            },
            "propertyNames": {
              "type": "string"
            }
          },
          "shell": {
            "type": [
              "string",
              "null"
            ],
            "description": "Default shell for scripts and interactive sessions.",
            "default": null
          },
          "user": {
            "type": [
              "string",
              "null"
            ],
            "description": "Guest user identity override.",
            "default": null
          },
          "workdir": {
            "type": [
              "string",
              "null"
            ],
            "description": "Working directory inside the guest.",
            "default": null
          }
        }
      },
      "SandboxSecretResponse": {
        "type": "object",
        "description": "A network secret, projected for the user: the env var, placeholder, and its\nallow-list / injection policy. The value isn't in the resolved spec at all,\nand the OpenBao vault path is never exposed.",
        "required": [
          "env_var",
          "placeholder",
          "allowed_hosts",
          "injection"
        ],
        "properties": {
          "allowed_hosts": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "env_var": {
            "type": "string"
          },
          "injection": {
            "type": "object"
          },
          "on_violation": {
            "type": "object"
          },
          "placeholder": {
            "type": "string"
          }
        }
      },
      "SandboxSpecResponse": {
        "type": "object",
        "description": "Resolved sandbox configuration. Secret values are omitted.",
        "required": [
          "resources",
          "runtime",
          "env",
          "labels",
          "rlimits",
          "lifecycle",
          "mounts",
          "network"
        ],
        "properties": {
          "env": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EnvVar"
            },
            "description": "Environment variables."
          },
          "image": {
            "type": [
              "string",
              "null"
            ],
            "description": "OCI image reference the sandbox boots; `null` for a non-OCI rootfs."
          },
          "labels": {
            "type": "object",
            "description": "User-defined labels.",
            "additionalProperties": {
              "type": "string"
            },
            "propertyNames": {
              "type": "string"
            }
          },
          "lifecycle": {
            "$ref": "#/components/schemas/SandboxPolicy",
            "description": "Lifecycle policy."
          },
          "mounts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SandboxMountResponse"
            },
            "description": "Volume mounts - guest path + options only; volume ids omitted."
          },
          "network": {
            "$ref": "#/components/schemas/SandboxNetworkResponse",
            "description": "Network config - secret values/vault paths omitted."
          },
          "resources": {
            "$ref": "#/components/schemas/CloudSandboxResources",
            "description": "CPU, memory, and writable-disk sizing."
          },
          "rlimits": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Rlimit"
            },
            "description": "Resource limits."
          },
          "runtime": {
            "$ref": "#/components/schemas/SandboxRuntimeOptions",
            "description": "Guest runtime options."
          }
        }
      },
      "SandboxStatus": {
        "type": "string",
        "description": "Sandbox lifecycle status.",
        "enum": [
          "created",
          "starting",
          "running",
          "temporarily_unavailable",
          "recovery_decision_required",
          "stopping",
          "stopped",
          "failed"
        ]
      },
      "SandboxStatusReason": {
        "type": "string",
        "description": "Why a submitted sandbox is still waiting for placement on a worker. Only\nmeaningful while `status` is `starting`.",
        "enum": [
          "scheduling",
          "insufficient_capacity"
        ]
      },
      "SandboxVolumeMount": {
        "type": "object",
        "description": "Public mount context for one resolved sandbox-volume binding.",
        "required": [
          "volume_id",
          "mount_path",
          "read_only"
        ],
        "properties": {
          "mount_path": {
            "type": "string",
            "description": "Absolute path where the volume is mounted inside the guest."
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "User-facing volume name; `None` identifies the organization host disk."
          },
          "read_only": {
            "type": "boolean",
            "description": "Whether this binding is read-only."
          },
          "volume_id": {
            "type": "string",
            "format": "uuid",
            "description": "Resolved volume identifier, suitable for the volume metrics API."
          }
        }
      },
      "SandboxWaitFor": {
        "type": "string",
        "enum": [
          "running"
        ],
        "description": "Lifecycle state an opt-in create request may wait to observe."
      },
      "ScopedUpstreamCaCert": {
        "type": "object",
        "description": "A CA certificate PEM file trusted only for matching upstream hosts.",
        "required": [
          "pattern",
          "path"
        ],
        "properties": {
          "path": {
            "type": "string",
            "description": "Path to the CA certificate PEM file."
          },
          "pattern": {
            "type": "string",
            "description": "Host pattern this CA applies to. Supports exact hosts and `*.suffix` wildcards."
          }
        }
      },
      "ScopedVerifyUpstream": {
        "type": "object",
        "description": "An upstream certificate verification override for matching hosts.",
        "required": [
          "pattern",
          "verify"
        ],
        "properties": {
          "pattern": {
            "type": "string",
            "description": "Host pattern this override applies to. Supports exact hosts and `*.suffix` wildcards."
          },
          "verify": {
            "type": "boolean",
            "description": "Whether to verify matching upstream server certificates."
          }
        }
      },
      "SecretInjection": {
        "type": "object",
        "description": "Where in the HTTP request a secret can be injected.",
        "properties": {
          "basic_auth": {
            "type": "boolean",
            "description": "Substitute in HTTP Basic Auth (default: true)."
          },
          "body": {
            "type": "boolean",
            "description": "Substitute in request body (default: false).\n\nFixed-length HTTP/1 bodies up to 16 MiB update `Content-Length`;\nlarger fixed-length bodies are blocked. Chunked HTTP/1 bodies are\ndecoded and re-encoded with fresh chunk sizes. Encoded bodies pass\nthrough unchanged. HTTP/2 DATA-frame body substitution is not\nsupported; matching body placeholders are blocked."
          },
          "headers": {
            "type": "boolean",
            "description": "Substitute in HTTP headers (default: true)."
          },
          "query_params": {
            "type": "boolean",
            "description": "Substitute in URL query parameters (default: false)."
          }
        }
      },
      "SecurityProfile": {
        "type": "string",
        "description": "Sandbox-level in-guest security profile.",
        "enum": [
          "default",
          "restricted"
        ]
      },
      "SnapshotDestinationKind": {
        "type": "string",
        "description": "Type of storage used by a snapshot.",
        "enum": [
          "managed",
          "host_volume"
        ]
      },
      "SnapshotFormat": {
        "type": "string",
        "description": "On-disk format of a file-state upper layer.",
        "enum": [
          "raw",
          "qcow2"
        ]
      },
      "SnapshotManifest": {
        "type": "object",
        "description": "Final schema-1 snapshot descriptor.\n\nField order is identity-bearing. Do not reorder these fields.\n\nGenerated bindings and API schemas expose this type as `SnapshotManifest`.",
        "required": [
          "schema",
          "artifact",
          "scope",
          "created_at",
          "image",
          "state",
          "labels",
          "extensions",
          "requires"
        ],
        "properties": {
          "artifact": {
            "type": "string",
            "description": "Artifact kind. Exactly [`SNAPSHOT_ARTIFACT_KIND`]."
          },
          "created_at": {
            "type": "string",
            "description": "Normalized RFC 3339 creation timestamp."
          },
          "extensions": {
            "type": "object",
            "description": "Namespaced additive extension values."
          },
          "image": {
            "$ref": "#/components/schemas/ImageRef",
            "description": "Pinned base image."
          },
          "labels": {
            "type": "object",
            "description": "User-supplied labels, sorted by key in canonical form.",
            "additionalProperties": {
              "type": "string"
            },
            "propertyNames": {
              "type": "string"
            }
          },
          "parent": {
            "type": [
              "string",
              "null"
            ],
            "description": "Exact snapshot identity of the logical lineage parent."
          },
          "requires": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Sorted unique must-understand extension keys."
          },
          "schema": {
            "type": "integer",
            "format": "int32",
            "description": "Schema version. Exactly [`SCHEMA_VERSION`].",
            "minimum": 0
          },
          "scope": {
            "$ref": "#/components/schemas/SnapshotScope",
            "description": "Snapshot payload scope."
          },
          "source_sandbox": {
            "type": [
              "string",
              "null"
            ],
            "description": "Informational source-sandbox name."
          },
          "state": {
            "$ref": "#/components/schemas/SnapshotState",
            "description": "Closed file/checkpoint state variant."
          }
        },
        "additionalProperties": false
      },
      "SnapshotOperationListItem": {
        "allOf": [
          {
            "$ref": "#/components/schemas/CloudSnapshotOperation",
            "description": "Public operation state and result."
          },
          {
            "type": "object",
            "required": [
              "snapshot_name",
              "destination_kind",
              "sandbox_name"
            ],
            "properties": {
              "destination_kind": {
                "$ref": "#/components/schemas/SnapshotDestinationKind",
                "description": "Storage destination selected for the snapshot."
              },
              "sandbox_id": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Source sandbox identifier, absent after sandbox deletion."
              },
              "sandbox_name": {
                "type": "string",
                "description": "Source sandbox name retained for operation history."
              },
              "snapshot_name": {
                "type": "string",
                "description": "Snapshot name requested by the user."
              }
            }
          }
        ],
        "description": "An explicit snapshot operation with enough request context to display it."
      },
      "SnapshotScope": {
        "type": "string",
        "description": "Snapshot payload scope.",
        "enum": [
          "disk",
          "resumable"
        ]
      },
      "SnapshotState": {
        "oneOf": [
          {
            "allOf": [
              {
                "$ref": "#/components/schemas/FileSnapshotState",
                "description": "Concrete file-backed disk state."
              },
              {
                "type": "object",
                "required": [
                  "kind"
                ],
                "properties": {
                  "kind": {
                    "type": "string",
                    "enum": [
                      "file"
                    ]
                  }
                }
              }
            ],
            "description": "Concrete file-backed disk state."
          },
          {
            "allOf": [
              {
                "$ref": "#/components/schemas/CheckpointSnapshotState",
                "description": "Manifest-backed disk or resumable state."
              },
              {
                "type": "object",
                "required": [
                  "kind"
                ],
                "properties": {
                  "kind": {
                    "type": "string",
                    "enum": [
                      "checkpoint"
                    ]
                  }
                }
              }
            ],
            "description": "Manifest-backed disk or resumable state."
          }
        ],
        "description": "Closed snapshot state family."
      },
      "StatVirtualization": {
        "type": "string",
        "description": "Stat virtualization policy for a virtiofs-backed volume mount.\n\nSerializes/deserializes as the lowercase variant name (`\"strict\"`, `\"relaxed\"`, `\"off\"`) so persisted JSON aligns with the CLI grammar (`stat-virt=strict|relaxed|off`) and the NAPI string contract.",
        "enum": [
          "strict",
          "relaxed",
          "off"
        ]
      },
      "StorageBreakdown": {
        "type": "object",
        "description": "Per-volume durable-storage usage breakdown for an org over a window:\nper-(volume, day) rows plus a directory of the volumes they reference. The\nstorage sibling of [`UsageBreakdown`](crate::UsageBreakdown). Deleted volumes'\nintervals persist, so `rows` may reference `volume_id`s absent from `volumes`;\nthe client labels those as deleted.",
        "required": [
          "rows",
          "volumes"
        ],
        "properties": {
          "rows": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/StorageDailyUsage"
            }
          },
          "volumes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/VolumeIdentity"
            }
          }
        }
      },
      "StorageDailyUsage": {
        "type": "object",
        "description": "Durable-storage usage for one volume on one UTC day, computed on read from\n`storage_metering_intervals`. `mib_seconds` is Σ(used_mib × overlap_seconds)\nover the day; display converts to GiB-hours or an average GiB. Sum over days\nfor a volume's period total; sum over volumes for the daily series. The\nstorage sibling of [`SandboxDailyUsage`](crate::SandboxDailyUsage).",
        "required": [
          "volume_id",
          "day",
          "mib_seconds"
        ],
        "properties": {
          "day": {
            "type": "string",
            "format": "date-time",
            "description": "UTC midnight of the day this usage falls in."
          },
          "mib_seconds": {
            "type": "number",
            "format": "double",
            "description": "Σ(used_mib × overlap_seconds) for this volume on this day."
          },
          "volume_id": {
            "type": "string",
            "format": "uuid"
          }
        }
      },
      "TlsConfig": {
        "type": "object",
        "description": "TLS interception configuration. Carried in [`NetworkSpec::tls`](NetworkSpec).\n\nThe local network engine terminates TCP at its in-process stack, so TLS MITM\nis handled by proxy tasks - these fields configure which ports/domains are\nintercepted and how the interception CA is sourced.",
        "properties": {
          "block_quic_on_intercept": {
            "type": "boolean",
            "description": "Drop UDP to intercepted ports when TLS interception is active, forcing\nQUIC traffic to fall back to TCP/TLS."
          },
          "bypass": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Domains to bypass (no MITM). Supports exact match and `*.suffix` wildcards."
          },
          "cache": {
            "$ref": "#/components/schemas/CertCacheConfig",
            "description": "Per-domain certificate cache configuration."
          },
          "enabled": {
            "type": "boolean",
            "description": "Whether TLS interception is enabled."
          },
          "intercept_ca": {
            "$ref": "#/components/schemas/InterceptCaConfig",
            "description": "Interception CA configuration. The TLS proxy uses this CA to sign\nper-domain certs it presents to the guest during interception."
          },
          "intercepted_ports": {
            "type": "array",
            "items": {
              "type": "integer",
              "format": "int32",
              "minimum": 0
            },
            "description": "TCP ports subject to TLS interception (default: `[443]`)."
          },
          "scoped_upstream_ca_cert": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ScopedUpstreamCaCert"
            },
            "description": "Host-scoped CA certificate PEM files to trust for upstream server verification."
          },
          "scoped_verify_upstream": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ScopedVerifyUpstream"
            },
            "description": "Host-scoped upstream verification overrides."
          },
          "upstream_ca_cert": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "CA certificate PEM files to trust for upstream server verification."
          },
          "verify_upstream": {
            "type": "boolean",
            "description": "Whether to verify the upstream server's TLS certificate."
          }
        }
      },
      "UpdateSandboxRequest": {
        "type": "object",
        "description": "Request body for `PATCH /v1/orgs/:slug/sandboxes/:id` - rename a sandbox.\nEach field updates only when present; omit one to leave it unchanged.\n`name` is the per-org display label; `slug` is the globally-unique SSH\naddress handle (changing it breaks existing `ssh <old-slug>@msb.run`).",
        "properties": {
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "New per-org display name. Omit to leave unchanged."
          },
          "slug": {
            "type": [
              "string",
              "null"
            ],
            "description": "New globally-unique slug (the SSH username token). Omit to leave unchanged."
          }
        }
      },
      "UpdateVolumeRequest": {
        "type": "object",
        "description": "PATCH /v1/orgs/{slug}/volumes/{id} - update the cap and/or labels (#496).\n\nEvery field is independently optional: an **omitted** field is left unchanged.\n`capacity_gib` uses the double-`Option` distinction - omit to leave as-is,\nexplicit `null` to clear the cap, a number to set it.",
        "properties": {
          "capacity_gib": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32",
            "description": "New per-volume storage cap in GiB. Omitted = unchanged; `null` = clear the\ncap (back to no per-volume limit); a value sets it. A set value must be\n`>= 1` and `<= the org's storage cap`.",
            "minimum": 0
          },
          "labels": {
            "type": [
              "object",
              "null"
            ],
            "description": "Replacement label set. Omitted = unchanged; an object replaces the labels\nwholesale (an empty object clears them). Reserved-prefix keys are rejected.",
            "additionalProperties": {
              "type": "string"
            },
            "propertyNames": {
              "type": "string"
            }
          }
        }
      },
      "UpperIntegrity": {
        "oneOf": [
          {
            "type": "object",
            "description": "Ordinary SHA-256 retained only for exact legacy compatibility and\narchive metadata. New file snapshots never emit this variant.",
            "required": [
              "digest",
              "algorithm"
            ],
            "properties": {
              "algorithm": {
                "type": "string",
                "enum": [
                  "sha256"
                ]
              },
              "digest": {
                "type": "string",
                "description": "Algorithm output in qualified digest form."
              }
            }
          },
          {
            "type": "object",
            "description": "Released logical-byte sparse SHA-256 representation.",
            "required": [
              "digest",
              "algorithm"
            ],
            "properties": {
              "algorithm": {
                "type": "string",
                "enum": [
                  "msb-sparse-sha256-v1"
                ]
              },
              "digest": {
                "type": "string",
                "description": "Algorithm output in qualified digest form."
              }
            }
          },
          {
            "type": "object",
            "description": "Current sparse-aware fixed-leaf BLAKE3 Merkle representation.",
            "required": [
              "root",
              "logical_size",
              "leaf_size",
              "algorithm"
            ],
            "properties": {
              "algorithm": {
                "type": "string",
                "enum": [
                  "msb-file-merkle-blake3-v1"
                ]
              },
              "leaf_size": {
                "type": "integer",
                "format": "int32",
                "description": "Fixed leaf size. Exactly [`FILE_MERKLE_BLAKE3_LEAF_SIZE`].",
                "minimum": 0
              },
              "logical_size": {
                "type": "integer",
                "format": "int64",
                "description": "Exact logical file length bound into the final root.",
                "minimum": 0
              },
              "root": {
                "type": "string",
                "description": "Domain-separated Merkle root in qualified digest form."
              }
            }
          }
        ],
        "description": "Content integrity descriptor for a file-state upper layer."
      },
      "UpperLayer": {
        "type": "object",
        "description": "Captured file-state upper-layer metadata.",
        "required": [
          "file",
          "size_bytes"
        ],
        "properties": {
          "file": {
            "type": "string",
            "description": "One normal filename relative to the artifact directory."
          },
          "integrity": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/UpperIntegrity",
                "description": "Optional semantic payload integrity. The field itself is required so\nreaders distinguish an intentional `null` from a malformed descriptor."
              }
            ]
          },
          "size_bytes": {
            "type": "integer",
            "format": "int64",
            "description": "Apparent file size, including sparse holes.",
            "minimum": 0
          }
        },
        "additionalProperties": false
      },
      "UsageBreakdown": {
        "type": "object",
        "description": "Compute-on-read usage breakdown for an org over a window: per-(sandbox, day)\nquantity rows plus a directory of the sandboxes they reference. The frontend\ngroups `rows` by sandbox (for the per-sandbox table) or by day (for the\nchart) and labels them via `sandboxes`.",
        "required": [
          "rows",
          "sandboxes"
        ],
        "properties": {
          "rows": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SandboxDailyUsage"
            }
          },
          "sandboxes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SandboxIdentity"
            }
          }
        }
      },
      "UsageDailyPoint": {
        "type": "object",
        "description": "One day's metered quantity for a single billable metric - the unit of a\nusage time-series chart. Cost is intentionally omitted: invoices are\nperiod-aggregated, so a per-day dollar figure would have to be derived.",
        "required": [
          "date",
          "metric",
          "quantity"
        ],
        "properties": {
          "date": {
            "type": "string",
            "format": "date-time",
            "description": "Start of the day window (UTC)."
          },
          "metric": {
            "type": "string",
            "description": "Billable-metric name this quantity belongs to."
          },
          "quantity": {
            "type": "number",
            "format": "double"
          }
        }
      },
      "UsageLineItem": {
        "type": "object",
        "description": "A single line item in a usage summary.",
        "required": [
          "name",
          "quantity",
          "unit",
          "amount_cents"
        ],
        "properties": {
          "amount_cents": {
            "type": "integer",
            "format": "int64"
          },
          "name": {
            "type": "string"
          },
          "quantity": {
            "type": "number",
            "format": "double"
          },
          "unit": {
            "type": "string"
          }
        }
      },
      "UsageSummary": {
        "type": "object",
        "description": "Usage summary from the billing provider.",
        "required": [
          "period_start",
          "period_end",
          "line_items",
          "total_cents"
        ],
        "properties": {
          "daily": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UsageDailyPoint"
            },
            "description": "Per-day metered quantities for charting. One entry per (day, metric).\nEmpty when no daily series is available (e.g. provider error)."
          },
          "line_items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UsageLineItem"
            },
            "description": "Period-aggregated breakdown - one entry per billable metric / charge."
          },
          "period_end": {
            "type": "string",
            "format": "date-time"
          },
          "period_start": {
            "type": "string",
            "format": "date-time"
          },
          "projected_spend_cents": {
            "type": "integer",
            "format": "int64",
            "description": "End-of-period spend projection: `total_cents` linearly extrapolated over\nthe billing period. Equals `total_cents` when the period can't be\nextrapolated (no future end, or zero elapsed). Never below `total_cents`."
          },
          "snapshot_storage_gib": {
            "type": [
              "number",
              "null"
            ],
            "format": "double",
            "description": "Current managed-snapshot storage (GiB), or `None` when never metered."
          },
          "storage_gib": {
            "type": [
              "number",
              "null"
            ],
            "format": "double",
            "description": "Current volume-storage usage (GiB), or `None` when never metered."
          },
          "total_cents": {
            "type": "integer",
            "format": "int64"
          }
        }
      },
      "VolumeFileContent": {
        "type": "string",
        "format": "binary",
        "description": "Raw bytes uploaded to or downloaded from a file."
      },
      "VolumeFileExistsResponse": {
        "type": "object",
        "description": "Whether the requested path exists.",
        "required": [
          "exists"
        ],
        "properties": {
          "exists": {
            "type": "boolean"
          }
        }
      },
      "VolumeFileInfo": {
        "type": "object",
        "description": "Metadata for a file or directory within a volume.",
        "required": [
          "path",
          "kind",
          "size",
          "mode",
          "uid",
          "gid",
          "readonly"
        ],
        "properties": {
          "accessed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "gid": {
            "type": "integer",
            "format": "int32",
            "minimum": 0
          },
          "kind": {
            "$ref": "#/components/schemas/VolumeFileKind"
          },
          "mode": {
            "type": "integer",
            "format": "int32",
            "description": "Unix file mode bits.",
            "minimum": 0
          },
          "modified_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "path": {
            "type": "string",
            "description": "Volume-relative path."
          },
          "readonly": {
            "type": "boolean"
          },
          "size": {
            "type": "integer",
            "format": "int64",
            "description": "Size in bytes.",
            "minimum": 0
          },
          "uid": {
            "type": "integer",
            "format": "int32",
            "minimum": 0
          }
        }
      },
      "VolumeFileKind": {
        "type": "string",
        "description": "Type of a volume entry.",
        "enum": [
          "file",
          "directory"
        ]
      },
      "VolumeFileListResponse": {
        "type": "object",
        "description": "Immediate entries in a directory.",
        "required": [
          "entries"
        ],
        "properties": {
          "entries": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/VolumeFileInfo"
            }
          }
        }
      },
      "VolumeFileMutationResponse": {
        "type": "object",
        "description": "Confirmation that a filesystem change succeeded.",
        "required": [
          "ok"
        ],
        "properties": {
          "ok": {
            "type": "boolean"
          }
        }
      },
      "VolumeFileStatResponse": {
        "type": "object",
        "description": "Metadata for the requested path.",
        "required": [
          "metadata"
        ],
        "properties": {
          "metadata": {
            "$ref": "#/components/schemas/VolumeFileInfo"
          }
        }
      },
      "VolumeIdentity": {
        "type": "object",
        "description": "Minimal volume display info - the directory that turns the `volume_id`s in a\n[`StorageBreakdown`](crate::StorageBreakdown)'s rows into human labels. A\ndeleted volume's intervals outlive its row, so a breakdown may reference ids\nabsent from this directory; the client labels those as deleted.",
        "required": [
          "id"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "`None` for the host volume (nameless)."
          }
        }
      },
      "VolumeKind": {
        "type": "string",
        "description": "Volume role: the organization's shared host volume or a named managed volume.",
        "enum": [
          "host",
          "managed"
        ]
      },
      "VolumeMetricsResponse": {
        "type": "object",
        "description": "Per-volume JuiceFS performance metrics over a time range.",
        "required": [
          "volume_id",
          "range",
          "metrics"
        ],
        "properties": {
          "metrics": {
            "type": "object",
            "description": "Metric key → aggregate series across every worker mounting the volume.\nAn empty series means \"no data in range\".",
            "additionalProperties": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/MetricSeries"
              }
            },
            "propertyNames": {
              "type": "string"
            }
          },
          "range": {
            "$ref": "#/components/schemas/MetricsRange",
            "description": "The resolved query window."
          },
          "volume_id": {
            "type": "string",
            "format": "uuid",
            "description": "The volume these metrics belong to."
          }
        }
      },
      "VolumeResponse": {
        "type": "object",
        "description": "Volume details and usage.",
        "required": [
          "id",
          "kind",
          "storage",
          "status",
          "labels",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "capacity_bytes": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "description": "The volume's configured per-volume storage cap in bytes, or `null` when\nno cap is set (#496). Derived from the stored `capacity_gib` (the user's\nconfigured intent) - the effective ceiling is `min(this, org cap)`.",
            "minimum": 0
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "kind": {
            "$ref": "#/components/schemas/VolumeKind",
            "description": "`host` - the org's shared host disk (not deletable); `managed` - a\nuser-created named volume."
          },
          "labels": {
            "type": "object",
            "description": "User-defined labels (empty object when none set).",
            "additionalProperties": {
              "type": "string"
            },
            "propertyNames": {
              "type": "string"
            }
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "User-facing name. `null` for the host volume (it has no name; it's\nidentified by `kind`)."
          },
          "status": {
            "$ref": "#/components/schemas/VolumeStatus"
          },
          "storage": {
            "$ref": "#/components/schemas/VolumeType",
            "description": "How the volume is physically backed (`block` today)."
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "used_bytes": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "description": "Bytes currently used under this volume's subtree. `null` when usage is\nunavailable - the usage service is disabled or unreachable (list degrades\ngracefully rather than failing) - or on create/delete, which don't sample\nit.",
            "minimum": 0
          }
        }
      },
      "VolumeStatus": {
        "type": "string",
        "description": "Lifecycle status of a volume.",
        "enum": [
          "active",
          "deleting"
        ]
      },
      "VolumeType": {
        "type": "string",
        "description": "How a volume is physically backed - distinct from [`VolumeKind`] (ownership).\nEvery volume is `Block` today; the enum exists so future backings can be added\n(and reported to the SDK) without another schema change.",
        "enum": [
          "block"
        ]
      }
    },
    "securitySchemes": {
      "api_key": {
        "type": "http",
        "scheme": "bearer",
        "description": "Organization API key (msb_…) - org-scoped programmatic access."
      }
    }
  }
}

---

--- METRICS ---
- Files Extracted: 40
- Estimated Token Budget: ~152183 tokens
- Canonical Reference: https://codewiki.google/github.com/superradcompany/microsandbox
