Skip to content

Sandboxes API

sandbox 是平台的核心资源,代表一个完全隔离的运行环境。

所有端点需要 developer 角色(GET 只读端点需要 viewer 及以上)。

Sandbox 能力速查

一张表看全 sandbox 的所有能力。

能力字段 / 端点说明
镜像image指定容器镜像(如 talon-alpine);空 = 使用默认镜像
CPUresources.cpu浮点 core 数,如 0.52;0 = 使用 worker 默认值
内存resources.memory字符串单位,如 "4GiB";空 = 使用 worker 默认值
磁盘resources.disk字符串单位,如 "20GiB";空 = 使用 worker 默认值
进程数上限resources.pids_limit最大并发进程数;0 = 使用 worker 默认值
空闲超时timeoutduration 字符串,如 "30m";sandbox 无操作超过此时长自动 pause;空 = 禁用
硬性 TTLttlduration 字符串,如 "6h";从创建时间起超过此时长自动 destroy;空 = 禁用
网络策略networkopen(全放行)/ allowlist(白名单)/ sealed(完全隔离);见网络策略
主机白名单network_allowed_hosts[]string;配合 allowlist 使用,支持域名 / IP / CIDR
凭证注入secrets[]将已创建的 secret 以 envfile 方式注入 sandbox
环境变量envmap[string]string;sandbox 启动时注入的环境变量
标签labelsmap[string]string;创建时写定的自定义 KV 元数据,不可变,不注入容器;见下方labels 详解
可读名称name自定义名称;空时 UI 显示 id
任务描述task本次任务的文字描述,显示在控制台概览
启动等待?wait=runningquery 参数;服务端阻塞直到 sandbox 进入 running 后再返回
执行命令POST /{id}/exec同步执行一次性命令,返回 stdout / stderr / exit_code
长驻进程POST /{id}/processes启动后台进程,支持 env / cwd / 声明暴露端口
端口暴露POST /{id}/expose注册端口并获得 preview URL;支持自定义子域名和签名 token
文件系统/v1/sandboxes/{id}/fs/*列目录、读文件、写文件
交互式终端WebSocket /{id}/pty交互式 PTY,全程录像为 asciicast v2 格式
浏览器POST /{id}/browser启动 Chromium,通过 CDP 协议控制
Agent RunPOST /{id}/agent/run自动化 agent 步骤执行,最多 100 步
录制GET /{id}/recordings/*获取 PTY 会话录像(asciicast v2 格式)

duration 字符串格式:30s / 5m / 2h / 1d / 1w(扩展支持 d / w 单位)。

size 字符串格式:512MiB / 4GiB / 20GB(binary KiB/MiB/GiB 和 decimal KB/MB/GB 均接受,大小写不敏感)。

POST /v1/sandboxes

创建 sandbox。

需要 developer 角色

http
POST /v1/sandboxes
Authorization: Bearer ask_...
Content-Type: application/json

请求体(v2 推荐)

json
{
  "image": "talon-alpine",
  "resources": {
    "cpu": 2,
    "memory": "4GiB",
    "disk": "10GiB"
  },
  "timeout": "30m",
  "ttl": "6h",
  "network": "allowlist",
  "env": { "NODE_ENV": "development" },
  "labels": { "project": "agent-x" },
  "secrets": [
    {
      "secret_id": "sec_xxx",
      "mount_type": "env",
      "target": "OPENAI_API_KEY"
    }
  ]
}
字段类型必填说明
imagestring镜像标签(如 talon-alpine);空 → worker 默认
resources.cpunumberCPU 核数,支持小数(如 0.5);0 = worker 默认
resources.memorystring内存上限,字符串单位(512MiB / 4GiB / 2GB);空 = worker 默认
resources.diskstring磁盘上限,字符串单位;空 = worker 默认
timeoutstring无操作自动 pause(30s / 5m / 2h / 1d);空 = 禁用
ttlstring绝对生存时间,同 duration 格式;空 = 禁用
networkstringsealed / allowlist / open(别名,见下);空 = 默认
envobject环境变量 dict,sandbox 启动时注入容器
labelsobject自定义 KV 元数据,map[string]string;仅控制面可见,不注入容器;见下方labels 详解
secretsarray注入的凭证列表(见下方说明)

network 别名:

别名含义
sealed完全断网,只有 lo
allowlistDNS + 配置好的白名单域(生产推荐)
open允许所有出站(开发调试)

duration 字符串30s / 5m / 2h / 1d / 1w,扩展 Go ParseDuration 加 d / w 单位。

size 字符串512KiB / 4GiB / 2GB / 1TiB,binary(KiB/MiB/GiB) 和 decimal(KB/MB/GB)都接受,大小写不敏感。

secrets 元素字段:

字段说明
secret_id已创建的 secret ID
mount_typefile(挂载为 tmpfs 文件)或 env(注入为环境变量)
targetfile 模式:/run/secrets/<target>env 模式:环境变量名

labels 详解

labels 是创建 sandbox 时附带的自定义 KV 元数据,类型为 map[string]string

格式约束(后端校验,违反返回 400)

约束项规则
最大条数32 个 key-value 对
key 字符集[a-zA-Z0-9_-],长度 1–64 字节
value 长度最长 256 字节;禁止控制字符(允许中文、邮箱等可见字符及 tab)

不可变性:labels 只在创建时写定,创建后无法修改,也没有更新 labels 的端点。

安全边界:labels 是纯控制面元数据,不会注入到容器环境变量,容器内进程读不到。 这与 env(会注入容器)是刻意区分的——适合存放终端用户标识等不该让 sandbox 内代码感知的数据。

可见性:本租户成员可查看自己租户 sandbox 的 labels;平台超管可跨租户查看(用于运营归因)。

响应里的 labels:GET sandbox 详情和 list sandbox 均会原样返回 labels 字段。

服务端过滤GET /v1/sandboxes 支持 label=key:value query 参数按 label 服务端过滤(见下方 按 label 过滤),大列表场景无需在客户端遍历。

典型用例:SaaS 二次分发

集成方用一个工作区 + 一个 API Key 代表整个平台,所有 sandbox 的 created_by 都是 同一个工作区账户。要归因"是哪个终端用户创建的",在创建时打入 labels:

json
{
  "image": "talon-alpine",
  "labels": {
    "end_user_id": "u_8821",
    "plan": "pro"
  }
}

后续可以:

  • 通过 GET /v1/sandboxes?label=end_user_id:u_8821 按用户过滤 sandbox 列表(见上方按 label 过滤
  • 通过用量计量 APIend_user_id 拆分资源用量,用于二次计费分账

响应

201 Created

响应体始终用规范化后的字段(v2 风格):

json
{
  "id": "sbx_xxxxxxxxxxxxxxxxxxxxxxxxxx",
  "state": "created",
  "image": "talon-alpine",
  "resources": {
    "cpu": 2,
    "memory": "4GiB",
    "disk": "10GiB"
  },
  "timeout": "30m",
  "ttl": "6h",
  "network": "allowlist",
  "created_at": 1716480000
}

调用方便利

请求时也支持 ?wait=running(Spec 45)— 服务端 block 到 sandbox 进入 running 再返回,省一轮 polling。SDK Sandbox.create() 默认带上这个 query。


GET /v1/sandboxes

列出当前租户所有 sandbox。

需要 viewer 角色

http
GET /v1/sandboxes
Authorization: Bearer ask_...

按 label 过滤

通过 label query 参数在服务端按 label 过滤,语法为 key:value(冒号分隔,因为 value 本身可能含等号):

http
GET /v1/sandboxes?label=end_user_id:u_8821&label=plan:pro
Authorization: Bearer ask_...
行为说明
可重复?label=k1:v1&label=k2:v2 逻辑为 AND:全部命中才返回
分隔符固定用 : 分隔 key 与 value(value 可含 =/、空格等可见字符)
服务端执行过滤在数据库层完成,不受响应列表大小影响
客户端兜底SDK 在收到响应后仍会做一次客户端过滤,兼容老版本服务端

CLI 等效写法:

bash
tsb list --label end_user_id=u_8821 --label plan=pro

CLI 用 key=value 格式(等号)是命令行习惯;底层转换为 API 的 key:value 格式再发给服务端。

响应

200 OK

json
{
  "sandboxes": [
    {
      "id": "sbx_xxx",
      "state": "running",
      "profile": "code-lite",
      "created_at": 1716480000
    }
  ]
}

GET /v1/sandboxes/

获取单个 sandbox 详情。

需要 viewer 角色

http
GET /v1/sandboxes/{id}
Authorization: Bearer ask_...

响应

200 OK — 返回 SandboxDTO(字段同 POST 响应)

404 Not Found

json
{ "error": "sandbox: not found" }

POST /v1/sandboxes//start

启动 sandbox(created / stoppedrunning)。

需要 developer 角色

http
POST /v1/sandboxes/{id}/start
Authorization: Bearer ask_...

响应

200 OK

json
{ "id": "sbx_xxx", "state": "running", ... }

409 Conflict — 状态不允许 start

json
{ "error": "sandbox: invalid state transition" }

POST /v1/sandboxes//stop

停止 sandbox(runningstopped)。所有进程会被 kill。

需要 developer 角色

http
POST /v1/sandboxes/{id}/stop
Authorization: Bearer ask_...

响应

200 OK

json
{ "id": "sbx_xxx", "state": "stopped", ... }

POST /v1/sandboxes//pause

暂停 sandbox(runningpaused)。进程被冻结,内存保留。

需要 developer 角色

http
POST /v1/sandboxes/{id}/pause
Authorization: Bearer ask_...

响应

200 OK

json
{ "id": "sbx_xxx", "state": "paused", ... }

POST /v1/sandboxes//resume

恢复 sandbox(pausedrunning)。毫秒级恢复。

需要 developer 角色

http
POST /v1/sandboxes/{id}/resume
Authorization: Bearer ask_...

响应

200 OK

json
{ "id": "sbx_xxx", "state": "running", ... }

POST /v1/sandboxes//exec

在 sandbox 内执行一次性命令(同步,等待完成返回结果)。

需要 developer 角色

sandbox 必须处于 running 状态。

http
POST /v1/sandboxes/{id}/exec
Authorization: Bearer ask_...
Content-Type: application/json
json
{
  "command": ["bash", "-c", "echo $HOME && ls /workspace"]
}

响应

200 OK

json
{
  "stdout": "/root\napp.py\npackage.json\n",
  "stderr": "",
  "exit_code": 0
}

同步阻塞

exec 是同步接口,会等待命令完成才返回。不适合长时间运行的命令(如服务器启动)——那类用 processes 端点。


DELETE /v1/sandboxes/

销毁 sandbox(任意 alive 状态 → destroyed)。所有数据永久删除。

需要 developer 角色

http
DELETE /v1/sandboxes/{id}
Authorization: Bearer ask_...

响应

204 No Content — 销毁成功

404 Not Found — sandbox 不存在


POST /v1/sandboxes//expose

显式暴露 sandbox 内一个端口,返回 preview URL(Spec 50)。

需要 developer 角色

http
POST /v1/sandboxes/{id}/expose
Authorization: Bearer ask_...
Content-Type: application/json

请求体

json
{
  "port": 5173,
  "subdomain": "my-app",
  "sign": true,
  "ttl": "1h"
}
字段类型必填说明
portint容器端口(1–65535)
subdomainstring自定义 subdomain;空 → 用 sb-{id}-{port}
signbool是否生成签名 token(Spec 48);默认 false
ttlstring签名 token 有效期(duration string);默认 1h,最大 24h

响应

201 Created

json
{
  "port": 5173,
  "url": "http://sb-xxx-5173.preview.example.com",
  "source": "explicit",
  "signed": false
}

签名时 URL 带 ?token= query:

json
{
  "port": 5173,
  "url": "http://sb-xxx-5173.preview.example.com/?token=eyJ...",
  "source": "explicit",
  "signed": true,
  "expires_at": "2026-05-24T15:04:05Z"
}

DELETE /v1/sandboxes//expose/{port}

取消显式暴露。注意:动态发现源(Spec 39)暴露的端口无法 unexpose,要关掉 端口需要 kill 持有该端口的进程。

需要 developer 角色

http
DELETE /v1/sandboxes/{id}/expose/{port}
Authorization: Bearer ask_...

响应

  • 204 No Content — 取消成功
  • 404 Not Found — 该端口没被显式 expose 过

GET /v1/sandboxes//expose

列出 sandbox 当前所有暴露的端口(显式 + 动态发现)。

需要 viewer 角色

http
GET /v1/sandboxes/{id}/expose
Authorization: Bearer ask_...

响应

200 OK

json
{
  "ports": [
    {
      "port": 5173,
      "url": "http://sb-xxx-5173.preview.example.com",
      "source": "explicit",
      "signed": false
    },
    {
      "port": 3000,
      "url": "http://sb-xxx-3000.preview.example.com",
      "source": "dynamic",
      "signed": false
    }
  ]
}
字段说明
sourceexplicit(通过 POST /expose 显式注册)或 dynamic(port-watcher sidecar 自动发现,Spec 39)
signed是否带签名 token

详见 端口暴露概念


Sandbox DTO 字段说明(响应)

字段类型说明
idstringsandbox ID(sbx_ + 24 hex)
statestring当前状态,见生命周期
imagestring镜像标签
resources.cpunumberCPU 核数
resources.memorystring内存上限(如 4GiB
resources.diskstring磁盘上限
resources.pids_limitint64PID 数量上限
timeoutstring空闲自动 pause(duration string)
ttlstring绝对生存时间(duration string)
networkstring网络策略别名(sealed / allowlist / open
envobject环境变量 dict(注入容器)
labelsobject自定义 KV 元数据(map[string]string);创建时写定,不可变,不注入容器;详见labels 详解
last_active_atint64最后活跃时间(Unix 秒)
created_atint64创建时间(Unix 秒)
secretsarray绑定的凭证(元数据,不含 value)

Signed Preview Token

Issue a short-lived token that lets anyone holding it access the preview proxy for a specific port — no account required.

See Signed Preview URL for the full guide.

POST /v1/sandboxes/{id}/preview-token

Requires developer or owner role

http
POST /v1/sandboxes/{id}/preview-token
Authorization: Bearer ask_...
Content-Type: application/json

{
  "port": 5173,
  "ttl_seconds": 3600
}

Request body

FieldTypeRequiredDescription
portintyesContainer port to authorise (1–65535)
ttl_secondsint64noToken lifetime in seconds (default 3600, max 86400)

Response — 201 Created

json
{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expires_at": "2026-05-24T15:04:05Z"
}

Use the token by appending ?token=<value> to the preview URL:

https://api.example.com/v1/sandboxes/sbx_xxx/preview/5173/?token=eyJ...

The token is stripped before forwarding to the upstream app and cannot be used on any other endpoint.

Error responses

CodeMeaning
400port out of range or request body invalid
401Not authenticated
403Insufficient role (viewer)
404Sandbox not found

基于 MIT License 发布