thunder-sandbox, then import it under the short thunder alias.
Client, Sandbox, and Process classes support synchronous and asynchronous applications. Every blocking operation has an awaitable _async twin, such as Sandbox.create_async(), sandbox.exec_async(), and process.wait_async(). Properties such as id, status, and returncode remain synchronous.
Sandbox.create
Starts a sandbox and returns immediately. If an image is supplied, it is built or imported first. If command arguments are supplied, it waits for the sandbox and starts that command before returning.
Signature
string
Optional command and arguments to start once the sandbox is running.
string
Optional label, unique among live sandboxes. Use the returned
id to address the sandbox.Mapping[str, str | None]
Environment variables set for SSH sessions. Entries whose value is
None are omitted. Names must match [A-Za-z_][A-Za-z0-9_]*.int | None
default:"300"
Sandbox lifetime in seconds, measured from creation. Use
None to disable automatic expiry.integer
default:"4"
Number of vCPUs.
integer
default:"32"
Memory in GiB.
integer
default:"50"
Ephemeral disk size in GiB.
GPUType
GPU type, such as
thunder.GPUType.H100. Supply it together with gpu_count.integer
Number of GPUs. Currently, sandboxes support one GPU. Supply it together with
gpu_type. Contact us if you are interested in larger configurations.boolean
default:"false"
Blocks outbound internet access. Choose either this option or an allowlist.
Sequence[str]
IPv4 CIDRs the sandbox may reach.
Sequence[str]
DNS names the sandbox may resolve. Use
*.example.com for subdomains.Client
Reuse an existing authenticated client.
*. for its subdomains.
Images
By default, commands run directly in the sandbox’s Ubuntu VM. Pass anImage to run them inside a container instead. The image is built or imported by Thunder before the sandbox is allocated.
Signature
username and password together for a private registry. from_dockerfile uploads the directory as the build context, honoring .dockerignore, up to 5 GiB.
An image-backed sandbox ignores the image’s ENTRYPOINT and CMD and keeps the container running for the sandbox’s lifetime. exec, upload, and download operate inside that container. sandbox.image_id reports the managed image in use.
To prepare an image ahead of time, call client.resolve_image(image, timeout=7200). It returns a ResolvedImage once the image is ready and raises SandboxFailedError if the build or import fails.
Sandbox.update_network_policy
Replaces the complete outbound policy of a running sandbox. It accepts the same network options as Sandbox.create.
Signature
None leaves that policy dimension unrestricted, while an empty sequence blocks it. The call returns after Thunder accepts the new policy; enforcement on the sandbox node converges asynchronously. Established connections may remain open after a policy is tightened.
Sandbox.from_id
Retrieves a sandbox by its permanent API ID.
Signature
Sandbox.from_name
Finds the live sandbox with the given name. It raises NotFoundError when no live sandbox has that name and ConflictError if the name is ambiguous.
Signature
Sandbox.from_id when you know the ID.
Sandbox handles expose these properties:
SSH authentication
The SDK authenticates your machine with one Ed25519 key and a short-lived SSH certificate scoped to your organization. The private key remains on your machine. The API signs its public half, and every sandbox in the organization trusts that authority, so you can reconnect to existing sandboxes from any authenticated machine. The credential is created lazily on the first SSH operation, reused across sandboxes, and renewed automatically before expiry. The SDK caches it under~/.thunder/sandbox_keys/ by default:
TNR_HOME to move the cache. When the cache directory is unwritable, the SDK holds the key and certificate in memory for command execution and file transfers.
sandbox.ssh exposes the host, port, user, and cached credential paths. sandbox.ssh_command returns a tuple suitable for subprocess or shell display, including the private key, certificate, and options required for short-lived hosts:
exec, upload, or download before shelling out. Manual SSH requires a writable cache directory. Because nodes reuse forwarded ports, the generated command sends host-key records to /dev/null; the SDK pins a sandbox’s host key in memory for the life of the process.
Sandbox.wait_until_ready
Waits until the sandbox reports ready. Thunder holds each request open until the sandbox is ready, so it returns moments after startup finishes. Transient connection, service, and rate-limit errors are retried for a short grace period. It raises SandboxFailedError for a terminal sandbox and SandboxTimeoutError at the deadline.
Sandbox.refresh
Fetches the current sandbox state and updates the handle in place.
Sandbox.poll
Checks whether the sandbox or its main command has finished without blocking. It returns None while running, 0 when stopped successfully, or 1 when the sandbox failed.
Sandbox.wait
Waits for the command passed to Sandbox.create(*args) or, when no command was provided, for the sandbox to stop.
Signature
SandboxTimeoutError.
Sandbox.exec
Starts a command over SSH. Pass the executable and each argument separately.
Signature
string
required
Command followed by its arguments.
float | None
Default timeout used by
process.wait().string
Remote working directory.
Mapping[str, str | None]
Variables for this command. A
None value unsets a variable.boolean
default:"true"
Returns text streams when true and byte streams when false.
boolean
default:"false"
Requests a pseudo-terminal for interactive commands. PTY commands stay attached to their SSH connection and are not durable.
"capture" | "discard"
default:"capture"
Captures standard output, or sends it to
/dev/null."capture" | "discard"
default:"capture"
Captures standard error, or sends it to
/dev/null.boolean
default:"false"
Keeps the command’s output on the sandbox after it has been read, so it can be recovered later with
get_process. Call process.cleanup() when finished.Process exposes id, stdin, stdout, stderr, returncode, is_durable, poll(), wait(), cleanup(), and terminate(). stdout and stderr remain readable after the process finishes. A process wait deadline raises asyncio.TimeoutError; Sandbox.wait() converts a timeout from the command passed to Sandbox.create(*args) into SandboxTimeoutError.
By default, commands run as durable jobs that do not depend on the SSH connection that started them. If the connection drops, the SDK reconnects and keeps reading the same job’s output without gaps or duplicates, rather than starting the command again. Once the job has finished and its captured output has been read, the SDK removes the job’s files from the sandbox unless retain=True.
Durable commands do not accept stdin: stdin.write() raises io.UnsupportedOperation. Pass input through arguments, environment variables, or uploaded files. Set pty=True for programs that need interactive input or terminal behavior; a PTY command whose SSH connection drops raises ConnectionError and its final state is unknown, so use the default mode for unattended or long-running work.
text=True, streams read and write strings. With text=False, they use bytes. terminate() stops the remote command while the sandbox continues running. For a durable command, it sends SIGTERM to the whole process group and then SIGKILL after five seconds, so wait() returns 143 or 137.
Sandbox.get_process
Recovers a durable command by its process.id, for example from another Python process or after a restart.
Signature
retain=True to exec when you plan to recover a command after its output has already been read. PTY commands cannot be recovered.
Sandbox.upload
Uploads a local file or directory over the sandbox’s authenticated SSH connection. Files are written to a staging path and renamed into place when complete, so partial data never appears at the destination. If the connection drops, the SDK reconnects and repeats the transfer.
Signature
recursive=True when uploading a directory.
Sandbox.download
Downloads a remote file or directory over the sandbox’s authenticated SSH connection. Like upload, it writes to a staging path first and repeats the transfer after a dropped connection.
Signature
recursive=True when downloading a directory.
Sandbox.terminate
Stops the sandbox and destroys its filesystem. If the sandbox is still being created, timeout controls how long to wait for it to become stoppable. Use None to wait indefinitely.
Signature
Client.from_cli
Creates an authenticated client from the Thunder CLI configuration.
TNR_API_TOKEN. TNR_API_URL overrides the API endpoint, and TNR_HOME overrides the directory containing CLI state and the shared sandbox SSH credential.
Use the client as a context manager to close it automatically:
Client.create_sandbox
Creates a sandbox using this client’s authentication and configuration. It accepts the same arguments as Sandbox.create.
Client.get_sandbox
Retrieves a sandbox by its permanent API ID.
Client.get_sandbox_by_name
Finds a live sandbox by its reusable name. Prefer Client.get_sandbox when you know the ID.
Client.list_sandboxes
Iterates over active sandboxes by default. Results are newest first, and pagination is automatic. Pass "all" for complete history or one of "created", "ready", "finished", or "failed" for a single lifecycle status.
Signature
Client.get_pricing
Fetches the current USD per-hour rates as a Pricing object. memory_gb and storage_gb are per GiB, and gpu is keyed by GPUType.
sandbox.get_hourly_price() returns a sandbox’s total hourly rate for all of its configured resources. It is a rate, not accrued charges.
Client.close
Closes the client. Later API calls through that client raise ConnectionError.
_async suffix.
Errors
Errors
All SDK errors inherit from
ThunderError. CapacityError, RateLimitError, and ServiceUnavailableError also inherit from RetryableError, so you can catch every transient condition at once.API-backed errors expose the HTTP
status, machine-readable code, and optional retry_after value.