内容
<p align="center">
<img src="assets/logo.svg" alt="boxkite" width="72" height="72">
</p>
<h1 align="center">boxkite</h1>
<p align="center">
<a href="LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-blue"></a>
<a href="https://github.com/EvAlssment/boxkite/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/EvAlssment/boxkite/actions/workflows/ci.yml/badge.svg"></a>
<a href="https://pypi.org/project/boxkite-sandbox/"><img alt="PyPI" src="https://img.shields.io/pypi/v/boxkite-sandbox?label=PyPI"></a>
<a href="https://www.npmjs.com/package/boxkite-client"><img alt="npm" src="https://img.shields.io/npm/v/boxkite-client?label=npm"></a>
</p>
**缺少的包含自托管沙盒,用于代理代码执行。**
大多数“代理沙盒”项目为您提供原始隔离——一个Pod、一个VM、一个容器——并让您在上面构建实际的工具表面,LLM代理需要这些工具。但是boxkite是另一半:一个完整的`bash`/`python`/file/search/process工具表面(15个即插即用的、框架无关的工具——LangChain、LangGraph、CrewAI、AutoGen或普通的OpenAI风格的函数调用,加上一个可选的git工具集,共计8个)运行在真正的Kubernetes Pod隔离中,基于行业标准的硬化实践构建——非root执行、所有Linux功能都已删除、可读的根文件系统、默认情况下网络出口被拒绝,输出在到达您的代理之前会清除泄漏的API密钥和云凭证。
boxkite本身处于早期阶段(v0.1.0)——隔离方法已经证明是有效的,但该项目的生产记录还不完善。然而,您可以自托管它,将您的代理框架指向它,并在几分钟内拥有一个真正的沙盒,而不是几周。
您可以自托管整个项目——沙盒运行时、托管API控制平面、所有四个客户端SDK和MCP服务器——端到端;这个产品没有单独的、封闭的托管产品。
**谁适合使用boxkite:** boxkite是一个自托管沙盒,适用于构建自己的代理产品的团队,他们需要隔离的、多租户的代码执行,适用于大规模场景——每个会话一个Kubernetes Pod,许多会话,许多租户。它**不是**像IDE或CLI编码代理中的内置`bash`工具那样的单用户本地开发会话沙盒——如果您只需要自己的编码助手在自己的机器上运行shell命令,boxkite是错误的层;它是为**您**是运行其他人使用的代理产品背后的沙盒基础设施的场景而设计的。
## 目录
- [为什么存在boxkite](#为什么存在boxkite)
- [boxkite中实际包含的内容](#boxkite中实际包含的内容)
- [隔离模型](#隔离模型)
- [安全性](#安全性)
- [自托管](#自托管)
- [boxkite CLI](#boxkite-CLI)
- [已发布的软件包和镜像](#已发布的软件包和镜像)
- [手册/示例](#手册示例)
- [扩展它:AuditSink和SessionMetadataStore](#扩展它auditsink和sessionmetadatastore)
- [未包含的内容](#未包含的内容)
- [许可证](#许可证)
- [贡献](#贡献)
## 为什么存在boxkite
原始的每个会话隔离已经不再是空白——[`kubernetes-sigs/agent-sandbox`](https://github.com/kubernetes-sigs/agent-sandbox)(一个Kubernetes SIG-Apps项目)已经很好地完成了这项工作,GKE的托管版本比这个项目的预热池声明更快。但是boxkite **不是**该原语的竞争对手——它旨在与之兼容并在其基础上运行,而boxkite风格的工具层运行在`agent-sandbox`的Pod生命周期之上是一个合理的未来发展方向。
boxkite填补了原始隔离之上的空白,即目前没有其他产品以完整、可自托管的软件包提供:
- **Daytona** 在我们看来是最可靠的开源自托管替代品,但据我们所知,其核心已经私有化,其开源仓库不再积极维护——请直接在Daytona的GitHub页面和网站上验证当前的许可和仓库活动状态,而不是依赖此信息。
- **LangChain自己的原生沙盒集成** 看起来已经存档,用户被重定向到仅限托管的服务——请直接检查LangChain的仓库/变更日志以获取当前状态,而不是将其视为永久事实。
- **E2B、Modal、Riza** 等类似产品优秀,但主要托管——自托管不是他们的产品重点。
boxkite的定位很窄也很具体:**如果您想要整个bash/python/file工具表面,加固并连接到真正的K8s Pod,完全在您控制的基础设施上运行 - 那么这是起点,而不是新型隔离原语。**
| | boxkite | Daytona | E2B | Modal |
|---|---|---|---|---|
| 可自托管 | 是 | 否 - 核心据报道在2026年中期私有化,OSS仓库认为不维护(验证当前状态) | 否 - 主要托管 | 否 - 主要托管 |
| 包含电池、框架无关的代理工具表面(bash/python/file工具,无需LangChain依赖) | 是 | 不是产品重点 | 不是产品重点 | 不是产品重点 |
| 隔离原语 | Kubernetes Pod(每个会话一个) | 不维护为OSS | 自己的托管沙盒运行时[^隔离] | 自己的托管沙盒运行时[^隔离] |
| 开源 | 是 - MIT(参见[许可证](#许可证)) | 不再 - 核心私有化 | 否 | 否 |
[^隔离]:E2B和Modal不公开他们的确切隔离原语作为此项目文档的一部分 - 请参阅每个供应商的网站以获取详细信息。 这里的比较是关于自托管和工具表面完整性,而不是隔离技术本身。
## boxkite中实际包含的内容
这是一个包含多个独立版本组件的仓库,它们被故意保留在一起(参见[CONTRIBUTING.md](CONTRIBUTING.md)以了解原因),而不是跨仓库拆分。 如果您只想自托管沙盒,**核心**部分下面是您需要的整个表面 - 其他部分都是可选的,大部分存在是为了与您自己运行的控制平面进行通信,而不是直接嵌入库。
**核心 - 直接将沙盒嵌入到您自己的Kubernetes集群中:**
- **`src/boxkite/`** - Python包(`pip install boxkite-sandbox`):`SandboxManager`(K8s Pod生命周期 + HTTP路由到边车),`WarmPoolManager`(预热的Pod池,以便会话启动是一个声明,而不是冷启动),`LazySandboxRuntime`(延迟Pod创建,直到实际需要工具调用),以及`boxkite.tools` - 15个框架无关的工具(LangChain `BaseTool`,LlamaIndex `FunctionTool`,OpenAI风格的函数调用模式,或CrewAI/AutoGen/其他任何东西的普通可调用函数 - 参见`boxkite.tools.adapters`):`bash_tool`,`python_interpreter`,`file_create`,`view`,`str_replace`,`present_files`,`ls`,`glob`,`grep`,`start_process`,`get_process_output`,`send_process_input`,`stop_process`,`list_processes`,`watch_directory`(一个长轮询文件系统监视器)。 测试由根`tests/`执行。 还有几个可选的工具集,每个工具集都位于其自己的标志后面,并在该工具模块的文档字符串中记录:一个git工具集(`git_clone`/`git_status`/`git_add`/`git_commit`/`git_push`/`git_pull`/`git_branch`/`git_checkout`),一个持久的`node_interpreter`(Node.js对应于`python_interpreter`),`run_tests`,浏览器自动化(`browser_navigate`/`browser_exec`/`browser_screenshot`/`browser_close`,此仓库中风险最高的工具,因为它需要广泛的非枚举HTTPS/DNS出口),以及LSP完成(`lsp_start`/`lsp_completion`/`lsp_stop`,一个持久的`pyright`/`typescript-language-server`进程,每个会话一个)。
- **`sidecar/main.py`** - FastAPI服务,与沙盒容器在每个Pod中一起运行。 它拥有实际的文件系统I/O,通过`nsenter`将代理命令运行到沙盒的命名空间中(降级到非root UID执行),并将文件同步到S3或Azure Blob存储。
- **`deploy/`** - Kubernetes清单(`pod-template.yaml`,`rbac.yaml`,`network-policy.yaml`),容器镜像(`sandbox.Dockerfile`,`sidecar.Dockerfile`,`control-plane/Dockerfile`),用于本地开发的`docker-compose.yml`,以及用于笔记本电脑上的真实(尽管小)Kubernetes开发循环的`deploy/local-kind/`。
**可选 - 运行或与您自己托管的控制平面通信,而不是直接嵌入`SandboxManager`:**
- **`control-plane/`** - 一个单独的FastAPI服务(自己的`pyproject.toml`,自己的`src/control_plane`,自己的`tests/`),它在`SandboxManager`前面提供了注册/API密钥身份验证,多租户帐户和公平使用速率限制。 仅在您想要托管API表面而不是直接嵌入库时需要。 可选的身份验证表面,默认情况下所有这些都不启用,等待操作员启用:GitHub/Google社交登录,MCP OAuth 2.1授权服务器(带有RFC 7591动态客户端注册)用于传入MCP客户端,以及通过托管代理的企业SAML/OIDC SSO。
- **`sdk-python/`**(PyPI上的`boxkite-client`),**`sdk-js/`**(npm上的`boxkite-client`),**`sdk-go/`**(`github.com/EvAlssment/boxkite/sdk-go`),以及**`sdk-rust/`** - 您的运行控制平面的薄HTTP客户端,而不是`src/boxkite`本身。 参见每个SDK自己的`README.md`以了解已实现的内容。
- **`mcp-server/`**(`boxkite-mcp`)- 将`sdk-python`包装为MCP工具源,用于指向Claude Code,Claude Desktop,Cursor或其他MCP客户端到您运行的控制平面。
- **`bastion/`** - 一个独立的SSH服务器(自己的`pyproject.toml`,自己的`tests/`),允许`ssh <session_id>@host`使用接管令牌作为SSH密码,并直接桥接到与控制平面公开的相同人类接管WebSocket。
**其他所有内容:**
- **`examples/`** - 可运行的 cookbook(LangGraph,LangChain,原始HTTP,自托管控制平面演练)。
- **`scripts/`** - 一次性维护/基准测试脚本,不是任何已发布的软件包的一部分。
- **`tests/`** - 特定于根`src/boxkite`包的测试;`control-plane/`,`sdk-python/`,`sdk-js/`,`sdk-go/`和`sdk-rust/`每个都有自己的测试,与其自己的源代码一起运行,独立运行(参见[CONTRIBUTING.md](CONTRIBUTING.md))。
## 隔离模型
每个会话都是一个具有两个共享卷的Kubernetes Pod:
| | `sandbox` 容器 | `sidecar` 容器 |
|---|---|---|
| 运行 | 代理生成的代码 | HTTP API + 存储同步 |
| 用户 | 非root(UID 1001),`runAsNonRoot: true` | Root(需要 `nsenter`)|
| 功能 | 所有功能都已删除(`capabilities.drop: [ALL]`) | 仅 `SYS_PTRACE`/`SYS_ADMIN` 需要 `nsenter` - 代理代码永远不会继承 |
| 文件系统 | 只读根文件系统 | 读写(拥有共享卷)|
| 网络 | 默认情况下被拒绝(`NetworkPolicy` + 每个 `exec` 的新网络命名空间) | 仅存储同步需要 |
| 特权提升 | `allowPrivilegeEscalation: false` | 相同 |
边车通过`nsenter --setuid --setgid`在执行任何代理命令之前降低到沙盒的UID/GID - 边车执行`nsenter`所需的功能提升永远不会对代理代码可用。 命令输出会在返回调用代理之前清除AWS/Azure密钥,JWT和通用密钥形状字符串(`src/boxkite/tools/bash_tool.py`)。 敏感的主机路径(`~/.ssh`,`~/.aws`,`~/.kube`,Docker套接字)位于沙盒容器无法看到的每个挂载之外。
参见`deploy/network-policy.yaml`和`deploy/rbac.yaml`以获取实际的清单 - 它们被编写为真实的起点,并带有内联注释,您可以根据集群进行修改,而不是说明性的片段。
## 安全性
boxkite执行任意的、代理生成的代码,因此其安全立场是深度防御 - 单独的控制措施不足以保证安全。
- **边车HTTP API身份验证。** 边车(`sidecar/main.py`)除了每个Pod共享的秘密(`SIDECAR_AUTH_TOKEN`,作为`X-Sidecar-Auth-Token`标头发送到每个路由,除了`/health`)之外,没有自己的身份验证。 `SandboxManager`/`WarmPoolManager`(`src/boxkite/sidecar_auth.py`)在Pod创建时为每个Pod生成新的秘密,并将其存储在专用的Kubernetes Secret中(通过`secretKeyRef`引用到Pod的env var中,而不是字面值),因此任何管理器进程都可以通过单独的`secrets: get` RBAC授予恢复它。 边车关闭时失败保护:如果`SIDECAR_AUTH_TOKEN`未设置,所有受保护的路由都返回503而不是静默运行未经身份验证。
- **每次执行网络隔离。** Kubernetes运行时模式下的每个`/exec`调用都在一个全新的、空的网络命名空间中运行(`unshare -n`执行`nsenter` - 参见`build_k8s_exec_command`中的`sidecar/main.py`),由`SANDBOX_EXEC_NETWORK_ISOLATION_ENABLED`控制(默认为`true`)。 这是代理执行命令的实际网络隔离边界 - 而不是单独的`deploy/network-policy.yaml`。 如果您将此标志设置为`false`,则`NetworkPolicy`成为唯一的后备措施,因此请确保它真正具有限制性(参见下一个要点),然后再进行操作。
- **NetworkPolicy出口必须限定为实际的存储端点。** `deploy/network-policy.yaml`配备了一个占位符,您必须用它填充(集群存储通过Pod/命名空间选择器,或云提供商的存储CIDR/FQDN) - 在应用它之前请阅读文件中的注释。 一个单独的显式命名为`sandbox-network-policy-permissive`的策略 - 保存在它自己的文件中,`deploy/network-policy-permissive-optin.yaml`,专门用于自托管者,他们想要完全开放的出口 - 它永远不会默认应用,并且不能分层在作用于scoped策略之上(重叠的NetworkPolicy选择器是累加的,因此应用两者会无声地重新引入广泛开放的行为)。
- **边车在Pod的整个生命周期中持有`CAP_SYS_ADMIN`。** 基于nsenter的设计需要它(`setns()`需要`CAP_SYS_ADMIN`,不仅仅是`CAP_SYS_PTRACE`) - 参见`deploy/pod-template.yaml`和`src/boxkite/manager.py`中的安全注释。 这是一个接近root的功能:边车自己的FastAPI请求处理的未来RCE类错误将相当于Pod内的完整`CAP_SYS_ADMIN`root - 这就是为什么上述两个层(边车身份验证,NetworkPolicy)很重要 - 它们是“可达”和“可利用”之间的隔离。
**已知、目前未缓解的漏洞**(而是您在部署前应该知道的漏洞):两个容器镜像(`sandbox.Dockerfile`/`sidecar.Dockerfile`)下载了一些上游依赖项,而没有校验和验证;docker-compose本地开发模式将主机的Docker套接字绑定到边车容器中(参见“自托管”中的关键警告 - 这在Kubernetes运行时中不存在);命令名称允许列表是对意外命令的防护措施,而不是沙盒逃脱边界。 参见[SECURITY.md](SECURITY.md)以获取完整的漏洞报告流程。 边车与沙盒的流量现在默认使用TLS(每个Pod的新、短寿命、自签名证书,边车验证 - 参见`src/boxkite/tls.py`中的逃脱口)。 ## 自托管
boxkite仅支持自托管 - 此仓库中的所有内容,包括`control-plane/`托管的多租户API(帐户、API密钥、身份验证的沙盒执行)都是您自己部署的内容。 本节中的所有内容都在您控制的基础设施上运行,无论是笔记本上的docker-compose还是真正的Kubernetes集群。
### 快速开始:docker-compose,无需Kubernetes
> **在运行此命令之前:docker-compose模式仅适用于单个开发人员本地开发,绝不能用于生产或多租户环境,原因是存在一个真实且目前尚未缓解的**关键安全漏洞**,不是例行安全警告。** `deploy/docker-compose.yml` 将主机的 `/var/run/docker.sock` 绑定挂载到边车容器中,以便其可以执行 `docker exec` 进入沙箱容器。任何具有该套接字的活动连接的人都可以轻松提升权限以完全**主机根目录**权限(例如 `docker run --privileged -v /:/host ...`)- 这已经直接验证过,包括标准的 `docker-socket-proxy` 缓解措施**不**关闭它。 沙箱代码和该套接字之间的唯一隔离是 `SIDECAR_AUTH_TOKEN` 控制边车自己的HTTP API;任何未来的身份验证绕过、路径遍历或命令注入漏洞都可以直接升级为主机权限泄露,而不仅仅是容器权限泄露。 边车本身在检测到挂载的套接字时会记录一个响亮的启动警告,作为最后的提醒。**这在Kubernetes运行时中根本不存在**(没有docker套接字,没有docker-in-docker)- 如果您想要真正的隔离模型从一开始,或者需要多于单个本地开发人员,请跳到 [快速开始:使用kind的真实Kubernetes](#快速开始使用kind的真实Kubernetes)。
纯本地迭代最快捷的方式是使用 `boxkite` CLI - `pip install boxkite-sandbox`(在PyPI上发布)或从此存储库的克隆中使用 `pip install -e .` 如果您想对其进行开发 - 请参见下面的“`boxkite` CLI”以获取完整的命令参考。
> **注意:** PyPI软件包名称为 `boxkite-sandbox`,而不是 `boxkite` - 普通的 `boxkite` 名称已经在PyPI上被占用。 这仅影响 `pip install` 命令; Python导入路径保持不变(`import boxkite`),`boxkite` CLI命令也是如此。 与 `beautifulsoup4` (PyPI名称)vs. `bs4` (导入名称)- `pip install boxkite` 将失败或安装无关的软件包,因此请确保安装 `boxkite-sandbox`。
```bash
git clone https://github.com/EvAlssment/boxkite.git boxkite && cd boxkite
# 不是“pip install boxkite” - 那是不同的、与此无关的PyPI软件包。
pip install -e .
boxkite up
boxkite exec "python3 -c 'print(1 + 1)'"
boxkite files create hello.txt --content "hello from boxkite\n"
boxkite files view hello.txt
```
> **首次运行构建时间:** `boxkite up` 始终从 `deploy/sandbox.Dockerfile` 本地构建 `sandbox` 镜像,而不是拉取 `ghcr.io/evalssment/boxkite-sandbox` - 这包括下载固定版本的Chrome-for-Testing构建(~300MB跨浏览器和无头shell zip)加上FFmpeg。 在具有暖Docker缓存的快速连接上,这通常需要几分钟,而不是几秒钟 - `docker ps` 将显示在构建完成之前没有正在运行的容器。 后续运行重用缓存的镜像层并在几秒钟内启动。 如果您想直接在自己的compose覆盖文件中引用它们,则在GHCR上存在已发布的镜像(请参见下面的“已发布的软件包和镜像”)。
`boxkite up` 构建并启动 `sandbox` 容器、`sidecar` HTTP API和用于S3兼容存储的本地MinIO - 生成一个新的 `SIDECAR_AUTH_TOKEN` 并将其存储在 `~/.boxkite/local.env` 中,以便 `boxkite exec`/`boxkite files` 自动拾取。 不再需要手动导出 `SIDECAR_AUTH_TOKEN`。
<details>
<summary>或者手动执行,无需CLI(原始HTTP合约)</summary>
```bash
git clone https://github.com/EvAlssment/boxkite.git boxkite && cd boxkite
cp .env.example .env
# 为边车的HTTP API生成一个秘密并将其放入.env - 有关为什么需要此操作,请参见上面的“安全”部分。
echo "SIDECAR_AUTH_TOKEN=$(openssl rand -hex 32)" >> .env
docker compose -f deploy/docker-compose.yml up -d --build
```
这将构建并启动 `sandbox` 容器、`sidecar` HTTP API和用于S3兼容存储的本地MinIO。 检查它是否健康:
```bash
curl http://localhost:8080/health
```
直接在此基础上进行操作,然后再连接代理框架(除 `/health` 之外的每个路由都需要 `X-Sidecar-Auth-Token` 头):
```bash
export SIDECAR_AUTH_TOKEN=$(grep ^SIDECAR_AUTH_TOKEN= .env | cut -d= -f2)
curl -X POST http://localhost:8080/exec \
-H "Content-Type: application/json" -H "X-Sidecar-Auth-Token: $SIDECAR_AUTH_TOKEN" \
-d '{"command": "python3 -c \"print(1 + 1)\""}'
curl -X POST http://localhost:8080/file-create \
-H "Content-Type: application/json" -H "X-Sidecar-Auth-Token: $SIDECAR_AUTH_TOKEN" \
-d '{"path": "hello.txt", "content": "hello from boxkite\n"}'
curl -X POST http://localhost:8080/view \
-H "Content-Type: application/json" -H "X-Sidecar-Auth-Token: $SIDECAR_AUTH_TOKEN" \
-d '{"path": "hello.txt"}'
```
</details>
然后,从Python开始,将沙箱工具连接到代理。 `SandboxManager` 从环境中获取compose与Kubernetes模式,因此请指向您刚刚启动的边车(使用与 `boxkite up` 生成的相同令牌,或手动放入 `.env`):
```bash
export RUNTIME_MODE=compose SIDECAR_URL=http://localhost:8080
# 如果您使用了 `boxkite up`,则令牌在 ~/.boxkite/local.env 中:
export SIDECAR_AUTH_TOKEN=$(grep ^SIDECAR_AUTH_TOKEN= ~/.boxkite/local.env | cut -d= -f2)
# 如果您手动启动了 compose,则它在 .env 中:
# export SIDECAR_AUTH_TOKEN=$(grep ^SIDECAR_AUTH_TOKEN= .env | cut -d= -f2)
```
boxkite的工具表面(`bash_tool`、`python_interpreter`、`file_create`、`view`、`str_replace`、`present_files`、`ls`、`glob`、`grep`、`start_process`、`get_process_output`、`send_process_input`、`stop_process`、`list_processes`,以及可选工具集)默认情况下是框架无关的 - `create_sandbox_tool_specs()` 返回纯 `ToolSpec`(一个名称、描述、JSON模式参数和一个普通的异步可调用函数),根本没有LangChain/LangGraph依赖:
```python
from uuid import uuid4
from boxkite import SandboxManager
from boxkite.tools import create_sandbox_tool_specs
manager = SandboxManager()
session_id = str(uuid4())
await manager.create_session(organization_id=uuid4(), session_id=session_id)
specs = create_sandbox_tool_specs(sandbox_manager=manager, session_id=session_id)
# specs = [ToolSpec(name="bash_tool", ...), ToolSpec(name="file_create", ...), ...]
bash_tool = next(s for s in specs if s.name == "bash_tool")
result = await bash_tool.handler(command="echo hello from boxkite")
# 直接调用 handler(**kwargs),将规格连接到 CrewAI/AutoGen/手动工具调用循环,或使用适配器转换整个列表:
# boxkite.tools.adapters.to_openai_functions(specs) -- OpenAI 风格的函数调用模式,仅限标准库,无额外依赖。
```
喜欢LangChain或LangGraph? `boxkite.tools.adapters.to_langchain_tools` 将相同的规格转换为LangChain `BaseTool` 对象(需要 `langchain` 额外依赖:`pip install boxkite-sandbox[langchain]`)- 或者使用向后兼容的 `create_sandbox_tools()`,它在内部执行相同的操作:
```python
from boxkite.tools import create_sandbox_tools
tools = create_sandbox_tools(sandbox_manager=manager, session_id=session_id)
# tools = [bash_tool, python_interpreter, file_create, view, str_replace,
# present_files, ls, glob, grep, start_process, get_process_output,
# send_process_input, stop_process, list_processes]
# 将这些交给任何LangChain/LangGraph代理。传入 enable_git_tools=True 还可以额外包含可选的git工具集。
```
喜欢LlamaIndex? `boxkite.tools.adapters.to_llamaindex_tools` 将相同的规格转换为LlamaIndex `FunctionTool` 对象(需要 `llamaindex` 额外依赖:`pip install boxkite-sandbox[llamaindex]`),可以直接交给 `ReActAgent`/`FunctionAgent` - 参见 `examples/llamaindex_agent/`。
**在一个地方查看完整的集成表面:**
| 框架/提供者 | 如何 |
|---|---|
| LangChain / LangGraph | `to_langchain_tools()` (`langchain` 额外依赖) |
| LlamaIndex | `to_llamaindex_tools()` (`llamaindex` 额外依赖) - 参见 `examples/llamaindex_agent/` |
| OpenAI Agents SDK | `to_openai_agents_tools()` (`openai-agents` 额外依赖) - 参见 `examples/openai_agents_sdk/` |
| CrewAI、AutoGen/AG2、手动循环 | 普通 `ToolSpec.handler(**kwargs)` - 无需适配器 |
| OpenAI 函数调用 | `to_openai_functions()` - 标准库,无 `openai` 依赖 - 参见 `examples/openai_function_calling/` |
| Anthropic 工具使用 | 与上述OpenAI相同的 `ToolSpec.parameters`/`handler` 形状 |
| Google Gemini | 相同的形状,解包为Gemini的 `FunctionDeclaration` - 参见 `examples/gemini_function_calling/` |
| Mistral | 相同的形状,Mistral自己的工具调用API - 参见 `examples/mistral_function_calling/` |
| Groq | 相同的形状,真正兼容OpenAI - 参见 `examples/groq_function_calling/` |
| Vercel AI SDK(JS) | `sdk-js`的 `createSandboxTools()` 来自 `boxkite-client/vercel-ai` (`ai` v5 额外依赖) |
| MCP(Claude Desktop,Claude Code,Cursor) | `mcp-server/` (`boxkite-mcp`) - 一个独立的MCP服务器,通过您运行的控制平面,零自定义集成代码 |
| Claude Code CLI、无头、沙箱内 | `examples/claude_code_sandbox/`,或 `examples/claude_code_declarative_builder/` 用于控制平面API路径,而不是手动维护的Dockerfile |
在 `examples/` 中查看上述每行的可运行版本。
### 快速开始:使用kind的真实Kubernetes
```bash
./deploy/local-kind/setup.sh
kubectl proxy --context kind-boxkite-dev --reject-paths='' &
export RUNTIME_MODE=k8s SANDBOX_USE_K8S_PROXY=true \
SANDBOX_IMAGE=boxkite-sandbox:local SIDECAR_IMAGE=boxkite-sidecar:local
```
参见 [`deploy/local-kind/README.md`](deploy/local-kind/README.md) 以获取完整的演练、验证步骤和拆除。
> **已知限制 - Apple Silicon / arm64:** `setup.sh` 在本地构建 `boxkite-sandbox` 镜像,并且该构建在arm64主机上故意失败(包括Apple Silicon Macs)。 这是一个刻意的**安全控制**,不是错误:沙箱的捆绑Chromium被替换为固定版本的Chrome-for-Testing构建,以清除已知漏洞扫描器发现的问题,并且Chrome for Testing不为任何版本的Linux arm64构建发布 - 因此没有可以修复此问题的固定版本,并且静默回落到旧的捆绑Chromium将重新引入漏洞修复程序存在的漏洞。 参见 [`deploy/local-kind/README.md`](deploy/local-kind/README.md)的“已知限制”以获取完整的解释和解决方法(在amd64 CI运行器/云主机上构建并推送到注册表,或在实际amd64机器上开发)。
对于真实的(非kind)集群,请应用 `deploy/rbac.yaml`、`deploy/network-policy.yaml` 和 `deploy/pod-security-policy.yaml`(首先编辑命名空间和服务帐户引用),从 `deploy/` 构建并推送两个镜像,并将 `SANDBOX_IMAGE`/`SIDECAR_IMAGE` 指向您的注册表。 `deploy/pod-security-policy.yaml` 是 `deploy/rbac.yaml` 自身公开限制的最终阻止措施(RBAC无法将pod/secret动词范围限定到仅标记为sandbox的资源)- 即使您遵循 `deploy/rbac.yaml` 建议的专用命名空间缓解措施,也请应用它,因为该缓解措施是一种部署约定,而不是Kubernetes本身强制执行的。 `deploy/helm/boxkite/` 将这三个清单(以及可选的镜像构建器RBAC/NetworkPolicy)打包为Helm图表,如果您想使用 `helm install` 而不是手动应用每个文件,请参见该目录的README。
**预构建镜像:** 自 `v0.1.0` 起,这些镜像发布到GHCR:
- `ghcr.io/evalssment/boxkite-sandbox`
- `ghcr.io/evalssment/boxkite-sidecar`
- `ghcr.io/evalssment/boxkite-control-plane`
从 `deploy/`(和 `control-plane/Dockerfile` 用于控制平面镜像)自行构建它们仍然有效,并且与您工作树中的内容保持同步,无论是固定镜像还是其他。
### 一键部署:Render上的控制平面API
[](https://render.com/deploy?repo=https://github.com/EvAlssment/boxkite)
`deploy/render.yaml` 是一个 [Render Blueprint](https://render.com/docs/blueprint-spec),它将 `control-plane/Dockerfile` 部署为一个独立的Web服务和一个托管的Postgres数据库 - 与该项目自己的Cloud Run部署使用的拓扑结构相同,即控制平面通过kubeconfig与Kubernetes通信,而不是直接运行Docker。
**这不会部署沙箱/边车执行堆栈** - `deploy/docker-compose.yml` 的边车容器需要主机的Docker套接字(一个记录的 host-root-escape 原语,仅限本地开发 - 参见上面的警告),而Render的平台根本不向服务暴露Docker套接字。因此,该堆栈不是此按钮的一部分。单击它后,控制平面是可访问的,但如果您将其指向真正的Kubernetes集群(运行 `deploy/helm/boxkite/` 或手动应用 `deploy/rbac.yaml`/`deploy/network-policy.yaml`),则无法创建沙箱 - 参见 `deploy/render.yaml` 自己的头部注释中的完整演练。
该Blueprint尚未针对实时Render帐户进行测试(编写时无可用帐户)- 它直接针对Render发布的Blueprint模式编写,并针对Render自己的JSON模式进行验证,与“已实施针对真实形状,但尚未针对实时服务进行测试”的公开披露相同(参见 `src/boxkite/resource_config.py` 的Kata Containers/GPU支持注释)。Railway和Fly.io等效项被推迟,未尝试 - Railway的一键式流程需要维护人员端的模板注册,而Fly.io没有静态配置部署按钮的等效项来翻译此Blueprint。
## `boxkite` CLI
上面的 `boxkite up`/`boxkite exec` 是CLI的两个命令(也是:`config`、`session`、`files`、`keys`、`whoami`、`log`、`watch`、`allowlist`、`signup`、`audit`)。CLI在两个模式下工作,根据配置情况自动检测:
- **本地模式** - `boxkite up` 之外的任何配置。每个命令都直接与 `boxkite up` 启动的单个docker-compose边车通信,使用它写入 `~/.boxkite/local.env` 的令牌。
- **托管模式** - 一旦 `boxkite config set-url`/`set-key`(或 `boxkite signup`)将控制平面URL和API密钥存储在 `~/.boxkite/config.toml` 中,每个命令切换到调用该 `control-plane/` API,身份验证为 `Authorization: Bearer <api-key>`。该控制平面是您自己部署的 - `boxkite signup` 与您指向的URL通信。
```bash
# 本地模式
boxkite up # 启动docker-compose(沙箱 + 边车 + MinIO)
boxkite exec "ls /workspace" # 对本地边车运行
boxkite files view hello.txt
boxkite files create notes.txt --content "hi"
boxkite files edit notes.txt --old "hi" --new "hello"
# 托管模式(针对您部署的控制平面)
boxkite signup # 注册 -> 登录令牌 -> 创建API密钥,一步到位
# 或:boxkite config set-url https://your-control-plane.example.com
# 配置密钥
`boxkite config set-key bxk_live_...`
# 会话管理
`boxkite session create --label "演示"` # 默认 size=small,一个沙盒
`boxkite session create --label "构建" --size medium \
--storage-gb 20 --lifetime-minutes 120 --count 3 # 可选覆盖,所有公平使用有界定
`boxkite session ls`
`boxkite exec "python3 -c 'print(1 + 1)'"` # 自动选择会话,如果只有一个活动会话
`boxkite exec "ls /workspace" --session <id>` # 或显式选择一个
`boxkite session rm <id>`
`boxkite log` # 分页执行/文件操作审计历史记录,适用于活动会话
`boxkite log --limit 20 --offset 0 --session <id>`
`boxkite watch` # 新执行/文件操作条目的实时馈送;阻塞直到 Ctrl-C
`boxkite allowlist get` # 默认情况下,对每个帐户无限制
`boxkite allowlist set ./allowed-commands.json`
`boxkite allowlist clear` # 恢复无限制
`--size` 是 CPU/内存预设(默认为 `small`,或 `medium`/`large`);
`--storage-gb` 和 `--lifetime-minutes` 重写沙盒的卷大小
以及它在回收者将其销毁之前保持活动的时间;`--count`
一次调用创建一批沙盒(默认为 `1`)。所有四个都是
可选的,并受帐户上的公平使用上限限制,绝不付费升级。
`boxkite allowlist`(仅托管模式)允许帐户在每次未来的 `boxkite exec` 调用中坚持一个选择加入
命令允许列表:`get` 显示当前规则(空表示无限制,默认为每个
帐户),`set <path-to-json-file>` 从 JSON
数组的纯命令名称字符串或 `{command, args_allow?, args_deny?}`
对象中全部替换它们,而 `clear` 恢复无限制的默认值。这是一个防护栏
防止意外/意外的命令,**不是** 沙盒逃逸边界 —
允许通用解释器(`python3`、`bash`、`node`)通过
允许列表仍然允许任意代码在启动后运行;沙盒
pod 的自身隔离(参见上面的“隔离模型”)实际上是
约束该代码可以执行的操作。
`boxkite log`/`boxkite watch`(仅托管模式)读取审计跟踪
SDK 的 `get_log`/`watch` 方法调用的路由。像 `exec`/`files`一样,两者都
自动选择活动会话,如果恰好存在一个,或采用显式
`--session`。
`boxkite audit verify --db <path-to-audit.db> [--session <id>]` 重新计算
一个自托管 `HashChainedSQLiteAuditSink`
数据库文件上的防篡改哈希链,并报告它是否完整,或者它
在哪个行开始中断 - 只读,对实时使用中的文件有效。这是自托管
对应于 `control-plane/scripts/verify_exec_log_chain.py`,
后者对托管控制平面的 Postgres/SQLite 支持的 `exec_log_entries`
表执行相同的操作。
托管模式按会话范围限定,因为控制平面管理多个
每个帐户的并发沙盒;本地模式故意 *不* — 一个
`boxkite up` 堆栈没有自己的会话概念,因此 `boxkite
session *`/`boxkite log`/`boxkite watch` 拒绝在此运行并解释
而不是发明本地不存在的会话管理。运行 `boxkite --help`(或任何子命令的 `--help`)以获取完整
参考。
## 已发布软件包和镜像
PyPI/npm 软件包为 `v0.1.1`;GHCR 镜像仍为 `v0.1.0`,待此仓库的新命名空间下重新构建(见下文)。
| 软件包/镜像 | 注册表 | 安装/拉取 |
|---|---|---|
| `boxkite-sandbox`(此仓库的根软件包 — `import boxkite`) | [PyPI](https://pypi.org/project/boxkite-sandbox/) | `pip install boxkite-sandbox` |
| `boxkite-client`(`sdk-python/` — 用于您托管的控制平面的 Python 客户端) | [PyPI](https://pypi.org/project/boxkite-client/) | `pip install boxkite-client` |
| `boxkite-mcp`(`mcp-server/` — 用于您托管的控制平面的 MCP 服务器) | [PyPI](https://pypi.org/project/boxkite-mcp/) | `pip install boxkite-mcp`(或 `pipx install boxkite-mcp` 以将其作为独立 MCP 服务器运行) |
| `boxkite-client`(`sdk-js/` — 用于您托管的控制平面的 TypeScript/JavaScript 客户端) | [npm](https://www.npmjs.com/package/boxkite-client) | `npm install boxkite-client` |
| `ghcr.io/evalssment/boxkite-sandbox` | GHCR | `docker pull ghcr.io/evalssment/boxkite-sandbox:0.1.0`(仅 amd64 — 参见 `deploy/sandbox.Dockerfile` 的 Chrome-for-Testing 约束) |
| `ghcr.io/evalssment/boxkite-sandbox-minimal` | GHCR | `docker pull ghcr.io/evalssment/boxkite-sandbox-minimal:0.1.0`(amd64 + arm64) |
| `ghcr.io/evalssment/boxkite-sidecar` | GHCR | `docker pull ghcr.io/evalssment/boxkite-sidecar:0.1.0`(amd64 + arm64) |
| `ghcr.io/evalssment/boxkite-control-plane` | GHCR | `docker pull ghcr.io/evalssment/boxkite-control-plane:0.1.0`(amd64 + arm64) |
| `sdk-go/` — 用于您托管的控制平面的 Go 客户端 | 暂未在 pkg.go.dev 上(在 [#182](https://github.com/EvAlssment/boxkite/issues/182) 中跟踪) | `go get github.com/EvAlssment/boxkite/sdk-go`(仅模块路径,暂无标记版本) |
| `sdk-rust/` — 用于您托管的控制平面的 Rust 客户端 | 暂未在 crates.io 上(在 [#182](https://github.com/EvAlssment/boxkite/issues/182) 中跟踪) | `cargo add --git https://github.com/EvAlssment/boxkite boxkite-client`(仅 Git 依赖,暂无 crates.io 发布) |
两个 `boxkite-client` 软件包在两个不同的注册表(PyPI 和 npm)下具有相同的名称 — 相同的产品,相同的版本,不同的生态系统,由于 PyPI 和 npm 是独立的名称空间,因此不会发生命名冲突。请注意与 `boxkite-sandbox` 的区别:`boxkite-sandbox` 是此 README 所关心的自托管核心(`SandboxManager`、工具工厂、CLI);而 `boxkite-client` 软件包和 `boxkite-mcp` 都是用于单独运行的控制平面的薄 HTTP 客户端 — 参见上面的[自托管](#自托管)以了解这一区别。
### 验证发布的镜像
从 `v0.1.1` 开始,每个镜像 `.github/workflows/publish-images.yml` 发布的内容都使用 [cosign](https://github.com/sigstore/cosign)(Sigstore/Fulcio/Rekor — 无需分发密钥,证书绑定到工作流运行的 GitHub Actions OIDC 身份)进行无密钥签名,并附带针对推送的镜像摘要生成的签名 SPDX SBOM 声明。`v0.1.0` 较早,不支持签名 — 不应期望 `cosign verify` 对其成功。
```bash
# 验证镜像签名
cosign verify \
--certificate-identity-regexp "^https://github\.com/EvAlssment/boxkite/\.github/workflows/publish-images\.yml@refs/tags/.+$" \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
ghcr.io/evalssment/boxkite-sandbox:<tag>
# 验证附带的 SBOM 声明并打印 SBOM 本身
cosign verify-attestation \
--type spdxjson \
--certificate-identity-regexp "^https://github\.com/EvAlssment/boxkite/\.github/workflows/publish-images\.yml@refs/tags/.+$" \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
ghcr.io/evalssment/boxkite-sandbox:<tag>
```
将 `boxkite-sandbox` 替换为 `boxkite-sandbox-minimal`、`boxkite-sidecar` 或
`boxkite-control-plane` 以获取其他三个镜像。成功的
`cosign verify` 确认镜像由此仓库的 `publish-images.yml` 工作流构建并推送(不是受损的注册表帐户或篡改的镜像)— 参见 `SECURITY.md` 的“验证发布的镜像”部分,了解这项保护的范围。
## 配方 / 示例
[`examples/`](examples/README.md) 包含可运行、已验证的示例:一个完整的
LangGraph 代理连接到所有 5 个沙盒工具,一个最小的单工具
LangChain 代理,没有代理框架的原生 OpenAI 函数调用,一个 LlamaIndex `ReActAgent`,Claude Code 在沙盒中无头运行,沙盒的原始 HTTP
API 的纯 curl/Python-requests 脚本,控制平面 API 的演练(注册 -> 沙盒 -> 执行 ->
拆除),以及 `python_interpreter` 和
`node_interpreter` 在调用之间具有持久性、保持活跃状态的并排演示。从
那里开始,如果您想要可复制的代码,而不是从此 README 中拼凑代码,请参阅
每个 SDK 的 `README.md`(`sdk-python/`、`sdk-js/`、
`sdk-go/`、`sdk-rust/`)全面介绍了该客户端的方法覆盖范围,而
`mcp-server/README.md` 则介绍了 MCP 服务器本身。
## 扩展:AuditSink 和 SessionMetadataStore
boxkite 与零个外部依赖项一起工作 — 每个工具都针对
侧车的 S3/Azure 存储进行操作。对于调用者希望拥有更多功能的两个可选钩子:
- **`boxkite.audit.AuditSink`** — 将文件写入镜像到您自己的系统
记录(基于数据库的文件浏览器、审计日志、Webhook)。每个
方法都是尽力而为的;损坏的接收器永远不会使沙盒操作失败。
- **`boxkite.session_store.SessionMetadataStore`** — 重建会话
所有权(组织/工作项 ID、存储前缀),如果在错误恢复期间
pod 丢失其 Kubernetes 标签/注释。 大多数调用者
不需要这个 — Kubernetes pod 元数据已经涵盖了常见的恢复
路径。
两者默认为无操作。仅实现您需要的功能;参见 `src/boxkite/audit.py` 和 `src/boxkite/session_store.py` 中的文档字符串。
对于想要一个可用的持久存储的调用者,附带的 SQLite 支持
引用实现 — `SQLiteAuditSink` 和 `SQLiteSessionMetadataStore` —
可用于嵌入。
**`AuditSink` 与控制平面的 Webhooks — 不是同一件事。**
`AuditSink` 是 **拉取**、进程内:您的代码嵌入
`SandboxManager` 直接调用其方法(您可以编写一个本身
发出 POST 请求到 URL 的接收器,但这是您的代码在您的
进程中调用)。 自托管的 `control-plane/` 另外提供真正的
**推送**、进程外 Webhooks — `POST /v1/webhooks` 注册一个 URL,
而控制平面本身调用它(带有 HMAC 签名的有效负载、重试
和退避)当沙盒生命周期事件触发时,与您的进程是否
运行无关 — 此功能仅适用于控制平面,不适用于嵌入的
`SandboxManager`。
## 未包含的内容
这是干净的提取,而不是对更大的内部系统的重新打包。
故意省略:
- **任何特定的代理编排框架。** 工具是普通的
LangChain `@tool`-decorated 函数 — 将它们连接到 LangGraph、自定义
代理循环或任何其他接受 LangChain 工具的代理。
- **“技能注入”系统。** 侧车的 `/ensure-skills` 端点
(在 `/mnt/skills` 下物化只读技能文件)被包含
因为它确实是沙盒自己的文件模型的一部分,但
哪些技能适用于哪些代理的**策略**是您应用程序的
问题,而不是此软件包的问题。
- **任何数据库或身份验证层。** 会话所有权存在于 Kubernetes pod
标签/注释中,这足以独立运行此软件。如果您想要
跨重启恢复 pod 已消失,请参见上面的 `SessionMetadataStore`。
- **完整的 IDE 形状的 LSP(悬停、签名帮助、实时诊断推送、
增量同步、IDE 附加)** — 内联波浪线/悬停提示/
每个按键增量同步是交互式编辑器 UX,
不适合 boxkite 的批量工具调用模型。**代理可调用
完成确实已发货**:`lsp_start`/`lsp_completion`/
`lsp_stop`(通过 `enable_lsp_tools` + `BOXKITE_LSP_ENABLED` 启用)运行一个
持久的 `pyright`(Python)或 `typescript-language-server`
(TypeScript/JS)服务器每个会话,请求/响应,完整的文档同步。
## 许可证
[MIT](LICENSE) — 完全允许。 使用、修改、自托管或在其基础上构建
竞争托管服务;没有限制。 有关确切条款,请参见
[LICENSE](LICENSE)。
## 贡献
参见 [CONTRIBUTING.md](CONTRIBUTING.md) — 我们使用开发者证书
来源(`git commit -s`),而不是 CLA。 发现安全问题? 请参见
[SECURITY.md](SECURITY.md) 并私下报告,而不是公开
问题 — 该项目执行任意代码,沙盒逃逸
报告需要快速、私有的路径。
连接信息
猜你喜欢
暂无推荐服务器