十个技能,十个 Docker 测试,九个在 Ubuntu 上通过,一个被跳过。这是整篇文章里唯一值得核对的数字,仓库里的 tests/run.sh 会把这个结果直接打印出来。

给代理读的操作手册,问题出在哪

写给人类看的操作手册会留白,人靠上下文就能补全。代理补全同样的空白靠的是猜,而关于 ufw --force enable 的一个错误猜测,就能把你锁在服务器外面。

所以这里的每个流程都打包成一个文件夹,代理可以整体加载:

  • skills/vps-basics/ 目录下包含 SKILL.md,YAML 前置元数据后面跟着编号步骤。
  • references/ 目录里放着 jail.local,也就是第三步要复制的文件。

前置元数据里带有名称、描述、版本、许可证和平台信息。Claude Code 原生就能读取这种目录结构。任何遵循 AgentSkills 规范的加载器也能读取同样的文件夹,不过目前只对 Claude Code 和我自己的运行器做过测试。

测试跑的是技能自己的步骤

技能是一份文档,文档会腐坏。这个测试装置通过执行文档本身来堵住这个缺口:

Docker 容器以特权模式启动,挂载 references 目录为只读,基于 ubuntu:22.04 镜像执行一串命令。先更新软件包并安装 ufw 和 fail2ban,然后设置 ufw 默认拒绝入站,放行 22、80、443 端口,强制启用防火墙,复制 jail.local 到 fail2ban 配置目录,启动 fail2ban 服务,最后用 grep 断言 ufw 状态为 active、22 端口在规则里、fail2ban 的 sshd 监狱状态正常。

这些命令就是 skills/vps-basics/SKILL.md 里的步骤,原样复制出来。改了 SKILL.md 却不改测试,两边就对不上;改了测试却不改 SKILL.md,断言就不再匹配。无论哪种情况,测试装置都会在读者拿到错误版本之前先变红。

tests/run.sh 会遍历每一个 tests/.sh,跳过前置元数据里标注 host-specific: true 的技能,遇到第一个失败就以非零状态退出。有一个技能是主机特定的:wan-failover-uplink-guard 需要两条物理上行链路,没有任何容器能提供这种条件。

这十个技能覆盖了什么

公开仓库里免费且采用 MIT 许可证的有四个:

  • vps-basics:在新机器上配置 ufw 默认拒绝策略,加上一个 fail2ban 的 sshd 监狱。
  • backup-restore-drill:每个备份步骤都配对一个恢复步骤,备份只有在被读回验证之后才算数。
  • service-health-check:按进程、端口、HTTP 的顺序依次检查。
  • systemd-unit-authoring:把一个脚本变成 systemd 单元,崩溃时自动重启,开机时自动启动。

付费部分在结账功能开放后提供,目前还没有开放:caddy-reverse-proxytailscale-funnelpi-hardeningtelegram-bot-deploymentwan-failover-uplink-guardwol-wake-and-remote-shutdown

健康检查为什么要分三层

service-health-check 先查进程,再查端口,最后查 HTTP,遇到第一个失败就停止。每一层抓住的是上一层漏掉的问题。只查进程,会放过一个还占着端口的挂起进程。只查端口,会放过一个接受连接但对每个请求都返回 500 的应用。只有 HTTP 检查读到的才是用户真正会读到的东西。

这个测试证明不了什么

容器测试证明的是技能里的命令在干净环境里能跑通。它证明不了这些步骤在生产环境里一定安全,也证明不了代理在真实场景中不会做出其他危险操作。测试能抓住的是文档和命令之间的漂移,抓不住的是文档本身的设计缺陷。