Per-second pricing and lifecycle timestampsBreaking:
sandbox.get_price_per_second()returns the sandbox’s current USD per second for all of its configured resources. It is a rate, not accrued charges. Async:await sandbox.get_price_per_second(), orget_price_per_second_async()on blocking objects.sandbox.info.ready_atis now read straight from the API, assandbox.info.stopped_atalready was. Sandboxes fromlist_sandboxes,get_sandbox, andcreateall carry both, so reading them never makes a request. Callsandbox.refresh()to see transitions since the last fetch.
get_hourly_price()(available since 0.7.2) is renamed toget_price_per_second()and now returns USD per second, the hourly rate divided by 3600.client.get_pricing()still returns hourly rates.- The cost helpers added in 0.8.1 are removed:
get_billed_seconds(),get_cost(),info.billed_seconds(),info.scheduled_at, andinfo.started_at.
client.resolve_image(..., workspace=)(a name or ID) builds or imports the image in that workspace. Without it,TNR_WORKSPACEapplies, then the main workspace.Sandbox.create(image=...)uses the sandbox’s workspace.ResolvedImage.workspace_idshows which workspace lists the image.- An organization builds each recipe once. A Dockerfile or registry digest already built in another workspace comes back as the same image, ready at once.
- Building or importing images in a workspace takes the developer role there.
- Added
info.ready_atandinfo.stopped_at. Also added cost helpers and theinfo.scheduled_atandinfo.started_attimestamps, which 0.8.2 removed.
Workspaces
- Pass
workspace=(a name or ID) toSandbox.create,client.list_sandboxes, andget_sandbox_by_name, or setTNR_WORKSPACE. Leave both out to use the main workspace. client.list_workspaces()lists the workspaces your credential can reach.sandbox.workspace_idshows a sandbox’s workspace.- Each workspace has its own SSH certificate, which the SDK manages for you.
- Arguments up to Linux’s 128 KiB limit now work; previously they failed above about 64 KiB with “Argument list too long”. Larger arguments fail immediately with
InvalidRequestErrorinstead of hanging. Upload large inputs as files. - Reading command output is faster: about 0.6 s instead of 3.5 s for a trivial command.
- Breaking: in image sandboxes, bare command names such as
sh,ls, andsetsidnow run the image’s own program fromPATH, likedocker exec. BusyBox is used only when the image doesn’t have that program. This fixessetsid -fandsh -cwaiting on background jobs, butshis now whatever the image ships (dash on Ubuntu and Debian). Bash-only syntax undersh -c, likeset -o pipefail,source,[[ ]], or${x:0:2}, now fails. Callbash -cinstead. - The SDK’s internal shell variables are now prefixed, so they no longer overwrite your environment variables (for example, one named
job).
download("dir/.")works (Harbor’sdownload_diruses this form). Previously it retried until the TTL.upload()anddownload()accepttimeout=.- Breaking: retries are bounded. An operation that keeps failing for 10 minutes raises
ConnectionErrorinstead of retrying forever, so a stuck transfer can’t keep a GPU billing. The job keeps running, andget_process()can reattach to it. - SFTP errors on a live connection are no longer retried.
- Breaking: naming only one allowlist restricts egress to that list. The list you leave out now allows nothing. Before, it allowed everything, which undid the restriction. Pass neither list for open egress, or pass the open value (
["0.0.0.0/0"]or["*"]) for the list you want open. The two lists add together, so["0.0.0.0/0"]opens all public egress whatever the domain list says. - Domain allowlists match TLS (HTTPS) traffic only. Plain HTTP to an allowed domain gets an empty reply. To reach a plain-HTTP host (for example an apt mirror), allow its IP range with
outbound_cidr_allowlist.
Queueing and CPU-only sandboxes
queue=TrueonSandbox.createwaits for capacity instead of failing with a capacity error. Onclient.resolve_image, it admits the image build without a capacity check. With no positional command,createreturns right away.sandbox.queuedtells you whether it’s still waiting, andwait_until_ready(timeout=None)waits with no limit.- CPU-only sandboxes: leave out the GPU options, or pass
gpu_type=thunder.GPUType.NONEandgpu_count=0. The API’s resource catalog sets which CPU, memory, and storage sizes are allowed. terminate()stops a queued sandbox immediately.
client.resolve_image(spec=...)checks capacity for the resources you plan to use before building. It doesn’t reserve them.Sandbox.createpasses its resources through automatically.- Docs: an image’s initial command runs once, and the sandbox stays available for commands and file transfers after it exits.
-
Breaking: image sandboxes now run the image’s
ENTRYPOINT/CMDonce at startup. Before, they were skipped. This is a platform change that applies to every SDK version, so pinning an older SDK doesn’t avoid it. Images that start a server will start it. Thevllm/vllm-openaiimage, for example, runsvllm serveand takes most of the GPU’s memory before your own job starts. Commands passed toSandbox.createorexecrun alongside the image’s command and don’t replace it. To skip it, build from the image and clear the entrypoint: -
execacceptsuser=in image sandboxes to run a command as a specific user. -
update_network_policy()now waits until the new policy is enforced. It takestimeout=(default 300 s) and raisesSandboxTimeoutErrorif enforcement isn’t confirmed in time. - Fixed a race where a job terminated just as it started could still begin running.