Files
storage-labos/flashops/docs/agent-protocol.md
T
yuanshuai c91a64fddb
CI / Python 3.12 (push) Waiting to run
CI / Python 3.9 (push) Waiting to run
chore(repo): initialize team collaboration repository
2026-07-27 20:40:12 +08:00

4.6 KiB
Raw Blame History

控制平面 ↔ Host Agent 协议

当前状态(2026-07-27:独立 Host Agent 已实现注册、注册口令、bearer token、 本地 0600 凭据文件、主机指纹和心跳;控制平面只保存 token SHA-256 摘要。 心跳迟到 15 秒会把 Host 降级为 DEGRADED,丢失 30 秒会标记为 OFFLINE; 安全预检会先刷新该状态,避免失联 Host 被误判为可用。 Step 租约、事件、完成与产物端点仍是下一阶段契约。make demo 暂时继续使用 控制平面内置模拟执行器验证状态、恢复与证据链。

Agent 是拉模式。控制平面永远不主动连接测试主机。

理由:客户测试主机在内网/NAT 后面,很多实验室根本不给外部反连; 拉模式还顺带解决了 Agent 重启后的重新接入问题。

端点

状态 方法 路径 用途
已实现 POST /api/v1/agent/register 首次注册 / token 轮换;上报主机指纹与工具能力
已实现 POST /api/v1/agent/{agent_id}/heartbeat bearer 心跳,上报健康和设备探针;返回待办指令
已实现 GET /api/v1/agent/hosts 查看 Host 与 Agent 在线投影,不返回 token
待实现 POST /api/v1/agent/{agent_id}/lease 领取下一个步骤(长轮询,最多挂 20s)
待实现 POST /api/v1/agent/{agent_id}/steps/{step_id}/events 批量上报执行事件与日志片段
待实现 POST /api/v1/agent/{agent_id}/steps/{step_id}/complete 上报步骤终态 + UnifiedResult + 产物清单
待实现 POST /api/v1/agent/{agent_id}/artifacts 上传产物(日志 / 结果文件 / 画面)

带外控制器走独立端点(它必须能在主机死后独立上报):

方法 路径 用途
POST /api/v1/oob/{controller_id}/heartbeat 带外心跳 + 电源状态 + 温度 + 画面指纹
POST /api/v1/oob/{controller_id}/observation 主动上报观测(蓝屏画面、掉电、按钮触发)

心跳返回的指令

心跳响应已经带 commands 字段;当前固定为空数组。命令队列接入后,Agent 收到后立即执行:

{
  "ok": true,
  "server_time": "2026-07-27T03:14:15Z",
  "commands": [
    {"kind": "CANCEL_STEP", "step_id": "..."},
    {"kind": "SOFT_RESTART", "reason": "recovery_L1"},
    {"kind": "EMERGENCY_STOP", "run_id": "..."},
    {"kind": "REFRESH_ASSETS"}
  ]
}

紧急停止走心跳而不是新连接——心跳是唯一能保证到达的通道。 最坏情况延迟 = 心跳间隔(5s)。真正需要毫秒级的停止靠带外物理急停,不靠软件。

租约(lease)语义

Agent  ──lease──▶  控制平面
                    ├─ 有活:分配步骤,step → DISPATCHED,写租约(TTL 60s
                    └─ 没活:长轮询挂起最多 20s,返回 204
Agent  ──ack───▶   step → RUNNING
(租约到期未 ack 或未 complete)→ step 回 PENDING,重新派发

租约 TTL 必须大于步骤超时?不——恰恰相反:租约由 Agent 在执行期间通过心跳续期。 Agent 死了 → 心跳停 → 租约到期 → 步骤回收。这是 Agent 崩溃能被发现的机制之一 (另一条是心跳超时判定,见 state-machine.md §5)。

认证

首次注册带 X-FlashOps-Enrollment-Token。开发态未配置注册口令时允许本机演示; 生产态未配置时注册接口返回 503。注册成功下发 bearer token,之后每个请求带 Authorization: Bearer <token>agent_idtest_host_id 绑定,重新注册会轮换 token。

原始 token 仅存于 Agent 本机 0600 state 文件;控制平面数据库只保存 SHA-256 摘要。 Agent 客户端保留可选 gateway Basic Auth 能力,供未来重新启用边界认证或部署在其他网关后使用; 当前 flashops.imagebrewing.com 预览环境未启用这层认证。

下一波必须升级:方案 §6.2 要求"每个 Agent 使用唯一证书或密钥"。 bearer token 在客户内网够用,但要进客户安全评审得上 mTLS。 升级点在 api/deps.py::require_agent() 一处。

幂等(待实现)

Step 租约接入时,所有 Agent → 控制平面的 Step 写请求必须带 Idempotency-Key(Agent 生成的 UUID)。控制平面需要记录并拒绝重复副作用; run_events 已有唯一约束 (run_id, idempotency_key),但当前注册 / 心跳链路尚未实现 通用的请求幂等存储。

这一条在真实实验室里不是可选项:主机重启、网线松动、Wi-Fi 掉线是常态, 没有幂等就会出现"一次故障记了三条"的时间线,取证时说不清。