Skip to main content
Install thunder-sandbox, then import it under the short thunder alias.
The same 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.
Image
Optional container image to run commands in. See Images.
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.
SSH authentication uses an organization certificate as described in SSH authentication. Public internet access is allowed by default. Private, metadata, and other sandbox networks remain blocked, and inbound access is limited to SSH.
CIDR and domain rules are independent: when both are restricted, a connection must pass both controls. A bare domain matches exactly; use *. for its subdomains.

Images

By default, commands run directly in the sandbox’s Ubuntu VM. Pass an Image to run them inside a container instead. The image is built or imported by Thunder before the sandbox is allocated.
Signature
Supply 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
Use IDs when storing or reconnecting to a sandbox. Names are reusable labels.

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
Names are unique only while a sandbox is live and may be reused after it stops. Prefer 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:
Set 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:
The command uses the cached files, so run at least one SDK SSH operation such as 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
A deadline raises 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.
The returned 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.
With 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
Pass 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
Set 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
Set 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.
For headless use, set 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 methods have the same behavior and options as their synchronous twins and return the same public object types. Stream operations also use the _async suffix.
Async stdin follows the same pattern:
Clients support both synchronous and asynchronous context managers. Async sandbox listings are async iterators:
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.