Skip to main content
For ordinary commands, start with Your first sandbox and its sandbox factory. This guide exposes lower-level controls. See Advanced: assemble a sandbox yourself for manual component assembly and resource ownership. Use spawn_detached() when a command must continue after the current Python process exits. The session records its UUID, source image, parent entry, branch, command options, and uploaded attachment metadata. It does not start an event reader. The server still applies the operation’s configured timeout. Use a SQLite store for persistence across processes. Memory stores implement the same API but retain records only while that store instance exists. Keep the same session ID, database, and endpoint when resuming.

Submit work

The first block submits work and closes the local store and client. It leaves the remote operation running. Store the operation UUID in your application or retrieve it through session.list_operations() after restarting Python. The async variant uses native transport and storage. No event loop or background reader must remain running between submission and collection. Run async blocks inside an async function or a compatible notebook.

Collect the result

Run the next block in the process that collects the result. wait_operation() uses the session’s commit policy and records completion atomically with history. It returns the process result, including a nonzero exit code. The consumer needs the same working directory and connection profile. It obtains the UUID from the store, so no Python variables from the submitting process are needed. This example has exactly one pending operation; applications with several operations must select the intended record or accept a UUID as an argument.

Branches and commit policy

Completion keeps the parent captured at spawn time. Switching branches or continuing other work does not redirect or reject the result. The completed entry is another child of its original parent. Obtain its ID from session.get_operation(uuid).history_id and use navigate() to select it. Waiting can advance the local position when it still matches the source; replaying an already completed result does not undo a later rollback. The default ApiSuccessCommitPolicy retains every successful non-disposable result, including nonzero exit codes. Pass commit_policy=ZeroExitCommitPolicy() when resuming to retain only exit code zero. See Customize the SDK for custom policies. Concurrent completion calls share the first stored decision. A later policy change or rollback cannot cause a second commit. Disposable and policy-rejected results remain readable without a history entry. Final API failures and cancellation remain readable as the same exceptions. Completed wait_operation() calls use the stored response without remote I/O. Transport errors remain pending. A timeout or cancellation of a live wait follows Operation.wait() semantics and requests remote cancellation; it does not merely stop observing the operation. wait_operation() closes its internal handle before returning or raising. Async cleanup completes even after repeated caller cancellation. Synchronous cleanup uses a bounded reader join; a blocked transport can keep its reader thread alive until the stream releases it. See Advanced: control a running sandbox for lifecycle details.

Control and inspect registered work

detach(operation) registers an existing session operation without closing its handle. restore_operation(uuid) reconstructs a handle through create_operation() with the original context. Override create_operation_record() and restore_operation_context() together to persist application-specific request fields. Both restoration and commit policies use these hooks. Use that handle to send signals, cancel the operation, or read events. After wait(), commit_result() on a restored handle uses the same atomic completion path. Use wait_operation() to replay a stored response. list_operations() returns pending records. Set pending_only=False to include completed records. OperationRecord.history_id identifies a retained result; response stores the final API response as a JSON object. Records include the effective command environment and output, but not client credentials. A different client endpoint is rejected before the UUID is used. Local file and stdin sources are not serialized or reopened. Attachments retain their uploaded UUIDs and destination permissions. Restored OperationContext values have request.files=None and request.stdin=None; inspect attachments and files for the submitted inputs. Commit policies must use this persisted context, rather than source objects. Detached titles and file metadata are fixed at registration; commit_result() cannot override them.

Failure boundaries and custom stores

Remote submission and local registration are separate operations. If registration raises normally, spawn_detached() requests cancellation and preserves the registration error. A process crash between remote submission and registration can leave an unregistered remote operation. Registration does not promise exactly one remote submission. Retrying submission can start another operation. A pending node stores the original context and the identity reserved for its result. Completion appends one immutable node containing the final response and optional retained image. An identical write can be retried. If concurrent collectors disagree on commit policy, the first saved completion is retained. Deleting the tree removes both records; it does not cancel the remote operation. Custom stores need only the tree contract, including atomic and idempotent append(). They do not implement a second operation registry API. See Tree stores for the guarantees required across processes.