# 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 supplies a default memory
size, with CPU derived by platform policy, but does not reserve 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.
When a team-owned template has the same ID as a builtin, get and claim prefer
the team's template. Template IDs are scoped to the team or public namespace
within a region.

## 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
```

Replace `<64-hex-digest>` with the image's actual immutable digest before
submitting the file.

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"
            }
        },
        "envVars": {"PYTHONUNBUFFERED": "1"},
        "network": {"mode": "block-all"}
    }
}
```

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
initial RootFS capacity and immutable device geometry 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 -fsS -X POST "$SANDBOX0_API_URL/api/v1/templates/from-sandbox" \
    -H "Authorization: Bearer $SANDBOX0_API_KEY" \
    -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.
The retained RootFS still contains ordinary files written by the source,
including any credentials or configuration saved there. The operation does
not scrub those files.

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.

For a template created from a sandbox, keeping the same image retains its
captured RootFS source. Changing `mainContainer.image` clears that source and
queues its cleanup; future claims initialize from the new image import rather
than the captured workspace. A resource change alone does not resize an
already captured RootFS generation.

## 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 fail when no template with that ID is visible.
Deleting a private template that shadows a builtin exposes the builtin again,
so subsequent claims with that ID use the builtin.

## 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>
