> ## Documentation Index
> Fetch the complete documentation index at: https://boxrouter.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Capabilities and Isolation

> See which parts of the Sandbox Provider Spec BoxRouter supports, what every provider does the same, and how to check which routes fit your workload.

BoxRouter checks every route against one specification of what a sandbox provider does. Every route gives you the baseline below. For
anything beyond it, list it under `requirements` when you create the sandbox, and BoxRouter skips
every route that can't provide it.

## What Every Provider Does

A route passes these checks before BoxRouter uses it. You never request them.

| Area | Same on Every Route |
| - | - |
| Lifecycle | Creates a sandbox with the environment variables you give it, finds it again, and destroys it. |
| Commands | Returns the exit status with standard output and standard error kept apart. Runs in the working directory and environment you give it. |
| Files | Reads and writes file bytes through the sandbox's inline file operations. |
| Processes | Starts a background process and reads its output while it runs. Closing its output does not mean it exited. |
| Environment | A sandbox created without an image has `bash` and `python3` on its path. |
| Network | Opens HTTPS connections to any public host, unless you request `network_lockdown`. |
| Isolation | Code in the sandbox cannot reach the host's instance metadata or credentials. |

Directory operations are not part of the baseline. List, create, and remove paths with commands
such as `ls -A` and `mkdir -p`.

Inside a sandbox, `nproc` and `free` can report the host instead of your allocation, so size work
from the `resources` you asked for.

A running sandbox, process, or file never moves between providers, so changing the route affects
only sandboxes you create afterward.

## Capabilities You Can Request

Request optional capabilities in `requirements` when you create the sandbox. The sandbox records the
capabilities it received in `contract.gates`.

You cannot add capabilities after creation. A later call that needs a capability outside that
contract is refused with `not_named`.

| Requirement | What It Gives You |
| - | - |
| `idle_shutdown` | Stop a sandbox automatically after it has been inactive for a period. |
| `pause_resume` | Pause a sandbox and resume it later. |
| `previews` | Provide a URL that reaches a port in the sandbox. |
| `network_lockdown` | Limit which outbound connections a sandbox can make. |
| `hidden_secrets` | Add credentials to outgoing requests without exposing their values to sandbox code. |
| `custom_images` | Start a sandbox from an environment supplied in the request. |
| `processes` | Control a background process beyond reading its output. |
| `terminal` | Provide an interactive shell connected to a pseudo-terminal in the sandbox. |
| `tcp_ports` | Forward internet TCP connections to ports in the sandbox. |
| `desktop` | Provide a graphical desktop that a program can view and control. |

This creates a sandbox that can pause, reads what it was granted, and destroys it:

```python Python theme={"theme":"css-variables"}
from boxrouter import BoxRouter

with BoxRouter() as client:
    with client.sandboxes.create(requirements=["pause_resume"]) as box:
        print(*sorted(box.contract.gates.to_dict()), sep="\n")
```

```typescript TypeScript theme={"theme":"css-variables"}
import { BoxRouter } from '@boxrouter/sdk';

await using client = new BoxRouter();
await using box = await client.sandboxes.create({ requirements: ['pause_resume'] });
console.log(Object.keys(box.contract?.gates ?? {}).sort().join('\n'));
```

```bash CLI theme={"theme":"css-variables"}
(
  set -eu
  id=$(boxrouter create --require pause_resume)
  trap 'boxrouter destroy "$id"' EXIT
  boxrouter get "$id" | jq -r '.contract.gates | keys[]'
)
```

Output

```text theme={"theme":"css-variables"}
custom_images
pause_resume
```

Some capabilities require a setting. For example, set `previews.access`; the call is refused until you
name a value or accept any value with `"any"`.

Only capabilities in this table can be named in `requirements`. Naming another capability there returns
`400 invalid_request` with a `fix`.
To keep files between sandboxes, attach a [Volume](https://boxrouter.ai/docs/volumes.md) with the top-level `volume` field.
Sizes, region, lifetime, and isolation go in `resources`; the [create reference](https://boxrouter.ai/docs/reference/create.md)
lists every field.

## Choose Isolation

Every route keeps your code away from the host's credentials. To require a particular boundary
between your code and the host, set `resources.kernel`. Routes with any other kind are skipped.

| `kernel` | What Separates Your Code from the Host |
| - | - |
| `shared` | A container. Your code runs on the host's kernel. |
| `filtered` | A container behind a layer, such as gVisor, that intercepts system calls before they reach the host's kernel. |
| `dedicated` | A virtual machine or microVM with its own kernel. |

```json theme={"theme":"css-variables"}
{"resources": {"kernel": ["filtered", "dedicated"]}}
```

A list accepts any kind in it. `kernel` names the kind of boundary, not the technology behind it,
such as Firecracker, and not who else runs on the host.

## Check Which Routes Fit

Support varies by provider, the environment offered on a route, and the account you connected. Both
checks below are read-only and reserve no capacity.

* [List providers](https://boxrouter.ai/docs/reference/list-providers.md) returns the routes your workspace can use now,
  each with the behavior it has been verified to provide.
* [Match](https://boxrouter.ai/docs/reference/match.md) takes the exact request you plan to send and says which routes fit.
  For each route it rules out, it names the failed check and the nearest change that would qualify.

```python Python theme={"theme":"css-variables"}
from boxrouter import BoxRouter

with BoxRouter() as client:
    for route in client.api.providers.list().data:
        print(f"{route.provider}: {route.supply}")
```

```typescript TypeScript theme={"theme":"css-variables"}
import { BoxRouter } from '@boxrouter/sdk';

const client = new BoxRouter();
try {
  const { data } = await client.api.providers.list();
  for (const { provider, supply } of data) {
    console.log(`${provider}: ${supply}`);
  }
} finally {
  await client.close();
}
```

```bash CLI theme={"theme":"css-variables"}
boxrouter api providers list | jq -r '.data[] | "\(.provider): \(.supply)"'
```

Output

```text theme={"theme":"css-variables"}
<PROVIDER_ID>: byok
```

Match a request that needs a desktop. Your output shows your provider's real ID where this page
writes `<PROVIDER_ID>`:

```python Python theme={"theme":"css-variables"}
import json
from boxrouter import BoxRouter

with BoxRouter() as client:
    result = client.sandboxes.match(requirements=["desktop"])
    summary = {
        "eligible": [route.provider for route in result.eligible],
        "excluded": [
            {
                "provider": route.provider,
                "failed": [
                    {"reason": failure.reason, "gate": failure.gate}
                    for failure in route.failed
                ],
            }
            for route in result.excluded
        ],
    }
    print(json.dumps(summary, indent=2))
```

```typescript TypeScript theme={"theme":"css-variables"}
import { BoxRouter } from '@boxrouter/sdk';

const client = new BoxRouter();
try {
  const result = await client.sandboxes.match({ requirements: ['desktop'] });
  const summary = {
    eligible: result.eligible.map((route) => route.provider),
    excluded: result.excluded.map(({ provider, failed }) => ({
      provider,
      failed: failed.map(({ reason, gate }) => ({ reason, gate })),
    })),
  };
  console.log(JSON.stringify(summary, null, 2));
} finally {
  await client.close();
}
```

```bash CLI theme={"theme":"css-variables"}
boxrouter match --requirements '{"desktop": {}}' | jq '{
  eligible: [.eligible[].provider],
  excluded: [
    .excluded[] | {
      provider,
      failed: [.failed[] | {reason, gate}]
    }
  ]
}'
```

Output

```json theme={"theme":"css-variables"}
{
"eligible": [],
"excluded": [
{
  "provider": "<PROVIDER_ID>",
  "failed": [
    {
      "reason": "gate_unsupported",
      "gate": "desktop"
    }
  ]
}
]
}
```

`gate_unsupported` means the route does not provide the capability named in `gate`. Creating a sandbox
with the same request fails with `409 no_eligible_route`. See
[routing](https://boxrouter.ai/docs/routing.md#when-no-route-is-left).

## Large Files

Use [Volumes](https://boxrouter.ai/docs/volumes.md) to upload, download, and keep files outside a sandbox.

## Native Provider Features

BoxRouter SDKs expose the common operations and the capabilities above. For anything outside
that, use the provider's own SDK with its own credentials. Those calls sit outside BoxRouter routing and tracking, and the
credentials don't reach sandboxes BoxRouter manages.

When a response includes provider details, distinguish application logic from diagnostics:

* For logic that works on every route, use `state`, `exit_code` and the error `code`.
* For diagnosis, use `native_state`, `native_error`, `provider`, `native_status` and `native_code`.

`route.provider` identifies the provider.

## Next Steps

> [card] [How Routing Works](https://boxrouter.ai/docs/routing.md)
>
> How BoxRouter filters and ranks routes.

> [card] [Check Your Limits](https://boxrouter.ai/docs/limits.md)
>
> Limits each route reports.

> [card] [Create a Sandbox](https://boxrouter.ai/docs/sandboxes.md)
>
> Name the capabilities and size when you create one.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.