Skip to content

Compute Isolation

Each sandbox runs in its own isolated environment, fully separated from other sandboxes and from any other tenants' workloads.

You pick an image and (optionally) a command — Tilde launches the sandbox, captures its output, and cleans up when it exits.

Supported Images

Sandboxes accept any Docker image reference in the image field. Pass a public image tag like python:3.12 or ubuntu:22.04, or a reference from any registry Tilde can pull from (e.g. ghcr.io/my-org/my-image:latest, my-registry.example.com/team/image:v1).

Creating a Sandbox

import tilde

repo = tilde.repository("my-team/my-data")
sandbox = repo.sandboxes.create(
    image="python:3.12",
    command=["python", "-c", "import os; print(os.listdir('/sandbox'))"],
)
print(sandbox.id)
# Run and stream output (default behavior)
tilde sandbox run -r my-team/my-data \
  --image python:3.12 \
  -- python -c "import os; print(os.listdir('/sandbox'))"

# Detach and print sandbox ID
tilde sandbox run -r my-team/my-data \
  --image python:3.12 -d \
  -- python -c "import os; print(os.listdir('/sandbox'))"
curl -H "Authorization: Bearer YOUR_API_TOKEN" \
  -X POST https://tilde.run/api/v1/organizations/my-team/repositories/my-data/sandboxes \
  -H "Content-Type: application/json" \
  -d '{
    "image": "python:3.12",
    "command": ["python", "-c", "import os; print(os.listdir(\"/sandbox\"))"]
  }'

Response: 201 Created

{ "sandbox_id": "a1b2c3d4-..." }

Configuration Options

Field Type Required Description
image string Yes Docker image reference (e.g. python:3.12, ubuntu:22.04, ghcr.io/my-org/my-image:latest)
command string[] No Command to execute inside the container
mountpoint string No Where to mount repo data (default: /sandbox)
path_prefix string No Only mount objects under this prefix
timeout_seconds integer No Maximum execution time in seconds
env_vars object No Environment variables to inject into the container
run_as object No Run as an agent or role instead of yourself (see Delegation)

Checking Status

Poll the sandbox to check its progress:

status = sandbox.status()
print(status.state)
tilde sandbox info -r my-team/my-data SANDBOX_ID
curl -H "Authorization: Bearer YOUR_API_TOKEN" \
  https://tilde.run/api/v1/organizations/my-team/repositories/my-data/sandboxes/SANDBOX_ID

Sandbox Statuses

Status Meaning
created Sandbox is queued
starting Container is being pulled and started
running Container is executing
committed Completed successfully, changes committed
awaiting_approval Completed, waiting for human review
failed Exited with an error
cancelled Cancelled by user

When a sandbox reaches committed, the response includes a commit_id pointing to the new commit with the sandbox's changes.

Exit Lifecycle

Exit code Cancelled? Outcome
0 No Session is committed; status becomes committed
0 (approval required) No Session needs approval; status becomes awaiting_approval
Non-zero No Session is rolled back; status becomes failed
Any Yes Session is rolled back; status becomes cancelled

Streaming Logs

Stream stdout and stderr from a running sandbox:

# Stream stdout line by line
status = sandbox.status()
with status.stdout() as stream:
    for line in stream:
        print(line, end="")
# Print captured output
tilde sandbox logs -r my-team/my-data SANDBOX_ID

# Stream output in real time
tilde sandbox logs -r my-team/my-data -f SANDBOX_ID
# Stream stdout
curl -H "Authorization: Bearer YOUR_API_TOKEN" \
  https://tilde.run/api/v1/organizations/my-team/repositories/my-data/sandboxes/SANDBOX_ID/stdout

# Stream stderr
curl -H "Authorization: Bearer YOUR_API_TOKEN" \
  https://tilde.run/api/v1/organizations/my-team/repositories/my-data/sandboxes/SANDBOX_ID/stderr

Both endpoints use chunked transfer encoding — new output is sent as it becomes available.

Cancelling a Sandbox

Cancel a running sandbox:

sandbox.cancel()
tilde sandbox cancel -r my-team/my-data SANDBOX_ID
curl -H "Authorization: Bearer YOUR_API_TOKEN" \
  -X DELETE https://tilde.run/api/v1/organizations/my-team/repositories/my-data/sandboxes/SANDBOX_ID

Response: 202 Accepted — the cancellation is asynchronous. The sandbox transitions to cancelled once the container stops.

Environment Variables and Secrets

You can pass environment variables directly when creating a sandbox:

{
  "image": "python:3.12",
  "command": ["python", "validate.py"],
  "env_vars": {
    "STRICT_MODE": "1",
    "OUTPUT_FORMAT": "json"
  }
}

For sensitive values, use secrets instead of passing them in env_vars. Secrets are encrypted at rest and injected automatically. See the Python SDK or the API Reference for details on creating repository and agent secrets.

Precedence (highest to lowest):

  1. env_vars passed in the sandbox request
  2. Agent secrets (if the sandbox runs as an agent)
  3. Repository secrets

Delegation

By default, sandboxes run under your identity. You can delegate execution to an agent or role using the run_as field:

{
  "image": "python:3.12",
  "command": ["python", "pipeline.py"],
  "run_as": {
    "type": "agent",
    "id": "AGENT_UUID"
  }
}

When a sandbox runs as an agent, it inherits the agent's secrets, RBAC permissions, and network policies. This is useful for automated pipelines where the sandbox should have a specific, limited set of permissions rather than your full user access.

Two policy checks run before the sandbox starts:

  1. Caller permission. Setting run_as.type = "agent" requires UseAgent on the target agent; run_as.type = "role" requires UseRole. Without this grant the request is rejected with 403 FORBIDDEN. The built-in Owner, SuperUser, and AgentManager policies include these grants; other principals must be granted them explicitly. See Impersonation.
  2. Target permission. The agent's inline policy must allow CreateSandbox, CreateSession, and CommitSession on the target repository — the sandbox lifecycle uses the agent's identity for these actions and they are no longer bypassed. Agents created through the Tilde web UI have these seeded by default; API and CLI callers setting inline_policy explicitly need to include them. See Running sandboxes as agents.

Examples

The examples below wire a sandbox to a trigger so it runs automatically when specific files change.

Document Summarization Agent

An agent that automatically summarizes documents when they're added to the repository. When a PDF or text file is uploaded to documents/, the agent reads it, generates a summary using an LLM, and commits the summary as a companion Markdown file.

1. Create the agent and configure its secrets:

import tilde

org = tilde.organizations.get("my-team")

agent = org.agents.create(
    "summarizer",
    description="Reads new documents and writes summaries",
)

# The agent needs an LLM API key to generate summaries
agent.secrets.create("OPENAI_API_KEY", "sk-...")

2. Create the trigger:

{
  "name": "Summarize new documents",
  "description": "Generate a Markdown summary for each new document in documents/",
  "conditions": [
    { "type": "prefix", "prefix": "documents/" }
  ],
  "sandbox_config": {
    "image": "my-org/doc-summarizer:latest",
    "command": ["python", "summarize.py"],
    "mountpoint": "/repo",
    "timeout_seconds": 300
  },
  "run_as": {
    "type": "agent",
    "id": "AGENT_UUID"
  }
}

When someone uploads documents/quarterly-review.pdf, the agent reads the file, calls the LLM (using OPENAI_API_KEY from its secrets), and commits documents/quarterly-review.summary.md. The commit appears in the timeline attributed to summarizer.

Form Auto-Fill Agent

An agent that processes blank or partially filled forms. When a form template is uploaded, the agent reads structured data already in the repository and fills in the form fields, committing the completed version for human review.

{
  "name": "Fill uploaded forms",
  "conditions": [
    { "type": "prefix", "prefix": "forms/pending/" }
  ],
  "sandbox_config": {
    "image": "my-org/form-filler:latest",
    "command": ["python", "fill_forms.py"],
    "mountpoint": "/repo",
    "timeout_seconds": 600
  },
  "run_as": {
    "type": "agent",
    "id": "AGENT_UUID"
  }
}

The agent reads the form from forms/pending/, pulls reference data from data/ in the same mount, writes the completed form to forms/completed/, and commits. With Require approval for agent commits enabled, a human reviews the filled form before it becomes part of the committed history.

Website Content Generator

An agent that turns rough ideas into published content. Writers drop short briefs into drafts/ideas/, and the agent expands each one into a full article, complete with metadata, and places it in the site's content directory.

Agent secrets:

org = tilde.organizations.get("my-team")
agent = org.agents.get("content-writer")

agent.secrets.create("ANTHROPIC_API_KEY", "sk-ant-...")

Trigger:

{
  "name": "Draft content from ideas",
  "conditions": [
    { "type": "prefix", "prefix": "drafts/ideas/" }
  ],
  "sandbox_config": {
    "image": "my-org/content-gen:latest",
    "command": ["python", "generate.py"],
    "mountpoint": "/site",
    "timeout_seconds": 900,
    "env_vars": {
      "OUTPUT_DIR": "/site/content/blog/"
    }
  },
  "run_as": {
    "type": "agent",
    "id": "AGENT_UUID"
  }
}

A writer uploads drafts/ideas/tilde-launch.md containing a short outline. The agent reads the brief, generates a full blog post with front matter, and commits it to content/blog/tilde-launch.md. The ANTHROPIC_API_KEY is injected from the agent's secrets — it never appears in the trigger config or container environment.

Limits

Resource Limit
Triggers per repository 50
Conditions per trigger 10
Trigger name length 100 characters
Environment variables per sandbox 50
Env var key length 256 characters
Env var value length 4,096 characters
Path/prefix length 1,024 bytes