> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.nvidia.com/nemo/gym/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.nvidia.com/nemo/gym/_mcp/server.

# nemo_gym.sandbox

Public sandbox API for NeMo Gym.

## Subpackages

* **[`nemo_gym.sandbox.adapters`](/nemo/gym/nemo-gym/nemo_gym/sandbox/adapters)**
* **[`nemo_gym.sandbox.providers`](/nemo/gym/nemo-gym/nemo_gym/sandbox/providers)**

## Submodules

* **[`nemo_gym.sandbox.access`](/nemo/gym/nemo-gym/nemo_gym/sandbox/access)**
* **[`nemo_gym.sandbox.agent_tools`](/nemo/gym/nemo-gym/nemo_gym/sandbox/agent_tools)**
* **[`nemo_gym.sandbox.api`](/nemo/gym/nemo-gym/nemo_gym/sandbox/api)**
* **[`nemo_gym.sandbox.attribution`](/nemo/gym/nemo-gym/nemo_gym/sandbox/attribution)**
* **[`nemo_gym.sandbox.config`](/nemo/gym/nemo-gym/nemo_gym/sandbox/config)**
* **[`nemo_gym.sandbox.process_supervisor`](/nemo/gym/nemo-gym/nemo_gym/sandbox/process_supervisor)**
* **[`nemo_gym.sandbox.shell`](/nemo/gym/nemo-gym/nemo_gym/sandbox/shell)**
* **[`nemo_gym.sandbox.utils`](/nemo/gym/nemo-gym/nemo_gym/sandbox/utils)**

## Package Contents

### Classes

| Name                                                                                                        | Description                                                                 |
| ----------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| [`AsyncSandbox`](#nemo_gym-sandbox-api-AsyncSandbox)                                                        | Async sandbox object backed by a runtime provider.                          |
| [`AsyncSandboxCompose`](#nemo_gym-sandbox-adapters-docker_compose-AsyncSandboxCompose)                      | Start services from a Compose YAML file and own their sandbox lifecycle.    |
| [`ConnectableProvider`](#nemo_gym-sandbox-providers-base-ConnectableProvider)                               | Optional capability: rebuild a handle in another process from a descriptor. |
| [`Sandbox`](#nemo_gym-sandbox-api-Sandbox)                                                                  | Synchronous wrapper around `AsyncSandbox`.                                  |
| [`SandboxCreateError`](#nemo_gym-sandbox-providers-base-SandboxCreateError)                                 | Raised when a provider cannot create a sandbox.                             |
| [`SandboxCreateVerificationError`](#nemo_gym-sandbox-providers-base-SandboxCreateVerificationError)         | Raised when a newly-created sandbox fails provider readiness checks.        |
| [`SandboxEndpoint`](#nemo_gym-sandbox-providers-base-SandboxEndpoint)                                       | Provider-neutral route to a long-lived service inside a sandbox.            |
| [`SandboxExecResult`](#nemo_gym-sandbox-providers-base-SandboxExecResult)                                   | Provider-neutral process execution result.                                  |
| [`SandboxHandle`](#nemo_gym-sandbox-providers-base-SandboxHandle)                                           | Provider-neutral handle to a created sandbox.                               |
| [`SandboxProvider`](#nemo_gym-sandbox-providers-base-SandboxProvider)                                       | Runtime/infra provider contract used by the public sandbox API.             |
| [`SandboxPty`](#nemo_gym-sandbox-api-SandboxPty)                                                            | PTY namespace of a sandbox: `await sandbox.pty.create(...)` for a live      |
| [`SandboxPtyError`](#nemo_gym-sandbox-providers-base-SandboxPtyError)                                       | Raised when a PTY session fails outside normal process exit.                |
| [`SandboxPtySession`](#nemo_gym-sandbox-providers-base-SandboxPtySession)                                   | One live interactive terminal. Async context manager; exit closes it.       |
| [`SandboxPtySpec`](#nemo_gym-sandbox-providers-base-SandboxPtySpec)                                         | Interactive PTY session request.                                            |
| [`SandboxResources`](#nemo_gym-sandbox-providers-base-SandboxResources)                                     | Provider-neutral resource request.                                          |
| [`SandboxSpec`](#nemo_gym-sandbox-providers-base-SandboxSpec)                                               | Sandbox creation request.                                                   |
| [`SandboxStatus`](#nemo_gym-sandbox-providers-base-SandboxStatus)                                           | Provider-neutral sandbox lifecycle status.                                  |
| [`SupportsSandboxEndpoint`](#nemo_gym-sandbox-providers-base-SupportsSandboxEndpoint)                       | Optional provider capability for resolving declared service ports.          |
| [`SupportsSandboxNetwork`](#nemo_gym-sandbox-providers-base-SupportsSandboxNetwork)                         | Optional direct networking between sandboxes, preserving service ports.     |
| [`SupportsSandboxPauseResume`](#nemo_gym-sandbox-providers-base-SupportsSandboxPauseResume)                 | Optional provider capability to pause and resume a sandbox.                 |
| [`SupportsSandboxPortForwarding`](#nemo_gym-sandbox-providers-base-SupportsSandboxPortForwarding)           | Optional loopback TCP forwarding, running until cancellation.               |
| [`SupportsSandboxPty`](#nemo_gym-sandbox-providers-base-SupportsSandboxPty)                                 | Optional provider capability: interactive PTY sessions.                     |
| [`SupportsSandboxPtyAttach`](#nemo_gym-sandbox-providers-base-SupportsSandboxPtyAttach)                     | Optional provider capability: re-attach to a PTY session by id.             |
| [`SupportsSandboxRuntimeRequirements`](#nemo_gym-sandbox-providers-base-SupportsSandboxRuntimeRequirements) | Optional validation and setup of task-declared runtime requirements.        |
| [`SupportsSandboxSharedStorage`](#nemo_gym-sandbox-providers-base-SupportsSandboxSharedStorage)             | Optional shared filesystem provisioned by the operator's provider config.   |

### Functions

| Name                                                                              | Description                                                                |
| --------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
| [`create_provider`](#nemo_gym-sandbox-providers-registry-create_provider)         | Instantiate a provider from a single-key provider config.                  |
| [`get_provider_class`](#nemo_gym-sandbox-providers-registry-get_provider_class)   | Return a provider class by name (explicit > built-in > entry point).       |
| [`list_providers`](#nemo_gym-sandbox-providers-registry-list_providers)           | List available provider names from all sources.                            |
| [`register_provider`](#nemo_gym-sandbox-providers-registry-register_provider)     | Register a sandbox provider class.                                         |
| [`resolve_provider_config`](#nemo_gym-sandbox-config-resolve_provider_config)     | Resolve a `sandbox_provider` field into a single-key provider config dict. |
| [`resolve_provider_metadata`](#nemo_gym-sandbox-config-resolve_provider_metadata) | Return a sandbox block's `default_metadata`.                               |
| [`rewrite_image`](#nemo_gym-sandbox-utils-rewrite_image)                          | Apply ordered image-prefix rewrites used by sandbox configs.               |

### Data

[`ExecResult`](#nemo_gym-sandbox-providers-base-ExecResult)

### API

```python
class nemo_gym.sandbox.AsyncSandbox(
    provider: collections.abc.Mapping[str, typing.Any] | nemo_gym.sandbox.providers.SandboxProvider,
    spec: nemo_gym.sandbox.providers.SandboxSpec | None = None,
    owns_provider: bool = True
)
```

Async sandbox object backed by a runtime provider.

With `owns_provider=False`, the caller closes the shared provider after
all of its sandboxes have stopped.

**`_handle`** `SandboxHandle | None = None`

---

**`_provider`**

---

**`pty`** `= SandboxPty(self)`

---

```python
nemo_gym.sandbox.AsyncSandbox.__aenter__() -> nemo_gym.sandbox.api.AsyncSandbox
```

async

```python
nemo_gym.sandbox.AsyncSandbox.__aexit__(
    exc_type: typing.Any,
    exc_val: typing.Any,
    exc_tb: typing.Any
) -> None
```

async

```python
nemo_gym.sandbox.AsyncSandbox._exec_uninstrumented(
    command: str,
    cwd: str | None = None,
    env: dict[str, str] | None = None,
    timeout_s: int | float | None = 180,
    user: str | int | None = None,
    preserve_background_services: bool = False
) -> nemo_gym.sandbox.providers.SandboxExecResult
```

async

```python
nemo_gym.sandbox.AsyncSandbox._require_handle() -> nemo_gym.sandbox.providers.SandboxHandle
```

```python
nemo_gym.sandbox.AsyncSandbox._telemetry_provider_name() -> str
```

Provider name for span attributes (`docker`, `daytona`, `opensandbox`, ...).

```python
nemo_gym.sandbox.AsyncSandbox.connect(
    descriptor: collections.abc.Mapping[str, typing.Any] | typing.Any,
    provider: nemo_gym.sandbox.providers.SandboxProvider,
    owns_provider: bool = True
) -> nemo_gym.sandbox.api.AsyncSandbox
```

async

classmethod

Rebuild a sandbox in this process from a descriptor produced by
`serialize`, using `provider` (which must support connect).

```python
nemo_gym.sandbox.AsyncSandbox.disconnect() -> None
```

async

Release this client without stopping a borrowed sandbox.

Use this only for a sandbox rebuilt with `connect`.
The component that created the sandbox remains responsible for stopping it.

```python
nemo_gym.sandbox.AsyncSandbox.download(
    remote_path: str,
    local_path: pathlib.Path | str
) -> None
```

async

```python
nemo_gym.sandbox.AsyncSandbox.endpoint(
    port: int
) -> nemo_gym.sandbox.providers.SandboxEndpoint
```

async

Resolve a declared sandbox service port without exposing provider state.

```python
nemo_gym.sandbox.AsyncSandbox.exec(
    command: str,
    cwd: str | None = None,
    env: dict[str, str] | None = None,
    timeout_s: int | float | None = 180,
    user: str | int | None = None,
    preserve_background_services: bool = False
) -> nemo_gym.sandbox.providers.SandboxExecResult
```

async

Run a command, optionally preserving services needed by later commands.

`preserve_background_services` selects a provider's service-preserving
execution when available; other providers use ordinary exec. Services
must redirect stdout and stderr. This mode does not accept per-command
`env` or `user` overrides on providers with a service-preserving path.

```python
nemo_gym.sandbox.AsyncSandbox.pause() -> None
```

async

Pause this sandbox while preserving its state.

Open PTY sessions are detached; whether processes survive and sessions
can be re-attached after `resume()` depends on the provider backend.

```python
nemo_gym.sandbox.AsyncSandbox.resume() -> None
```

async

Resume this sandbox and wait until it is ready.

On timeout the server-side state is unknown: reconnect and check
`status()` before retrying.

```python
nemo_gym.sandbox.AsyncSandbox.serialize(
    scope: str | None = None
) -> dict[str, typing.Any]
```

async

Return a JSON descriptor another process can rebuild this box from.

Requires a provider that supports the connect capability (the remote
provider, or an external-control-plane provider such as OpenSandbox). For
the remote provider, `scope` mints a co-lease (`scope="operate"`).

```python
nemo_gym.sandbox.AsyncSandbox.start(
    spec: nemo_gym.sandbox.providers.SandboxSpec | None = None
) -> nemo_gym.sandbox.api.AsyncSandbox
```

async

```python
nemo_gym.sandbox.AsyncSandbox.start_with_setup(
    spec: nemo_gym.sandbox.providers.SandboxSpec | None,
    setup: collections.abc.Callable[[AsyncSandbox], collections.abc.Awaitable[None]]
) -> nemo_gym.sandbox.api.AsyncSandbox
```

async

Start the sandbox, then run `setup` against it.

If `setup` raises, the sandbox is stopped before the exception
propagates.

```python
nemo_gym.sandbox.AsyncSandbox.status() -> nemo_gym.sandbox.providers.SandboxStatus
```

async

```python
nemo_gym.sandbox.AsyncSandbox.stop() -> None
```

async

```python
nemo_gym.sandbox.AsyncSandbox.upload(
    local_path: pathlib.Path | str,
    remote_path: str
) -> None
```

async

```python
class nemo_gym.sandbox.AsyncSandboxCompose(
    provider,
    compose_file: str | pathlib.Path | None,
    service_specs: collections.abc.Mapping[str, nemo_gym.sandbox.providers.SandboxSpec] | None = None,
    timeout_s: float = 1200,
    poll_interval_s: float = 0.5,
    volume_init_image: str = 'alpine:3.22',
    volume_sources: collections.abc.Mapping[str, str] | None = None
)
```

Start services from a Compose YAML file and own their sandbox lifecycle.

Deployment settings stay in the provider and optional `service_specs`.
Compose fields override the corresponding spec fields.

**`_plans`** `dict[str, dict[str, Any]] = {}`

---

**`_processes`** `dict[str, Task] = {}`

---

**`_runtime_metadata`** `dict[str, dict[str, str]] = {}`

---

**`_seeds`** `list[AsyncSandbox] = []`

---

**`_stop_task`** `Task | None = None`

---

**`_volume_helper`** `AsyncSandbox | None = None`

---

**`compose_file`**

---

**`document`** `dict[str, Any] = {}`

---

**`project`** `= 'compose-' + uuid.uuid4().hex`

---

**`provider`**

---

**`service_specs`** `= dict(service_specs or {})`

---

**`services`** `dict[str, AsyncSandbox] = {}`

---

**`volume_sources`** `= dict(volume_sources or {})`

---

```python
nemo_gym.sandbox.AsyncSandboxCompose.__aenter__()
```

async

```python
nemo_gym.sandbox.AsyncSandboxCompose.__aexit__(
    exc = ()
)
```

async

```python
nemo_gym.sandbox.AsyncSandboxCompose._load()
```

```python
nemo_gym.sandbox.AsyncSandboxCompose._prepare()
```

async

```python
nemo_gym.sandbox.AsyncSandboxCompose._prepare_volumes()
```

async

```python
nemo_gym.sandbox.AsyncSandboxCompose._stop()
```

async

```python
nemo_gym.sandbox.AsyncSandboxCompose._validate() -> list[str]
```

```python
nemo_gym.sandbox.AsyncSandboxCompose._wait(
    name,
    condition
)
```

async

```python
nemo_gym.sandbox.AsyncSandboxCompose.connect(
    descriptor: collections.abc.Mapping[str, typing.Any],
    provider
) -> nemo_gym.sandbox.adapters.docker_compose.AsyncSandboxCompose
```

async

classmethod

Connect to all members without provisioning or restarting services.

Like AsyncSandbox.connect, stop() closes the connected sandboxes using
provider semantics. The creator retains ownership of managed-volume
cleanup and running service/forwarding tasks and must also call stop().

```python
nemo_gym.sandbox.AsyncSandboxCompose.serialize(
    scope: str | None = None
) -> dict[str, typing.Any]
```

async

Describe a running collection using provider connection descriptors.

The creating process must keep the collection alive: it owns service
and forwarding tasks and managed-volume cleanup. Provider configuration
and YAML are not included.

```python
nemo_gym.sandbox.AsyncSandboxCompose.start()
```

async

```python
nemo_gym.sandbox.AsyncSandboxCompose.stop()
```

async

```python
class nemo_gym.sandbox.ConnectableProvider()
```

Protocol

Optional capability: rebuild a handle in another process from a descriptor.

Providers whose sandboxes are reachable by id (external control plane, e.g.
OpenSandbox and Fargate, and the sandbox server's remote provider) implement
this. A provider that does not implement it can only be shared by fronting it
with a sandbox server. Membership is checked with `isinstance` because the
protocol is `runtime_checkable`.

```python
nemo_gym.sandbox.ConnectableProvider.connect(
    descriptor: collections.abc.Mapping[str, typing.Any]
) -> nemo_gym.sandbox.providers.base.SandboxHandle
```

async

Rebuild a live handle in this process from a descriptor.

```python
nemo_gym.sandbox.ConnectableProvider.serialize_handle(
    handle: nemo_gym.sandbox.providers.base.SandboxHandle,
    scope: str | None = None
) -> dict[str, typing.Any]
```

async

Return a JSON-serializable descriptor that `connect` can rebuild a
handle from. `scope` is honored by providers that mint leases (the
remote provider) and ignored by the rest.

```python
class nemo_gym.sandbox.Sandbox(
    provider: collections.abc.Mapping[str, typing.Any] | nemo_gym.sandbox.providers.SandboxProvider,
    spec: nemo_gym.sandbox.providers.SandboxSpec | None = None
)
```

Synchronous wrapper around `AsyncSandbox`.

`pty`, `serialize` and `connect` are async-only; use `AsyncSandbox`
for those.

**`_async_sandbox`**

---

**`_runner`** `= _AsyncLoopRunner()`

---

```python
nemo_gym.sandbox.Sandbox.__del__() -> None
```

```python
nemo_gym.sandbox.Sandbox.__enter__() -> nemo_gym.sandbox.api.Sandbox
```

```python
nemo_gym.sandbox.Sandbox.__exit__(
    exc_type: typing.Any,
    exc_val: typing.Any,
    exc_tb: typing.Any
) -> None
```

```python
nemo_gym.sandbox.Sandbox.download(
    remote_path: str,
    local_path: pathlib.Path | str
) -> None
```

```python
nemo_gym.sandbox.Sandbox.endpoint(
    port: int
) -> nemo_gym.sandbox.providers.SandboxEndpoint
```

```python
nemo_gym.sandbox.Sandbox.exec(
    command: str,
    cwd: str | None = None,
    env: dict[str, str] | None = None,
    timeout_s: int | float | None = 180,
    user: str | int | None = None,
    preserve_background_services: bool = False
) -> nemo_gym.sandbox.providers.SandboxExecResult
```

```python
nemo_gym.sandbox.Sandbox.pause() -> None
```

```python
nemo_gym.sandbox.Sandbox.resume() -> None
```

```python
nemo_gym.sandbox.Sandbox.start(
    spec: nemo_gym.sandbox.providers.SandboxSpec | None = None
) -> nemo_gym.sandbox.api.Sandbox
```

```python
nemo_gym.sandbox.Sandbox.status() -> nemo_gym.sandbox.providers.SandboxStatus
```

```python
nemo_gym.sandbox.Sandbox.stop() -> None
```

```python
nemo_gym.sandbox.Sandbox.upload(
    local_path: pathlib.Path | str,
    remote_path: str
) -> None
```

```python
class nemo_gym.sandbox.SandboxCreateError()
```

**Bases:** `RuntimeError`

Raised when a provider cannot create a sandbox.

```python
class nemo_gym.sandbox.SandboxCreateVerificationError()
```

**Bases:** [SandboxCreateError](#nemo_gym-sandbox-providers-base-SandboxCreateError)

Raised when a newly-created sandbox fails provider readiness checks.

```python
class nemo_gym.sandbox.SandboxEndpoint(
    endpoint: str,
    headers: dict[str, str] = dict()
)
```

Dataclass

Provider-neutral route to a long-lived service inside a sandbox.

`endpoint` is an absolute URL. `headers` carries provider-required
authentication or routing headers without exposing the provider's opaque
handle to callers.

**`endpoint`** `str`

---

**`headers`** `dict[str, str] = field(default_factory=dict)`

---

```python
nemo_gym.sandbox.SandboxEndpoint.__post_init__() -> None
```

```python
class nemo_gym.sandbox.SandboxExecResult(
    stdout: str | None,
    stderr: str | None,
    return_code: int,
    error_type: str | None = None
)
```

Dataclass

Provider-neutral process execution result.

`return_code` is the process exit code when the sandbox actually ran the
command. Providers may use a non-process sentinel with `error_type` set
when the sandbox runtime reports an execution failure without a process
exit code.

**`error_type`** `str | None = None`

---

**`return_code`** `int`

---

**`stderr`** `str | None`

---

**`stdout`** `str | None`

---

```python
class nemo_gym.sandbox.SandboxHandle(
    sandbox_id: str,
    provider_name: str,
    raw: typing.Any
)
```

Dataclass

Provider-neutral handle to a created sandbox.

`raw` is provider-owned opaque state. Public code should pass it back to
the provider through this handle rather than inspecting or mutating it
directly.

**`provider_name`** `str`

---

**`sandbox_id`** `str`

---

```python
class nemo_gym.sandbox.SandboxProvider()
```

Protocol

Runtime/infra provider contract used by the public sandbox API.

**`name`** `str`

---

```python
nemo_gym.sandbox.SandboxProvider.aclose() -> None
```

async

Close provider-scoped resources such as SDK clients.

```python
nemo_gym.sandbox.SandboxProvider.close(
    handle: nemo_gym.sandbox.providers.base.SandboxHandle
) -> None
```

async

End the sandbox lifecycle and close provider resources for it.

```python
nemo_gym.sandbox.SandboxProvider.create(
    spec: nemo_gym.sandbox.providers.base.SandboxSpec
) -> nemo_gym.sandbox.providers.base.SandboxHandle
```

async

Create a ready sandbox and return a provider-neutral handle.

Providers must return only after the sandbox is healthy enough to run
commands and transfer files. If the sandbox cannot become ready before
the configured timeout, providers should raise `SandboxCreateError`
or a provider-specific subclass.

```python
nemo_gym.sandbox.SandboxProvider.download_file(
    handle: nemo_gym.sandbox.providers.base.SandboxHandle,
    source_path: str,
    target_path: pathlib.Path
) -> None
```

async

Download one sandbox file to the local filesystem.

```python
nemo_gym.sandbox.SandboxProvider.exec(
    handle: nemo_gym.sandbox.providers.base.SandboxHandle,
    command: str,
    cwd: str | None = None,
    env: dict[str, str] | None = None,
    timeout_s: int | float | None = None,
    user: str | int | None = None
) -> nemo_gym.sandbox.providers.base.SandboxExecResult
```

async

Run a command inside a sandbox.

```python
nemo_gym.sandbox.SandboxProvider.status(
    handle: nemo_gym.sandbox.providers.base.SandboxHandle
) -> nemo_gym.sandbox.providers.base.SandboxStatus
```

async

Return the current sandbox lifecycle status.

```python
nemo_gym.sandbox.SandboxProvider.upload_file(
    handle: nemo_gym.sandbox.providers.base.SandboxHandle,
    source_path: pathlib.Path,
    target_path: str
) -> None
```

async

Upload one local file into a sandbox.

```python
class nemo_gym.sandbox.SandboxPty(
    sandbox: nemo_gym.sandbox.api.AsyncSandbox
)
```

PTY namespace of a sandbox: `await sandbox.pty.create(...)` for a live
session, `await sandbox.pty.exec(...)` for one-shot run-and-collect.

**`_default_session`** `SandboxPtySession | None = None`

---

**`_session_exec_lock`** `= asyncio.Lock()`

---

```python
nemo_gym.sandbox.SandboxPty._exec_detached(
    command: str,
    session: nemo_gym.sandbox.providers.SandboxPtySession | None,
    cwd: str | None,
    env: dict[str, str] | None,
    user: str | int | None,
    rows: int,
    cols: int,
    pty: bool,
    timeout_s: int | float | None,
    poll_interval_s: float
) -> nemo_gym.sandbox.providers.SandboxExecResult
```

async

`exec(detach=True)`: hand the command to the session's detached
runner, which holds the socket only for brief completion polls.

```python
nemo_gym.sandbox.SandboxPty.attach(
    session_id: str,
    takeover: bool = True,
    since: int | None = None
) -> nemo_gym.sandbox.providers.SandboxPtySession
```

async

Re-attach to a session opened earlier, here or in another process.

Sessions outlive the client that opened them, so `session.session_id`
is all another process needs. `takeover` evicts the current holder,
whose session then fails with `SandboxPtyError`; without it,
attaching to a held session fails. `since` replays retained output
from that byte offset first (`0` replays all of it).

```python
nemo_gym.sandbox.SandboxPty.create(
    command: str | None = None,
    cwd: str | None = None,
    env: dict[str, str] | None = None,
    rows: int = 24,
    cols: int = 80,
    user: str | int | None = None,
    pty: bool = True
) -> nemo_gym.sandbox.providers.SandboxPtySession
```

async

Open an interactive terminal; the returned session carries
`read`/`read_stderr`/`write`/`resize`/`send_signal`/
`wait_exit`/`close` and is an async context manager.

`pty=False` selects pipe mode: no TTY, stdout/stderr split across
`read()`/`read_stderr()`. `rows`/`cols` are applied right after
the terminal connects, because the backend has no spawn-time size, so a
`command` that reads the size in its first moments can still see the
80x24 default; programs that honor SIGWINCH pick up the real size.
Close the session before the sandbox stops: a session that outlives its
sandbox fails subsequent reads with `SandboxPtyError`. Async-only;
the sync `Sandbox` facade does not mirror it.

```python
nemo_gym.sandbox.SandboxPty.exec(
    command: str,
    session: nemo_gym.sandbox.providers.SandboxPtySession | None = None,
    cwd: str | None = None,
    env: dict[str, str] | None = None,
    timeout_s: int | float | None = 180,
    user: str | int | None = None,
    rows: int = 24,
    cols: int = 80,
    pty: bool = True,
    detach: bool = False,
    poll_interval_s: float = 15.0
) -> nemo_gym.sandbox.providers.SandboxExecResult
```

async

Run one command in a terminal session and collect its output.

Without `session`, the sandbox's default-shell session — the oldest
live one opened by `create()` with no `command` — is reused,
provided the call sets none of the session-shaping arguments
(`cwd`/`env`/`user`, non-default `rows`/`cols`, or
`pty=False`), since those are fixed at `create()`. Custom-command
and attached sessions run arbitrary programs, so they are only used
when passed explicitly. When no default-shell session exists (or
shaping arguments are given) a private session is opened for the
command, drained and closed. Session-mode execs are serialized per
sandbox: concurrent calls into one shared stream would corrupt it.
With `session` the command runs in that live session, which stays open
and keeps its shell state. In a live session the output also contains
the shell's echo of the command, `stderr` is best-effort (pipe mode
only), and a command that ends the shell (`exit`) raises
`SandboxPtyError`.

With `detach=True` the command runs without holding a connection
while it works: it starts in a session, the socket is dropped, and the
session is briefly re-attached every `poll_interval_s` to drain
output and check for completion, so a long command occupies a
connection for milliseconds per poll instead of its whole runtime
(completion latency is bounded by `poll_interval_s`). Nothing is
written to the sandbox filesystem; output rides the server's retained
window (\~1 MiB) between polls, comes back as one merged stream
(`stderr` is `None`), and exceeding the window raises rather than
returning truncated output — run bulk-output commands attached or via
the exec API instead. A detached exec never reuses the default-shell
session implicitly: without `session` it opens a private one. With
`session`, the session is detached while the command works, must
not be used concurrently, and is attached and reusable again when
this returns.

PTY mode returns all output on `stdout` and `None` stderr; pipe mode
splits the two. A command that outlives `timeout_s` returns
`error_type="timeout"` like `sandbox.exec()` rather than raising;
in an explicitly passed session that command keeps running and leaves
unread output behind, so discard the session rather than reusing it (an
implicitly reused session is retired automatically).

```python
class nemo_gym.sandbox.SandboxPtyError()
```

**Bases:** `RuntimeError`

Raised when a PTY session fails outside normal process exit.

```python
class nemo_gym.sandbox.SandboxPtySession()
```

Protocol

One live interactive terminal. Async context manager; exit closes it.

**`closed`** `bool`

Whether `close()` has run; a closed session cannot run commands.

---

**`mode`** `str | None`

`"pty"` or `"pipe"` once connected, `None` before that. Only pipe
mode splits stderr; in PTY mode all output arrives through `read()`.

---

**`session_id`** `str`

---

```python
nemo_gym.sandbox.SandboxPtySession.__aenter__() -> nemo_gym.sandbox.providers.base.SandboxPtySession
```

async

```python
nemo_gym.sandbox.SandboxPtySession.__aexit__(
    exc_type: typing.Any,
    exc_val: typing.Any,
    exc_tb: typing.Any
) -> None
```

async

```python
nemo_gym.sandbox.SandboxPtySession.__aiter__() -> collections.abc.AsyncIterator[bytes]
```

Yield output chunks until EOF.

```python
nemo_gym.sandbox.SandboxPtySession.close() -> None
```

async

Idempotent: release local resources; a session this client created
is also ended, while an attached one is merely detached and lives on
for its owner.

```python
nemo_gym.sandbox.SandboxPtySession.read(
    timeout_s: float | None = None
) -> bytes
```

async

Return the next output chunk (all terminal output in PTY mode;
stdout only in pipe mode). `b""` means the process exited and the
stream is drained. Raises `TimeoutError` on timeout and
`SandboxPtyError` if the session died without exiting.

```python
nemo_gym.sandbox.SandboxPtySession.read_stderr(
    timeout_s: float | None = None
) -> bytes
```

async

Return the next stderr chunk. Only pipe mode (`pty=False`)
carries stderr separately; in PTY mode this stream is empty and
returns `b""` once the process exits. Same timeout/error semantics
as `read()`.

```python
nemo_gym.sandbox.SandboxPtySession.resize(
    rows: int,
    cols: int
) -> None
```

async

Resize the terminal.

```python
nemo_gym.sandbox.SandboxPtySession.run_detached(
    command: str,
    poll_interval_s: float = 15.0
) -> tuple[bytes, int | None]
```

async

Run one command holding the transport only for brief completion
polls; returns `(merged output, exit code or None)`. The server
retains a bounded window of output between polls, and exceeding it
raises rather than returning truncated output.

```python
nemo_gym.sandbox.SandboxPtySession.send_signal(
    signal: str
) -> None
```

async

Deliver a named signal, e.g. `"SIGTERM"`, to the session's process
group. Interactive shells run foreground jobs in their own group, so
signals reliably reach the process only for command sessions; whether
`SIGINT` interrupts at all also depends on the sandbox runtime's
inherited signal dispositions.

```python
nemo_gym.sandbox.SandboxPtySession.wait_exit(
    timeout_s: float | None = None
) -> int
```

async

Block until the process exits and return its exit code.

```python
nemo_gym.sandbox.SandboxPtySession.write(
    data: bytes
) -> None
```

async

Send raw bytes to the terminal's stdin.

```python
class nemo_gym.sandbox.SandboxPtySpec(
    command: str | None = None,
    cwd: str | None = None,
    env: dict[str, str] | None = None,
    rows: int = 24,
    cols: int = 80,
    user: str | int | None = None,
    pty: bool = True
)
```

Dataclass

Interactive PTY session request.

`command` runs under the backend's interactive shell; `None` spawns the
shell itself. Backends without native env/user support may rewrite the
command (mirroring `exec()`'s user rewrite) and must raise `ValueError`
for values they cannot honor. `pty=False` selects pipe mode: no TTY,
stdout and stderr delivered as separate streams, `rows`/`cols` ignored.
Backends that cannot size a terminal at spawn apply `rows`/`cols` as
soon as it connects, so a `command` reading the size immediately may
observe the backend default.

**`cols`** `int = 80`

---

**`command`** `str | None = None`

---

**`cwd`** `str | None = None`

---

**`env`** `dict[str, str] | None = None`

---

**`pty`** `bool = True`

---

**`rows`** `int = 24`

---

**`user`** `str | int | None = None`

---

```python
class nemo_gym.sandbox.SandboxResources(
    cpu: float | None = None,
    memory_mib: int | None = None,
    disk_gib: int | None = None,
    gpu: int | None = None,
    gpu_type: str | None = None
)
```

Dataclass

Provider-neutral resource request.

**`cpu`** `float | None = None`

---

**`disk_gib`** `int | None = None`

---

**`gpu`** `int | None = None`

---

**`gpu_type`** `str | None = None`

---

**`memory_mib`** `int | None = None`

---

```python
nemo_gym.sandbox.SandboxResources.from_mapping(
    resources: collections.abc.Mapping[str, typing.Any] | None
) -> nemo_gym.sandbox.providers.base.SandboxResources
```

classmethod

```python
class nemo_gym.sandbox.SandboxSpec(
    image: str | None = None,
    ttl_s: int | float | None = None,
    ready_timeout_s: int | float | None = None,
    workdir: str | None = None,
    env: dict[str, str] = dict(),
    files: dict[str, str] = dict(),
    metadata: dict[str, str] = dict(),
    resources: nemo_gym.sandbox.providers.base.SandboxResources | collections.abc.Mapping[str, typing.Any] = SandboxResources(),
    entrypoint: list[str] | None = None,
    provider_options: dict[str, typing.Any] = dict(),
    ports: tuple[int, ...] | list[int] = tuple()
)
```

Dataclass

Sandbox creation request.

**`entrypoint`** `list[str] | None = None`

---

**`env`** `dict[str, str] = field(default_factory=dict)`

---

**`files`** `dict[str, str] = field(default_factory=dict)`

---

**`image`** `str | None = None`

---

**`metadata`** `dict[str, str] = field(default_factory=dict)`

---

**`ports`** `tuple[int, ...] | list[int] = field(default_factory=tuple)`

---

**`provider_options`** `dict[str, Any] = field(default_factory=dict)`

---

**`ready_timeout_s`** `int | float | None = None`

---

**`resources`** `SandboxResources | Mapping[str, Any] = field(default_factory=SandboxResources)`

---

**`ttl_s`** `int | float | None = None`

---

**`workdir`** `str | None = None`

---

```python
nemo_gym.sandbox.SandboxSpec.__post_init__() -> None
```

```python
class nemo_gym.sandbox.SandboxStatus
```

**Bases:** `enum.Enum`

Provider-neutral sandbox lifecycle status.

**`ERROR`** `= 'error'`

---

**`PAUSED`** `= 'paused'`

---

**`RUNNING`** `= 'running'`

---

**`STARTING`** `= 'starting'`

---

**`STOPPED`** `= 'stopped'`

---

**`UNKNOWN`** `= 'unknown'`

---

```python
class nemo_gym.sandbox.SupportsSandboxEndpoint()
```

Protocol

Optional provider capability for resolving declared service ports.

```python
nemo_gym.sandbox.SupportsSandboxEndpoint.endpoint(
    handle: nemo_gym.sandbox.providers.base.SandboxHandle,
    port: int
) -> nemo_gym.sandbox.providers.base.SandboxEndpoint
```

async

Resolve a declared service port to a caller-reachable endpoint.

```python
class nemo_gym.sandbox.SupportsSandboxNetwork()
```

Protocol

Optional direct networking between sandboxes, preserving service ports.

```python
nemo_gym.sandbox.SupportsSandboxNetwork.network_address(
    handle: nemo_gym.sandbox.providers.base.SandboxHandle
) -> str
```

async

```python
nemo_gym.sandbox.SupportsSandboxNetwork.set_hosts(
    handle: nemo_gym.sandbox.providers.base.SandboxHandle,
    hosts: collections.abc.Mapping[str, str]
) -> None
```

async

```python
nemo_gym.sandbox.SupportsSandboxNetwork.validate_networking() -> None
```

```python
class nemo_gym.sandbox.SupportsSandboxPauseResume()
```

Protocol

Optional provider capability to pause and resume a sandbox.

What survives a pause beyond the filesystem is backend-specific.

```python
nemo_gym.sandbox.SupportsSandboxPauseResume.pause(
    handle: nemo_gym.sandbox.providers.base.SandboxHandle
) -> None
```

async

Pause a sandbox while preserving its state.

```python
nemo_gym.sandbox.SupportsSandboxPauseResume.resume(
    handle: nemo_gym.sandbox.providers.base.SandboxHandle
) -> None
```

async

Resume a paused sandbox and refresh its handle.

```python
class nemo_gym.sandbox.SupportsSandboxPortForwarding()
```

Protocol

Optional loopback TCP forwarding, running until cancellation.

```python
nemo_gym.sandbox.SupportsSandboxPortForwarding.forward_ports(
    handle: nemo_gym.sandbox.providers.base.SandboxHandle,
    target_address: str,
    ports: tuple[int, ...],
    ready_file: str
) -> None
```

async

```python
nemo_gym.sandbox.SupportsSandboxPortForwarding.validate_port_forwarding() -> None
```

```python
class nemo_gym.sandbox.SupportsSandboxPty()
```

Protocol

Optional provider capability: interactive PTY sessions.

```python
nemo_gym.sandbox.SupportsSandboxPty.create_pty(
    handle: nemo_gym.sandbox.providers.base.SandboxHandle,
    spec: nemo_gym.sandbox.providers.base.SandboxPtySpec
) -> nemo_gym.sandbox.providers.base.SandboxPtySession
```

async

Open an interactive terminal inside a sandbox.

```python
class nemo_gym.sandbox.SupportsSandboxPtyAttach()
```

Protocol

Optional provider capability: re-attach to a PTY session by id.

Separate from `SupportsSandboxPty` because it requires sessions that live
in the sandbox rather than in the client, so a provider may offer terminals
without offering re-attach.

```python
nemo_gym.sandbox.SupportsSandboxPtyAttach.attach_pty(
    handle: nemo_gym.sandbox.providers.base.SandboxHandle,
    session_id: str,
    takeover: bool = True,
    since: int | None = None
) -> nemo_gym.sandbox.providers.base.SandboxPtySession
```

async

Re-attach to an existing session by id.

`takeover` evicts the current holder, whose session then fails with
`SandboxPtyError`; without it, attaching to a held session fails.
`since` is a byte offset into the session's retained output to replay
before live output (`0` replays everything still retained).

```python
class nemo_gym.sandbox.SupportsSandboxRuntimeRequirements()
```

Protocol

Optional validation and setup of task-declared runtime requirements.

```python
nemo_gym.sandbox.SupportsSandboxRuntimeRequirements.configure_runtime(
    handle: nemo_gym.sandbox.providers.base.SandboxHandle,
    cap_add: tuple[str, ...],
    shm_size: int | None
) -> None
```

async

```python
nemo_gym.sandbox.SupportsSandboxRuntimeRequirements.validate_runtime_requirements(
    cap_add: tuple[str, ...],
    shm_size: int | None
) -> dict[str, str] | None
```

Validate support and return any required create-time metadata.

```python
class nemo_gym.sandbox.SupportsSandboxSharedStorage()
```

Protocol

Optional shared filesystem provisioned by the operator's provider config.

`shared_volume_options` returns provider options containing a `volumes`
list of opaque mount descriptors. Collections concatenate these lists.
`source` is relative to the configured shared root; `None` mounts that
root for initialization and cleanup. Metadata supplies placement settings.

```python
nemo_gym.sandbox.SupportsSandboxSharedStorage.shared_volume_metadata() -> dict[str, str]
```

```python
nemo_gym.sandbox.SupportsSandboxSharedStorage.shared_volume_options(
    source: str | None,
    target: str,
    read_only: bool = False
) -> dict[str, typing.Any]
```

```python
nemo_gym.sandbox.create_provider(
    config: collections.abc.Mapping[str, typing.Any]
) -> nemo_gym.sandbox.providers.base.SandboxProvider
```

Instantiate a provider from a single-key provider config.

```python
nemo_gym.sandbox.get_provider_class(
    name: str
) -> nemo_gym.sandbox.providers.registry.ProviderClass
```

Return a provider class by name (explicit > built-in > entry point).

```python
nemo_gym.sandbox.list_providers() -> list[str]
```

List available provider names from all sources.

```python
nemo_gym.sandbox.register_provider(
    name: str,
    provider_class: nemo_gym.sandbox.providers.registry.ProviderClass,
    override: bool = False
) -> None
```

Register a sandbox provider class.

```python
nemo_gym.sandbox.resolve_provider_config(
    sandbox_provider: str | collections.abc.Mapping[str, typing.Any],
    named_configs: collections.abc.Mapping[str, typing.Any] | None = None
) -> dict[str, typing.Any]
```

Resolve a `sandbox_provider` field into a single-key provider config dict.

**Parameters:**

**`sandbox_provider`** `str | Mapping[str, Any]`

Either the name of a top-level sandbox config block
(resolved from `named_configs`) or an inline single-key provider
mapping of the form `&#123;provider_name: &#123;...&#125;&#125;`.

---

**`named_configs`** `Mapping[str, Any] | None` — default: None

Mapping of top-level config name to config block, typically
the merged global config dict. Required when `sandbox_provider` is a
name reference.

---

**Returns:** `dict[str, Any]`

A plain `&#123;provider_name: provider_kwargs&#125;` dict suitable for

**Raises:**

* `TypeError`: If `sandbox_provider` is neither a string nor a mapping.
* `ValueError`: If a named reference cannot be found, or if the block does not
  hold exactly one provider key.

```python
nemo_gym.sandbox.resolve_provider_metadata(
    sandbox_provider: str | collections.abc.Mapping[str, typing.Any],
    named_configs: collections.abc.Mapping[str, typing.Any] | None = None
) -> dict[str, typing.Any]
```

Return a sandbox block's `default_metadata`.

These are provider-contributed defaults to merge into `SandboxSpec.metadata`.
Returns an empty dict when the block has no `default_metadata` key. See
`resolve_provider_config` for argument semantics.

```python
nemo_gym.sandbox.rewrite_image(
    image: str | None,
    rewrites: list[dict[str, str]]
) -> str | None
```

Apply ordered image-prefix rewrites used by sandbox configs.

```python
nemo_gym.sandbox.providers.base.ExecResult = SandboxExecResult
```