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

# Templates

> Build and manage custom sandbox templates using the Template class, TemplateBase fluent builder, and BuildInfo models.

```python theme={null}
from declaw import Template, TemplateBase, BuildInfo, TemplateBuildStatus, CopyItem
```

Templates let you pre-build sandbox images with specific packages, files, and environment variables installed. Once built, a template can be referenced by alias in `Sandbox.create(template="my-template")` to boot sandboxes that start from a known state.

## TemplateBase

`TemplateBase` is a fluent builder for defining the contents of a template. Build up the definition by chaining methods, then pass it to `Template.build()`.

```python theme={null}
from declaw import TemplateBase

template = (
    TemplateBase()
    .from_base_image("ubuntu:22.04")
    .apt_install("python3", "python3-pip", "curl")
    .run_cmd(["pip3", "install", "pandas", "numpy", "matplotlib"])
    .copy("./local_script.py", "/home/user/script.py")
    .set_envs({"PYTHONPATH": "/home/user"})
    .set_start_cmd("sleep infinity")
)
```

### Methods

#### `.from_base_image(image)`

Set the base Docker image.

<ParamField body="image" type="str" default="'ubuntu:22.04'">
  Docker image tag to use as the base.
</ParamField>

**Returns** `TemplateBase` (for chaining)

***

#### `.apt_install(*packages)`

Install apt packages.

```python theme={null}
template.apt_install("git", "curl", "jq")
```

<ParamField body="*packages" type="str" required>
  One or more package names to install via `apt-get install`.
</ParamField>

**Returns** `TemplateBase`

***

#### `.run_cmd(cmds)`

Add a shell command to run during the build (equivalent to a Dockerfile `RUN`).

```python theme={null}
template.run_cmd(["pip3", "install", "torch", "--index-url", "https://download.pytorch.org/whl/cpu"])
```

<ParamField body="cmds" type="list[str]" required>
  Command as a list of strings (the executable and its arguments).
</ParamField>

**Returns** `TemplateBase`

***

#### `.copy(src, dst, mode)`

Copy a local file into the template image at build time.

```python theme={null}
template.copy("./requirements.txt", "/app/requirements.txt")
template.copy("./startup.sh", "/usr/local/bin/startup.sh", mode=0o755)
```

<ParamField body="src" type="str" required>
  Local path to the file to copy.
</ParamField>

<ParamField body="dst" type="str" required>
  Destination path inside the image.
</ParamField>

<ParamField body="mode" type="int | None" default="None">
  Unix file permission bits (e.g. `0o755` for executable).
</ParamField>

**Returns** `TemplateBase`

***

#### `.set_envs(envs)`

Set environment variables baked into the template.

```python theme={null}
template.set_envs({"APP_ENV": "production", "PORT": "8080"})
```

<ParamField body="envs" type="dict[str, str]" required>
  Key-value pairs to set as environment variables.
</ParamField>

**Returns** `TemplateBase`

***

#### `.set_start_cmd(cmd, ready_check)`

Set a command to run when the sandbox boots.

```python theme={null}
template.set_start_cmd("python3 /app/server.py")
```

<ParamField body="cmd" type="str" required>
  Shell command to execute on sandbox start.
</ParamField>

<ParamField body="ready_check" type="Any | None" default="None">
  Optional readiness probe configuration. Reserved for future use.
</ParamField>

**Returns** `TemplateBase`

***

#### `.to_dict()`

Serialize the template definition to a dict suitable for the API.

**Returns** `dict`

***

## Template

`Template` is a static class for building and querying templates via the API.

### `Template.build()`

Submit a template build and wait for it to complete.

```python theme={null}
from declaw import Template, TemplateBase

template = (
    TemplateBase()
    .apt_install("python3-pip")
    .run_cmd(["pip3", "install", "pandas"])
)

build_info = Template.build(
    template=template,
    alias="data-analysis",
    cpu_count=2,
    memory_mb=2048,
    on_build_logs=print,
    api_key="your-api-key",
    domain="104.198.24.180:8080",
)
print("Template ID:", build_info.template_id)
```

<ParamField body="template" type="TemplateBase" required>
  The template definition to build.
</ParamField>

<ParamField body="alias" type="str" required>
  Human-readable name for the template. Used as the `template` parameter
  in `Sandbox.create()`.
</ParamField>

<ParamField body="cpu_count" type="int" default="1">
  Number of CPUs for the build worker.
</ParamField>

<ParamField body="memory_mb" type="int" default="1024">
  Memory in MB for the build worker.
</ParamField>

<ParamField body="on_build_logs" type="Callable[[str], None] | None" default="None">
  Callback invoked for each log line emitted during the build.
</ParamField>

<ParamField body="api_key" type="str | None" default="$DECLAW_API_KEY">
  API key override.
</ParamField>

<ParamField body="domain" type="str | None" default="$DECLAW_DOMAIN">
  Domain override.
</ParamField>

<ParamField body="request_timeout" type="float | None" default="None">
  Per-request HTTP timeout in seconds.
</ParamField>

**Returns** `BuildInfo`

***

### `Template.build_in_background()`

Submit a template build and return immediately without waiting for it to finish.

```python theme={null}
build_info = Template.build_in_background(
    template=template,
    alias="my-template",
    api_key="your-api-key",
    domain="104.198.24.180:8080",
)
print("Build started:", build_info.build_id)
```

Parameters are the same as `Template.build()` except `on_build_logs` is not accepted.

**Returns** `BuildInfo`

***

### `Template.get_build_status()`

Poll the status of a background build.

```python theme={null}
import time

build_info = Template.build_in_background(template, "my-template", api_key="key", domain="host:8080")

while True:
    status = Template.get_build_status(
        build_id=build_info.build_id,
        api_key="your-api-key",
        domain="104.198.24.180:8080",
    )
    print(status.status, status.logs[-1] if status.logs else "")
    if status.status in ("succeeded", "failed"):
        break
    time.sleep(3)
```

<ParamField body="build_id" type="str" required>
  Build ID from a previous `build()` or `build_in_background()` call.
</ParamField>

<ParamField body="api_key" type="str | None" default="$DECLAW_API_KEY">
  API key override.
</ParamField>

<ParamField body="domain" type="str | None" default="$DECLAW_DOMAIN">
  Domain override.
</ParamField>

<ParamField body="request_timeout" type="float | None" default="None">
  Per-request HTTP timeout in seconds.
</ParamField>

**Returns** `TemplateBuildStatus`

***

## Data models

### `BuildInfo`

```python theme={null}
@dataclass
class BuildInfo:
    build_id: str
    status: str
    template_id: str | None = None   # Set once build succeeds
```

### `TemplateBuildStatus`

```python theme={null}
@dataclass
class TemplateBuildStatus:
    build_id: str
    status: str             # e.g. "pending", "running", "succeeded", "failed"
    logs: list[str]         # Build log lines accumulated so far
```

### `CopyItem`

```python theme={null}
@dataclass
class CopyItem:
    src: str
    dst: str
    mode: int | None = None
```

***

## AsyncTemplate

Use `AsyncTemplate` for async applications. It has the same static methods as
`Template` but all are coroutines:

```python theme={null}
from declaw import AsyncTemplate, TemplateBase

template = TemplateBase().apt_install("nodejs", "npm")

build_info = await AsyncTemplate.build(
    template=template,
    alias="node-env",
    api_key="your-api-key",
    domain="104.198.24.180:8080",
)
```

Methods: `await AsyncTemplate.build(...)`, `await AsyncTemplate.build_in_background(...)`, `await AsyncTemplate.get_build_status(...)`.

***

## Using a template in Sandbox.create()

Once a template is built successfully, reference it by alias:

```python theme={null}
from declaw import Sandbox

sbx = Sandbox.create(
    template="data-analysis",   # alias set during build
    api_key="your-api-key",
    domain="104.198.24.180:8080",
)
# pandas is already installed — no pip install needed
result = sbx.commands.run("python3 -c \"import pandas; print(pandas.__version__)\"")
print(result.stdout)
sbx.kill()
```
