> 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.providers.opensandbox.provider

OpenSandbox provider implementation.

## Module Contents

### Classes

| Name                                                                                                                            | Description                                                                              |
| ------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| [`OpenSandboxAttributionConfig`](#nemo_gym-sandbox-providers-opensandbox-provider-OpenSandboxAttributionConfig)                 | Job attribution merged into every sandbox's metadata (Kubernetes labels on the sandbox). |
| [`OpenSandboxConnectionConfig`](#nemo_gym-sandbox-providers-opensandbox-provider-OpenSandboxConnectionConfig)                   | OpenSandbox server connection settings.                                                  |
| [`OpenSandboxCreateConfig`](#nemo_gym-sandbox-providers-opensandbox-provider-OpenSandboxCreateConfig)                           | OpenSandbox create/reconnect retry settings.                                             |
| [`OpenSandboxCreateError`](#nemo_gym-sandbox-providers-opensandbox-provider-OpenSandboxCreateError)                             | Raised when OpenSandbox cannot create a sandbox.                                         |
| [`OpenSandboxCreateTimeoutError`](#nemo_gym-sandbox-providers-opensandbox-provider-OpenSandboxCreateTimeoutError)               | Raised when OpenSandbox sandbox creation exceeds the client timeout.                     |
| [`OpenSandboxCreateVerificationError`](#nemo_gym-sandbox-providers-opensandbox-provider-OpenSandboxCreateVerificationError)     | Raised when a newly-created sandbox cannot execute a probe command.                      |
| [`OpenSandboxNetworkingConfig`](#nemo_gym-sandbox-providers-opensandbox-provider-OpenSandboxNetworkingConfig)                   | Operator assertion that direct sandbox IPs are mutually reachable.                       |
| [`OpenSandboxOperationConfig`](#nemo_gym-sandbox-providers-opensandbox-provider-OpenSandboxOperationConfig)                     | Retry and timeout settings for SDK operations after create.                              |
| [`OpenSandboxProbeConfig`](#nemo_gym-sandbox-providers-opensandbox-provider-OpenSandboxProbeConfig)                             | Post-create probe settings.                                                              |
| [`OpenSandboxProvider`](#nemo_gym-sandbox-providers-opensandbox-provider-OpenSandboxProvider)                                   | Provider backed by the OpenSandbox SDK/server API.                                       |
| [`OpenSandboxProviderOptions`](#nemo_gym-sandbox-providers-opensandbox-provider-OpenSandboxProviderOptions)                     | Recognized per-sandbox create options read from `SandboxSpec.provider_options`.          |
| [`OpenSandboxRuntimeRequirementsConfig`](#nemo_gym-sandbox-providers-opensandbox-provider-OpenSandboxRuntimeRequirementsConfig) | Operator-supplied capability probes and create-time runtime metadata.                    |
| [`OpenSandboxSharedStorageConfig`](#nemo_gym-sandbox-providers-opensandbox-provider-OpenSandboxSharedStorageConfig)             | Shared host mount and optional deployment-specific placement metadata.                   |
| [`SandboxBackendUnreachableError`](#nemo_gym-sandbox-providers-opensandbox-provider-SandboxBackendUnreachableError)             | Raised when the server proxy cannot open a TCP connection to a sandbox's exec daemon.    |
| [`_MissingSandboxTerminateFilter`](#nemo_gym-sandbox-providers-opensandbox-provider-_MissingSandboxTerminateFilter)             | Hide the SDK warning emitted when terminate reaches its desired state.                   |

### Functions

| Name                                                                                                                      | Description                                                                     |
| ------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| [`_exception_status_code`](#nemo_gym-sandbox-providers-opensandbox-provider-_exception_status_code)                       | -                                                                               |
| [`_has_retryable_error_marker`](#nemo_gym-sandbox-providers-opensandbox-provider-_has_retryable_error_marker)             | -                                                                               |
| [`_is_missing_sandbox_delete_error`](#nemo_gym-sandbox-providers-opensandbox-provider-_is_missing_sandbox_delete_error)   | Match kill errors meaning the sandbox is already gone (terminate's goal state). |
| [`_is_retryable_create_error`](#nemo_gym-sandbox-providers-opensandbox-provider-_is_retryable_create_error)               | Return whether a sandbox create failure is likely transient.                    |
| [`_is_retryable_sdk_operation_error`](#nemo_gym-sandbox-providers-opensandbox-provider-_is_retryable_sdk_operation_error) | Return whether an SDK operation can be retried.                                 |
| [`_log_create_retry`](#nemo_gym-sandbox-providers-opensandbox-provider-_log_create_retry)                                 | -                                                                               |
| [`_log_operation_retry`](#nemo_gym-sandbox-providers-opensandbox-provider-_log_operation_retry)                           | -                                                                               |
| [`_metadata_map`](#nemo_gym-sandbox-providers-opensandbox-provider-_metadata_map)                                         | -                                                                               |
| [`_metadata_value`](#nemo_gym-sandbox-providers-opensandbox-provider-_metadata_value)                                     | -                                                                               |
| [`_normalize_spec`](#nemo_gym-sandbox-providers-opensandbox-provider-_normalize_spec)                                     | -                                                                               |
| [`_require_opensandbox_sdk`](#nemo_gym-sandbox-providers-opensandbox-provider-_require_opensandbox_sdk)                   | -                                                                               |
| [`_require_tenacity`](#nemo_gym-sandbox-providers-opensandbox-provider-_require_tenacity)                                 | -                                                                               |
| [`_resource_map`](#nemo_gym-sandbox-providers-opensandbox-provider-_resource_map)                                         | -                                                                               |
| [`_resource_quantity`](#nemo_gym-sandbox-providers-opensandbox-provider-_resource_quantity)                               | -                                                                               |
| [`_sdk_error_attributes`](#nemo_gym-sandbox-providers-opensandbox-provider-_sdk_error_attributes)                         | -                                                                               |
| [`_string_map`](#nemo_gym-sandbox-providers-opensandbox-provider-_string_map)                                             | -                                                                               |
| [`_to_image_spec`](#nemo_gym-sandbox-providers-opensandbox-provider-_to_image_spec)                                       | -                                                                               |
| [`_to_network_policy`](#nemo_gym-sandbox-providers-opensandbox-provider-_to_network_policy)                               | -                                                                               |
| [`_to_platform_spec`](#nemo_gym-sandbox-providers-opensandbox-provider-_to_platform_spec)                                 | -                                                                               |
| [`_to_sandbox_status`](#nemo_gym-sandbox-providers-opensandbox-provider-_to_sandbox_status)                               | -                                                                               |
| [`_to_volumes`](#nemo_gym-sandbox-providers-opensandbox-provider-_to_volumes)                                             | -                                                                               |
| [`split_domain_scheme`](#nemo_gym-sandbox-providers-opensandbox-provider-split_domain_scheme)                             | Split a configured OpenSandbox domain into `(domain, scheme)`.                  |
| [`validate_image_pull_policy`](#nemo_gym-sandbox-providers-opensandbox-provider-validate_image_pull_policy)               | Validate a Kubernetes-compatible container image pull policy.                   |

### Data

[`ATTRIBUTION_KEY_PREFIX_RE`](#nemo_gym-sandbox-providers-opensandbox-provider-ATTRIBUTION_KEY_PREFIX_RE)

[`DEFAULT_ATTRIBUTION_KEY_PREFIX`](#nemo_gym-sandbox-providers-opensandbox-provider-DEFAULT_ATTRIBUTION_KEY_PREFIX)

[`DEFAULT_IMAGE_PULL_POLICY`](#nemo_gym-sandbox-providers-opensandbox-provider-DEFAULT_IMAGE_PULL_POLICY)

[`IMAGE_PULL_POLICY_ANNOTATION_EXTENSION_KEY`](#nemo_gym-sandbox-providers-opensandbox-provider-IMAGE_PULL_POLICY_ANNOTATION_EXTENSION_KEY)

[`IMAGE_PULL_POLICY_EXTENSION_KEY`](#nemo_gym-sandbox-providers-opensandbox-provider-IMAGE_PULL_POLICY_EXTENSION_KEY)

[`LOGGER`](#nemo_gym-sandbox-providers-opensandbox-provider-LOGGER)

[`METADATA_VALUE_RE`](#nemo_gym-sandbox-providers-opensandbox-provider-METADATA_VALUE_RE)

[`RETRYABLE_ERROR_MARKERS`](#nemo_gym-sandbox-providers-opensandbox-provider-RETRYABLE_ERROR_MARKERS)

[`RETRYABLE_HTTP_STATUS_CODES`](#nemo_gym-sandbox-providers-opensandbox-provider-RETRYABLE_HTTP_STATUS_CODES)

[`STATUS_CODE_RE`](#nemo_gym-sandbox-providers-opensandbox-provider-STATUS_CODE_RE)

[`VALID_IMAGE_PULL_POLICIES`](#nemo_gym-sandbox-providers-opensandbox-provider-VALID_IMAGE_PULL_POLICIES)

### API

```python
class nemo_gym.sandbox.providers.opensandbox.OpenSandboxAttributionConfig(
    enabled: bool = True,
    team: str | None = None,
    user: str | None = None,
    workload: str | None = None,
    run: str | None = None,
    key_prefix: str = DEFAULT_ATTRIBUTION_KEY_PREFIX
)
```

Dataclass

Job attribution merged into every sandbox's metadata (Kubernetes labels on the sandbox).

OpenSandbox propagates sandbox metadata as Kubernetes labels on the sandbox resources, so
attribution is queryable both through the OpenSandbox list API and at the cluster level
(e.g. `kubectl get pods -l nemo-gym.nvidia.com/team=my-team`). `key_prefix` namespaces
the label keys (Kubernetes prefixed-key convention); set it to `""` for bare
`team` / `user` / `workload` / `run` keys.

Unset fields are auto-detected: `NEMO_GYM_TEAM` / `NEMO_GYM_USER` / `NEMO_GYM_WORKLOAD`
environment variables first, then Slurm job env vars (`SLURM_JOB_ACCOUNT` /
`SLURM_JOB_USER` / `SLURM_JOB_NAME`), then the OS login name for `user` (`root` is
ignored) and the gym CLI's `NEMO_GYM_CONFIG_PATH` server instance name for `workload`.
Fields that cannot be resolved are omitted. `run` scopes sandboxes to one launch of the
creating process (`NEMO_GYM_RUN_ID`, else generated per process and logged) so a run's
sandboxes can be listed and cleaned up exactly. Explicit `SandboxSpec.metadata` keys
always take precedence over attribution keys.

**`enabled`** `bool = True`

---

**`key_prefix`** `str = DEFAULT_ATTRIBUTION_KEY_PREFIX`

---

**`run`** `str | None = None`

---

**`team`** `str | None = None`

---

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

---

**`workload`** `str | None = None`

---

```python
nemo_gym.sandbox.providers.opensandbox.OpenSandboxAttributionConfig.__post_init__() -> None
```

```python
class nemo_gym.sandbox.providers.opensandbox.OpenSandboxConnectionConfig(
    domain: str | None = None,
    api_key: str | None = None,
    protocol: str | None = None,
    request_timeout_s: int | None = None,
    use_server_proxy: bool = False,
    disable_connection_pooling: bool = False,
    keepalive_expiry_s: float | None = 3.0,
    max_keepalive_connections: int = 20,
    max_connections: int | None = 100,
    connect_retries: int = 2,
    transport_backend: str = 'aiohttp',
    tls_verify: bool = False
)
```

Dataclass

OpenSandbox server connection settings.

With the legacy httpx backend, `keepalive_expiry_s` must stay below the
server's own keep-alive idle timeout (uvicorn defaults to 5s), or sockets are reused after the
server has closed them; null falls back to the SDK's default transport only
when certificate verification is enabled and pooling is not disabled.
`transport_backend=aiohttp` uses Gym's global client and connector limits;
provider-local pooling settings apply only to `transport_backend=httpx`.
`tls_verify` applies to every connection the provider opens (SDK transport
and PTY sockets) and is off by
default; set it for endpoints whose certificate the client can verify.
`domain` may carry its scheme (`https://sandbox.example`). The scheme is
moved into `protocol` and takes precedence over a configured `protocol`,
so every URL the provider builds itself (the PTY WebSocket target) agrees
with the SDK's base URL; the SDK receives the host plus any path prefix
(`sandbox.example:8080/prefix`). The SDK would accept a scheme in
`domain` on its own, but the provider reads `protocol` directly, hence
the normalization here. Only `scheme://host[:port][/path-prefix]` is
accepted: a query string or fragment is a configuration error.

**`api_key`** `str | None = None`

---

**`connect_retries`** `int = 2`

---

**`disable_connection_pooling`** `bool = False`

---

**`domain`** `str | None = None`

---

**`keepalive_expiry_s`** `float | None = 3.0`

---

**`max_connections`** `int | None = 100`

---

**`max_keepalive_connections`** `int = 20`

---

**`protocol`** `str | None = None`

---

**`request_timeout_s`** `int | None = None`

---

**`tls_verify`** `bool = False`

---

**`transport_backend`** `str = 'aiohttp'`

---

**`use_server_proxy`** `bool = False`

---

```python
nemo_gym.sandbox.providers.opensandbox.OpenSandboxConnectionConfig.__post_init__() -> None
```

```python
class nemo_gym.sandbox.providers.opensandbox.OpenSandboxCreateConfig(
    request_timeout_s: int | None = None,
    timeout_s: float | None = None,
    retries: int = 2,
    retry_delay_s: float = 5.0,
    retry_max_delay_s: float = 60.0,
    image_pull_policy: str | None = DEFAULT_IMAGE_PULL_POLICY,
    skip_health_check: bool = False,
    connect_attempt_timeout_s: float = 30.0,
    connect_poll_s: float = 2.0,
    renew_interval_s: float | None = None
)
```

Dataclass

OpenSandbox create/reconnect retry settings.

**`connect_attempt_timeout_s`** `float = 30.0`

---

**`connect_poll_s`** `float = 2.0`

---

**`image_pull_policy`** `str | None = DEFAULT_IMAGE_PULL_POLICY`

---

**`renew_interval_s`** `float | None = None`

---

**`request_timeout_s`** `int | None = None`

---

**`retries`** `int = 2`

---

**`retry_delay_s`** `float = 5.0`

---

**`retry_max_delay_s`** `float = 60.0`

---

**`skip_health_check`** `bool = False`

---

**`timeout_s`** `float | None = None`

---

```python
nemo_gym.sandbox.providers.opensandbox.OpenSandboxCreateConfig.__post_init__() -> None
```

```python
class nemo_gym.sandbox.providers.opensandbox.OpenSandboxCreateError()
```

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

Raised when OpenSandbox cannot create a sandbox.

```python
class nemo_gym.sandbox.providers.opensandbox.OpenSandboxCreateTimeoutError()
```

**Bases:** [OpenSandboxCreateError](#nemo_gym-sandbox-providers-opensandbox-provider-OpenSandboxCreateError)

Raised when OpenSandbox sandbox creation exceeds the client timeout.

```python
class nemo_gym.sandbox.providers.opensandbox.OpenSandboxCreateVerificationError()
```

**Bases:** [SandboxCreateVerificationError](/nemo/gym/nemo-gym/nemo_gym/sandbox/providers/base#nemo_gym-sandbox-providers-base-SandboxCreateVerificationError)

Raised when a newly-created sandbox cannot execute a probe command.

```python
class nemo_gym.sandbox.providers.opensandbox.provider.OpenSandboxNetworkingConfig(
    enabled: bool = False,
    loopback_forwarding: bool = False,
    python_executable: str = 'python3',
    setup_command: str | None = None
)
```

Dataclass

Operator assertion that direct sandbox IPs are mutually reachable.

**`enabled`** `bool = False`

---

**`loopback_forwarding`** `bool = False`

---

**`python_executable`** `str = 'python3'`

---

**`setup_command`** `str | None = None`

---

```python
class nemo_gym.sandbox.providers.opensandbox.OpenSandboxOperationConfig(
    retries: int = 3,
    retry_delay_s: float = 1.0,
    retry_max_delay_s: float = 15.0,
    command_retries: int = 0,
    close_timeout_s: float | None = 30.0,
    pause_resume_timeout_s: float = 600.0,
    background_exec: bool = False,
    background_poll_initial_s: float = 0.25,
    background_poll_interval_s: float = 2.0,
    status_poll_timeout_s: float | None = 10.0
)
```

Dataclass

Retry and timeout settings for SDK operations after create.

**`background_exec`** `bool = False`

---

**`background_poll_initial_s`** `float = 0.25`

---

**`background_poll_interval_s`** `float = 2.0`

---

**`close_timeout_s`** `float | None = 30.0`

---

**`command_retries`** `int = 0`

---

**`pause_resume_timeout_s`** `float = 600.0`

---

**`retries`** `int = 3`

---

**`retry_delay_s`** `float = 1.0`

---

**`retry_max_delay_s`** `float = 15.0`

---

**`status_poll_timeout_s`** `float | None = 10.0`

---

```python
nemo_gym.sandbox.providers.opensandbox.OpenSandboxOperationConfig.__post_init__() -> None
```

```python
class nemo_gym.sandbox.providers.opensandbox.OpenSandboxProbeConfig(
    command: str | None = 'printf nemo-gym-sandbox-re...,
    expected_stdout: str | None = 'nemo-gym-sandbox-ready',
    timeout_s: int = 30,
    deadline_s: float | None = None,
    stable_count: int = 1,
    stable_delay_s: float = 0.0,
    user: str | int | None = None
)
```

Dataclass

Post-create probe settings.

**`command`** `str | None = 'printf nemo-gym-sandbox-ready'`

---

**`deadline_s`** `float | None = None`

---

**`expected_stdout`** `str | None = 'nemo-gym-sandbox-ready'`

---

**`stable_count`** `int = 1`

---

**`stable_delay_s`** `float = 0.0`

---

**`timeout_s`** `int = 30`

---

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

---

```python
nemo_gym.sandbox.providers.opensandbox.OpenSandboxProbeConfig.__post_init__() -> None
```

```python
class nemo_gym.sandbox.providers.opensandbox.OpenSandboxProvider(
    connection: nemo_gym.sandbox.providers.opensandbox.provider.OpenSandboxConnectionConfig | collections.abc.Mapping[str, typing.Any] | None = None,
    create: nemo_gym.sandbox.providers.opensandbox.provider.OpenSandboxCreateConfig | collections.abc.Mapping[str, typing.Any] | None = None,
    probe: nemo_gym.sandbox.providers.opensandbox.provider.OpenSandboxProbeConfig | collections.abc.Mapping[str, typing.Any] | None = None,
    operations: nemo_gym.sandbox.providers.opensandbox.provider.OpenSandboxOperationConfig | collections.abc.Mapping[str, typing.Any] | None = None,
    attribution: nemo_gym.sandbox.providers.opensandbox.provider.OpenSandboxAttributionConfig | collections.abc.Mapping[str, typing.Any] | None = None,
    networking: nemo_gym.sandbox.providers.opensandbox.provider.OpenSandboxNetworkingConfig | collections.abc.Mapping[str, typing.Any] | None = None,
    shared_storage: nemo_gym.sandbox.providers.opensandbox.provider.OpenSandboxSharedStorageConfig | collections.abc.Mapping[str, typing.Any] | None = None,
    runtime_requirements: nemo_gym.sandbox.providers.opensandbox.provider.OpenSandboxRuntimeRequirementsConfig | collections.abc.Mapping[str, typing.Any] | None = None
)
```

Provider backed by the OpenSandbox SDK/server API.

**`_attribution`**

---

**`_connection`**

---

**`_create`** `= _coerce_config(create, OpenSandboxCreateConfig)`

---

**`_networking`**

---

**`_operations`**

---

**`_probe`** `= _coerce_config(probe, OpenSandboxProbeConfig)`

---

**`_pty_sessions`** `set[Any] = set()`

---

**`_renewals`** `dict[str, Task[None]] = {}`

---

**`_runtime_requirements`**

---

**`_shared_storage`**

---

**`_transport`** `Any | None = None`

---

**`name`** `= 'opensandbox'`

---

```python
nemo_gym.sandbox.providers.opensandbox.OpenSandboxProvider._attribution_metadata() -> dict[str, str]
```

```python
nemo_gym.sandbox.providers.opensandbox.OpenSandboxProvider._await_sdk_call(
    awaitable: typing.Any,
    operation: str,
    sandbox_id: str,
    timeout_s: float | None
) -> typing.Any
```

async

```python
nemo_gym.sandbox.providers.opensandbox.OpenSandboxProvider._await_sdk_operation(
    operation_factory: typing.Callable[[], typing.Awaitable[typing.Any]],
    operation: str,
    sandbox_id: str,
    timeout_s: float | None,
    retries: int | None = None,
    is_retryable: typing.Callable[[BaseException], bool] = _is_retryable_sdk_operation...
) -> typing.Any
```

async

```python
nemo_gym.sandbox.providers.opensandbox.OpenSandboxProvider._build_transport() -> typing.Any
```

Use Gym's global HTTP pool, or the explicitly selected legacy backend.

```python
nemo_gym.sandbox.providers.opensandbox.OpenSandboxProvider._cleanup_failed_create_handle(
    handle: nemo_gym.sandbox.providers.base.SandboxHandle
) -> None
```

async

```python
nemo_gym.sandbox.providers.opensandbox.OpenSandboxProvider._command_retry_count() -> int
```

```python
nemo_gym.sandbox.providers.opensandbox.OpenSandboxProvider._connect_after_create(
    handle: nemo_gym.sandbox.providers.base.SandboxHandle,
    spec: nemo_gym.sandbox.providers.base.SandboxSpec
) -> nemo_gym.sandbox.providers.base.SandboxHandle
```

async

Reconnect after SDK create so follow-up calls use a fresh SDK handle.

```python
nemo_gym.sandbox.providers.opensandbox.OpenSandboxProvider._connection_config(
    request_timeout_s: int | float | None = None
) -> typing.Any
```

```python
nemo_gym.sandbox.providers.opensandbox.OpenSandboxProvider._create_once(
    spec: nemo_gym.sandbox.providers.base.SandboxSpec
) -> nemo_gym.sandbox.providers.base.SandboxHandle
```

async

Create a sandbox through `opensandbox.Sandbox.create`.

```python
nemo_gym.sandbox.providers.opensandbox.OpenSandboxProvider._create_with_retries(
    spec: nemo_gym.sandbox.providers.base.SandboxSpec
) -> nemo_gym.sandbox.providers.base.SandboxHandle
```

async

```python
nemo_gym.sandbox.providers.opensandbox.OpenSandboxProvider._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,
    retries: int | None = None
) -> nemo_gym.sandbox.providers.base.SandboxExecResult
```

async

Run a command inside an OpenSandbox sandbox.

```python
nemo_gym.sandbox.providers.opensandbox.OpenSandboxProvider._exec_background(
    handle: nemo_gym.sandbox.providers.base.SandboxHandle,
    command: str,
    opts_kwargs: dict[str, typing.Any],
    sdk_timeout_s: float | None,
    total_timeout_s: int | float | None,
    retries: int
) -> nemo_gym.sandbox.providers.base.SandboxExecResult
```

async

Run a command as a background execution polled via short requests.

The logs endpoint returns one combined stream, so unlike the foreground
path `stdout` carries both streams and `stderr` is set only when the
sandbox itself reports an error.

```python
nemo_gym.sandbox.providers.opensandbox.OpenSandboxProvider._get_transport() -> typing.Any
```

Return the provider-owned shared transport, building it on first use.

```python
nemo_gym.sandbox.providers.opensandbox.OpenSandboxProvider._oom_death_notice(
    handle: nemo_gym.sandbox.providers.base.SandboxHandle,
    any_death: bool = False
) -> str | None
```

async

Briefly poll the sandbox status; describe an OOM kill, else None.

With `any_death` every terminal state is reported, not just an OOM
kill — for callers that need to know whether the sandbox is gone at
all, not specifically why. When the backend stops answering it usually
takes the control plane a moment to record why, so poll for up to 5s
before giving up.

```python
nemo_gym.sandbox.providers.opensandbox.OpenSandboxProvider._pty_http_client() -> typing.Any
```

Return the aiohttp client for one PTY session (same `tls_verify` as the SDK transport).

```python
nemo_gym.sandbox.providers.opensandbox.OpenSandboxProvider._pty_session_missing(
    base_url: str,
    headers: dict[str, str],
    session_id: str,
    request_timeout_s: float | None
) -> bool
```

async

True only when execd itself reports the PTY session does not exist.

A proxy 404 (route not registered yet) lacks execd's error code, and a
failed check is treated as unknown so the attach proceeds as before.

```python
nemo_gym.sandbox.providers.opensandbox.OpenSandboxProvider._pty_target(
    handle: nemo_gym.sandbox.providers.base.SandboxHandle
) -> tuple[str, dict[str, str], float | None]
```

async

Resolve the sandbox's execd base URL, headers and request timeout.

```python
nemo_gym.sandbox.providers.opensandbox.OpenSandboxProvider._read_file(
    handle: nemo_gym.sandbox.providers.base.SandboxHandle,
    source_path: str
) -> bytes
```

async

Read one file from an OpenSandbox sandbox.

```python
nemo_gym.sandbox.providers.opensandbox.OpenSandboxProvider._resolve_extensions(
    extensions: collections.abc.Mapping[str, str]
) -> dict[str, str]
```

Add the configured default image pull policy to SDK create extensions.

```python
nemo_gym.sandbox.providers.opensandbox.OpenSandboxProvider._retire_closed_pty_sessions() -> None
```

async

Release sessions that ended on their own; their aiohttp client is
only freed by `close()`. Called from create/attach so the tracking
set cannot grow without bound.

```python
nemo_gym.sandbox.providers.opensandbox.OpenSandboxProvider._start_renewal(
    handle: nemo_gym.sandbox.providers.base.SandboxHandle,
    ttl_s: int | float
) -> None
```

```python
nemo_gym.sandbox.providers.opensandbox.OpenSandboxProvider._stop_renewal(
    sandbox_id: str
) -> BaseException | None
```

async

```python
nemo_gym.sandbox.providers.opensandbox.OpenSandboxProvider._submit_command(
    operation_factory: typing.Callable[[], typing.Awaitable[typing.Any]],
    operation: str,
    sandbox_id: str,
    timeout_s: float | None,
    retries: int
) -> typing.Any
```

async

Retry backend-connect 502s that `command_retries` deliberately skips.

A proxy 502 is a TCP-connect failure: the command never reached execd, so
retrying under `operations.retries` cannot double-run it (unlike a real
command failure). When that budget is exhausted the backend is dead, so
raise a typed error and fail fast instead of retrying for hours.

```python
nemo_gym.sandbox.providers.opensandbox.OpenSandboxProvider._verify_created_handle(
    handle: nemo_gym.sandbox.providers.base.SandboxHandle
) -> None
```

async

```python
nemo_gym.sandbox.providers.opensandbox.OpenSandboxProvider._write_file(
    handle: nemo_gym.sandbox.providers.base.SandboxHandle,
    target_path: str,
    data: str | bytes
) -> None
```

async

Write one file into an OpenSandbox sandbox.

```python
nemo_gym.sandbox.providers.opensandbox.OpenSandboxProvider.aclose() -> None
```

async

Close provider-owned resources.

```python
nemo_gym.sandbox.providers.opensandbox.OpenSandboxProvider.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 execd PTY session by id.

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

async

Terminate the sandbox and close local SDK resources.

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

async

Probe capabilities and verify the shared memory allocated at creation.

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

async

Rebuild a live handle from an OpenSandbox sandbox id via the SDK.

Running sandboxes are health-checked unless the caller opts out. A
paused sandbox has no exec daemon to check; resume rebuilds its
endpoints and performs the health check instead.

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

async

Create one sandbox through the configured OpenSandbox path.

Job attribution keys (`team` / `user` / `workload` / `run`) are merged into the
spec's metadata (explicit spec keys win) so every sandbox is attributable via its labels.

```python
nemo_gym.sandbox.providers.opensandbox.OpenSandboxProvider.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 execd PTY session inside a sandbox.

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

async

Download one file from an OpenSandbox sandbox.

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

async

Resolve one client-reachable direct or server-proxied service URL.

```python
nemo_gym.sandbox.providers.opensandbox.OpenSandboxProvider.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 an OpenSandbox sandbox.

```python
nemo_gym.sandbox.providers.opensandbox.OpenSandboxProvider.exec_with_background_services(
    handle: nemo_gym.sandbox.providers.base.SandboxHandle,
    command: str,
    cwd: str | None = None,
    timeout_s: int | float | None = None
) -> nemo_gym.sandbox.providers.base.SandboxExecResult
```

async

Preserve background services that redirect their stdout and stderr.

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

async

Run loopback listeners in a foreground command owned by the collection.

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

async

Resolve a direct container IP, independently of client proxy mode.

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

async

Pause a sandbox and wait until it reports paused.

Local PTY clients are detached first, while execd can still answer the
close handshake; server sessions are never deleted, so they remain
attachable if the pause request fails. After resume, the Kubernetes
backend has replaced the runtime (open a new PTY); the Docker backend
thawed it (re-attach by id).

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

async

Resume a paused sandbox and rebuild its SDK clients and endpoints.

One `pause_resume_timeout_s` deadline covers the request, endpoint
rebuild and readiness check. On timeout the server-side state is
unknown: reconnect and check `status()` before retrying. See
`pause()` for what happens to PTY sessions.

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

async

Return a descriptor for reattaching to this sandbox by id.

OpenSandbox sandboxes are reachable by id from any process that has the
connection config, so the id alone is enough to reconnect and no sandbox
server is needed to share one. `scope` is ignored: OpenSandbox has no
lease concept of its own.

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

async

Append validated peer aliases to the sandbox hosts file.

```python
nemo_gym.sandbox.providers.opensandbox.OpenSandboxProvider.shared_volume_metadata() -> dict[str, str]
```

Return placement metadata required by the configured shared storage.

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

Mount a project directory, or the shared root for bootstrap when source is None.

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

async

Return the current OpenSandbox lifecycle status.

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

async

Upload one local file into an OpenSandbox sandbox.

```python
nemo_gym.sandbox.providers.opensandbox.OpenSandboxProvider.validate_networking() -> None
```

Require explicit deployment support for inter-sandbox networking.

```python
nemo_gym.sandbox.providers.opensandbox.OpenSandboxProvider.validate_port_forwarding() -> None
```

Require explicit deployment support for loopback TCP listeners.

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

Reject requirements without an operator-configured implementation.

```python
class nemo_gym.sandbox.providers.opensandbox.provider.OpenSandboxProviderOptions(
    image_auth: collections.abc.Mapping[str, typing.Any] | None = None,
    network_policy: collections.abc.Mapping[str, typing.Any] | None = None,
    platform: collections.abc.Mapping[str, typing.Any] | None = None,
    snapshot_id: str | None = None,
    volumes: tuple[collections.abc.Mapping[str, typing.Any], ...] = (),
    skip_health_check: bool | None = None,
    extensions: collections.abc.Mapping[str, str] = dict(),
    resource_requests: collections.abc.Mapping[str, typing.Any] | typing.Literal['limits'] | None = None
)
```

Dataclass

Recognized per-sandbox create options read from `SandboxSpec.provider_options`.

`image_auth`, `network_policy`, `platform`, and `volumes` entries are passed through to the
OpenSandbox SDK, so their inner fields are validated by the SDK rather than here.

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

---

**`image_auth`** `Mapping[str, Any] | None = None`

---

**`network_policy`** `Mapping[str, Any] | None = None`

---

**`platform`** `Mapping[str, Any] | None = None`

---

**`resource_requests`** `Mapping[str, Any] | Literal['limits'] | None = None`

---

**`skip_health_check`** `bool | None = None`

---

**`snapshot_id`** `str | None = None`

---

**`volumes`** `tuple[Mapping[str, Any], ...] = ()`

---

```python
nemo_gym.sandbox.providers.opensandbox.provider.OpenSandboxProviderOptions.from_mapping(
    options: collections.abc.Mapping[str, typing.Any] | None
) -> nemo_gym.sandbox.providers.opensandbox.provider.OpenSandboxProviderOptions
```

classmethod

```python
class nemo_gym.sandbox.providers.opensandbox.provider.OpenSandboxRuntimeRequirementsConfig(
    capability_probes: dict[str, str] = dict(),
    capability_metadata: dict[str, dict[str, str]] = dict(),
    shm_size_metadata_key: str | None = None
)
```

Dataclass

Operator-supplied capability probes and create-time runtime metadata.

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

---

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

---

**`shm_size_metadata_key`** `str | None = None`

---

```python
class nemo_gym.sandbox.providers.opensandbox.provider.OpenSandboxSharedStorageConfig(
    host_path: str | None = None,
    metadata: dict[str, str] = dict()
)
```

Dataclass

Shared host mount and optional deployment-specific placement metadata.

**`host_path`** `str | None = None`

---

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

---

```python
class nemo_gym.sandbox.providers.opensandbox.provider.SandboxBackendUnreachableError()
```

**Bases:** `RuntimeError`

Raised when the server proxy cannot open a TCP connection to a sandbox's exec daemon.

A submission 502 means the command never started. A status or log polling
502 can mean the backend died while the command was running.

```python
class nemo_gym.sandbox.providers.opensandbox.provider._MissingSandboxTerminateFilter()
```

**Bases:** `Filter`

Hide the SDK warning emitted when terminate reaches its desired state.

```python
nemo_gym.sandbox.providers.opensandbox.provider._MissingSandboxTerminateFilter.filter(
    record: logging.LogRecord
) -> bool
```

```python
nemo_gym.sandbox.providers.opensandbox.provider._exception_status_code(
    exception: BaseException
) -> int | None
```

```python
nemo_gym.sandbox.providers.opensandbox.provider._has_retryable_error_marker(
    exception: BaseException
) -> bool
```

```python
nemo_gym.sandbox.providers.opensandbox.provider._is_missing_sandbox_delete_error(
    exception: BaseException
) -> bool
```

Match kill errors meaning the sandbox is already gone (terminate's goal state).

Only the terminate path may treat this as success; other operations must
keep failing loudly on not-found.

```python
nemo_gym.sandbox.providers.opensandbox.provider._is_retryable_create_error(
    exception: BaseException
) -> bool
```

Return whether a sandbox create failure is likely transient.

```python
nemo_gym.sandbox.providers.opensandbox.provider._is_retryable_sdk_operation_error(
    exception: BaseException,
    seen: set[int] | None = None
) -> bool
```

Return whether an SDK operation can be retried.

```python
nemo_gym.sandbox.providers.opensandbox.provider._log_create_retry(
    retry_state: typing.Any
) -> None
```

```python
nemo_gym.sandbox.providers.opensandbox.provider._log_operation_retry(
    retry_state: typing.Any,
    operation: str = '?',
    sandbox_id: str = '?'
) -> None
```

```python
nemo_gym.sandbox.providers.opensandbox.provider._metadata_map(
    values: dict[str, typing.Any]
) -> dict[str, str]
```

```python
nemo_gym.sandbox.providers.opensandbox.provider._metadata_value(
    value: typing.Any
) -> str
```

```python
nemo_gym.sandbox.providers.opensandbox.provider._normalize_spec(
    spec: nemo_gym.sandbox.providers.base.SandboxSpec
) -> nemo_gym.sandbox.providers.base.SandboxSpec
```

```python
nemo_gym.sandbox.providers.opensandbox.provider._require_opensandbox_sdk() -> tuple[typing.Any, typing.Any, typing.Any, typing.Any, typing.Any]
```

```python
nemo_gym.sandbox.providers.opensandbox.provider._require_tenacity() -> tuple[typing.Any, typing.Any, typing.Any, typing.Any]
```

```python
nemo_gym.sandbox.providers.opensandbox.provider._resource_map(
    resources: nemo_gym.sandbox.providers.base.SandboxResources
) -> dict[str, str]
```

```python
nemo_gym.sandbox.providers.opensandbox.provider._resource_quantity(
    value: float | int
) -> str
```

```python
nemo_gym.sandbox.providers.opensandbox.provider._sdk_error_attributes(
    exception: BaseException,
    operation: str,
    sandbox_id: str,
    attempt_number: int | None = None,
    max_attempts: int | None = None,
    sleep_s: float | None = None
) -> dict[str, typing.Any]
```

```python
nemo_gym.sandbox.providers.opensandbox.provider._string_map(
    values: collections.abc.Mapping[str, typing.Any]
) -> dict[str, str]
```

```python
nemo_gym.sandbox.providers.opensandbox.provider._to_image_spec(
    image: str,
    image_auth: collections.abc.Mapping[str, typing.Any] | None
) -> typing.Any
```

```python
nemo_gym.sandbox.providers.opensandbox.provider._to_network_policy(
    network_policy: collections.abc.Mapping[str, typing.Any]
) -> typing.Any
```

```python
nemo_gym.sandbox.providers.opensandbox.provider._to_platform_spec(
    platform: dict[str, typing.Any]
) -> typing.Any
```

```python
nemo_gym.sandbox.providers.opensandbox.provider._to_sandbox_status(
    state: typing.Any
) -> nemo_gym.sandbox.providers.base.SandboxStatus
```

```python
nemo_gym.sandbox.providers.opensandbox.provider._to_volumes(
    volumes: list[collections.abc.Mapping[str, typing.Any]]
) -> list[typing.Any]
```

```python
nemo_gym.sandbox.providers.opensandbox.provider.split_domain_scheme(
    domain: str
) -> tuple[str, str | None]
```

Split a configured OpenSandbox domain into `(domain, scheme)`.

`"https://sandbox.example:8080/prefix/"` -> `("sandbox.example:8080/prefix", "https")`;
a bare host is returned unchanged with scheme `None`. Surrounding whitespace
is stripped either way. Only `scheme://host[:port][/path-prefix]` is accepted:
other schemes, a missing host, or a query string / fragment raise `ValueError`.

```python
nemo_gym.sandbox.providers.opensandbox.provider.validate_image_pull_policy(
    image_pull_policy: str
) -> str
```

Validate a Kubernetes-compatible container image pull policy.

```python
nemo_gym.sandbox.providers.opensandbox.provider.ATTRIBUTION_KEY_PREFIX_RE = re.compile('(?=.{1,253}$)[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-...
```

```python
nemo_gym.sandbox.providers.opensandbox.provider.DEFAULT_ATTRIBUTION_KEY_PREFIX = 'nemo-gym.nvidia.com/'
```

```python
nemo_gym.sandbox.providers.opensandbox.provider.DEFAULT_IMAGE_PULL_POLICY = 'IfNotPresent'
```

```python
nemo_gym.sandbox.providers.opensandbox.provider.IMAGE_PULL_POLICY_ANNOTATION_EXTENSION_KEY = 'opensandbox.extensions.image-pull-policy'
```

```python
nemo_gym.sandbox.providers.opensandbox.provider.IMAGE_PULL_POLICY_EXTENSION_KEY = 'imagePullPolicy'
```

```python
nemo_gym.sandbox.providers.opensandbox.provider.LOGGER = logging.getLogger(__name__)
```

```python
nemo_gym.sandbox.providers.opensandbox.provider.METADATA_VALUE_RE = re.compile('[^A-Za-z0-9_.-]+')
```

```python
nemo_gym.sandbox.providers.opensandbox.provider.RETRYABLE_ERROR_MARKERS = ('all connection attempts failed', 'connection refused', 'connection reset', 'ga...
```

```python
nemo_gym.sandbox.providers.opensandbox.provider.RETRYABLE_HTTP_STATUS_CODES = {408, 409, 425, 429, 500, 502, 503, 504}
```

```python
nemo_gym.sandbox.providers.opensandbox.provider.STATUS_CODE_RE = re.compile('(?:status code|http)\\D+(\\d{3})', re.IGNORECASE)
```

```python
nemo_gym.sandbox.providers.opensandbox.provider.VALID_IMAGE_PULL_POLICIES = {'Always', 'IfNotPresent', 'Never'}
```