# 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 控制、HWPOD node-ops 转发和 live build inventory。
- 是 `hwlab-cloud-web`、Code Agent session、HWPOD CLI、Admin Access API、AgentRun 工具注入和 gateway outbound poll 的唯一应用层收口点；普通用户不直接访问 OpenFGA Service 或 host 侧 node 资源。
- 读取 `hwlab-cloud-api-v02-db/database-url`、`hwlab-v02-master-server-admin-api-key/api-key`、`hwlab-v02-code-agent-provider/openai-api-key` 和 `hwlab-v02-code-agent-codex-auth/auth.json` 等 v02 独立 SecretRef；用户 API key 存在 Postgres `api_keys`，HWPOD 用户态授权只能从该表恢复到用户 actor。master server 管理员 API key 通过 bootstrap env 同步为 `usr_v02_admin` 的固定 `key_master_server_admin`，只记录 SecretRef、key id、scope 摘要、字节数或哈希指纹，不记录值。
- 读取 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、HWPOD node-ops、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、tool capability 和 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 可见字段。AgentRun dispatch、dynamic profile slug 和 nested child `spawn` env-only 继承的权威规则见 [agentrun-code-agent-dispatch.md](agentrun-code-agent-dispatch.md)。
- `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`、`HWLAB_CODE_AGENT_PROVIDER_PROFILE`、`HWLAB_CODE_AGENT_PARENT_TRACE_ID` 和映射到当前 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、HWPOD node-ops 和 M3 能力摘要。 |
| `POST /rpc`、`POST /json-rpc` | JSON-RPC 入口，支持 system、adapter、gateway、hardware、audit、evidence 和 M3 方法。 |
| `POST /v1/rpc/{method}` | REST 到 JSON-RPC 的桥接入口。 |
| `POST /v1/hwpod-node-ops` | 接收 `hwpod-node-ops-v1` plan，经 cloud-api 鉴权和工具能力校验后转发到 `hwpod-node` 执行，返回 node/action/result/blocker 摘要；不回退到 fake 数据或旧内部 executor。 |
| `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` | `admin` 管理用户 role/status 的入口。 |
| `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/DELETE /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/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)；用户、权限和工具能力管理 API 的最终规格见 [spec-user-access.md](spec-user-access.md)。HWPOD 概念体系见 [spec-hwpod-harness.md](spec-hwpod-harness.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` 中先完成一轮“检查 HWPOD node 状态”这类 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 手动测试以下内容：未登录访问 `POST /v1/hwpod-node-ops` 必须返回认证错误；登录后提交 `node.health` 只读 plan 时必须显示 `contractVersion=hwpod-node-ops-v1`、目标 node/action 摘要和真实 blocker/result，不得出现 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 | 已实现 | AgentRun v0.1 delegated execution、运行中 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` 分发。 |
| HWPOD node-ops 转发 | 已实现 | `POST /v1/hwpod-node-ops` 接收 `hwpod-compiler-cli` 生成的 plan，经 cloud-api 转发到 `hwpod-node`；无在线 node 时返回结构化 blocker。 |
| Provider API Key 管理委托 | 已实现 | `/v1/admin/provider-profiles*` 由 HWLAB 鉴权后委托 AgentRun 后端管理 profile Secret/配置和 canary；HWLAB 不直写 AgentRun Secret。 |
| v0.2 登录鉴权与 admin/user 权限模型 | 部分实现 | 当前 `/auth/*` 是本地账号密码 bootstrap fallback 和 Web session；Keycloak OIDC、24 小时 Web session、CLI API key 和 `AuthPrincipal` 归一仍需按 spec-v02-auth 收敛。admin user/tool capability API 和 Code Agent owner binding 已接入；生产 bootstrap 依赖 SecretRef。 |
| OpenFGA 细粒度授权与 Admin Access API | 目标状态 | 需要实现 OpenFGA client/bootstrap、`enforce`、`/v1/admin/access*`、tool capability check 和 runner env 过滤。 |
| 账号共享 workspace authority | 已实现 | `account_workspaces` 持久化同账号 Web/CLI 共享 workspace，支持 revision 冲突可见性、账号隔离、最近 active trace 记录和 reset；同一用户不同 session/run 允许并发，互斥只在 session/thread/run 层处理。 |

