# Template

A template is the reusable blueprint for a sandbox. It defines the image,
default resources, environment, metadata, and default network policy. Every
sandbox is claimed from a template.

Warm capacity is shared across templates. A template does not own a CPU/memory
profile or a private idle pool; see [Unified Warm Pool](/docs/sandbox/template/pool).

<Callout variant="info">
Creating, updating, and deleting team-owned templates requires team admin
permissions. `developer` and `builder` roles can read templates.
</Callout>

## Template Types

| Type | Description | Visibility |
| --- | --- | --- |
| **Builtin** | Platform-provided templates | Public to all teams |
| **Custom** | Templates created and owned by a team | Private to that team |

A platform system identity manages builtin templates. Team identities cannot
write public template state.

## Choose A Creation Method

| Need | Prefer |
| --- | --- |
| Reproducible image build or a different base image | Create a template from an image |
| Reusable template ID from an initialized sandbox | Create a template from a sandbox |
| Reuse initialized RootFS state without a new template | Create a RootFS snapshot and claim with `snapshot_id` |

A template created from a sandbox retains an immutable regional block-COW
RootFS generation. It does not republish the writable workspace as an OCI
layer. The source template's digest-pinned base image and platform remain the
runtime identity for the retained generation.

A RootFS snapshot is often better when the existing image, resource defaults,
and network policy already fit. Snapshots do not appear in the template list;
they can initialize new sandboxes claimed from a compatible template.

See [Snapshot And Restore](/docs/sandbox/snapshot-restore) for snapshot,
restore, fork, and rebase behavior.

## Create From An Image

<Endpoint method="POST">
/api/v1/templates
</Endpoint>

```yaml
# template.yaml
spec:
    mainContainer:
        image: docker.io/library/python@sha256:<64-hex-digest>
        resources:
            memory: 4Gi
            ephemeralStorage: 8Gi
    envVars:
        PYTHONUNBUFFERED: "1"
    network:
        mode: block-all
```

```bash
s0 template create --id my-python-env --spec-file template.yaml
```

The request shape is:

```json
{
    "template_id": "my-python-env",
    "spec": {
        "mainContainer": {
            "image": "docker.io/library/python@sha256:<64-hex-digest>",
            "resources": {
                "memory": "4Gi",
                "ephemeralStorage": "8Gi"
            }
        }
    }
}
```

Image-based template creation returns `201 Created` after validating the
canonical digest-pinned source. Manager then idempotently discovers every
required runtime platform and imports the exact image, `procd` identity, and
RootFS size into the regional block store. A claim returns data-plane-not-ready
until that durable import reaches `ready`. Template IDs are canonical within
their scope and cannot be renamed.

<Callout variant="warning">
The public template API accepts memory but not CPU. CPU is derived by platform
policy. Scheduling class and warm-capacity configuration are platform-level
inputs rather than template fields. Image tags are mutable and are rejected;
use a normalized OCI reference ending in `@sha256:` and 64 lowercase hex
characters.
</Callout>

## Create From A Sandbox

<Endpoint method="POST">
/api/v1/templates/from-sandbox
</Endpoint>

This operation accepts a running or paused source and returns `202 Accepted`.
It is asynchronous because Sandbox0 must capture and retain an immutable RootFS
generation.

| Field | Required | Description |
| --- | --- | --- |
| `template_id` | Yes | New team-owned template ID. |
| `sandbox_id` | Yes | Source sandbox owned by the same team. |
| `spec_overrides` | No | Only `description`, `displayName`, and `tags` may be overridden. |

```bash
curl -X POST "$SANDBOX0_BASE_URL/api/v1/templates/from-sandbox" \
    -H "Authorization: Bearer $SANDBOX0_TOKEN" \
    -H "Content-Type: application/json" \
    -H "Idempotency-Key: python-workspace-v1" \
    -d '{
        "template_id": "python-workspace",
        "sandbox_id": "SOURCE_SANDBOX_ID",
        "spec_overrides": {
            "displayName": "Python workspace",
            "tags": ["python", "initialized"]
        }
    }'
```

The new template inherits safe runtime inputs from the source template. It does
not copy sandbox identity, processes, memory, sessions, claim-time environment
or network overrides, or services.

For a running source, `ctld` briefly barriers the exact active writer and
publishes a committed RootFS checkpoint. Concurrent writes can continue after
the short barrier. Request acceptance is not the capture point:
`status.creation.capturedAt` records the committed capture time.

Keep the source available while the status stage is `capturing`. Poll the
returned `Location` until `status.creation.state` becomes `ready` or `failed`:

```bash
s0 template get python-workspace
```

Creation states are:

| State | Stage | Meaning |
| --- | --- | --- |
| `creating` | `capturing` | Exact source writer is being validated and captured. |
| `creating` | `publishing` | The immutable block-COW generation is being bound to the template. |
| `ready` | `publishing` | The regional template source is committed and claimable. |
| `failed` | Current stage | Creation failed closed; inspect `reason` and `message`. |

`ready` does not mean the RootFS is already attached on every node. A claim
still needs a compatible carrier, node capacity, and RootFS attachment.

Use the same `Idempotency-Key` and body to retry after an uncertain client
response. Reusing a key with a different body returns a conflict.

## Get And List

<Endpoint method="GET">
/api/v1/templates/{'{id}'}
</Endpoint>

<Endpoint method="GET">
/api/v1/templates
</Endpoint>

```bash
s0 template get my-python-env
s0 template list
```

Lists include builtin templates and team-owned templates visible to the current
identity. Internal RootFS attestation, build lease, and idempotency state are
not exposed in the public representation.

## Update

<Endpoint method="PUT">
/api/v1/templates/{'{id}'}
</Endpoint>

Updates replace the template spec. They affect future claims and do not rewrite
already-running sandbox generations.

```bash
s0 template update my-python-env --spec-file template.yaml
```

A template still being created, or one whose asynchronous creation failed,
cannot be updated and returns `409 Conflict`. CPU and scheduling fields remain
platform-controlled on update.

## Delete

<Endpoint method="DELETE">
/api/v1/templates/{'{id}'}
</Endpoint>

```bash
s0 template delete my-python-env
```

Deletion removes the logical template and durably queues cleanup of an internal
RootFS generation, when present. Existing sandboxes keep their own lifecycle
and RootFS state; new claims by the deleted template ID are rejected.

## Next Steps

<CardGroup>
  <Card title="Template Configuration" href="/docs/sandbox/template/configuration" cta="Continue">
    Configure image, memory, environment, and network defaults.
  </Card>
  <Card title="Unified Warm Pool" href="/docs/sandbox/template/pool" cta="Continue">
    Understand resource-neutral carriers and claim-time resource leases.
  </Card>
</CardGroup>
