本文档统一收录 Onlyboxes 当前公开的全部 API。
- Console HTTP 基地址:
http://<console-host>:8089 - Console Worker gRPC 地址:
<console-host>:50051 - REST API 前缀:
/api/v1
Onlyboxes 有两套鉴权路径:
- 控制台会话(Cookie):用于管理类 API
- 访问令牌(Bearer Token):用于执行类 API 与 MCP
- Cookie 名称:
onlyboxes_console_session - 由
POST /api/v1/console/login创建 - 用于:
/api/v1/console/session/api/v1/console/logout/api/v1/console/password/api/v1/console/register/api/v1/console/accounts*/api/v1/console/tokens*/api/v1/workers*(按角色作用域)
- 会话有效期为 12 小时(内存态);console 重启后会话全部失效。
- 请求头格式:
Authorization: Bearer <access-token> - 用于:
/api/v1/commands/*/api/v1/tasks*/mcp
- 若系统中没有 token,所有 token 鉴权接口会返回
401。
- 内容类型:
application/json - 常见错误结构:
{ "error": "message" }- 时间字段使用 RFC3339。
- ID 字段均为不透明字符串(如
acc_*、tok_*、worker UUID、task ID)。
POST /api/v1/console/login
请求:
{
"username": "admin",
"password": "secret"
}成功 200:
{
"authenticated": true,
"account": {
"account_id": "acc_xxx",
"username": "admin",
"is_admin": true
},
"registration_enabled": false,
"console_version": "v0.0.0",
"console_repo_url": "https://..."
}错误:
400JSON 结构非法401用户名或密码错误500会话创建失败
GET /api/v1/console/session
- 需要控制台 Cookie 会话。
- 成功返回结构与登录响应一致。
错误:
401未登录
POST /api/v1/console/logout
- 清理 Cookie,并删除内存会话(若存在)。
响应:
204 No Content
POST /api/v1/console/register
请求:
{
"username": "dev-user",
"password": "strong-password"
}成功 201:
{
"account": {
"account_id": "acc_xxx",
"username": "dev-user",
"is_admin": false
},
"created_at": "2026-02-21T00:00:00Z",
"updated_at": "2026-02-21T00:00:00Z"
}校验与错误:
403注册开关关闭(CONSOLE_ENABLE_REGISTRATION=false)403当前账号不是管理员400用户名为空、用户名长度 > 64、或密码为空409用户名冲突(不区分大小写)500数据库或内部错误
POST /api/v1/console/password
请求:
{
"current_password": "old-password",
"new_password": "new-password"
}响应:
204修改成功400JSON 结构非法、current_password缺失或new_password缺失401当前密码错误500内部错误
说明:
- 该接口需要控制台会话鉴权。
- 密码更新后会轮换该账号的活动会话。
GET /api/v1/console/accounts?page=1&page_size=20
查询参数:
page:正整数,默认1page_size:正整数,默认20,最大100
成功 200:
{
"items": [
{
"account_id": "acc_xxx",
"username": "admin",
"is_admin": true,
"created_at": "2026-02-21T00:00:00Z",
"updated_at": "2026-02-21T00:00:00Z"
}
],
"total": 1,
"page": 1,
"page_size": 20
}错误:
400查询参数非法403当前账号不是管理员500数据库或内部错误
DELETE /api/v1/console/accounts/:account_id
响应:
204删除成功403禁止删除当前登录账号403禁止删除管理员账号404账号不存在500内部错误
Token 按账号隔离;每个账号只能管理自己的 token。
GET /api/v1/console/tokens
成功 200:
{
"items": [
{
"id": "tok_xxx",
"name": "default-token",
"token_masked": "obx_******abcd",
"created_at": "2026-02-21T00:00:00Z",
"updated_at": "2026-02-21T00:00:00Z"
}
],
"total": 1
}POST /api/v1/console/tokens
请求:
{
"name": "ci-prod",
"token": "optional-manual-token"
}约束:
name必填,trim 后长度 <= 64,同账号内大小写不敏感唯一token可选:- 省略时自动生成(
obx_<hex>) - 手动提供时:trim 后不能为空、不能含空白字符、长度 <= 256
- 省略时自动生成(
成功 201:
{
"id": "tok_xxx",
"name": "ci-prod",
"token": "obx_plaintext_or_manual",
"token_masked": "obx_******abcd",
"generated": true,
"created_at": "2026-02-21T00:00:00Z",
"updated_at": "2026-02-21T00:00:00Z"
}错误:
400参数校验失败409token 名称冲突或 token 值冲突500内部错误
DELETE /api/v1/console/tokens/:token_id
响应:
204删除成功404token 不存在(或不属于当前账号)
GET /api/v1/console/tokens/:token_id/value
固定返回 410 Gone:
{
"error": "token value is only returned at creation time; delete and recreate the token to obtain a new value"
}Worker 类型:
normal(对应worker-docker)worker-sys(对应worker-sys)
权限矩阵:
- 管理员:
- list/stats/inflight:查看全部
- delete:可删任意 worker
- create:可创建
normal与worker-sys
- 普通用户:
- list/stats/inflight:仅本人
worker-sys - delete:仅本人
worker-sys(其他目标返回404) - create:仅可创建
worker-sys,且每账号最多一个
- list/stats/inflight:仅本人
GET /api/v1/workers?page=1&page_size=20&status=all
查询参数:
page:正整数,默认1page_size:正整数,默认20,最大100status:all|online|offline,默认all
成功 200:
{
"items": [
{
"node_id": "worker-1",
"node_name": "node-a",
"executor_kind": "docker",
"capabilities": [
{ "name": "echo", "max_inflight": 4 }
],
"labels": {
"region": "us",
"obx.owner_id": "acc_xxx",
"obx.worker_type": "normal"
},
"version": "v0.1.0",
"status": "online",
"registered_at": "2026-02-21T00:00:00Z",
"last_seen_at": "2026-02-21T00:00:00Z"
}
],
"total": 1,
"page": 1,
"page_size": 20
}错误:
400查询参数非法
GET /api/v1/workers/stats?stale_after_sec=30
查询参数:
stale_after_sec:正整数,默认30
成功 200:
{
"total": 5,
"online": 4,
"offline": 1,
"stale": 1,
"stale_after_sec": 30,
"generated_at": "2026-02-21T00:00:00Z"
}说明:普通用户响应仅统计本人 worker-sys。
GET /api/v1/workers/inflight
成功 200:
{
"workers": [
{
"node_id": "worker-1",
"capabilities": [
{ "name": "pythonExec", "inflight": 1, "max_inflight": 4 }
]
}
],
"generated_at": "2026-02-21T00:00:00Z"
}说明:普通用户响应仅包含本人 worker-sys。
POST /api/v1/workers
请求体:
{
"type": "normal"
}规则:
type必填,取值normal|worker-sys- 仅管理员可创建
normal - 每个账号最多创建一个
worker-sys
成功 201:
{
"node_id": "2f51f8f9-77f2-4c1a-a4f5-2036fc9fcb9e",
"type": "normal",
"command": "WORKER_CONSOLE_GRPC_TARGET=127.0.0.1:50051 WORKER_ID=... WORKER_SECRET=... WORKER_HEARTBEAT_INTERVAL_SEC=5 WORKER_HEARTBEAT_JITTER_PCT=20 ./path-to-binary"
}说明:
WORKER_SECRET仅在该接口创建时返回一次。./path-to-binary为占位符,需要替换为实际 worker 启动命令。
错误:
400请求体不合法 /type非法403普通用户创建normal409当前账号已存在worker-sys503provisioning 不可用500创建失败
DELETE /api/v1/workers/:node_id
响应:
204删除成功404worker 不存在(普通用户删除越权目标也返回404)400缺少node_id503provisioning 不可用
GET /api/v1/workers/:node_id/startup-command
固定返回 410 Gone:
{
"error": "worker secret is returned only when creating the worker; delete and recreate to get a new startup command"
}POST /api/v1/commands/echo
请求:
{
"message": "hello",
"timeout_ms": 5000
}约束:
message必填,trim 后不能为空timeout_ms可选,范围1..60000,默认5000
成功 200:
{ "message": "hello" }错误:
400请求体错误 / message 缺失 / timeout 超范围429无可用并发容量503无在线 echo worker504超时502执行或内部异常
POST /api/v1/commands/terminal
请求:
{
"command": "pwd",
"session_id": "optional-session",
"create_if_missing": false,
"lease_ttl_sec": 60,
"timeout_ms": 60000,
"request_id": "optional-idempotency-key"
}约束:
command必填,trim 后不能为空timeout_ms可选,范围1..600000,默认60000request_id可选,幂等键(按账号隔离)
成功 200:
{
"session_id": "sess_xxx",
"created": true,
"stdout": "...",
"stderr": "...",
"exit_code": 0,
"stdout_truncated": false,
"stderr_truncated": false,
"lease_expires_unix_ms": 1770000000000
}错误:
400请求参数非法或invalid_payload404session_not_found409session_busy或任务被取消429无可用并发容量503无可用 worker504超时502其他执行失败
POST /api/v1/commands/computer-use
请求:
{
"command": "pwd",
"timeout_ms": 60000,
"request_id": "optional-idempotency-key"
}约束:
command必填,trim 后不能为空timeout_ms可选,范围1..600000,默认60000request_id可选,幂等键(按账号隔离)- 兼容旧客户端时,传入
lease_ttl_sec会被忽略 - 调度只会路由到调用账号自己的
worker-sys - 单账号并发固定为 1(
max_inflight=1)
成功 200:
{
"stdout": "...",
"stderr": "...",
"exit_code": 0,
"stdout_truncated": false,
"stderr_truncated": false
}错误:
400请求参数非法或invalid_payload409workersession_busy或任务被取消429无可用并发容量(no_capacity)503当前账号无在线worker-sys(no_worker)504超时502其他执行失败
Task 所有权按账号隔离(由 token 对应账号决定)。
POST /api/v1/tasks
请求:
{
"capability": "pythonExec",
"input": { "code": "print(1)" },
"mode": "auto",
"wait_ms": 1500,
"timeout_ms": 60000,
"request_id": "optional-idempotency-key"
}约束:
capability必填且非空input必须是合法 JSON(省略时默认为{})mode:sync|async|auto,默认autowait_ms:1..60000,默认1500timeout_ms:1..600000,默认60000request_id:可选幂等键(账号维度去重)
可能响应:
202任务未完成(包含status_url)200任务完成且成功409任务完成且被取消504任务完成且超时429任务完成失败且error.code=no_capacity503任务完成失败且error.code=no_worker502任务完成失败(其他错误码)
202 示例:
{
"task_id": "task_xxx",
"request_id": "req-1",
"command_id": "cmd_xxx",
"capability": "pythonexec",
"status": "running",
"created_at": "2026-02-21T00:00:00Z",
"updated_at": "2026-02-21T00:00:01Z",
"deadline_at": "2026-02-21T00:01:00Z",
"status_url": "/api/v1/tasks/task_xxx"
}已完成示例:
{
"task_id": "task_xxx",
"capability": "pythonexec",
"status": "succeeded",
"result": {
"output": "1\n",
"stderr": "",
"exit_code": 0
},
"created_at": "2026-02-21T00:00:00Z",
"updated_at": "2026-02-21T00:00:01Z",
"deadline_at": "2026-02-21T00:01:00Z",
"completed_at": "2026-02-21T00:00:01Z"
}任务错误字段:
"error": {
"code": "execution_failed",
"message": "..."
}提交阶段错误:
400参数/模式/时间范围/请求体非法409request_id 已在处理中429无可用并发容量503无匹配能力 worker504请求超时502提交失败
GET /api/v1/tasks/:task_id
响应:
200返回任务快照404任务不存在(包含跨账号访问)
POST /api/v1/tasks/:task_id/cancel
响应:
200取消成功(或已受理 best-effort 取消)404任务不存在(包含跨账号访问)409任务已终态(返回任务快照)500取消失败
端点:POST /mcp
- 传输:MCP Streamable HTTP
- 服务模式:无状态 JSON 响应
GET /mcp返回405,Allow: POST- 需要请求头:
Authorization: Bearer <access-token> - 建议请求头:
Content-Type: application/jsonAccept: application/json, text/event-stream
支持标准 MCP 调用流程,包括:
initializetools/listtools/call
所有工具参数 schema 都是 additionalProperties=false。
传入未定义参数会返回 JSON-RPC -32602 invalid params。
在
CONSOLE_HIDDEN_TOOLS中列出的工具不会出现在tools/list中;如果客户端已知工具名,仍可继续通过tools/call调用。
每个工具的
title/description、以及每个参数的description可以通过CONSOLE_MCP_TOOL_<TOOL>_TITLE、CONSOLE_MCP_TOOL_<TOOL>_DESCRIPTION、CONSOLE_MCP_TOOL_<TOOL>_PARAM_<PARAM>_DESCRIPTION在运行时覆盖。将参数描述设置为空字符串会把该参数从inputSchema.properties与required中移除(并把对应 schema 的additionalProperties翻转为true),但tools/call依然会接受该字段。完整的<TOOL>/<PARAM>映射详见 Console 配置文档。
输入:
{ "message": "hello", "timeout_ms": 5000 }message必填timeout_ms可选,1..60000,默认5000
输出:
{ "message": "hello" }输入:
{ "code": "print(1)", "timeout_ms": 60000 }code必填timeout_ms可选,1..600000,默认60000
输出:
{ "output": "1\n", "stderr": "", "exit_code": 0 }说明:exit_code 非 0 也按正常工具输出返回,不是协议错误。
输入:
{
"command": "pwd",
"session_id": "optional",
"create_if_missing": false,
"lease_ttl_sec": 60,
"timeout_ms": 60000
}command必填session_id可选create_if_missing可选,默认falselease_ttl_sec可选timeout_ms可选,1..600000,默认60000
输出:
{
"session_id": "sess_xxx",
"created": true,
"stdout": "...",
"stderr": "...",
"exit_code": 0,
"stdout_truncated": false,
"stderr_truncated": false,
"lease_expires_unix_ms": 1770000000000
}输入:
{
"command": "pwd",
"timeout_ms": 60000,
"request_id": "optional-idempotency-key"
}command必填timeout_ms可选,1..600000,默认60000request_id可选,幂等键(账号维度)- 只会路由到调用账号自己的
worker-sys - 不包含终端会话字段(
session_id、create_if_missing、created)
输出:
{
"stdout": "...",
"stderr": "...",
"exit_code": 0,
"stdout_truncated": false,
"stderr_truncated": false
}输入:
{ "session_id": "sess_xxx", "file_path": "/workspace/a.png", "timeout_ms": 60000 }session_id必填file_path必填timeout_ms可选,1..600000,默认60000- 当
session_id精确等于computerUse时,路由到调用账号自有worker-sys的readImagecapability - 其他
session_id仍路由到terminalResourcecapability
行为:
- 若目标 MIME 为
image/*:返回一个图片内容项。 - 若目标 MIME 非图片:返回一个文本内容项:
unsupported mime type: <mime>; expected image/*
- Token 缺失或无效:HTTP
401 - 参数校验失败:JSON-RPC
-32602 - 执行异常:作为 MCP tool error 内容返回(
isError=true)
服务定义:
service WorkerRegistryService {
rpc Connect(stream ConnectRequest) returns (stream ConnectResponse);
}Worker 建立双向流后,通常会发送:
ConnectRequest.hello(ConnectHello)- 周期性
ConnectRequest.heartbeat(HeartbeatFrame) - 对调度任务回传
ConnectRequest.command_result(CommandResult)
Console 回包:
ConnectResponse.connect_ack(ConnectAck)ConnectResponse.heartbeat_ack(HeartbeatAck)- 下发执行任务
ConnectResponse.command_dispatch(CommandDispatch)
ConnectHello包含 worker 标识、能力声明、labels、version、worker_secret。CommandDispatch包含:command_idcapabilitypayload_jsondeadline_unix_ms
CommandResult包含:command_id- 可选
error { code, message } payload_jsoncompleted_unix_ms
- 当前版本 console gRPC 不提供内建 TLS/mTLS。
worker-docker默认会拒绝不安全 console 端点,只有显式设置WORKER_CONSOLE_INSECURE=true才允许明文连接。worker-sys的computerUse在宿主机直接执行/bin/sh -lc,不提供容器隔离。worker-sys的readImage直接读取宿主机文件,且仅接受session_id=computerUse。worker-sys必须部署在独立主机并配合严格的操作系统权限控制。- 请将 console HTTP(
:8089)和 gRPC(:50051)端点放在反向代理/网关之后,并对外访问强制 TLS。 - 生产环境应将 gRPC 端口保持内网并通过隧道/链路加密。
- Token 明文与
WORKER_SECRET仅在创建时返回一次。 GET /api/v1/console/tokens/:token_id/value与GET /api/v1/workers/:node_id/startup-command设计为永久410 Gone。