Templates

Shardflux templates: versioned images that workspaces start from, with environment, inputs, start commands and services, and python-node-browser.

What a template is

A template is the image a workspace starts from: an operating system, languages, packages and files, plus the settings every workspace of it gets when it opens. You name it by its slug when you use a key:

TypeScript
workspace('customer-42/main', { template: 'default' });

The default template

default names the platform default template, python-node-browser: use it when you have no preference. A workspace opened with default records python-node-browser as its template, and it starts as fast as any other. An organization that builds its own template with the slug default replaces it for its own workspaces.

Templates come in two kinds:

Kind Who publishes it Examples
Platform template Shardflux. Every project can open it. python-node-browser (category stack, also named default), ubuntu-24.04 (category os)
Organization template You, by building one or saving a workspace as one acme-dev

Versions

A template has numbered, immutable versions. When a key is opened for the first time, the slug resolves to the template's latest published version, and the workspace keeps that version for its whole life. Publishing a new version changes what new keys get; existing workspaces keep theirs, and pick up the template's immutable paths from the newest published version. Fork keeps the source's version, and reset returns to it.

Platform security fixes reach every workspace on their own. Your templates follow their base template onto the release that carries the fix, as a new published version, and existing workspaces move onto it at their next resume, keeping their files, packages and work. Other platform releases reach new workspaces, so a resume keeps its memory.

workspace.template (TypeScript) and shard ws get <key> show the slug and version a workspace runs.

Public templates

List what your project can open, and look one up:

Shell
shard templates list
shard templates get python-node-browser
TypeScript
const page = await cloud.templates.list();
for (const t of page.data) console.log(t.slug);
const detail = await cloud.templates.get('python-node-browser');
Python
print([t["slug"] for t in sf.templates.list(owner="platform").data])
detail = sf.templates.get("python-node-browser")

python-node-browser is the template these docs use. It is Ubuntu 24.04 with Python 3.12 (pip, venv), Node.js 24 (npm), git, curl, build-essential and a headless Chromium browser. A workspace of it sees up to 8 GiB of memory from boot and holds what its commands use, 1 GiB while it is idle; caps.memory_mib sets a lower maximum. ubuntu-24.04 is a minimal base (bash, apt, sudo, curl and certificates) to build your own templates on.

Look inside a version without opening a workspace:

Shell
shard templates get acme-dev                        # versions, and which one open resolves to
shard templates files acme-dev 3 /home/user          # one directory of version 3
shard templates diff acme-dev --from 2 --to 3        # what changed between two versions

Versions without a file list answer 409 (details.reason: file_list_unavailable).

Settings: environment, inputs, start commands and services

A template version built from a recipe (see Build a template) or saved from a workspace carries settings that apply every time one of its workspaces opens:

Setting What it does Limits
env Environment variables for every command, terminal, start command and service 128 names; values up to 4,096 bytes; names starting with SHARDFLUX_ are reserved
inputs Values supplied when a workspace is opened: text (put into the environment) or secret (binds the stored secret of the same name) 32 inputs; text values up to 4,096 bytes, no CR, LF or NUL
start Commands run at create (first boot of a new workspace), boot (a later cold boot) or resume 32 commands; timeout_seconds 1 to 1800 (default 300), 3,600 seconds in total
services Long-running processes kept up by the workspace, with restart (always, on_failure, never) and a readiness check (port or command) 16 services; ready_timeout_seconds 1 to 600 (default 60)
defaults Default lifetime, idle_timeout_seconds for sessions, an egress ceiling (internet, allowlist, none) and resource limits

Environment precedence, lowest first: the workspace's base environment, the template's env, the workspace's text inputs, then the command's own env.

Pass inputs when you open a key:

TypeScript
const ws = await cloud.workspaces.open({ key: 'customer-42/main', template: 'acme-dev', inputs: { PROJECT_NAME: 'acme' } });
console.log(await ws.inputs(), ws.startup);
Python
ws = sf.open(key="customer-42/main", template="acme-dev", inputs={"PROJECT_NAME": "acme"})
print(ws.inputs(), ws.startup)
Shell
shard ws open customer-42/main --template acme-dev --input PROJECT_NAME=acme

On a new key, each input takes the given value, else its declared default. On an existing key, inputs replaces them all; leaving it out keeps them. A missing required input is 422 (details.reason: input_required), an undeclared one input_unknown, and a value that breaks the rules input_invalid.

Start commands and services at open

open waits for the start commands and services: a workspace is ready when they have run and its services report ready. workspace.startup shows the progress (pending, running, ready or failed).

  • Which commands run. A new workspace (and a reset one) runs its create and boot commands, in the declared order. A later cold boot runs boot commands. A resume or fork restores memory, so it runs resume commands, and the services are still running.
  • When a step fails. The open fails with startup_failed (retryable). The workspace keeps running so you can look at it: workspace.startup names the failed step, its exit code and the last 4 KiB of its output. The next open runs the failed step again.
  • Changed inputs apply to later commands, not to services that are already running.

Layered disks

Template versions that support it give workspaces a layered disk: the template is a read-only layer, and the workspace's own changes are a separate layer. That separation makes reset, saving a workspace as a template and listing a workspace's changes against its template possible:

TypeScript
const page = await workspace.changes({ pathPrefix: '/home/user', summary: true });   // added | modified | metadata | deleted | replaced

Workspaces created from older versions are legacy (one disk) and answer those calls with 409 (details.reason: legacy_disk_layout). The layout is chosen at the first open and never changes; workspace.diskLayout (TypeScript) and ws.disk_layout (Python) report it.

Immutable paths

Ship your tooling once and every workspace runs the current release. A template declares the directories that hold it, such as a CLI, an SDK or the instructions your agent reads, as immutable. Every workspace of the template shows those directories from the template's newest published version, and everything else on its disk stays its own:

Part of the disk What it holds When it changes
Immutable paths The directories the template declares, read-only At each start and resume, to the template's newest published version
Everything else The workspace's files, packages and work, created from its version at the first open Only when the workspace changes it

Declare them in template.yaml with immutable (see Build a template):

YAML
# acme/template.yaml
base: ubuntu-24.04@2
immutable: [/opt/acme]
build:
  files:
    - { from: ./acme, to: /opt/acme }   # bin/acme, lib/ and AGENTS.md
  steps:
    - { name: link, run: ln -s /opt/acme/bin/acme /usr/local/bin/acme }
Shell
shard templates build acme/template.yaml --slug acme-dev --publish --wait

With /opt/acme declared since version 3, a workspace created from version 3 keeps its own files, packages and work, and runs the acme CLI and reads the AGENTS.md of version 5 once version 5 is published:

TypeScript
const ws = await cloud.workspaces.open({ key: 'customer-42/main', template: 'acme-dev' });
console.log(ws.template.version, ws.template.immutable_version);   // 3 5
  • Rollout is publishing. A workspace shows the newest published version at its next start or resume: an open that boots it, a reset, or a resume from suspend. A running workspace never sees its immutable paths change, so a task never has its tooling replaced half way. Lifecycle has each transition.
  • Rollback is archiving. Archive the newest version (shard templates archive acme-dev@5, or the API) and the previous one is current again; workspaces return to it at their next start or resume.
  • Read-only, by design. One release serves every workspace, so nothing inside a workspace changes it. A write under an immutable path fails with Read-only file system, and the file API answers 409 conflict (details.reason: read_only_path). An agent that needs a variant copies the tool into its own files and changes the copy: cp -r /opt/acme ~/acme.
  • The list carries forward. A version whose template.yaml leaves immutable out keeps the current version's list. A new version may add paths and keeps every path the current version declares (dropping one is 422 immutable_path_removed), so a forgotten line never takes tooling away from every workspace.
  • Saving keeps tooling current. Save as template takes the immutable paths from the template's newest published version, never from the workspace being saved. In a draft and its test instances the immutable paths are ordinary, writable directories: change the tooling there, try it, and publishing the draft publishes it. A version test instance shows that version's own files, so you can try an unpublished release before you publish it.
  • Which workspaces follow. Workspaces opened on a version that declares immutable paths, and their forks. Workspaces created from an earlier version keep their own copy of those directories.

Your organization's templates declare immutable paths, and the API reports them on three objects:

Object Field Meaning
Workspace template.immutable_version The template version whose immutable paths the workspace shows; null when it shows none
Template version immutable { paths, bytes }: the paths the version declares and the size of their image; null when it declares none
Template build immutable_paths The paths the version it produces declares, written in the recipe or carried forward; empty for none

shard ws get <key> shows a workspace's immutable_version and shard templates get <slug> each version's paths (CLI 0.8.0+). Counts and sizes are in Pricing and limits.

Ways to make your own template

Way When How
Build from template.yaml Reproducible templates kept in your repository Build a template
Save a workspace as a template You set a workspace up by hand or with an agent workspace.saveAsTemplate({ templateSlug }), ws.save_as_template(slug), shard ws save-as-template <key> --template <slug>
Draft (dev mode) Iterate on a template in a live workspace, test copies, then publish cloud.templates.draft(slug), sf.templates.draft(slug), shard templates draft ...

Saving and drafts need a layered workspace. Organization templates are stored once per distinct layer, and their storage counts toward your plan's retained storage (see Pricing and limits).

View this page as Markdown