Skip to main content
Sandboxes are the live runtime unit in Nullspace. They are created from a template and expose command, filesystem, desktop, PTY, lifecycle, and port URL helpers.

Create

from nullspace import Sandbox

with Sandbox.create(
    template="base",
    vcpus=2,
    memory_mb=512,
    timeout=300,
    on_timeout="pause",
    auto_resume=True,
    envs={"APP_ENV": "dev"},
    metadata={"user_id": "user_123"},
    cwd="/workspace",
    internet_access=True,
    network={"mask_request_host": "localhost:${PORT}"},
) as sandbox:
    print(sandbox.id)
Use on_timeout="pause", auto_resume=True when preview URL traffic should wake a paused sandbox before forwarding the original request. auto_resume=True is only valid with pause/hibernate timeout behavior. Use a template warm pool for ready capacity before a burst:
sandbox = Sandbox.create(
    template="team/fastapi-app:stable",
    warm_pool="twp_fastapi_small",
    warm_pool_mode="prefer",
    warm_pool_wait_ms=1500,
)
print(sandbox.get_info().warm_pool_checkout)
warm_pool_mode="require" fails with warm_pool_unavailable when no matching ready instance is available. Snapshot restore, resume, fork, hibernate, and pause do not use template warm-pool checkout.

Inspect and connect

sandboxes = Sandbox.list(limit=20)
for item in sandboxes.items:
    print(item.id, item.status, item.template)

while sandboxes.has_next:
    for item in sandboxes.next_items():
        print(item.id)

sandbox = Sandbox.connect("sb_...")
print(sandbox.get_info().status)
Sandbox.connect(id) is an explicit reconnect operation. If id currently points at a paused snapshot, connect() resumes it and returns the running sandbox execution. Sandbox.get_info_by_id(id) is read-only and does not wake a paused sandbox. Pass fields=["id", "status"] to Sandbox.list() or Sandbox.get_info_by_id() for compact raw dictionaries instead of full model objects. This is useful for dashboards and polling loops that only need a few attributes.

Preview URLs and timeout

sandbox.set_timeout(120, timeout_action="hibernate")
host_info = sandbox.get_host_info(8080)
print(host_info.url, host_info.websocket_url)
print(sandbox.get_url(8080))
preview = sandbox.create_signed_preview_url(8080, expires_in_seconds=900)
readiness = sandbox.wait_for_preview(8080, timeout_secs=30)
print(readiness.ready, preview.grant.id)
print(sandbox.get_metrics())
For sandboxes created with on_timeout="pause", auto_resume=True, HTTP and websocket traffic to those preview URLs wakes the paused sandbox, waits for the bounded resume window, and then forwards to the resumed execution. If auto-resume is disabled or the wake does not complete in time, the preview URL returns 503 Service Unavailable with Retry-After: 5. Use sandbox.create_preview_proxy_target(8080) when your application proxy should forward to Nullspace with x-nullspace-preview-proxy-token instead of putting signed edge tokens in browser-visible URLs. Use redact_preview_url(...) or redact_preview_token(...) before printing preview credentials to logs or terminal output.

Lifecycle events

Use sandbox.stream_events() to tail lifecycle events related to one sandbox without polling:
from nullspace import Sandbox

sandbox = Sandbox.connect("sb_123")
try:
    for event in sandbox.stream_events():
        print(event.operation, event.status, event.snapshot_id)
finally:
    sandbox.close()
async for event in sandbox.stream_events(live_only=True):
    print(event.id, event.operation)
The iterator uses GET /v1/sandboxes/{id}/lifecycle/ws, reconnects automatically, and resumes with after_event_id from the last yielded event. Use live_only=True to skip the initial replay while still keeping reconnects gap-resistant after the first event. Shared volume data is external durable storage. Resume and fork remount shared volume attachments, but do not make VM memory or mutable rootfs state portable across incompatible runtime hosts.

One-shot runs

result = Sandbox.run_once(
    "python3",
    ["-c", "print('hello')"],
    template="base",
    timeout=60,
)
print(result.stdout)
Use run_once for simple tasks where the SDK should create a sandbox, run the code, and clean up without exposing a long-lived handle.

Async API

from nullspace import AsyncSandbox

async with await AsyncSandbox.create(template="base", timeout=300) as sandbox:
    result = await sandbox.commands.run("echo async", shell=True)
    print(result.stdout)

    async for event in sandbox.stream_events(live_only=True):
        print(event.operation, event.status)
        break
Async helpers follow the same pagination and fields behavior as the synchronous SDK. Concepts: Create, Destroy, Fork. API reference: createSandbox, getSandbox, getHost, setTimeout.