> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gmicloud.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Sandboxes

> Launch, label, pause, resume and delete sandboxes.

A sandbox is an isolated instance launched from an Agent. This page covers its lifecycle. To run commands and move files, see [Commands and files](https://docs.gmicloud.ai/agentbox-sdk/commands-and-files.md).

## Launch a sandbox

Launch from an Agent, then wait until the sandbox is `running`. A new sandbox starts as `pending`.

```python Python theme={null}
sandbox = agent.launch(instance_type="gmi.sandbox.small")
# or: client.sandboxes.launch(agent.slug, instance_type=..., idc_name=agent.idc)

sandbox.wait_until_running(timeout=300, poll_interval=2.0)
print(sandbox.status, sandbox.endpoint_url)
```

```go Go theme={null}
sandbox, err := agent.Launch(ctx, agentbox.LaunchParams{InstanceType: "gmi.sandbox.small"})
// or: client.Sandboxes.Launch(ctx, agent.Slug(), agentbox.LaunchParams{InstanceType: ..., IdcName: agent.Idc()})

err = sandbox.WaitUntilRunning(ctx, agentbox.WaitUntilRunningParams{
	Timeout:      300 * time.Second,
	PollInterval: 2 * time.Second,
})
fmt.Println(sandbox.Status(), sandbox.EndpointURL())
```

* A sandbox launches in the Agent's data center. Launch cannot move it to a different one.
* Wait for `running` before you run commands or transfer files.
* The endpoint URL is empty until an endpoint is assigned. It can still be empty for a short time after the sandbox is `running`. Python returns `None`.

Waiting ends in one of three ways:

| Result | Python | Go |
| - | - | - |
| Status becomes `running` | returns the sandbox | returns `nil` |
| Status becomes `error`, `deleted`, `stopped` or `stopping` | raises `SandboxFailed` | returns `*agentbox.SandboxFailed` |
| The timeout passes first | raises `SandboxWaitTimeout` | returns `*agentbox.SandboxWaitTimeout` |

## Labels and lifetime

Add your own labels with `metadata`, and set how long the sandbox lives with `timeout_seconds`.

```python Python theme={null}
sandbox = agent.launch(
    instance_type="gmi.sandbox.small",
    metadata={"session": "abc"},
    timeout_seconds=3600,
)
sandbox.refresh()
print(sandbox.metadata, sandbox.expires_at, sandbox.expiry_pinned)

sandbox.set_timeout(6 * 3600)  # extend or shorten the remaining lifetime
```

```go Go theme={null}
timeoutSeconds := 3600
sandbox, err := agent.Launch(ctx, agentbox.LaunchParams{
	InstanceType:   "gmi.sandbox.small",
	Metadata:       map[string]string{"session": "abc"},
	TimeoutSeconds: &timeoutSeconds,
})
err = sandbox.Refresh(ctx)
fmt.Println(sandbox.Metadata(), sandbox.ExpiresAt(), sandbox.ExpiryPinned())

err = sandbox.SetTimeout(ctx, 6*3600) // extend or shorten the remaining lifetime
```

**Metadata**

* Up to 32 keys. Keys match `^[A-Za-z0-9_.-]{1,63}$`; values are at most 256 characters.
* `source` and `task_name` are reserved.
* Metadata is visible in the Console and in API responses. Do not put secrets in it.

**Lifetime**

* `timeout_seconds` is the lifetime in seconds from now. The minimum is 60.
* If you omit it, the sandbox uses the platform default lifetime of 24 hours, which renews automatically.
* A positive value pins the expiry, so it no longer renews automatically.
* `set_timeout` works while the sandbox is running or paused. It pins the expiry the same way.
* Launch does not return the expiry. Call `refresh` and read `expires_at`.

> [!TIP]
>
> In tests, always set `timeout_seconds`. If your program exits before cleanup, the sandbox is still reclaimed when it expires.

## List and filter sandboxes

```python Python theme={null}
page = client.sandboxes.list()  # omits stopped and deleted
page = client.sandboxes.list(status=["running", "creating", "error"])
page = client.sandboxes.list(agent_id=agent.id, status="running")
page = client.sandboxes.list(metadata={"session": "abc"})
page = agent.sandboxes(metadata={"session": "abc"})

for sandbox in page.items:
    print(sandbox.id, sandbox.status)
```

```go Go theme={null}
page, err := client.Sandboxes.List(ctx, agentbox.SandboxListParams{}) // omits stopped and deleted
page, err = client.Sandboxes.List(ctx, agentbox.SandboxListParams{Status: []string{"running", "creating", "error"}})
page, err = client.Sandboxes.List(ctx, agentbox.SandboxListParams{AgentID: agent.ID(), Status: []string{"running"}})
page, err = client.Sandboxes.List(ctx, agentbox.SandboxListParams{Metadata: map[string]string{"session": "abc"}})
page, err = agent.ListSandboxes(ctx, agentbox.AgentSandboxListParams{Metadata: map[string]string{"session": "abc"}})

for _, sandbox := range page.Items {
	fmt.Println(sandbox.ID(), sandbox.Status())
}
```

* By default the list **excludes** `stopped` and `deleted` sandboxes. Pass `status` to include them.
* Metadata filters match exactly. Several keys must all match.
* To clean up a batch, filter by a label you set for that run, not by status. Deleting by status can remove other people's sandboxes in your organization.

## Pause and resume

Pause stops run time. Resume brings the sandbox back.

```python Python theme={null}
import time

sandbox.pause()
while sandbox.status != "paused":
    time.sleep(2)
    sandbox.refresh()
print(sandbox.paused_expire_at)

sandbox.resume()
sandbox.wait_until_running()
```

```go Go theme={null}
err = sandbox.Pause(ctx)
for sandbox.Status() != "paused" {
	time.Sleep(2 * time.Second)
	if err := sandbox.Refresh(ctx); err != nil {
		return err
	}
}
fmt.Println(sandbox.PausedExpireAt())

err = sandbox.Resume(ctx, agentbox.ResumeParams{})
err = sandbox.WaitUntilRunning(ctx, agentbox.WaitUntilRunningParams{})
```

**What a pause keeps**

| Kept | Not kept |
| - | - |
| Files on disk, for example in the home directory and `/opt` | Files in `/tmp` and `/dev/shm` |
| | Memory and running processes |

Resume is a cold start. Processes are not restored and the start command does not run again, so restart any services you need after resume.

**Timing**

* Pause and resume return as soon as the request is accepted. The status is usually `pausing` or `resuming`. Poll with `refresh`, or with `wait_until_running` after resume.
* A paused sandbox is kept for up to 7 days. Resume it before `paused_expire_at`, or it is deleted together with its disk.
* Resume without `timeout_seconds` keeps the run time that was left when you paused. Pass `timeout_seconds` (60 or more) to restart the lifetime from now and pin it.

## Watch status events

Open an event stream to receive status changes as they happen, for example during a pause or resume.

```python Python theme={null}
for event in sandbox.stream():
    print(event.event, event.data)
    if event.event == "status":
        break
```

```go Go theme={null}
sctx, cancel := context.WithTimeout(ctx, 90*time.Second)
defer cancel()

stream, err := sandbox.Stream(sctx)
if err != nil {
	return err
}
defer stream.Close()
for {
	event, ok := stream.Next()
	if !ok {
		break
	}
	fmt.Println(event.Event, event.Data)
}
if err := stream.Err(); err != nil {
	return err
}
```

* Event names: `status` is sent by the service, `heartbeat` marks keep-alive lines, and `message` is used for unnamed events.
* In Go, the client request timeout does not apply to streams. Control the duration with the context and always close the stream.

## Delete

```python Python theme={null}
sandbox.delete()  # one-way; the sandbox cannot be restarted
agent.delete()    # only after all of its sandboxes are deleted
```

```go Go theme={null}
sandbox.Delete(ctx) // one-way; the sandbox cannot be restarted
agent.Delete(ctx)   // only after all of its sandboxes are deleted
```

* Deleting a sandbox is permanent.
* Deleting an Agent returns HTTP 409 while it still has sandboxes, and can keep returning 409 for a few seconds after the last one is deleted. Delete the sandboxes and snapshots first, then retry the Agent on 409.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.