airflow.providers.common.ai.sandbox.openshell¶
NVIDIA OpenShell backend for SandboxToolset.
Attributes¶
Classes¶
Run sandbox tools through an NVIDIA OpenShell gateway. |
Module Contents¶
- class airflow.providers.common.ai.sandbox.openshell.OpenShellSandboxBackend(*, gateway=None, workspace='default', image='python:3.12-slim', cpu='1', memory='2Gi', ready_timeout=120.0, request_timeout=30.0)[source]¶
Bases:
airflow.providers.common.ai.sandbox.base.SandboxBackendRun sandbox tools through an NVIDIA OpenShell gateway.
Note
Experimental: this can change or be removed in a minor release of this provider. See Stable and experimental features.
OpenShell is a self-hosted gateway that runs each sandbox on Docker, Podman or Kubernetes with Landlock and seccomp inside the container and a per-sandbox supervisor that mediates every outbound connection. Airflow workers only need gRPC access to the gateway, with the
openshellextra installed.Credentials are ambient. The backend reads the gateway registration the
openshellCLI keeps under$XDG_CONFIG_HOME/openshell/gateways/<name>/: its endpoint and either mTLS material or an OIDC token. There is no Airflow connection type for it; see the backend page for how to provision a worker. An OIDC registration must be writable and per worker, because the SDK refreshes the token and writes it back into the registration; mTLS is the supported setup for a worker that mounts the registration read-only.Egress is deny-all unless listed, and verified. A spec’s hostnames become one policy rule admitting each host on port 443 only, and a default spec sends no rule at all, which OpenShell enforces as no egress; name resolution is answered by the supervisor without leaving the sandbox. After create the backend reads the effective policy back and destroys the sandbox unless it is exactly what was asked for, comes from the sandbox rather than a gateway-wide override, and cannot be widened by auto-approved proposals. The same check runs before every command and file operation and after each one that succeeds, because a gateway administrator, an approved draft or a global policy can widen a running sandbox; a change fails the task. What it cannot see is the network the gateway runs on: on Kubernetes the NetworkPolicy that keeps a sandbox pod behind its supervisor is the cluster’s to enforce.
Commands run through a guest wrapper. OpenShell’s own exec timeout leaves the command running, so the wrapper enforces the budget itself and kills the processes in the command’s session when it runs out. Output is spooled to the sandbox’s
/tmpand each stream is capped tomax_output_bytesbefore it leaves the sandbox.allow_egress_to_cidrs, an open network andSandboxSpec.ownerare refused. The image needssetsid, GNU coreutils and findutils, and a/bin/shthat is dash or bash;python:*-slimhas them. BusyBoxash, as in Alpine, is not supported: the timeout sweep does not finish under it.- Parameters:
gateway (str | None) – Name of the OpenShell CLI gateway registration to use.
Noneuses$OPENSHELL_GATEWAY, then the CLI’s active gateway.workspace (str) – OpenShell workspace the sandboxes are created in.
image (str) – Container image used for each sandbox.
cpu (str) – CPU limit, as a Kubernetes quantity.
memory (str) – Memory limit, as a Kubernetes quantity.
ready_timeout (float) – Seconds to wait for a new sandbox to become ready.
request_timeout (float) – Seconds allowed for each gateway call other than a command.
- create(*, spec=None)[source]¶
Provision one sandbox and return its handle (name or id).
specofNonemeans “no requirements stated”: the backend applies its own defaults and makes no guarantee. It is not the same as a defaultSandboxSpec, which is an explicit request for an isolated sandbox. The toolset always sends a concrete spec, soNoneonly reaches a backend a caller drives directly.Raise
SandboxTerminalErrorifspecasks for something this backend cannot enforce, rather than provisioning something weaker than was asked for. It is terminal rather than recoverable because it states a configuration fact the model cannot see and cannot fix by retrying.Every failure raised here is terminal, whichever class carries it. The model has no input into provisioning, so a
SandboxErrorfromcreateis not something it can work around; the toolset re-raises one asSandboxTerminalErrorand fails the task, so Airflow’s retry attempts the provisioning again.
- run_command(sandbox, command, *, timeout, max_output_bytes)[source]¶
Run
commandthrough a shell in the sandbox, bounded bytimeoutseconds.max_output_bytesbounds what the backend retains per stream while reading, so unbounded command output cannot exhaust worker memory before the toolset gets a chance to format it.
- read_file(sandbox, path, *, max_bytes)[source]¶
Read a file as raw bytes, stopping one byte past
max_bytesinside the guest.
- write_file(sandbox, path, content)[source]¶
Write
contentover stdin, creating parent directories.The base class’s in-command payload is base64 inside the one request that carries the command, so it stops at about 750 KB of content. Raw stdin in chunks has no such ceiling. A file larger than one chunk is not written atomically: a failure part way leaves the chunks written so far.