mcpField
H
Headmesh Agent Sandbox

Headmesh Agent Sandbox

远程服务
headmesh.dev开发工具11 天前更新

关于 Headmesh Agent Sandbox

Headmesh Agent Sandbox 为需要操作真实文件、依赖、Git 和私有服务的 Agent 提供持久化 Docker 沙箱。Go 守护进程和主机 CLI 提供 MCP 访问、持久会话及常规与 YOLO 模式。

主要功能

  1. 01持久化 Docker 沙箱
  2. 02命令执行与文件编辑
  3. 03Git 和 tmux 会话管理
  4. 04作用域令牌与审计
  5. 05常规与 YOLO 模式

适用场景

  1. 01让编码 Agent 操作真实项目文件
  2. 02断线重连后保留 Agent 会话
  3. 03向 MCP 客户端开放沙箱工具

MCP 相关

`headmesh-sandboxd` 守护进程通过 MCP 暴露沙箱工具,包括命令执行、会话操作和文件系统操作。支持 Unix 套接字与 HTTP 访问,HTTP 使用 bearer 令牌认证。

配置

远程服务无需本地安装。选择你使用的客户端,复制配置或使用一键安装。

服务地址https://{sandbox_host}/mcp
一键复制安装命令
Claude CodeTerminal
Cursor.cursor/mcp.json
VS Code.vscode/mcp.json
Claude Desktopclaude_desktop_config.json
Windsurf~/.codeium/windsurf/mcp_config.json

命令在终端中运行;JSON 粘贴到对应客户端的 MCP 配置后重启。需要授权的服务会在首次连接时提示登录。

全部服务地址
Streamable HTTPhttps://{sandbox_host}/mcp需要授权

工具

详细介绍

Headmesh Agent 沙箱

ASUS 上的持久化 Docker 容器,作为独立的已标记节点加入 Aaryn 的 Headscale tailnet, 让云端 Agent 能使用真实文件、真实依赖项和私有服务。Agent 可通过多个入口访问它:

  • MCP 服务 headmesh-sandboxd;
  • Claude 可自托管的 runner 和 Claude Remote Control;
  • 通过 Tailscale SSH 使用 Codex;
  • OpenAI Secure MCP Tunnel;
  • codex exec-server,由主机通过 Agents API 驱动。

它实现了计划 §6 和 §7(docs/plan.md)。

路径内容
image/基于 ubuntu:26.04(按摘要固定)的多阶段 Dockerfile、s6-overlay 服务、git hooks、指南模板、docker-bake.hcl、container-structure-test、hadolint 配置
compose/包含 regular 和 yolo 配置的 compose.yaml(嵌入 CLI 中)
config/sandbox.example.toml 及其 JSON Schema(sandbox.schema.json)
sandboxd/MCP 守护进程:工具、会话(tmux)、身份验证、审计、状态
cmd/headmesh-sandboxd/守护进程二进制文件,以及容器内的辅助工具(hm)
cmd/headmesh-sandbox/主机 CLI
host/出站防火墙和 restic 备份单元。Aaryn 使用 sudo 安装;详见 host/README.md
tests/acceptance/五项检查的测试框架、重建加分项和 prompt.md
tests/e2e/针对运行中容器的实时测试
docs/container-contract.md(镜像、Compose、CLI 和守护进程之间的接口)以及 ADR

这是独立的 Go 模块 github.com/headmesh-dev/headmesh/sandbox;根目录下的 go.work 已包含它。在此运行 go test -race ./...。

已验证内容(2026-10-27)

以下所有测试均在本地运行,未加入 tailnet:

  • go test -race ./... 通过。其中包括针对真实 tmux 和真实 git 的集成测试,以及针对进程内 sandboxd 的测试框架。使用仓库配置运行 golangci-lint run,报告 0 个问题。
  • docker buildx bake -f sandbox/image/docker-bake.hcl sandbox-local 成功构建 headmesh-sandbox:dev(2.39 GB)。hadolint 检查通过,container-structure-test 43/43 项通过。
  • headmesh-sandbox up 通过 Compose 启动容器,并启用 §6.3 加固配置。tailscaled 尚未登录时,容器报告健康状态;未配置的各入口处于空闲状态,并说明原因。status 和 doctor 均可用。
  • 针对运行中的容器,通过 unix socket 和 HTTP bearer token,tools/list(31 个工具)、exec、session_* 和 fs_* 均通过。两种协议版本均可运行:2026-07-28 无状态版本和 2025-11-25 握手版本 (tests/e2e)。
  • 验收测试,路由 mcp 和路由 ssh(在容器内运行第 1 阶段,再从新进程运行第 2 阶段):
    • 检查 1–3 通过:uid 1000、Ubuntu 26.04、文件哈希与主机上的副本匹配,且依赖项可以运行。
    • 检查 5 的机制通过:重连后会话仍在运行,标记文件也存在。
    • 检查 4 和 5 仍未通过,因为节点尚未加入网络。
  • acceptance recreate 以 5/5 通过:home 和 ~/.bash_history 均会保留,启动标识会变化,旧会话会报告 lost (container restarted at …)。 tailnet 身份部分已跳过(尚未加入网络)。
  • YOLO:
    • 没有新的备份记录时会拒绝启动。
    • 有备份记录时,会通过按子树只读挂载的 /host 启动。即使以 root 身份运行,写入也会失败;传播模式为 private;读取 /etc/shadow 得到 0 字节;~/.ssh 已屏蔽;主机 tailscaled 状态和 Docker 数据根目录均未挂载。
    • 到达 TTL 时会自行停止,并写入审计记录;下一次 up 将使用 Regular 模式。

Spike 的结果见 docs/adr/spike-t0{1,2,3,7,8}-*.md 和 docs/adr/spike-t16-*.md。其他实现决策见 docs/adr/0001-sandbox-implementation-decisions.md。

需要处理的安全发现

  1. 注册前安装出站防火墙(下文步骤 1 中的 host/firewall)。否则,Docker 桥接容器可以访问主机能够访问的任何内容:其局域网、自身服务,以及主机本身也是 tailnet 节点时的 tailnet。主机特定的发现记录在内部运维说明中。
  2. YOLO 以只读方式显示主机。这包括数据目录和证书,只要文件权限允许即可访问。Agent 以 uid 1000 运行;仅 root 可读的文件对它们仍不可读,但 tailscaled 和 runner 在容器中以 root 身份运行。将任何敏感内容添加到 [mounts] extra_denylist:被列入拒绝名单的子树不会挂载到 /host。
  3. 在沙盒中,UV_PROJECT_ENVIRONMENT=.venv-sandbox,因此 uv 不会使用容器路径重建项目的主机 .venv。确保每个项目的 .gitignore 都包含 .venv-sandbox(或 .venv*)。

快速开始(主机)

cd /mnt/storage/projects/headmesh/sandbox
go build -o ~/.local/bin/headmesh-sandbox ./cmd/headmesh-sandbox
docker buildx bake -f image/docker-bake.hcl sandbox-local     # headmesh-sandbox:dev
headmesh-sandbox init                                          # /mnt/storage/headmesh/sandbox
$EDITOR /mnt/storage/headmesh/sandbox/config/sandbox.toml      # image = "headmesh-sandbox:dev" until GHCR has a release
headmesh-sandbox scan-secrets                                  # review, then --apply
headmesh-sandbox token create claude-code-mac > ~/.config/headmesh/sandbox-token
headmesh-sandbox render --diff
headmesh-sandbox up
headmesh-sandbox status
headmesh-sandbox doctor

其他命令:down [--yolo|--all]、recreate、logs [service|audit] [-f]、shell [--root]、attach(在 tmux 中观察 Agent)、backup、token list|revoke、acceptance run|verify|report|recreate,以及 agent run --provider openai。

密钥存放在 /mnt/storage/headmesh/sandbox/secrets/<name> 中,权限模式为 0600;空文件表示对应入口已关闭。密钥绝不会被挂载。CLI 通过环境变量将其传递给 docker compose,Compose 会将每个密钥复制到容器中,所有者为 root:root,权限为 0400(spike T-1)。

需要 Aaryn 执行的步骤

每个步骤都需要 sudo、生产环境变更或账户。目前这些步骤均未完成。

1. 主机(sudo)
  • 安装出站防火墙和家庭备份单元,然后执行首次备份。具体命令见 host/README.md。运行 headmesh-sandbox doctor 检查结果;应显示 egress firewall ✓ 和 home backup ✓。
  • 安装 restic(计划 §5.3)。休眠目标已屏蔽(doctor 会检查)。
2. Headscale 策略(生产 ACL 变更;维护窗口)

此步骤基于计划 §5.2 和 §6.6。将标签和授权规则添加到 Xcelsior 仓库中的 api/infra/headscale/policy.hujson。先运行 headscale policy check;策略中的 tests 必须通过。

"tagOwners": { "tag:sandbox": ["group:admin"], "tag:sandbox-yolo": ["group:admin"] },
"grants": [
  // Regular: only the private services you list (keep sandbox.toml [mode.regular] reach in sync).
  { "src": ["tag:sandbox"], "dst": ["tower-server"], "ip": ["tcp:11434"] },
  { "src": ["tag:sandbox-yolo"], "dst": ["*"], "ip": ["*"] },
],
"ssh": [
  { "action": "accept", "src": ["xcelsior@"], "dst": ["tag:sandbox", "tag:sandbox-yolo"], "users": ["agent"] },
],

xcelsior@ 必须仅表示 Aaryn 自己的设备。提供商主机打上标签后(§5.2)即可满足这一点。

3. 注册节点
# On prod-hetzner: a single-use pre-auth key that carries the tag (Headscale 0.29.6).
sudo headscale preauthkeys create --user <owner> --tags tag:sandbox --expiration 1h
# On the ASUS: store it and recreate.
install -m 600 /dev/stdin /mnt/storage/headmesh/sandbox/secrets/ts_authkey   # paste, then Ctrl-D
headmesh-sandbox recreate
headmesh-sandbox status        # tailnet Running, node asus-sandbox, dns-check ok
  • 密钥本身会设置标签。绝不传递 --advertise-tags,因为 Headscale 会拒绝与带标签的密钥一起使用该参数(spike T-8)。tailscale-up 会改为检查已注册的标签是否与 TS_TAGS 一致。
  • 仅在状态尚不存在时,tailscale up 才会使用密钥,因此之后重启时不会重新进行身份验证。带标签的节点不会过期。注册后清除密钥文件。
  • YOLO 是一个独立节点(asus-sandbox-yolo、tag:sandbox-yolo),使用自己的状态目录。首次执行 up --yolo 时,使用 tag:sandbox-yolo 密钥按相同方式注册。
4. 入口

通过 tailnet 使用 MCP(Claude Code、任何 MCP 客户端):

headmesh-sandbox token create claude-code-mac          # prints hms_… once
claude mcp add --transport http sandbox http://asus-sandbox.mesh.xcelsior.ca:8780/mcp \
  --header "Authorization: Bearer hms_…"

令牌具有作用域(sandbox:read|exec|write|admin),并且可以撤销: headmesh-sandbox token revoke claude-code-mac 会在几秒内生效。 OAuth(RFC 9728 元数据已通过 /.well-known/oauth-protected-resource/mcp 提供)将在 M3 随 Headmesh 授权服务器一同推出。

Claude 可自托管 Runner(计划 §7.1):

  1. 在 claude.ai 管理设置中开启“Allow self-hosted environments”。
  2. 将环境密钥放入 secrets/claude_env_secret,并在 secrets/github_token 中填入细粒度 PAT。
  3. 设置 [entrypoints.claude_runner] enabled = true,然后运行 headmesh-sandbox up。

Runner 以 root 身份运行。每个会话都会经过 claude-session,该程序会使用 setpriv 降权为 agent,因此会话无法读取密钥(验证 T-1)。 checkout 钩子会将 [repos] 中映射的仓库链接到共享工作副本的 main 分支。仍有一项实际检查待完成:确认 Runner 会在会话环境中设置什么。

Claude Remote Control(计划 §7.2):

  1. 设置 [entrypoints.claude_rc] enabled = true,然后运行 headmesh-sandbox up。
  2. 运行 headmesh-sandbox shell,然后运行 claude auth login(完整登录 claude.ai), 信任 /home/agent,并运行一次 claude remote-control,回答 “Enable Remote Control? y”。
  3. 运行 headmesh-sandbox recreate。

切勿设置 DISABLE_TELEMETRY、DO_NOT_TRACK、 CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC 或 DISABLE_GROWTHBOOK:Remote Control 在设置其中任何一项时都会拒绝启动(验证 T-2)。登录后还需检查:在没有 TTY 的情况下,它是否会自行重启。

通过 Tailscale SSH 使用 Codex(计划 §7.3):

  • 这需要第 2 步中的 ssh 规则。
  • 在 Codex 桌面应用中,添加远程主机 [email protected]。
  • 冒烟测试:ssh [email protected] codex --version。
  • Tailscale SSH 可在容器内运行(验证 T-3)。Codex 桌面应用本身尚未测试。

OpenAI Secure MCP Tunnel(计划 §7.4):

  1. 在 Platform 中创建一个 Tunnel,并创建一个仅具有 Read+Use 权限的运行时密钥。将密钥放入 secrets/openai_tunnel_key,并将 ID 填入 [entrypoints.openai_tunnel] tunnel_id。设置 enabled = true,然后运行 up。
  2. 为 Tunnel 路由创建令牌: headmesh-sandbox token create chatgpt-tunnel --route tunnel(exec ≤ 110 s, 响应 ≤ 8 MiB)。
  3. 在 ChatGPT 中:Plugins → Add custom MCP server → Connection: Tunnel,并使用 Bearer 身份验证和该令牌。

通过 Agents API 使用 Codex exec-server(计划 §7.6):

  1. 将应用密钥存放在沙箱外。CLI 以你的身份运行,因此请使用 secrets/openai_app_key(0600;绝不会挂载,并且已列入 YOLO 的 /host 拒绝列表)。如果你有读取权限,也可以使用 /etc/headmesh/openai-app.key。

  2. 将环境密钥(Agents 选项卡,其他所有权限均设为 None)放入 secrets/openai_env_key。

  3. 运行:

    headmesh-sandbox agent run --provider openai --repo xcelsior \
      --prompt-file sandbox/tests/acceptance/prompt.md
    

执行器以 agent 身份在仓库中运行,使用 --linux-sandbox-pid-namespace inherit,并在容器内拥有完全访问权限。Codex 的 bubblewrap 沙箱无法在容器的 seccomp 和 AppArmor 配置文件下运行,因此容器就是安全边界(验证 T-16)。环境密钥通过 stdin 传给执行器,转录记录写入 logs/agent-runs/<session>.jsonl。

5. 验收(计划 §6.15)
headmesh-sandbox acceptance run --route mcp \
  --url http://asus-sandbox.mesh.xcelsior.ca:8780/mcp --token-file ~/.config/headmesh/sandbox-token
ssh [email protected] headmesh-sandbox acceptance run --route ssh --phase 1
ssh [email protected] headmesh-sandbox acceptance run --route ssh --phase 2
# runner, exec-server, tunnel: the agent follows tests/acceptance/prompt.md
headmesh-sandbox acceptance recreate
headmesh-sandbox acceptance verify
headmesh-sandbox acceptance report --out ../docs/acceptance/$(date +%F).md

在 sandbox.toml [acceptance] 中设置目标:

  • 必须将 allowed_target 授予 tag:sandbox。
  • 必须阻止 denied_target。请仅在安装防火墙后运行此探测。

检查 4 只接受 MagicDNS 名称对应的 tailnet 地址;同名的公共 DNS 记录不计入。

6. YOLO
  1. 创建专用部署密钥,将其作为“headmesh-sandbox-yolo (asus)”添加到 GitHub, 并固定 known_hosts:

    ssh-keygen -t ed25519 -f /mnt/storage/headmesh/sandbox/secrets/github_ssh_key \
      -C "headmesh-sandbox-yolo (asus)"
    

    此密钥只会传递给 YOLO 容器。

  2. 运行 headmesh-sandbox backup,然后运行 headmesh-sandbox up --yolo --ttl 4h。

如果没有创建时间不超过 backup_max_age 的备份,CLI 会拒绝执行,并要求你确认正在监看。

7. 发布镜像

推送一个 sandbox-v0.1.0 标签。.github/workflows/sandbox-image.yml 会构建镜像, 运行 hadolint、container-structure-test 和 trivy,将镜像推送到 ghcr.io/headmesh-dev/sandbox,并使用 cosign 为其签名。然后设置 image = "ghcr.io/headmesh-dev/sandbox:0.1.0@sha256:…"。

故障排查

  • 首先运行 headmesh-sandbox doctor;每个故障都会附带修复方法。
  • headmesh-sandbox logs sandboxd|tailscaled|tailscale-up|claude-runner|claude-rc|openai-tunnel|audit。
  • 要实时查看 Agent:headmesh-sandbox attach。
  • “IP 可用,名称无法解析”:dns-check 会说明修复方法(问题 3)。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。