# v0.2 hwlab-cloud-api 服务规格 `hwlab-cloud-api` 是 `v0.2` 应用层核心服务,运行在 `hwlab-v02` namespace,内部端口 `6667`,公网经 `hwlab-edge-proxy` 和 FRP 暴露为 `http://74.48.78.17:19667`。 Provider API Key 管理属于 Cloud API 的 authenticated admin surface:前端只调用 `/v1/admin/provider-profiles*`,Cloud API 使用 HWLAB `AuthPrincipal` 和授权模型判定后,把 provider profile 状态查询、API Key 写入和 canary 验证委托给 AgentRun 后端。Cloud API 不直接写 AgentRun Secret,不把 API Key 原文写入日志或响应;详细合同见 [spec-v02-provider-management.md](spec-v02-provider-management.md)。 ## 在系统中的职责划分 - 承担 runtime health、DB readiness、登录鉴权、`AuthPrincipal`、OpenFGA 授权 check/write、用户/session/API key 权限、Code Agent 对话、trace/result 轮询、gateway outbound registry、M3 IO 控制、device-pod authority/job 和 live build inventory。 - 是 `hwlab-cloud-web`、Code Agent session、device-pod 用户态操作、Admin Access API、AgentRun 工具注入和 gateway outbound poll 的唯一应用层收口点;普通用户不直接访问内部 `hwlab-device-pod` Service 或 OpenFGA Service。 - 读取 `hwlab-cloud-api-v02-db/database-url`、`hwlab-v02-code-agent-provider/openai-api-key` 和 `hwlab-v02-code-agent-codex-auth/auth.json` 等 v02 独立 SecretRef;用户 API key 存在 Postgres `api_keys`,device-pod 用户态授权只能从该表恢复到用户 actor。文档和日志只允许记录 SecretRef 名称、key、字节数或哈希指纹,不记录值。 - 读取 OpenFGA URL、auth token、store/model 指针和 mode 时只能通过 env/SecretRef/Postgres runtime config;`/health/live` 和 `/v1/admin/access/summary` 只输出 readiness、mode、storeId/modelId 摘要和 degraded reason,不输出 token、Postgres URL 或 tuple secret。 ## 内部架构 - `cmd/hwlab-cloud-api/main.ts` 负责启动 HTTP server、解析端口和 Code Agent timeout。 - `internal/cloud/server.ts` 负责 HTTP route、REST/RPC bridge、health、live-builds、device-pod authority、gateway poll/result 和 Code Agent chat。 - `internal/cloud/openfga-authorization.ts` 或等价模块负责 OpenFGA client、store/model bootstrap、`enforce` 策略、check/write 和 structured authorization decision;最终规格见 [spec-v02-openfga-authorization.md](spec-v02-openfga-authorization.md)。 - `internal/cloud/access-control.ts` 负责 `/auth/*`、OIDC callback、Web session、用户 API key、admin/user、device pod profile/relation、device job lifecycle 和 Code Agent owner binding;登录与鉴权 authority 见 [spec-v02-auth.md](spec-v02-auth.md)。当前本地 `/auth/login` 只作为 bootstrap fallback。 - `internal/cloud/access-control.ts` 也是账号 workspace authority:`account_workspaces` 记录同一账号的 Workbench 当前 workspace、selected conversation/session、active trace、provider profile 和 revision。 - Code Agent session 生命周期必须显式化。Cloud API 目标入口为 `POST /v1/agent/sessions` 创建 session、`GET/PATCH /v1/agent/sessions*` 查询/选择/标记状态;`POST /v1/agent/chat` 只接受显式传入或账号 workspace 中已显式选中的 usable session。没有 session 时返回 `session_required`,session failed/stale/canceled 时返回 `session_not_usable`,不得自动创建、滚动或替换 session。 - Code Agent session record 是 provider profile authority。`POST /v1/agent/chat` 带显式 session 且请求未显式给出 provider profile 时,Cloud API 必须继承该 session 的 `providerProfile` 并映射为 AgentRun `backendProfile`;请求显式覆盖 provider profile 时,覆盖必须进入 trace/result 可见字段。账号 workspace 的 provider profile 只能在 selected session 与目标 session 完全一致时作为 fallback,不得让旧 workspace 覆盖显式 session。 - `internal/db/runtime-store.ts` 和 `internal/cloud/db-contract.ts` 负责 Postgres runtime store 与 readiness 分层。 - `internal/cloud/code-agent-*.ts` 负责 Codex stdio session、trace store、result cache、provider profile 和取消/轮询。 - AgentRun dispatch 的 policy 字段由 Cloud API adapter 固定归一化:`tenantId=hwlab`、`projectId=pikasTech/HWLAB`、`providerId=G14`。Workbench project/workspace 只能作为 HWLAB metadata 或 `workspaceRef` 子字段保存,不能污染 AgentRun `projectId`;出现 project/tenant 混乱时,临时处理是修 adapter 字段映射并用真实运行面最小请求复测,不放宽 AgentRun manager policy。 - AgentRun v0.1 接入只使用标准 `threadId` 路径:`POST /v1/agent/chat` 收到的 `conversationId/sessionId/threadId` 必须写入 AgentRun command `payload.threadId` 和 `SessionRef.threadId`;协议字段、trace、result 和 conversation facts 都以该字段为唯一 thread identity。 - AgentRun run 级 events 写回 HWLAB trace 时必须按当前 `commandId` 归属过滤;同一 run 的旧 command 尾部事件不能混入后续 command trace。取消、失败或 blocked 轮次如果已有 assistant/tool 可读进展,必须以脱敏、限长的 conversation facts 写入 UI/trace/inspect 证据,供后续 `inspect`/`--from-trace` 可见性使用;这些 facts 不得作为下一轮模型上下文或 prompt 拼接来源。 - AgentRun completed 轮次续接必须依赖 Codex stdio 原生 session continuation。Cloud API 只把本轮原始 `message/prompt` 和显式 session 的标准 `conversationId/sessionId/threadId` 写入 AgentRun command payload 与 `SessionRef`;不得从请求、account workspace 或 account conversation 生成 `conversationContext`,不得把历史消息拼入 prompt,也不得把请求体里的 `conversationContext/messages` 当作模型上下文。历史 conversation facts 只用于 UI、inspect、trace 和 `--from-trace` 的可见性证据;收到 synthetic context 字段时只能记录 ignored trace 并剥离。`thread/resume` 失败时按 AgentRun `thread-resume-failed` 终止本轮并标记当前 session failed/stale,不自动创建新 session。 - Code Agent 不允许存在 turn/session/conversation 总时长 timeout;只允许无新 app-server 响应、无 notification、无 assistant/tool/event activity 的 idle timeout。AgentRun command 失败、provider 失败或 idle timeout 只终结当前 command,并按 session policy 标记当前 session 状态;系统不得自动滚动到新 session。后续消息要么继续同一个 usable session/thread,要么由用户显式创建或选择另一个 session。 - runner pod 被删、runner Job 被重建或旧 lease 失效后,同一 HWLAB session 的恢复判定必须基于同一个 `sessionId`、`threadId`、`providerProfile/backendProfile`、AgentRun `SessionRef` 和 PVC。replacement run/job 只能作为恢复执行壳;Cloud API 仍要把它映射为同一业务 session 的后续 turn,并在 trace/result 中分离业务 session identity 与执行 run/job identity。长期目标仍是在 runner reuse window 内复用同一 AgentRun run/runner。 - Cloud API 通过 AgentRun v0.1 `runner-jobs.transientEnv` 传递本次 Code Agent turn 的短期上下文,例如 `HWLAB_RUNTIME_*`、`HWLAB_CODE_AGENT_ASSEMBLED_RUNTIME` 和映射到当前 session owner 的 `HWLAB_API_KEY`。`transientEnv` 不设固定 8 项上限,新增短期上下文时必须按 name 去重、只传本次 Job 需要的 value;`HWLAB_RUNTIME_API_URL` 必须指向当前 namespace 内的 `hwlab-cloud-api` Service,`HWLAB_RUNTIME_WEB_URL` 才指向 `hwlab-cloud-web`;`HWLAB_API_KEY` 必须对应当前 `agent_sessions.owner_user_id`,并继续禁止承载 GitHub token、provider key、长期 SSH key 或其他无关 credential;文档、日志和 trace 只允许保留脱敏后的 name、来源或摘要,不打印 Secret 值。 - AgentRun runner 工具环境注入必须经过 OpenFGA tool capability check。用户没有 `tool:hwpod#can_use` 时不注入可用 `HWLAB_API_KEY` 给 `hwpod`;没有 `tool:unidesk_ssh#can_use` 时不注入 UniDesk SSH token、route alias 或相关 helper;没有 `tool:trans_cmd#can_use` 时不暴露 trans cmd 透传入口;GitHub issue/PR 写工具也必须独立授权。工具授权不绕过 UniDesk route/operation 边界。 - 同 Pod sidecar `hwlab-codex-api-forwarder` 监听 `127.0.0.1:49280/responses`,用于 `codex-api` profile 直连 hyueapi,并保持 hyueapi 在 `NO_PROXY` 中。 - `hwlab-code-agent-workspace` PVC 挂载到 `/workspace/hwlab`,用于长会话 workspace;它是 cloud-api 运行资源,不是独立用户入口。 ## API 接口说明 | 接口 | 说明 | | --- | --- | | `GET /health`、`GET /health/live` | 返回 service identity、environment、revision、DB/runtime/Code Agent readiness 和 blocker。 | | `GET /live` | 轻量 live 标记。 | | `GET /v1` | REST adapter 索引、RPC 方法、runtime readiness 和 device-pod/M3 能力摘要。 | | `POST /rpc`、`POST /json-rpc` | JSON-RPC 入口,支持 system、adapter、gateway、hardware、audit、evidence 和 M3 方法。 | | `POST /v1/rpc/{method}` | REST 到 JSON-RPC 的桥接入口。 | | `GET /v1/device-pods...` | 经 cloud-api 鉴权后读取服务端 profile/OpenFGA relation/job authority;probe GET 会创建只读 device job 并经 `hwlab-device-pod` executor/gateway 执行或返回同源 blocker,不会回退到 fake device pod 数据。 | | `GET /auth/oidc/login`、`GET /auth/oidc/callback`、`GET /auth/session`、`POST /auth/logout` | Keycloak OIDC、Web session 24 小时轮换和 logout 入口,最终规格见 [spec-v02-auth.md](spec-v02-auth.md)。 | | `GET /v1/auth/session`、`GET /v1/users/me`、`GET /v1/access/status`、`GET /v1/setup/status` | v0.2 用户/session/setup 的 REST 状态和兼容入口;响应不得暴露 password hash、session token 原文或 Secret 值。 | | `GET/POST /v1/api-keys...` | 用户 API key 管理入口;CLI 和 AgentRun runner 内 `hwpod` 都使用 `HWLAB_API_KEY`,映射到用户后再按权限表授权。 | | `POST /v1/admin/users`、`POST/PUT /v1/admin/device-pods` | `admin` 管理用户和 device pod profile 的入口。 | | `GET/PATCH/PUT/DELETE /v1/admin/access...` | Admin Access API;唯一正式授权管理入口,读写 OpenFGA 细粒度授权、tool capability、role/status 和 effective matrix。 | | `POST /v1/admin/access/check` | 管理员调试授权 check;返回 mode、decision、object/relation 和 redacted actor,不返回 OpenFGA token。 | | `GET/PUT/POST /v1/admin/provider-profiles...` | Provider API Key 管理入口;Cloud API 鉴权和审计后委托 AgentRun 后端,返回脱敏 profile 状态、SecretRef 摘要和 canary 结果,不返回 Secret value。 | | `GET/PATCH /v1/workbench/workspace`、`POST /v1/workbench/workspace/{id}/reset`、`GET /events` | 账号级共享 workspace authority;所有读写按 ownerUserId 隔离,写入使用 revision 观测冲突,active trace 只表示最近活动/当前选中 trace,不作为同账号 Code Agent 并发互斥锁。 | | `POST /v1/agent/sessions`、`GET/PATCH /v1/agent/sessions...` | 显式 Code Agent session 生命周期入口;创建/选择/状态标记 session,并与账号 workspace selection 同步。 | | `GET /v1/m3/status`、`POST /v1/m3/io` | M3 只读/受控 IO 入口;写操作必须有明确 approval。 | | `GET /v1/diagnostics/gate`、`GET /v1/live-builds` | 诊断和 live build inventory。 | | `GET /v1/gateway/sessions`、`POST /v1/gateway/poll`、`POST /v1/gateway/result` | gateway 主动出站注册、取任务和回传结果。 | | `POST /v1/internal/device-pod/gateway-dispatch` | 仅接受 `hwlab-device-pod` 内部服务凭据,用于把 executor job dispatch 到 gateway poll/result;普通用户和 Code Agent 不可调用。 | | `POST /v1/agent/chat`、`POST /v1/agent/chat/steer`、`GET /v1/agent/chat/result/{traceId}`、`GET /v1/agent/chat/trace/{traceId}`、`POST /v1/agent/chat/cancel` | Code Agent 短连接提交、运行中 steer、轮询、trace 和取消;普通 chat 必须绑定显式 usable session。 | ### `/v1/live-builds` 语义 `/v1/live-builds` 返回的是运行面服务清单,不是 Cloud Web 专属 health。`latest` 字段按所有 HWLAB runtime service 的 build time 排序,可能指向 `hwlab-cloud-api`、`hwlab-cloud-web`、`hwlab-agent-skills` 或其他本轮最近滚动的服务;当 API 比 Web 更新时,`latest.serviceId=hwlab-cloud-api` 是正常状态,不能据此判断 Cloud Web 构建时间缺失或 Web rollout 失败。 需要验证 Cloud Web 时必须读取 `services[]` 中 `serviceId=hwlab-cloud-web` 的行,并区分 `build.createdAt`、`image.tag/digest`、`commit.id`、`revision` 和 `build.liveMetadataMatch`。`/health/live` 只能证明当前服务 health/revision;Cloud Web 顶部 build chip 和详情弹窗展示的构建时间、env image 和实际 runtime commit 以 `/v1/live-builds` 的对应 service row 为准。 登录鉴权 API 的最终规格见 [spec-v02-auth.md](spec-v02-auth.md);用户、权限、device-pod 管理 API 的最终规格见 [spec-user-access.md](spec-user-access.md) 和 [spec-device-pod.md](spec-device-pod.md)。 OpenFGA、Access API、工具能力和 shadow/enforce 策略的最终规格见 [spec-v02-openfga-authorization.md](spec-v02-openfga-authorization.md)。 ## 测试规格 ## T1 阅读 docs/reference/spec-v02-hwlab-cloud-api.md,然后用 cli 手动测试以下内容:访问 `http://74.48.78.17:19667/health/live`,确认 `environment=v02`、`ready=true`、DB runtime 为 Postgres,且响应不包含 Secret 值。 ## T2 阅读 docs/reference/spec-v02-hwlab-cloud-api.md,然后用 cli 手动测试以下内容:先显式创建 Code Agent session,再使用短连接 `POST /v1/agent/chat` 绑定该 session 提交一次对话,并轮询 `/v1/agent/chat/result/{traceId}`;只有 `status=completed` 且 assistant reply 非空才算 Code Agent 通过。未绑定 session 的普通 chat 必须返回 `session_required`。对运行中请求使用 `POST /v1/agent/chat/steer` 时,响应必须包含 target trace、steerTraceId、AgentRun runId/targetCommandId/steerCommandId 和继续观察原 trace 的 result/trace URL。 ## T2.1 阅读 docs/reference/spec-v02-hwlab-cloud-api.md,然后用 cli 手动测试以下内容:在同一个显式 `conversationId/sessionId/threadId` 中先发起一轮会产生可见 assistant/tool 进展的 Code Agent 请求并取消,再发送“回答一下刚才调查结果”这类后续消息;如果 session 仍 usable,第二轮必须继续使用同一个标准 `threadId`,且 trace/result 不能混入上一 command 的尾部事件。如果取消导致 session failed/stale,API 必须拒绝继续并要求显式创建新 session,不能自动滚动。 ## T2.2 阅读 docs/reference/spec-v02-hwlab-cloud-api.md,然后用 cli 手动测试以下内容:在同一个显式 `conversationId/sessionId/threadId` 中先完成一轮“看看 device-pod 可用性?”这类 Code Agent 请求,再发送“总结我们刚才的对话内容”;第二轮 AgentRun command 必须只包含本轮原始 message/prompt、同一个标准 threadId 和必要运行元数据,不得包含 `conversationContext/messages` 或历史 prompt 拼接。assistant reply 必须通过 Codex stdio 原生 `thread/resume` 记住第一轮,不能回答“这是本会话第一条消息”或等价的新会话结论;若 `thread/resume` 失败,应以 `thread-resume-failed` 终止并标记 session failed/stale,继续前必须显式创建新 session。 ## T3 阅读 docs/reference/spec-v02-hwlab-cloud-api.md,然后用 cli 手动测试以下内容:未登录访问 `/v1/device-pods` 必须返回认证错误;登录后访问 device-pod list/status 时必须显示 `contractVersion=device-pod-authority-v1` 和 `fake=false` 来源,不得出现 fake fallback。 ## T4 阅读 docs/reference/spec-v02-hwlab-cloud-api.md 和 docs/reference/spec-v02-openfga-authorization.md,然后用 cli 手动测试以下内容:用 admin 调用 `GET /v1/admin/access/summary` 和 `POST /v1/admin/access/check`,确认 OpenFGA mode/readiness/store/model/decision 可见且 Secret 不泄漏;普通用户调用 admin access API 必须返回 403。 ## T4.1 阅读 docs/reference/spec-v02-provider-management.md,然后用 cli 手动测试以下内容:用 admin 调用 `GET /v1/admin/provider-profiles`、`PUT /v1/admin/provider-profiles/deepseek/credential` 和 `POST /v1/admin/provider-profiles/deepseek/validate`,确认 Cloud API 先恢复 HWLAB actor,再委托 AgentRun 后端,响应只包含 SecretRef/resourceVersion/hash 后缀和 validation/run/command/job/trace 摘要;普通用户或未登录请求必须返回 auth/authorization blocker,响应、日志和 trace 不输出 API Key 原文。 ## T5 阅读 docs/reference/spec-v02-hwlab-cloud-api.md 和 docs/reference/spec-v02-openfga-authorization.md,然后用 cli 手动测试以下内容:撤销某用户 `tool:unidesk_ssh` 或 `tool:hwpod` 后创建 Code Agent session,确认 runner transient env 和 trace 摘要不再注入对应工具能力,强行调用返回结构化 authorization blocker。 ## 规格的实现情况 | 规格项 | 状态 | 说明 | | --- | --- | --- | | health/readiness | 已实现 | `/health/live` 汇总 DB、runtime、Code Agent 和 blocker。 | | Code Agent 短连接 submit/steer/result/trace/cancel | 已实现 | repo-owned Codex stdio、运行中 steer 和 provider profile 已接入。 | | 显式 Code Agent session 生命周期 | 目标状态 | `/v1/agent/sessions*` 管理 session create/select/status;`/v1/agent/chat` 无 session 时返回 `session_required`,失败 session 不自动滚动。 | | Postgres durable runtime | 已实现 | 通过 v02 独立 DB SecretRef 和 migration ledger 判定。 | | gateway outbound poll/result | 已实现 | 支持 gateway 主动轮询和 `hardware.invoke.shell` 分发。 | | device-pod 正式权限/profile/job | 部分实现 | profile/relation/list/status/job 持久化在 cloud-api;用户态 probe GET 已收敛为只读 job;已提供内部 gateway dispatch route 供 `hwlab-device-pod` executor 下发到 device-host-cli,无在线 gateway/device-host-cli 时返回 blocker。 | | v0.2 登录鉴权与 admin/user 权限模型 | 部分实现 | 当前 `/auth/*` 是本地账号密码 bootstrap fallback 和 Web session;Keycloak OIDC、24 小时 Web session、CLI API key 和 `AuthPrincipal` 归一仍需按 spec-v02-auth 收敛。admin user/device-pod/relation API 和 Code Agent owner binding 已接入;生产 bootstrap 依赖 SecretRef。 | | OpenFGA 细粒度授权与 Admin Access API | 目标状态 | 需要实现 OpenFGA client/bootstrap、`enforce`、`/v1/admin/access*`、tool capability check 和 runner env 过滤。 | | Provider API Key 管理委托 | 目标状态 | 需要实现 `/v1/admin/provider-profiles*`,由 HWLAB 鉴权后委托 AgentRun 后端管理 profile Secret/配置和 canary。 | | 账号共享 workspace authority | 已实现 | `account_workspaces` 持久化同账号 Web/CLI 共享 workspace,支持 revision 冲突可见性、账号隔离、最近 active trace 记录和 reset;同一用户不同 session/run 允许并发,互斥只在 session/thread/run 层处理。 |