--- title: 架构 description: 三个进程、一条反向隧道,以及一个请求到底走了哪条路。 --- # 架构 ``` 你的智能体 / SDK / CLI │ HTTPS ▼ ┌──────────────────────────────┐ │ 控制面 (pkg/server) │ 调度 · 隧道 · 策略 · API └──────────────┬───────────────┘ │ 反向 WebSocket + yamux ← worker 主动拨出 ▼ ┌──────────────────────────────┐ │ worker (cmd/poder) │ 一台机器上的 Docker 生命周期 └──────────────┬───────────────┘ │ 容器网络 ▼ ┌──────────────────────────────┐ │ toolbox(在沙箱内) │ 代码执行 · PTY · 文件 └──────────────────────────────┘ ``` ## 控制面 `pkg/server` 就是整个 API:把创建调度到某个 worker、持有隧道对象、做鉴权和配额、 调用准入闸门、发出生命周期事件。它可以作为库被 import —— `server.New(...)` 返回的 handler 就是命令行二进制在服务的那个 handler —— 商业构建正是靠这一点加上账号和 计费,而不需要 fork。 持久化可以是 SQLite(单实例)或 PostgreSQL(机队)。schema 只做加法:旧二进制能在 新 schema 上跑,这也是"回滚只是改个镜像 tag"的原因。 ## Worker `cmd/poder` 跑在有 Docker socket 的机器上。它向控制面拨一次号,然后一直保持这条 连接:**worker 上不开放任何入站端口**。正是这条性质,让它能用在 NAT 后面的笔记本、 客户的虚拟机、或者员工工位上。 通过这条隧道它收到"建这个容器""删那个容器",并把执行请求转发给每个沙箱内的 toolbox。 ## Toolbox `cmd/toolbox` 跑在**每个沙箱内部**。真正执行代码的是它:语言运行时、PTY、文件操作、 流式输出、端口预览代理。它在容器内监听 `8080`,外面不直接访问它。 ## Agent(不需要 Docker) `cmd/agent` 把本机自身注册成沙箱,内嵌 toolbox。这就是 SandrPod 能跑在 macOS 笔记本 或没有 Docker 的裸机上的方式:机器本身就是沙箱。同一个二进制还带着权限闸门、决策 审计、出网策略,以及操作系统级沙箱(`bwrap`、`seatbelt`)。 ## 一个请求实际做了什么 对运行中的沙箱发 `POST /sandboxes/execute`: 1. 控制面鉴权、解析沙箱、校验归属; 2. 找到该沙箱所属 worker 的隧道 —— 本地,或在某个对端实例上(那就转发,见 [多实例](/zh/multi-instance)); 3. 在隧道上开一条 yamux 流,请求访问 toolbox; 4. worker 通过网络连接容器内的 toolbox; 5. toolbox 执行进程,把结果沿原路返回。 延迟主要由进程决定,而不是这些跳数:隧道是一条热连接,不是每个请求新建一条。 ## 八家云厂商,两种机制 在云上创建 worker,八家的流程形状一致,底下有两种远程执行机制: | 机制 | 厂商 | |---|---| | 托管命令通道 | AWS(SSM)、阿里云(云助手)、Azure、腾讯云(TAT)、Oracle | | 临时密钥 SSH | GCP、DigitalOcean、Hetzner | 两种方式殊途同归:机器启动、装 Docker、带 join token 启动 worker、控制面等它上线。 如果它一直不上线,那台机器会被**终止**而不是留着计费 —— 这条流程里每个失败分支都会 把机器还回去。 ## 接下来读什么 - [Worker 与隧道](/zh/workers) —— 这条连接、它的身份、它的生命周期。 - [隔离与放置](/zh/isolation) —— 谁能跑在谁上面。 - [生产部署](/zh/deploy) —— 真实流量前推荐采用的形态。