diff --git a/AGENTS.md b/AGENTS.md index c96c2159..ab06aed8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -70,9 +70,9 @@ HWLAB 是硬件实验室运行面和控制面项目。本文是 agent、指挥 - 规格文档是微服务、稳定外部服务、短连接 CLI 和系统能力的权威出处;代码开发和测试代码编写必须先对齐对应 `docs/reference/spec-*.md`,再修改实现或测试。 - v0.2 服务规格总览、保留服务清单、稳定外部服务边界和废弃范围:[docs/reference/spec-v02-services.md](docs/reference/spec-v02-services.md);`hwlab-gateway-simu`、`hwlab-box-simu` 和 `hwlab-patch-panel` 已废弃,不再保留 spec。 - v0.2 登录与鉴权规格、Keycloak OIDC、Web session 和 CLI API key:[docs/reference/spec-v02-auth.md](docs/reference/spec-v02-auth.md)。 -- v0.2 用户和权限管理规格、code agent session 归属和 device pod 授权:[docs/reference/spec-user-access.md](docs/reference/spec-user-access.md)。 +- v0.2 用户和权限管理规格、code agent session 归属和 hwpod 授权:[docs/reference/spec-user-access.md](docs/reference/spec-user-access.md)。 - v0.2 OpenFGA 细粒度授权、Admin Access 管理页和同路径 CLI 规格:[docs/reference/spec-v02-openfga-authorization.md](docs/reference/spec-v02-openfga-authorization.md)。 -- Device Pod 正式规格、profile 服务端权威、REST/job 和多 devicePodId 服务口径:[docs/reference/spec-device-pod.md](docs/reference/spec-device-pod.md)。 +- HWPOD Harness 当前规格,定义 `hwpod`、`hwpod-spec`、`hwpod-cli`、`hwpod-ctl`、`hwpod-compiler-cli`、`hwpod-node-ops` 和 `hwpod-node`:[docs/reference/spec-hwpod-harness.md](docs/reference/spec-hwpod-harness.md)。 - v0.2 CI/CD 加法 lane、`v0.2-gitops`、`hwlab-v02` 和 `19666/19667` 规格:[docs/reference/spec-v02-cicd.md](docs/reference/spec-v02-cicd.md)。 - v0.2 `hwlab-cloud-api` API 核心服务规格:[docs/reference/spec-v02-hwlab-cloud-api.md](docs/reference/spec-v02-hwlab-cloud-api.md)。 - v0.2 `hwlab-cloud-web` 浏览器工作台规格:[docs/reference/spec-v02-hwlab-cloud-web.md](docs/reference/spec-v02-hwlab-cloud-web.md)。 @@ -80,8 +80,7 @@ HWLAB 是硬件实验室运行面和控制面项目。本文是 agent、指挥 - v0.2 Code Agent 由 `hwlab-cloud-api` 接入 AgentRun v0.1 共享执行基础设施,不再保留 HWLAB 自有 agent manager/worker 控制面:[docs/reference/agentrun-code-agent-dispatch.md](docs/reference/agentrun-code-agent-dispatch.md)。 - v0.2 `hwlab-agent-skills` 技能包服务规格:[docs/reference/spec-v02-hwlab-agent-skills.md](docs/reference/spec-v02-hwlab-agent-skills.md)。 - v0.2 `hwlab-cli` 固定 repo 短连接 client 规格:[docs/reference/spec-v02-hwlab-cli.md](docs/reference/spec-v02-hwlab-cli.md)。 -- HWPOD Harness 快速迭代规格,定义 workspace-local `hwpod-spec`、`hwpod-cli`、`hwpod-ctl`、`hwpod-compiler-cli`、`hwpod-node-ops` 和 v0.2 Device Pod 迁移映射:[docs/reference/spec-hwpod-harness.md](docs/reference/spec-hwpod-harness.md)。 -- v0.2 `hwlab-device-pod` 部署服务规格:[docs/reference/spec-v02-hwlab-device-pod-service.md](docs/reference/spec-v02-hwlab-device-pod-service.md)。 +- Device Pod 迁移对照只用于识别旧命名和 API 残留,不能作为当前概念或新开发规格:[docs/reference/spec-device-pod.md](docs/reference/spec-device-pod.md)。 - v0.2 `hwlab-gateway` 硬件 transport 边界规格:[docs/reference/spec-v02-hwlab-gateway.md](docs/reference/spec-v02-hwlab-gateway.md)。 - v0.2 `hwlab-edge-proxy` API edge proxy 规格:[docs/reference/spec-v02-hwlab-edge-proxy.md](docs/reference/spec-v02-hwlab-edge-proxy.md)。 - v0.2 Observability Monitoring 接入规格,应用侧 `/metrics`、ServiceMonitor、PrometheusRule 和 G14 共享监控边界:[docs/reference/spec-v02-observability-monitoring.md](docs/reference/spec-v02-observability-monitoring.md)。 @@ -106,7 +105,7 @@ HWLAB 是硬件实验室运行面和控制面项目。本文是 agent、指挥 - AgentRun 手动调度装配、UniDesk SSH passthrough 与 GitHub tool credential 边界:[docs/reference/agentrun-code-agent-dispatch.md](docs/reference/agentrun-code-agent-dispatch.md) - DEV runtime hotfix runbook 与只读审计:[docs/reference/dev-runtime-hotfix-runbook.md](docs/reference/dev-runtime-hotfix-runbook.md) - Gateway 主动出站 demo、poll/result 和本地 smoke:[docs/reference/gateway-outbound-demo.md](docs/reference/gateway-outbound-demo.md) -- Device Pod 旧链接兼容入口:[docs/reference/device-pod.md](docs/reference/device-pod.md) +- Device Pod 旧链接兼容入口,只指向 HWPOD 当前规格和迁移对照:[docs/reference/device-pod.md](docs/reference/device-pod.md) - MVP E2E 验收测试与带编号测试报告 issue 规则:[docs/reference/MVP-e2e-acceptance.md](docs/reference/MVP-e2e-acceptance.md) - 指挥官协作、PR 和 runner 交接:[docs/reference/commander-collaboration.md](docs/reference/commander-collaboration.md) - M3 闭环发布运行手册:[docs/reference/m3-loop-rollout-runbook.md](docs/reference/m3-loop-rollout-runbook.md) diff --git a/docs/reference/agentrun-code-agent-dispatch.md b/docs/reference/agentrun-code-agent-dispatch.md index 75442d17..451cd0ff 100644 --- a/docs/reference/agentrun-code-agent-dispatch.md +++ b/docs/reference/agentrun-code-agent-dispatch.md @@ -29,17 +29,24 @@ - UniDesk SSH passthrough 通过 `toolCredentials[].tool=unidesk-ssh` 注入,默认 SecretRef 是 `agentrun-v01-tool-unidesk-ssh` key `UNIDESK_SSH_CLIENT_TOKEN`。 - `UNIDESK_SSH_CLIENT_TOKEN` 只授予 UniDesk frontend `/ws/ssh` scoped client 能力;route allowlist 由 UniDesk frontend 配置控制。HWLAB 不持有 provider token、主 server SSH key 或完整 frontend 登录态。 - `runnerJob.transientEnv` 只能承接本次 runner job 需要的短期执行上下文,例如 owner-scoped `HWLAB_API_KEY`、`HWLAB_RUNTIME_API_URL` 和 `UNIDESK_MAIN_SERVER_IP`;敏感项必须标记 sensitive,不得承载 GitHub token、UniDesk SSH client token、provider key、长期 SSH key 或 registry token。 -- HWLAB v0.2 device-pod 能力在 runner 内只通过 `hwpod -> HWLAB_RUNTIME_API_URL -> hwlab-cloud-api` 进入。`HWLAB_RUNTIME_API_URL` 指向当前 namespace 的 `hwlab-cloud-api` Service;`HWLAB_RUNTIME_WEB_URL` 仅供需要浏览器同源语义的工具查看 Cloud Web。AgentRun 不以 Cloud Web 代理替代设备 API。 +- HWLAB v0.2 HWPOD 能力在 runner 内只通过 `hwpod -> HWLAB_RUNTIME_API_URL -> hwlab-cloud-api` 进入。`HWLAB_RUNTIME_API_URL` 指向当前 namespace 的 `hwlab-cloud-api` Service;`HWLAB_RUNTIME_WEB_URL` 仅供需要浏览器同源语义的工具查看 Cloud Web。AgentRun 不以 Cloud Web 代理替代设备 API。 + +## 当前权限装配状态 + +- Runner 中的 HWLAB 用户凭据只允许是 cloud-api 为当前 Code Agent session owner 选择或创建的用户级 `HWLAB_API_KEY`。该 key 恢复为同一个 `users.id`,不等同于残留执行链路内部 service token、OpenFGA token、Keycloak token 或 Web cookie。 +- Tool surface 由 OpenFGA tool capability 决定。缺少 `tool:hwpod#can_use` 时不暴露可用 `hwpod` 环境;缺少 `tool:unidesk_ssh#can_use` 时不注入 UniDesk SSH tool credential;缺少 `tool:trans_cmd#can_use` 时不暴露 trans cmd 透传入口。 +- `tool:trans_cmd` 只允许进入 UniDesk 受控 passthrough 命令面;实际 route、operation、写操作和 Secret 可见性仍受 UniDesk CLI/route 边界以及当前任务授权限制。 +- 即使 runner 侧误尝试调用未授权工具,cloud-api 仍是 HWPOD、Code Agent session 和 Admin Access 的最终 enforcement point,必须返回结构化 authorization blocker。 ## ResourceBundle - AgentRun run 必须使用 Git-only `resourceBundleRef`,默认 repo 是 `http://git-mirror-http.devops-infra.svc.cluster.local/pikasTech/HWLAB.git`。 - `commitId` 必须是完整 40 字符小写 SHA,不接受 branch、tag、`HEAD` 或短 SHA。 - `workspaceRef` 只描述目标 repo/branch,默认 branch 是 `v0.2`;实际 checkout 身份以 `resourceBundleRef.repoUrl + commitId` 为准。 -- 默认 `toolAliases` 会把 `tools/device-pod-cli.mjs` 暴露为 `hwpod`,并把 `tools/unidesk-ssh.mjs` 暴露为 runner shell 内的 `unidesk-ssh` 命令。Device Pod、D601-F103-V2、Keil build/download、job status/output 和 UART/debug-probe 操作必须优先走 `hwpod`;`unidesk-ssh` 只用于 UniDesk passthrough 任务,不能替代 Device Pod 正式路径。 -- Device Pod lease 不是独立 runner 控制命令;标准 `hwpod` surface 只提交命名 Device Pod job。需要 lease 时,token 只能作为 `--lease-token` 跟随实际 mutating job 请求转发给 cloud-api。Agent 不得调用 `hwpod lease ...`,也不得新增 gateway shell 或其他 fallback 来绕过 job authority。 +- 默认 `toolAliases` 会把迁移期 CLI shim 暴露为 `hwpod`,并把 `tools/unidesk-ssh.mjs` 暴露为 runner shell 内的 `unidesk-ssh` 命令。HWPOD、D601-F103-V2、Keil build/download、job status/output 和 UART/debug-probe 操作必须优先走 `hwpod`;`unidesk-ssh` 只用于 UniDesk passthrough 任务,不能替代 HWPOD 正式路径。 +- HWPOD lease 不是独立 runner 控制命令;标准 `hwpod` surface 只提交命名 HWPOD job。需要 lease 时,token 只能作为 `--lease-token` 跟随实际 mutating job 请求转发给 cloud-api。Agent 不得调用 `hwpod lease ...`,也不得新增 gateway shell 或其他 fallback 来绕过 job authority。 - 默认 `promptRefs` 指向 `internal/agent/prompts/hwlab-v02-runtime.md`,`inject=thread-start` 且 `required=true`。该 prompt 只在 AgentRun/Codex stdio 新 thread 首轮注入;后续 turn 必须依赖原生 `thread/resume`,不得在 command payload 中拼接历史、旧 skill 列表或长业务 prompt。 -- 默认 `skillRefs` 指向 `skills/device-pod-cli/SKILL.md` 和 `skills/hwlab-agent-runtime/SKILL.md`,都为 `required=true`,由 AgentRun 聚合到当前 workspace `.agents/skills//SKILL.md`。HWLAB 不再依赖 `/app/skills`、hostPath、默认 Codex skill registry、ConfigMap 或用户长 prompt 作为 skill 注入 fallback。 +- 默认 `skillRefs` 仍可引用迁移期 `skills/device-pod-cli/SKILL.md` 和 `skills/hwlab-agent-runtime/SKILL.md`,但 runner 中的当前业务入口只暴露为 `hwpod`;这两个 skill 由 AgentRun 聚合到当前 workspace `.agents/skills//SKILL.md`。HWLAB 不再依赖 `/app/skills`、hostPath、默认 Codex skill registry、ConfigMap 或用户长 prompt 作为 skill 注入 fallback。 - AgentRun runtime image 已预装 `gh`,结合 `tool=github` 注入的 `GH_TOKEN` 即可访问 HWLAB/UniDesk PR 与 issue;不得把 GitHub token 放入 prompt 或 `transientEnv`。 ## 验收 @@ -47,5 +54,5 @@ - 源码合同测试:`node --test internal/agent/agentrun-dispatch.test.mjs`。 - 语法检查:`node --check internal/agent/agentrun-dispatch.mjs && node --check internal/agent/agentrun-dispatch.test.mjs`。 - 合同必须证明 `UNIDESK_SSH_CLIENT_TOKEN` 不出现在 `transientEnv`,GitHub/UniDesk SSH 能力都通过 AgentRun `toolCredentials` SecretRef 装配,且 runner resource bundle 默认暴露 `hwpod`、`unidesk-ssh`、`promptRefs` 和 `skillRefs`。 -- 真实 CLI 验收默认使用短 prompt 走 `backendProfile=deepseek`:首轮 prompt “不调用工具的情况下,你可见的 skill 有哪些?”应能回答 HWLAB bundle skill;同一会话 continuation 应显示 AgentRun 原生 resume 语义且不重复注入 initial prompt;“编译 D601-F103-V2”应触发 `hwpod` Device Pod 路径,若失败则报告正式 blocker,不切换 fallback。 +- 真实 CLI 验收默认使用短 prompt 走 `backendProfile=deepseek`:首轮 prompt “不调用工具的情况下,你可见的 skill 有哪些?”应能回答 HWLAB bundle skill;同一会话 continuation 应显示 AgentRun 原生 resume 语义且不重复注入 initial prompt;“编译 D601-F103-V2”应触发 `hwpod` HWPOD 路径,若失败则报告正式 blocker,不切换 fallback。 - 如果 DeepSeek profile 的 trace 明确失败为 AgentRun `provider-auth-failed`,且上游错误码或消息是 `INSUFFICIENT_BALANCE` / account balance 不足,则该次 CLI 验收改用 `--provider-profile minimax-m3` 继续执行同一短 prompt 组。这个规则只替换 provider profile,不替换 AgentRun 装配标准:仍必须使用同一个 `ResourceBundleRef`、`promptRefs`、`skillRefs`、`toolAliases`、Codex stdio `thread/start` / `thread/resume`,不得拼接历史上下文,不得切回旧 HWLAB prompt/skill 注入方式,也不得用 codex-api、generic shell、gateway shell 或诊断镜像作为替代验收。 diff --git a/docs/reference/architecture.md b/docs/reference/architecture.md index b202dbc6..34572082 100644 --- a/docs/reference/architecture.md +++ b/docs/reference/architecture.md @@ -35,14 +35,14 @@ For M3 hardware proof, the required runtime participants are: - two distinct `hwlab-gateway-simu` identities; - one `hwlab-patch-panel` that owns the route decision. -真实设备目标以 `device-pod` 作为运行时能力单元。`device-pod` -统一封装 `deviceTarget`、`debugInterface`、`projectWorkspace` 和 -`ioInterface` 四要素,并通过拆分的 debug/io 接口提供受控 REST/job -能力;正式 profile authority、REST/job 和服务部署规格见 -[spec-device-pod.md](spec-device-pod.md)。 +真实设备目标当前以 HWPOD 作为硬件研发执行逻辑实体。`hwpod` +统一封装 target device、workspace、debug probe 和 io probe 四要素; +快速迭代阶段由 `hwpod-spec`、`hwpod-compiler-cli`、`hwpod-node-ops` +和 `hwpod-node` 组成,权威规格见 [spec-hwpod-harness.md](spec-hwpod-harness.md)。 +旧 `device-pod` 文档只作为迁移对照,见 [spec-device-pod.md](spec-device-pod.md)。 `v0.2` 用户和权限管理规格只保留 `admin` 和 `user` 两类基础角色:code agent -session 归属于创建用户,device pod 和工具能力由 Cloud API 通过 OpenFGA 按用户、对象和 relation 授权; +session 归属于创建用户,hwpod 和工具能力由 Cloud API 通过 OpenFGA 按用户、对象和 relation 授权; Kubernetes 只做运行时隔离和资源兜底,不承载最终用户权限。权威口径见 [spec-user-access.md](spec-user-access.md) 和 [spec-v02-openfga-authorization.md](spec-v02-openfga-authorization.md)。 diff --git a/docs/reference/device-pod.md b/docs/reference/device-pod.md index 2af630a2..c91b7b9e 100644 --- a/docs/reference/device-pod.md +++ b/docs/reference/device-pod.md @@ -1,13 +1,11 @@ -# Device Pod 参考入口 +# Device Pod 旧链接入口 -本文保留为旧链接兼容入口。HWLAB `v0.2` 正式 `device-pod` 接入规格以 [spec-device-pod.md](spec-device-pod.md) 为权威。 +本文仅保留为旧链接入口。HWLAB 当前概念系统只承认 `hwpod`,权威规格是 [spec-hwpod-harness.md](spec-hwpod-harness.md)。[spec-device-pod.md](spec-device-pod.md) 只作为迁移对照,用于识别和清理旧命名、旧 API 和旧文档残留。 关键口径: -- `device-pod` 是逻辑设备能力单元,不是 Kubernetes Pod name。 -- profile 定义 `device-pod`,因此正式接入后 profile 必须由 `admin` 和 `cloud-api` 服务端权威管理。 -- code agent 本地 `.device-pod/` profile 只属于早期 CLI MVP 闭环;正式多用户系统中不得作为路由、授权或硬件资源边界的 source of truth。 -- v0.2 第一阶段使用一个 `hwlab-device-pod` Deployment/Service 管理多个逻辑 `devicePodId`,避免过早引入 per-device k8s workload 运维压力。 -- 正式访问路径是 `device-pod-cli/cloud-web -> cloud-api -> hwlab-device-pod -> gateway -> device-host-cli -> target`。 +- 新开发、授权、CLI、WebUI 和 AgentRun 文档都必须使用 `hwpod`、`hwpod-spec`、`hwpod-cli`、`hwpod-node-ops` 和 `hwpod-node`。 +- `device-pod`、`devicePodId`、`/v1/device-pods`、`device_pods`、`hwlab-device-pod` 和 `device-pod-cli` 这类名字如果仍出现在 source/runtime 中,只表示实现残留或向后兼容 API path,不是当前产品概念。 +- 处理残留时直接删除或改写到 HWPOD 当前合同,不新增负向 gate、legacy mode 或双路径长期文档。 -实施跟踪见 [pikasTech/HWLAB#533](https://github.com/pikasTech/HWLAB/issues/533)。旧迁移计划和 CLI MVP 闭环全文已迁入该 issue 评论;其中本地 profile 权威口径不适用于正式 v0.2 多用户接入。 +需要理解历史映射时只读 [spec-device-pod.md](spec-device-pod.md)。不得从旧文档恢复旧服务规格或旧概念命名。 diff --git a/docs/reference/gateway-outbound-demo.md b/docs/reference/gateway-outbound-demo.md index 9e1280ce..fad3ad37 100644 --- a/docs/reference/gateway-outbound-demo.md +++ b/docs/reference/gateway-outbound-demo.md @@ -7,7 +7,7 @@ - `hwlab-gateway` 运行在用户 PC 或本地环境,主动访问 `hwlab-cloud-api`;cloud 不需要也不应入站访问 gateway。 - demo 采用普通 HTTP poll/result:gateway 调 `POST /v1/gateway/poll` 拉取命令,执行后调 `POST /v1/gateway/result` 回传 JSON-RPC response。 - `hardware.invoke.shell` 仍是 cloud-api 对外 RPC 方法;有在线 gateway 时通过主动出站链路派发,没有在线 gateway 时保留 `not_connected` 降级返回。 -- 当前命令执行能力只用于受限 demo;正式真实硬件控制以 [spec-device-pod.md](spec-device-pod.md) 和 [spec-user-access.md](spec-user-access.md) 为权威:用户权限由 `cloud-api` 的 `admin/user` 与 OpenFGA relation 判断;设备执行收敛到 `cloud-api -> hwlab-device-pod -> gateway`,硬件 trace/evidence/audit 只作为硬件证据链,不作为用户权限模型。 +- 当前命令执行能力只用于受限 demo;真实硬件控制概念以 [spec-hwpod-harness.md](spec-hwpod-harness.md) 和 [spec-user-access.md](spec-user-access.md) 为权威:用户权限由 `cloud-api` 的 `admin/user` 与 OpenFGA relation 判断;设备执行目标收敛到 HWPOD 的 `cloud-api/hwlab-api -> hwpod-node-ops -> hwpod-node`,硬件 trace/evidence/audit 只作为硬件证据链,不作为用户权限模型。 ## Cloud API 入口 diff --git a/docs/reference/spec-device-pod.md b/docs/reference/spec-device-pod.md index 656921e2..4f01358f 100644 --- a/docs/reference/spec-device-pod.md +++ b/docs/reference/spec-device-pod.md @@ -1,17 +1,23 @@ # Device Pod 迁移对照规格 -本文保留 HWLAB `v0.2` 既有 `device-pod` 正式接入口径,作为 HWPOD Harness 迁移对照。新的 HWPOD 业务方向以 [spec-hwpod-harness.md](spec-hwpod-harness.md) 和 [pikasTech/HWLAB#897](https://github.com/pikasTech/HWLAB/issues/897) 为准:快速迭代阶段先把 `hwpod-spec`、`hwpod-cli`、`hwpod-ctl` 和 `hwpod-compiler-cli` 放在 Code Agent workspace 内,`hwlab-api` 只做 `hwpod-node-ops` 转发,`hwpod-node` 只维护少量稳定 ops。 +本文只保留 HWLAB `v0.2` 既有 `device-pod` 口径作为 HWPOD 迁移对照。当前概念系统只承认 [spec-hwpod-harness.md](spec-hwpod-harness.md) 定义的 `hwpod`、`hwpod-spec`、`hwpod-cli`、`hwpod-ctl`、`hwpod-compiler-cli`、`hwpod-node-ops` 和 `hwpod-node`。本文不得作为当前概念或新开发规格。 -旧 `device-pod` 是一个逻辑设备能力单元,不是 Kubernetes Pod 名称,也不是 HWPOD 目标状态下的产品主概念。旧 profile/server authority 路径仍可用于现有 v0.2 兼容和迁移对照,但新增业务翻译应优先进入 `hwpod-compiler-cli`,不要继续堆在 `hwlab-device-pod` executor 或 `device-host-cli.mjs` 中。 +旧 `device-pod` 是迁移前的逻辑设备能力单元,不是 Kubernetes Pod 名称,也不是当前产品主概念。旧 profile/server authority 路径只能用于识别实现残留和迁移对照;新增业务翻译必须进入 `hwpod-compiler-cli`,不要继续堆在残留 executor 或 `device-host-cli.mjs` 中。 + +当前状态: + +- Source/runtime 中仍存在旧 REST path、DB table、CLI shim、skill path 和 workload/service 命名。它们都是实现残留,不是当前 HWPOD 概念。 +- 旧 API 仍可在过渡期承载已上线用户入口,但任何新文档、测试和实现都必须以 [spec-hwpod-harness.md](spec-hwpod-harness.md) 为目标,把旧命名逐步删除或改写到 HWPOD 当前合同。 +- 本文中出现 `device-pod`、`devicePodId`、`device_pods` 或旧服务名时,只表达迁移对照和残留状态,不表达长期目标。 实施跟踪见 [pikasTech/HWLAB#533](https://github.com/pikasTech/HWLAB/issues/533),原 `docs/plan/v02-device-pod-spec-migration.md` 和旧 device-pod MVP 计划全文已迁入该 issue 评论。 -旧的 `device-pod-cli` 本地 profile 闭环只用于 CLI MVP 和真实硬件最小验证。既有正式多用户 device-pod 路径曾收敛到: +旧的 `device-pod-cli` 本地 profile 闭环只用于 CLI MVP 和真实硬件最小验证。既有多用户路径曾收敛到: ```text browser Cloud Web UI or hwpod/device-pod-cli -> cloud-api AuthPrincipal + OpenFGA relation check --> hwlab-device-pod internal REST +-> residual internal executor REST -> gateway transport -> device-host-cli -> Keil / pyOCD / UART / target @@ -21,7 +27,7 @@ AgentRun runner 和 `hwpod` 的标准设备 API 入口是 `HWLAB_RUNTIME_API_URL ## 在系统中的职责划分 -`device-pod` 是云端可授权、可审计的逻辑设备能力单元。`hwlab-cloud-api` 是用户身份、用户 API key、grant、profile authority 和用户态 REST API 的入口;`hwlab-device-pod` 是内部执行服务;`hwlab-gateway` 只承载 transport;`device-host-cli` 只在硬件 host 侧执行 Keil、pyOCD、UART 和 workspace 操作。 +迁移对照中,旧 `device-pod` 是云端可授权、可审计的逻辑设备能力单元。当前 HWPOD 口径下,这个能力应改写为 `hwpod`,由 `hwpod-spec` 描述、由 `hwpod-compiler-cli` 翻译为 `hwpod-node-ops`,再交给 `hwpod-node` 执行。 普通用户、浏览器和 Code Agent session 不直接持有 gateway route、host workspace route、Kubernetes Service 直连能力或 profile 修改权。 @@ -29,9 +35,9 @@ AgentRun runner 和 `hwpod` 的标准设备 API 入口是 `HWLAB_RUNTIME_API_URL - 用最少组件把 `device-pod-cli` 从“本地 profile + RPC/gateway 调用”迁到“1:1 REST 请求”。 - `cloud-api` 是用户身份、用户 API key、OpenFGA relation 和 profile authority 判断入口。 -- `hwlab-device-pod` 承接设备业务:profile 校验后的运行、job 生命周期、freshness、blocker、bounded output 和 gateway 调用。 -- `device-pod-cli` 只做 selector 解析、cloud-api REST 请求和 JSON 输出;默认正式模式不读取 `.device-pod/*.json`,不保存、不上传、不修改权威 profile。 -- 第一阶段只部署一个 `hwlab-device-pod` Deployment/Service,管理多个逻辑 `devicePodId`,避免为每台设备创建独立 k8s Service/Deployment。 +- 残留 executor 只承接尚未迁走的内部执行请求;它不是当前产品概念,不拥有用户权限、profile authority 或业务翻译权。 +- `device-pod-cli` 只做 selector 解析、cloud-api REST 请求和 JSON 输出;迁移期正式调用模式不读取 `.device-pod/*.json`,不保存、不上传、不修改权威 profile。 +- 残留 runtime 如果仍有单 Deployment/Service,只作为当前实现事实;HWPOD 当前目标不以该名字定义服务。 - 普通用户和 code agent session 不获得 Kubernetes 用户、Service 直连权限、gateway route 或 host workspace route。 ## 逻辑模型 @@ -61,7 +67,7 @@ device-pod admin UI/API -> cloud-api -> device_pods.profile_json + profile_hash --> hwlab-device-pod internal execution +-> residual internal execution ``` code agent 本地文件只能作为非权威 hint/cache,最多包含: @@ -84,13 +90,20 @@ code agent 本地文件只能作为非权威 hint/cache,最多包含: - Windows workspace 路径 - probe UID、串口端口、Keil 路径等硬件路由字段 -正式 profile 必须由 `cloud-api` 从 DB 读取;`hwlab-device-pod` 不接受浏览器、code agent 或 CLI 上传的 profile 作为执行依据。若 `hwlab-device-pod` 需要 profile snapshot,应只接受 `cloud-api` 内部服务凭据转发的 snapshot,或通过内部服务凭据向 `cloud-api` 拉取。该凭据不得挂载进 code agent session Pod。 +迁移期 profile 必须由 `cloud-api` 从 DB 读取;残留 executor 不接受浏览器、code agent 或 CLI 上传的 profile 作为执行依据。若残留 executor 需要 profile snapshot,应只接受 `cloud-api` 内部服务凭据转发的 snapshot,或通过内部服务凭据向 `cloud-api` 拉取。该凭据不得挂载进 code agent session Pod。 + +当前 profile 和权限边界: + +- profile 创建和修改只走 cloud-api 管理入口,执行前先恢复 `AuthPrincipal` 并按 OpenFGA 检查 admin 或 `profile_editor` relation。 +- 残留 executor 不拥有 profile 修改 API,不保存用户权限,不从内部 service token 恢复用户 actor;它只处理 cloud-api 已授权、已脱敏或已快照的内部执行请求。 +- `hwlab-v02-device-pod-internal` 只是一条服务间调用凭据,不能作为用户 API key、CLI 凭据、runner env、Admin Access 工具能力或授权 source。 +- 用户、CLI 和 Code Agent 只能通过 cloud-api job REST 使用 HWPOD;即使本地 workspace 中存在 hint/cache,也不能改变 gateway route、host workspace、probe UID、UART port 或 OpenFGA relation。 ## 内部架构 -正式 device-pod 由 profile registry、job lifecycle、freshness/blocker、bounded output、gateway/device-host adapter 和用户 API key integration 组成。第一阶段只有一个 `hwlab-device-pod` Deployment 管理多个 `devicePodId`;profile authority、OpenFGA relation 和 `api_keys` 在 cloud-api/Postgres 中,device-pod 服务只接受 cloud-api 内部调用。 +迁移对照中的旧路径由 profile registry、job lifecycle、freshness/blocker、bounded output、gateway/device-host adapter 和用户 API key integration 组成。当前 HWPOD 目标把业务翻译上收到 `hwpod-compiler-cli`,把稳定执行下沉到 `hwpod-node-ops` / `hwpod-node`;旧 executor 名称只作为实现残留。 -当前 v02 部署中的 `hwlab-device-pod` 微服务实现情况见 [spec-v02-hwlab-device-pod-service.md](spec-v02-hwlab-device-pod-service.md)。 +当前 HWPOD 目标实现情况见 [spec-hwpod-harness.md](spec-hwpod-harness.md)。旧单服务规格不再作为权威入口。 ## Profile Shape @@ -133,7 +146,7 @@ code agent 本地文件只能作为非权威 hint/cache,最多包含: ## 数据表口径 -正式规格推荐 `device_pods` 直接保存权威 profile 和 hash,避免额外 profile 微服务: +旧规格曾推荐 `device_pods` 直接保存权威 profile 和 hash,当前只作为迁移期实现表继续说明,避免额外 profile 微服务: ```sql CREATE TABLE IF NOT EXISTS device_pods ( @@ -208,7 +221,7 @@ DELETE /v1/admin/access/users/{userId}/device-pods/{devicePodId}/{relation} - `io.uart.write` - `io.uart.jsonrpc` -`GET /debug-probe/chip-id`、`GET /io-probe/uart/1` 和 `GET /io-probe/uart/1/tail` 是用户态便捷 REST surface,但不能停留在静态面板或 fake probe;cloud-api 必须在完成 authenticate/grant 后创建对应只读 job,再经 `hwlab-device-pod` executor/gateway/device-host-cli 执行或返回同一套 blocker。 +`GET /debug-probe/chip-id`、`GET /io-probe/uart/1` 和 `GET /io-probe/uart/1/tail` 是迁移期用户态便捷 REST surface,但不能停留在静态面板或 fake probe;cloud-api 必须在完成 authenticate/grant 后创建对应只读 job,再经当前可用执行链路执行或返回同一套 blocker。 所有 job/status/output 和 probe GET 响应必须包含 `devicePodId`、`targetId`、`profileHash`、`traceId`、`operationId`、`status`、`freshness`、`blocker` 和 bounded output metadata。job output 文本默认最大 12000 bytes;超出时必须设置 `truncation.truncated=true`、`truncation.originalBytes`,并避免把完整 executor/gateway 原始输出嵌回 JSON。真实硬件响应不得把 fake、dry-run、SOURCE、LOCAL 或过期缓存标为 `DEV-LIVE`。 @@ -216,53 +229,53 @@ DELETE /v1/admin/access/users/{userId}/device-pods/{devicePodId}/{relation} | 服务 | 职责 | | --- | --- | -| `hwlab-cloud-api` | 用户身份、admin/user、用户 API key、OpenFGA relation、profile authority、用户态 REST API、转发到内部 device-pod。 | -| `hwlab-device-pod` | 多 `devicePodId` 运行 registry、profile runtime validation、job store、freshness、bounded output、gateway/device-host-cli adapter。 | +| `hwlab-cloud-api` | 用户身份、admin/user、用户 API key、OpenFGA relation、profile authority、用户态 REST API、转发到残留执行链路。 | +| 残留 executor | 迁移期内部执行壳;不作为当前 HWPOD 概念,不保存用户权限,不承接新增业务翻译。 | | `device-pod-cli` | 把 `devicePodId:surface:path operation args` 1:1 转成 cloud-api REST;不保存权威 profile、不读取本地 profile 作为默认 authority、不直连 gateway。 | | `device-host-cli` | Windows host 侧自包含业务工具,负责 Keil、pyOCD、UART、workspace 文件操作。 | -| `hwlab-gateway` | 只做受控 transport,不理解用户权限和 device-pod 授权。 | -| `hwlab-cloud-web` | 展示用户可见 device pod、admin 管理 profile/relation、显示 job/status/freshness。 | +| `hwlab-gateway` | 只做受控 transport,不理解用户权限和 HWPOD 授权。 | +| `hwlab-cloud-web` | 展示用户可见 HWPOD、admin 管理 profile/relation、显示 job/status/freshness。 | ## Kubernetes 口径 -v0.2 第一阶段使用一个 `hwlab-device-pod` Deployment 和一个 ClusterIP Service: +迁移期 runtime 曾使用一个残留 Deployment 和一个 ClusterIP Service: ```text -hwlab-v02/hwlab-device-pod +hwlab-v02/ replicas: 1 manages: many devicePodId ``` 不为每个 `devicePodId` 创建 Deployment、Service、Ingress、Secret 或 namespace。这样更符合当前规模:运维对象少、GitOps diff 少、问题定位简单,也不会把设备数量直接放大成 k8s 资源数量。 -只有在满足以下条件时,才考虑拆分为多个 `hwlab-device-pod` shard 或 per-device workload: +只有在满足以下条件时,才考虑拆分为多个 HWPOD node shard 或 per-device workload: - 单个服务内 job 队列和 freshness 监控互相影响; - 不同设备需要不同 host network、USB、Secret 或资源 request; - 设备数量增长到单实例状态管理明显吃力; - 强隔离需求超过应用层 OpenFGA relation 和内部服务凭据能覆盖的范围。 -普通用户和 code agent session Pod 不应直接调用 `hwlab-device-pod` Service。正式路径是 `code agent -> cloud-api -> hwlab-device-pod`。 +普通用户和 code agent session Pod 不应直接调用残留 executor Service。当前目标路径是 `code agent -> cloud-api/hwlab-api -> hwpod-node-ops -> hwpod-node`。 ## 验收标准 -- `device-pod-cli` 在正式模式下不读取 `.device-pod/.json` 作为权威 profile,只向 cloud-api 提交 `devicePodId`、intent 和 args。 -- 普通用户无授权时不能看到或使用任何 device pod;拥有 `viewer` 只能看到摘要,拥有 `operator/job_submitter` 才能提交对应 job,拥有 `profile_editor` 才能修改 profile。 +- `device-pod-cli` 在迁移期正式调用模式下不读取 `.device-pod/.json` 作为权威 profile,只向 cloud-api 提交 `devicePodId`、intent 和 args。 +- 普通用户无授权时不能看到或使用任何 HWPOD;拥有 `viewer` 只能看到摘要,拥有 `operator/job_submitter` 才能提交对应 job,拥有 `profile_editor` 才能修改 profile。 - code agent 不能通过修改本地文件改变 gateway session、resource、host workspace、probe UID 或 UART port。 -- `hwlab-device-pod` 不接受无内部服务凭据的 profile snapshot 或 job 请求。 -- `hwlab-device-pod` 一个实例可以列出并执行多个 `devicePodId` 的状态/job。 -- cloud-api legacy compatibility entry 只能返回 blocked authority payload,不得合成 fake device pod 数据或作为正式 device-pod DEV-LIVE 证据。 -- 强副作用 job 必须有 `reason`;正式路径只使用 Web session/cookie 或映射到具体用户的 `HWLAB_API_KEY` 做身份授权。 -- AgentRun runner 访问 device-pod 必须使用 cloud-api 组装的用户 `HWLAB_API_KEY`,该 key 恢复为发起 Code Agent session 的 owner 用户;权限继续由 OpenFGA relation 判定。 -- 撤销 device pod relation 必须同时影响该用户通过 Web session、CLI API key 和 AgentRun `hwpod` 的可见性与使用权;revoke API key 后 CLI 和 runner 内旧 key 都必须失效。 +- 残留 executor 不接受无内部服务凭据的 profile snapshot 或 job 请求。 +- 多 `devicePodId` 运行能力只作为迁移期实现事实;HWPOD 目标以 `hwpod` / `hwpod-node` 表达。 +- cloud-api legacy compatibility entry 只能返回 blocked authority payload,不得合成 fake 数据或作为 HWPOD DEV-LIVE 证据。 +- 强副作用 job 必须有 `reason`;迁移期路径也只使用 Web session/cookie 或映射到具体用户的 `HWLAB_API_KEY` 做身份授权。 +- AgentRun runner 访问 hwpod 必须使用 cloud-api 组装的用户 `HWLAB_API_KEY`,该 key 恢复为发起 Code Agent session 的 owner 用户;权限继续由 OpenFGA relation 判定。 +- 撤销 HWPOD relation 必须同时影响该用户通过 Web session、CLI API key 和 AgentRun `hwpod` 的可见性与使用权;revoke API key 后 CLI 和 runner 内旧 key 都必须失效。 ## CLI 实现口径 -`tools/device-pod-cli.ts` 是 v0.2 正式 CLI 实现;HWLAB code-agent runner 内的唯一稳定短入口是 `hwpod`。runner 镜像必须把 `hwpod` 放入 PATH;缺少 `hwpod` 时应判定为 runner image/package 错误并修复镜像或封装,不得改走 `/app/skills/device-pod-cli/scripts/device-pod-cli.mjs` 长路径。正式 CLI 的默认行为是: +`tools/device-pod-cli.ts` 是迁移期 CLI shim;HWLAB code-agent runner 内的唯一稳定短入口是 `hwpod`。runner 镜像必须把 `hwpod` 放入 PATH;缺少 `hwpod` 时应判定为 runner image/package 错误并修复镜像或封装,不得改走 `/app/skills/device-pod-cli/scripts/device-pod-cli.mjs` 长路径。迁移期 CLI 的默认行为是: - `profile list/show` 调用 cloud-api `/v1/device-pods` 和 `/status`,只显示服务端脱敏 profile 摘要和 `profileHash`。 - AgentRun runner 中只使用装配好的 `HWLAB_RUNTIME_API_URL` 和映射到当前用户的 `HWLAB_API_KEY`,直接访问 `hwlab-cloud-api`;不得手动传 `--api-base-url`,也不得改走 Cloud Web 同源代理。 -- `setup first-admin` 和 `admin device-pod upsert` 只作为 cloud-api REST wrapper,用于首次空库 seed 或 admin profile 管理;device pod 授权统一使用 `hwlab-cli client access device-pods grant/revoke` 的 Admin Access API。 +- `setup first-admin` 和 `admin device-pod upsert` 只作为 cloud-api REST wrapper,用于首次空库 seed 或 admin profile 管理;HWPOD 授权统一使用 `hwlab-cli client access device-pods grant/revoke` 的 Admin Access API。 - `devicePodId:workspace|debug-probe|io-probe...` selector 转换为 `POST /v1/device-pods/{devicePodId}/jobs` 或 job status/output/cancel REST,不直接调用 `/v1/rpc/hardware.invoke.shell`。 - `bootsharp --pod-id ` 和 `:workspace:/ bootsharp` 都转换为 `workspace.bootsharp` job,用于返回 workspace tree、AGENTS.md 摘要和当前路径提示;该入口是上下文恢复和 DS 派单的首个探测动作,不读取本地 profile。 - workspace 写操作覆盖 `apply-patch`、`put`、`rm`、`rmdir`、`build` 和 `keil add-source/remove-source` 等 Keil 工程维护动作。 @@ -277,7 +290,7 @@ manages: many devicePodId D601 Windows F103 gateway 的稳定命名使用 `gws_D601_F103`。不要因为当前 `devicePodId` 或样例设备名是 `device-pod-71-freq` / `71-FREQ`,把 gateway session 改成旧的 `gws_d601_win_71_freq`;profile route 和 Windows gateway 运行脚本必须使用同一组 F103 命名。 -当前正式接入的 F103 v2 逻辑设备名为 `D601-F103-V2`。它的 Windows workspace 固定为 `F:\Work\D601-HWLAB`,Keil 工程为 `projects/01_baseline/Projects/MDK-ARM/atk_f103.uvprojx`,Keil target 为 `USART`,Keil 可执行文件为 `C:\Keil_v5\UV4\UV4.exe`,UART 为 `COM9`/`115200`。v0.2 服务端 profile 必须使用 `devicePodId=D601-F103-V2`,不得继续把该 workspace 暴露成 `device-pod-71-freq`。 +当前 HWPOD F103 v2 profile 身份为 `D601-F103-V2`。它的 Windows workspace 固定为 `F:\Work\D601-HWLAB`,Keil 工程为 `projects/01_baseline/Projects/MDK-ARM/atk_f103.uvprojx`,Keil target 为 `USART`,Keil 可执行文件为 `C:\Keil_v5\UV4\UV4.exe`,UART 为 `COM9`/`115200`。迁移期服务端 profile 字段仍使用 `devicePodId=D601-F103-V2`,不得继续把该 workspace 暴露成 `device-pod-71-freq`。 Windows 侧固定入口: @@ -326,7 +339,7 @@ powershell -NoProfile -Command "Invoke-RestMethod http://127.0.0.1:7001/status | 通过 UniDesk Windows route 操作时,工作目录应直接定位到 gateway 目录,例如 `D601:win/c/Users/liang/device-pod-gateway-rust`,再读取 `run-D601-F103.cmd` 或查询 `/status`。`/status` 中至少应看到 `gatewaySessionId=gws_D601_F103`、`cloudUrl=http://74.48.78.17:19667`、`session.status=connected`、`outbound.lastPollError=null`。 -G14 v0.2 验收在 `G14:/root/hwlab-v02` 执行,先确认 Cloud Web 同源 CLI 能看到 device pod 状态,再用正式 `device-pod-cli` 创建并轮询 job: +G14 v0.2 迁移期验收在 `G14:/root/hwlab-v02` 执行,先确认 Cloud Web 同源 CLI 能看到当前 hwpod/profile 状态,再用迁移期 `device-pod-cli` shim 创建并轮询 job: ```bash bun tools/hwlab-cli/bin/hwlab-cli.ts client device-pods status device-pod-71-freq \ @@ -338,9 +351,9 @@ bun tools/device-pod-cli.ts device-pod-71-freq:workspace:/ ls \ --api-base-url http://74.48.78.17:19667 \ --cookie "$COOKIE" \ --reason "v02 gws_D601_F103 smoke" \ - --timeout-ms 120000 | tee /tmp/hwlab-device-pod-job.json + --timeout-ms 120000 | tee /tmp/hwlab-hwpod-job.json -JOB=$(node -e 'const fs=require("fs"); const payload=JSON.parse(fs.readFileSync("/tmp/hwlab-device-pod-job.json", "utf8")); console.log(payload.body.job.id)') +JOB=$(node -e 'const fs=require("fs"); const payload=JSON.parse(fs.readFileSync("/tmp/hwlab-hwpod-job.json", "utf8")); console.log(payload.body.job.id)') bun tools/device-pod-cli.ts job status --pod-id device-pod-71-freq "$JOB" \ --api-base-url http://74.48.78.17:19667 \ @@ -410,7 +423,7 @@ hwpod D601-F103-V2:workspace:/ rg \ ## T2 -阅读 docs/reference/spec-device-pod.md,然后用 cli 手动测试以下内容:尝试通过本地 `.device-pod/*.json` 修改 gateway route 或 workspace route,正式 cloud-api/device-pod 路径必须忽略该本地文件并继续使用服务端 profile authority。 +阅读 docs/reference/spec-device-pod.md,然后用 cli 手动测试以下内容:尝试通过本地 `.device-pod/*.json` 修改 gateway route 或 workspace route,迁移期 cloud-api path 必须忽略该本地文件并继续使用服务端 profile authority。 ## T3 @@ -418,16 +431,16 @@ hwpod D601-F103-V2:workspace:/ rg \ ## T4 -阅读 docs/reference/spec-device-pod.md,然后用 cli 手动测试以下内容:对授权 Device Pod 运行 `workspace put`、`workspace rm`、`workspace rmdir`、`workspace keil add-source/remove-source` 和 `io-probe jsonrpc` 的 `--dry-run` 与一次真实小闭环。确认请求只经过 cloud-api job REST,输出 intent、reason、traceId 和 bounded output,不读取本地 profile、不直连 gateway。 +阅读 docs/reference/spec-device-pod.md,然后用 cli 手动测试以下内容:对授权 HWPOD/迁移期 profile 运行 `workspace put`、`workspace rm`、`workspace rmdir`、`workspace keil add-source/remove-source` 和 `io-probe jsonrpc` 的 `--dry-run` 与一次真实小闭环。确认请求只经过 cloud-api job REST,输出 intent、reason、traceId 和 bounded output,不读取本地 profile、不直连 gateway。 ## 规格的实现情况 | 规格项 | 状态 | 说明 | | --- | --- | --- | | 逻辑 device-pod 模型 | 已实现为规格 | 四要素、profile shape 和 Kubernetes 口径已定义。 | -| profile server authority | 部分实现 | cloud-api 保存正式 DB profile 并向用户返回脱敏摘要;device-pod executor 不接受用户上传 profile。 | -| 用户 relation + 用户 API key | 部分实现 | cloud-api 已实现 Admin Access relation、可见性过滤和强副作用 job reason 校验;AgentRun/hwpod 仍需从旧 shared device-pod key 收敛到映射用户的 `HWLAB_API_KEY`。 | -| REST/job API | 部分实现 | cloud-api 已实现 list/status/events/probe/job/output/cancel,并可把已授权 job 转发给内部 `hwlab-device-pod` executor;executor 已实现内部 job create/get/output/cancel lifecycle 和 gateway/device-host-cli dispatch adapter,无在线 gateway/device-host-cli 时返回 blocker。 | +| profile server authority | 迁移期实现/待收敛 | cloud-api 保存 DB profile 并向用户返回脱敏摘要;残留 executor 不接受用户上传 profile。 | +| 用户 relation + 用户 API key | 已实现/持续约束 | cloud-api 已实现 Admin Access relation、可见性过滤和强副作用 job reason 校验;AgentRun/hwpod 只能使用映射到当前 owner 的用户级 `HWLAB_API_KEY`,权限继续由 OpenFGA relation 判定。 | +| REST/job API | 迁移期实现/待收敛 | cloud-api 已实现 list/status/events/probe/job/output/cancel,并可把已授权 job 转发给残留 executor;后续 HWPOD 收敛应把稳定执行改写到 `hwpod-node-ops` / `hwpod-node`。 | | G14 device-host 功能吸收 | 部分实现 | v0.2 job intent 已覆盖 workspace put/rm/rmdir、Keil 工程维护和 UART JSON-RPC,保持 cloud-api profile authority、OpenFGA relation 和用户 API key runtime auth。 | | 禁止 fake 作为 DEV-LIVE | 已实现/持续约束 | 规格和服务 payload 要求显式标记 fake/source。 | diff --git a/docs/reference/spec-hwpod-harness.md b/docs/reference/spec-hwpod-harness.md index 0c8b38e7..8dfb26cc 100644 --- a/docs/reference/spec-hwpod-harness.md +++ b/docs/reference/spec-hwpod-harness.md @@ -1,9 +1,22 @@ # HWPOD Harness 规格 -本文是 HWLAB `v0.2` 向 HWPOD Harness 迁移的长期规格。概念体系和实施 issue 见 [pikasTech/HWLAB#897](https://github.com/pikasTech/HWLAB/issues/897)。旧 Device Pod 规格和实现仍作为迁移对照保留在 [spec-device-pod.md](spec-device-pod.md),但新的业务方向以本文为准。 +本文是 HWLAB `v0.2` 的 HWPOD Harness 长期规格。概念体系和实施 issue 见 [pikasTech/HWLAB#897](https://github.com/pikasTech/HWLAB/issues/897)。当前概念系统只承认 `hwpod`;旧 Device Pod 规格和实现只作为迁移对照保留在 [spec-device-pod.md](spec-device-pod.md),不能作为当前概念或新开发规格。 当前阶段只设计核心业务闭环,不把鉴权、安全、并发、计量、复杂调度或完整 Evidence 体系作为前置条件。目标是让 Code Agent 在自己的 workspace 内先获得可观察、可修改、可快速改进的 HWPOD harness 闭环,再逐步把稳定部分平台化。 +## 当前状态 + +- 当前产品概念和新文档入口统一为 `hwpod`。`hwlab-device-pod` 不是当前概念系统里的服务名;如果 source/runtime 仍出现该名称,只表示旧实现命名残留。 +- 当前快速迭代入口已经存在 `tools/hwpod-cli.ts`、`tools/hwpod-ctl.ts`、`tools/hwpod-compiler-cli.ts`、`tools/hwpod-node.ts`、`tools/src/hwpod-node-ops-contract.ts` 和对应测试。 +- 当前 cloud-api 仍保留若干旧 REST path、DB table 和 workload/service 命名,例如 `device-pods`、`device_pods` 或 `hwlab-device-pod`。这些是实现残留,不是产品概念;后续清理目标是把稳定入口收敛到 HWPOD 当前合同。 +- 授权当前仍由 Cloud API 的 `AuthPrincipal`、Admin Access 和 OpenFGA 判定;HWPOD 只是受授权的硬件研发执行逻辑实体,不拥有独立用户身份系统。 + +## 残留命名处理 + +- 新增业务翻译、CLI 示例、WebUI 文案、AgentRun prompt、SPEC 和测试都必须写 `hwpod`,不能继续引入 `hwlab-device-pod` 作为当前服务概念。 +- 旧 API path、表名、CLI shim 或 runtime workload 名称只允许作为迁移对照出现。处理它们时直接改到 HWPOD 当前合同,不能增加 legacy mode、feature flag、负向 gate 或双路径长期说明。 +- 如果某段实现暂时仍需要旧名字才能运行,文档必须把它标为“实现命名残留”,并指向本规格作为目标;不得把残留名字写入服务总表或当前权限系统职责划分。 + ## 分阶段口径 ### 快速迭代阶段 @@ -181,7 +194,7 @@ bun tools/hwpod-node.ts serve --host 127.0.0.1 --port 19678 | `devicePodId` / server profile | workspace-local `hwpod-spec` | 先把 profile 内容迁移成 `.hwlab/hwpod-spec.yaml`,后续再上云 | | `device-pod-cli` / 旧 `hwpod` alias | `hwpod-cli` | 旧 selector/REST 入口作为迁移参考,新任务入口使用 HWPOD intent -> compiler -> node-ops | | profile 管理脚本 | `hwpod-ctl` | 变成 workspace-local spec 管理和 smoke 工具 | -| `hwlab-device-pod` executor 中的 intent -> host argv | `hwpod-compiler-cli` | 高层业务翻译先下放到 workspace-local compiler-cli 快速迭代 | +| `hwlab-device-pod` 残留 executor 中的 intent -> host argv | `hwpod-compiler-cli` | 高层业务翻译先下放到 workspace-local compiler-cli 快速迭代;残留 executor 命名不得作为当前概念保留 | | `device-host-cli.mjs` 中的高层业务拼接 | `hwpod-compiler-cli` | 能上收的命令编排和 profile 解释上收到 compiler-cli | | `device-host-cli.mjs` 中的基础执行能力 | `hwpod-node` | 稳定 op handler 留在 node 侧 | | `hwlab-gateway` / `devicepod-gateway` | `hwpod-node` 内部 transport/gateway | 不再作为产品主概念 | @@ -195,4 +208,4 @@ bun tools/hwpod-node.ts serve --host 127.0.0.1 --port 19678 2. `hwpod-compiler-cli compile` 能把同一个 spec 和高层 intent 编译为稳定 `hwpod-node-ops-v1` plan。 3. `hwpod-cli --dry-run` 输出的 plan 不需要云端 spec,也不读取旧 `.device-pod/*.json` profile authority。 4. `hwlab-api` 的 node-ops 入口能接收 plan,返回 JSON result;无可用 node 时必须返回结构化 blocker,而不是静默伪造 DEV-LIVE。 -5. 旧 Device Pod 路径仍可作为迁移对照,但新增业务翻译应优先进入 `hwpod-compiler-cli`,不要继续堆在 `hwlab-device-pod` executor 或 `device-host-cli.mjs` 中。 +5. 旧 Device Pod 路径只作为迁移对照;新增业务翻译必须进入 `hwpod-compiler-cli`,不要继续堆在残留 executor 或 `device-host-cli.mjs` 中。 diff --git a/docs/reference/spec-user-access.md b/docs/reference/spec-user-access.md index 0e9e3773..82cc5091 100644 --- a/docs/reference/spec-user-access.md +++ b/docs/reference/spec-user-access.md @@ -1,16 +1,16 @@ # v0.2 用户和权限管理规格 -本文是 HWLAB `v0.2` 用户和权限管理的规格说明。目标是用最少概念支持真实用户使用 code agent session,并让管理员能按用户独立调整 device pod、Code Agent session 和工具功能权限,同时避免把用户体系、Kubernetes 租户、设备授权、硬件证据链和审计系统混成一套复杂门禁。 +本文是 HWLAB `v0.2` 用户和权限管理的规格说明。目标是用最少概念支持真实用户使用 code agent session,并让管理员能按用户独立调整 HWPOD/profile、Code Agent session 和工具功能权限,同时避免把用户体系、Kubernetes 租户、设备授权、硬件证据链和审计系统混成一套复杂门禁。 -本规格与 [spec-device-pod.md](spec-device-pod.md) 配套:用户和权限规格定义谁可以看见、创建和使用 device pod;device-pod 规格定义 profile authority、REST/job 和硬件执行边界。 +本规格与 [spec-hwpod-harness.md](spec-hwpod-harness.md) 配套:用户和权限规格定义谁可以看见、创建和使用 HWPOD;HWPOD 规格定义当前 `hwpod`、`hwpod-spec`、node-ops 和硬件执行边界。[spec-device-pod.md](spec-device-pod.md) 只作为旧 API/table 命名的迁移对照。 -登录入口、Keycloak OIDC、Web session、CLI API key 和 `AuthPrincipal` 归一见 [spec-v02-auth.md](spec-v02-auth.md)。OpenFGA、Admin Access WebUI 和同路径 CLI 细节见 [spec-v02-openfga-authorization.md](spec-v02-openfga-authorization.md)。本文只定义认证完成后的角色、资源归属、device pod capability、tool capability 和 code agent owner 授权;正式用户鉴权只有 Web session 与 CLI/API key 两类。 +登录入口、Keycloak OIDC、Web session、CLI API key 和 `AuthPrincipal` 归一见 [spec-v02-auth.md](spec-v02-auth.md)。OpenFGA、Admin Access WebUI 和同路径 CLI 细节见 [spec-v02-openfga-authorization.md](spec-v02-openfga-authorization.md)。本文只定义认证完成后的角色、资源归属、HWPOD capability、tool capability 和 code agent owner 授权;正式用户鉴权只有 Web session 与 CLI/API key 两类。 实施跟踪见 [pikasTech/HWLAB#531](https://github.com/pikasTech/HWLAB/issues/531),原 `docs/plan/v02-multi-user-migration.md` 迁移计划全文已迁入该 issue 评论。 ## 在系统中的职责划分 -用户和权限管理不是独立微服务,权威实现收敛在 `hwlab-cloud-api`:它消费 [spec-v02-auth.md](spec-v02-auth.md) 产出的 `AuthPrincipal`,负责角色、OpenFGA check/write、用户 API key、device pod capability、tool capability 和 code agent session owner 校验。`hwlab-cloud-web` 只提供浏览器 UI 和同源代理;`hwlab-device-pod` 只执行设备语义;AgentRun v0.1 只消费 cloud-api 按用户权限注入的 actor/session/device/tool 上下文,不成为 HWLAB 用户权限 authority。 +用户和权限管理不是独立微服务,权威实现收敛在 `hwlab-cloud-api`:它消费 [spec-v02-auth.md](spec-v02-auth.md) 产出的 `AuthPrincipal`,负责角色、OpenFGA check/write、用户 API key、hwpod capability、tool capability 和 code agent session owner 校验。`hwlab-cloud-web` 只提供浏览器 UI 和同源代理;HWPOD 执行节点只执行受控硬件语义;AgentRun v0.1 只消费 cloud-api 按用户权限注入的 actor/session/hwpod/tool 上下文,不成为 HWLAB 用户权限 authority。 Postgres 是用户、session、业务对象和迁移 ledger 的持久化边界;OpenFGA 是细粒度授权关系与授权判定边界。Kubernetes namespace、ServiceAccount、Service 直连和 gateway route 都不能替代用户权限模型;普通用户不获得 kubeconfig、内部 Service 直连能力、OpenFGA token 或长期 Secret。 @@ -18,16 +18,30 @@ Postgres 是用户、session、业务对象和迁移 ledger 的持久化边界 运行时仍只通过 `hwlab-v02-bootstrap-admin/password-hash` SecretRef 注入本地 bootstrap password hash;Postgres、API 响应、日志、CLI session 和文档不得保存或输出 password hash、session token 原文或 Secret 值。 如果 live Secret 需要重建或旋转,必须保持目标 OIDC/Web session 与 API key 登录链路可用。 +## 当前实现状态 + +- 当前 v0.2 runtime 的权限相关组件包含 `hwlab-cloud-api`、`hwlab-cloud-web`、HWPOD 相关实现、OpenFGA、v0.2 Postgres 和 Keycloak 外部 issuer。`hwlab-cloud-api` 是应用层用户身份恢复、OpenFGA check/write、Admin Access 写入、hwpod/profile/job 和 Code Agent owner 校验的收口点。 +- 用户、session、API key、account workspace、HWPOD profile、HWPOD job 和访问摘要保存在 v0.2 Postgres;细粒度 relation 的判定 authority 是 OpenFGA tuple。Postgres 中的摘要或缓存不能在 OpenFGA 不可用时变成独立 allow source。 +- 当前授权管理入口是 Admin Access API、Cloud Web Access 页面和同路径 `hwlab-cli client access ...`。管理员或具备 `access_manager` 的用户通过这些入口调整 role/status、HWPOD relation 和 tool capability。 +- 迁移期 hwpod/profile 的创建和修改仍通过 `POST /v1/admin/device-pods`、`PUT /v1/admin/device-pods/{devicePodId}` 和 `profile_editor`/admin 授权完成;这些 path 名称属于实现残留。执行节点只执行 cloud-api 已授权的内部请求,不拥有用户权限判断或 profile 修改权。 +- AgentRun runner 只能收到 cloud-api 按当前 Code Agent session owner 装配的用户级 `HWLAB_API_KEY` 和已授权工具面。`trans_cmd` 是独立 tool capability,表示允许进入受控 UniDesk passthrough 命令面,不授予 Kubernetes Secret、OpenFGA token、残留执行链路内部 token 或任意控制面写权限。 + +## 当前状态收敛规则 + +- 代码、测试和长期文档只能表达当前 Web session/API key/OpenFGA/Admin Access 权限系统。发现绕过当前 `AuthPrincipal -> OpenFGA/Admin Access` 判定的授权入口、共享用户绕过凭据、兼容写分支或只为已移除路径存在的断言时,处理方式是删除或改写为当前合同。 +- 不把历史授权路径迁移成 feature flag、legacy mode、兼容表、负向测试清单或新增门禁。需要证明当前状态时,优先写当前 allow/deny 行为、当前 authority 和当前用户入口验收。 +- issue/PR 评论可以保留排障证据;`docs/reference/` 只保留当前状态、目标状态和稳定判定标准,不保存过程流水账或已移除对象名称清单。 + ## 规格目标 - 只保留两类角色:`admin` 和 `user`。 - `code agent session` 直接归属于创建它的用户;普通用户只能查看、继续和取消自己的 session。 -- `device pod` 由 `admin` 或被授予 `profile_editor` 的用户管理;普通用户只有在被授权后才能看到、操作或提交对应 device pod job。 -- device pod 授权按 `viewer`、`operator`、`profile_editor`、`job_submitter` 等 OpenFGA relation 表达。 +- HWPOD/profile 由 `admin` 或被授予 `profile_editor` 的用户管理;普通用户只有在被授权后才能看到、操作或提交对应 HWPOD job。 +- HWPOD 授权按 `viewer`、`operator`、`profile_editor`、`job_submitter` 等 OpenFGA relation 表达。 - 工具能力必须独立授权,例如 `hwpod`、`unidesk_ssh`、`trans_cmd` 和 GitHub 写工具;拥有 Code Agent session 不等于拥有这些工具。 - MVP 不新增产品级 `audit_events` 用户审计表,也不把用户权限依赖到 audit。现有硬件 trace/evidence/audit 字段属于硬件闭环证据,不是多用户权限模型的一部分。 -- 强副作用 device-pod job 只额外要求业务 `reason`;设备互斥由 executor、gateway 和硬件 host 串行化或返回 blocker,不进入用户权限模型。 -- 普通用户不获得 Kubernetes 用户、kubeconfig、namespace 管理权或直接访问 device pod Service 的权限;所有用户权限判断在 cloud-api 应用层完成。 +- 强副作用 HWPOD job 只额外要求业务 `reason`;设备互斥由 executor、gateway 和硬件 host 串行化或返回 blocker,不进入用户权限模型。 +- 普通用户不获得 Kubernetes 用户、kubeconfig、namespace 管理权或直接访问残留执行 Service 的权限;所有用户权限判断在 cloud-api 应用层完成。 - v0.2 权限数据必须与 `hwlab-dev`/`hwlab-prod` 运行数据隔离。优先在 `hwlab-v02` namespace 内使用独立 Postgres StatefulSet/PVC;若未来显式复用共享 Postgres 实例,也必须使用独立 database 或 schema、独立 Secret 和独立 migration ledger,不得直接复用 `hwlab-dev` 的 pgdata。 ## 简化边界 @@ -38,8 +52,8 @@ Postgres 是用户、session、业务对象和迁移 ledger 的持久化边界 | 用户组 | `groups`、`group_members` | 不引入 | | 项目隔离 | `projects`、`project_members` | 不引入 | | Code Agent 会话 | `ownerUserId + projectId + sessionId` | `owner_user_id + session id` | -| Device Pod 管理 | 平台管理员和设备管理员分工 | `admin` 统一管理 | -| Device Pod 授权 | 可按 group/project 授权 | 第一版只按具体 `user_id` 授权,relation 由 OpenFGA 表达 | +| HWPOD 管理 | 平台管理员和设备管理员分工 | `admin` 统一管理 | +| HWPOD 授权 | 可按 group/project 授权 | 第一版只按具体 `user_id` 授权,relation 由 OpenFGA 表达 | | 设备权限粒度 | `io.read`、`io.write` 等硬件寄存器级 capability | `viewer`、`operator`、`profile_editor`、`job_submitter` 等产品级 relation | | Viewer | 单独只读角色 | 不引入 | | Audit | 独立用户审计表 | 不引入 | @@ -68,7 +82,7 @@ CREATE TABLE IF NOT EXISTS users ( - bootstrap 阶段必须至少有一个 `admin`。 - `password_hash` 只用于 v0.2 本地 bootstrap 账号;OIDC identity 扩展字段、`api_keys` 表和 API key 规则见 [spec-v02-auth.md](spec-v02-auth.md)。接入 OIDC 后仍保留 `users.id`、`role` 和授权表稳定,不把外部 IdP subject 直接暴露给业务授权。 -- `disabled` 用户不能创建 session、继续 session 或使用 device pod。 +- `disabled` 用户不能创建 session、继续 session 或使用 HWPOD。 ### `user_sessions` @@ -109,7 +123,7 @@ CREATE INDEX IF NOT EXISTS idx_agent_sessions_conversation ON agent_sessions(con ### `device_pods` -设备能力单元的管理表;正式 profile authority 和执行语义以 [spec-device-pod.md](spec-device-pod.md) 为准。profile 定义 device pod,因此 profile 必须由 `admin` 通过 cloud-api 管理,不能由 code agent 本地 `.device-pod/` 文件决定。 +迁移期 hwpod/profile 管理表;字段名仍是实现残留,目标概念以 [spec-hwpod-harness.md](spec-hwpod-harness.md) 为准。profile/spec 必须由 `admin` 或具备 `profile_editor` 的用户通过 cloud-api 管理,不能由 code agent 本地 `.device-pod/` 文件决定。 ```sql CREATE TABLE IF NOT EXISTS device_pods ( @@ -133,11 +147,11 @@ CREATE TABLE IF NOT EXISTS device_pods ( | 创建自己的 code agent session | 可以 | 可以 | | 查看、继续、取消自己的 code agent session | 可以 | 拥有该 session 的 `viewer/operator` 时可以 | | 查看、取消别人的 code agent session | 可以 | 需要该 session 的显式 relation | -| 创建、更新、删除 device pod | 可以 | 需要目标 device pod 的 `profile_editor` | -| 给用户授权或撤销 device pod/tool | 可以 | 需要 `access_manager` | -| 查看 device pod | 可以查看全部 | 需要目标 device pod 的 `viewer` 或更高 relation | -| 使用 device pod 的 workspace/debug/io 能力 | 可以使用全部 | 需要 `tool:hwpod#can_use` 且目标 device pod 具备 `operator/job_submitter` | -| 提交强副作用 device-pod job | 必须填写 reason | 被授权后仍必须填写 reason | +| 创建、更新、删除 HWPOD/profile | 可以 | 需要目标 HWPOD 的 `profile_editor` | +| 给用户授权或撤销 HWPOD/tool | 可以 | 需要 `access_manager` | +| 查看 HWPOD | 可以查看全部 | 需要目标 HWPOD 的 `viewer` 或更高 relation | +| 使用 HWPOD 的 workspace/debug/io 能力 | 可以使用全部 | 需要 `tool:hwpod#can_use` 且目标 HWPOD 具备 `operator/job_submitter` | +| 提交强副作用 HWPOD job | 必须填写 reason | 被授权后仍必须填写 reason | | 调用 UniDesk SSH / trans cmd / GitHub 写工具 | 可以,但仍受工具边界约束 | 需要对应 `tool:*#can_use` | ## 请求链路 @@ -181,7 +195,7 @@ browser admin UI 普通用户请求该接口必须返回 `403`。响应不得返回 `password_hash`、session token 或 Secret 值。 -### admin 授权 device pod +### admin 授权 HWPOD ```text browser admin Access UI @@ -195,9 +209,9 @@ browser admin Access UI 撤销授权走 `DELETE /v1/admin/access/users/{userId}/device-pods/{devicePodId}/{relation}`。管理员只能通过 Admin Access API 写入或删除 OpenFGA relation。 -需要给用户开通 device pod 或工具能力时,统一使用 Admin Access API、Admin Access WebUI 或同路径 `hwlab-cli client access ...`,并按具体 relation 或 `tool:*#can_use` 写入 OpenFGA tuple。 +需要给用户开通 HWPOD 或工具能力时,统一使用 Admin Access API、Admin Access WebUI 或同路径 `hwlab-cli client access ...`,并按具体 relation 或 `tool:*#can_use` 写入 OpenFGA tuple。 -### 用户列出 device pod +### 用户列出 HWPOD ```text browser or code agent tool @@ -205,10 +219,10 @@ browser or code agent tool -> authenticate actor -> if admin/access manager: list all active device_pods -> if user: check OpenFGA viewer/operator relation for each active device_pod --> return visible device pod summaries +-> return visible HWPOD summaries ``` -未授权普通用户看到空列表或对单个未授权 device pod 收到 `403`;不得回退到 fake default device pod。 +未授权普通用户看到空列表或对单个未授权 HWPOD 收到 `403`;不得回退到 fake default HWPOD。 ### 用户创建或继续 code agent session @@ -224,7 +238,7 @@ browser `GET /v1/agent/chat/result/{traceId}`、`GET /v1/agent/chat/trace/{traceId}` 和 `POST /v1/agent/chat/cancel` 必须通过 `agent_sessions.owner_user_id` 校验 owner;`admin` 可跨用户查看和取消。 -### code agent 使用 device pod +### code agent 使用 HWPOD ```text code agent turn @@ -233,17 +247,17 @@ code agent turn -> verify agent_sessions.owner_user_id == actor.id -> authorize OpenFGA tool:hwpod and device_pod relation -> require reason for mutating operations --> cloud-api -> hwlab-device-pod internal Service +-> cloud-api -> HWPOD execution path -> gateway/device-host-cli/hardware path ``` -code agent prompt、runner 或 worker 不得直接绕过 cloud-api 调用 device pod Service。device pod 服务只信任来自 cloud-api 的内部调用,不做最终用户权限判断。Cloud API 给 AgentRun runner 注入 `hwpod`、UniDesk SSH、`trans_cmd` 或 GitHub 写工具前,必须先按 [spec-v02-openfga-authorization.md](spec-v02-openfga-authorization.md) 检查对应 `tool:*#can_use`。 +code agent prompt、runner 或 worker 不得直接绕过 cloud-api 调用残留执行 Service。残留执行服务只信任来自 cloud-api 的内部调用,不做最终用户权限判断。Cloud API 给 AgentRun runner 注入 `hwpod`、UniDesk SSH、`trans_cmd` 或 GitHub 写工具前,必须先按 [spec-v02-openfga-authorization.md](spec-v02-openfga-authorization.md) 检查对应 `tool:*#can_use`。 ## 内部架构 -`hwlab-cloud-api` 内部应按 auth/session/API key、authorization、agent session owner、device-pod relation 和 admin API 模块分层。所有模块共享同一 Postgres runtime store 和 migration ledger,避免拆出早期 `hwlab-user-api` 造成跨服务一致性成本。 +`hwlab-cloud-api` 内部应按 auth/session/API key、authorization、agent session owner、HWPOD relation 和 admin API 模块分层。所有模块共享同一 Postgres runtime store 和 migration ledger,避免拆出早期 `hwlab-user-api` 造成跨服务一致性成本。 -`user_sessions` 存浏览器 session token hash;`api_keys` 存映射到用户的 CLI/runner API key;`agent_sessions.owner_user_id` 绑定 Code Agent session;`device_pods` 存 profile authority;OpenFGA tuple 表示用户对 device pod、agent session 和工具的细粒度能力。cloud-api 调用 device-pod 内部执行服务使用内部 service token,该 token 不参与用户鉴权、不写入 runner env,也不产生 actor。 +`user_sessions` 存浏览器 session token hash;`api_keys` 存映射到用户的 CLI/runner API key;`agent_sessions.owner_user_id` 绑定 Code Agent session;迁移期 `device_pods` 存 HWPOD profile authority;OpenFGA tuple 表示用户对 HWPOD、agent session 和工具的细粒度能力。cloud-api 调用残留执行服务使用内部 service token,该 token 不参与用户鉴权、不写入 runner env,也不产生 actor。 ## API 接口说明 @@ -256,19 +270,19 @@ code agent prompt、runner 或 worker 不得直接绕过 cloud-api 调用 device | `POST /v1/setup/first-admin` | 仅当 `users` 表为空时创建第一个 `admin` 并建立 session;可选 `devicePod` 或 `devicePods[]` 一次性种下首批服务端权威 profile 并授权给首个 admin;一旦已有用户必须返回 `409 setup_already_completed`。该入口不读取 Kubernetes Secret,不替代正常 admin API。 | | `POST /auth/logout` | 设置 `revoked_at`,撤销当前 browser session。 | | `POST /v1/admin/users` | admin 创建用户,响应不得返回 `password_hash` 或 token。 | -| `POST /v1/admin/device-pods`、`PUT /v1/admin/device-pods/{devicePodId}` | admin 管理 device pod profile authority。 | +| `POST /v1/admin/device-pods`、`PUT /v1/admin/device-pods/{devicePodId}` | admin 管理 HWPOD profile authority;URL path 是迁移期实现名。 | | `GET/PATCH/PUT/DELETE /v1/admin/access...` | admin Access API,读写 OpenFGA 授权、tool capability、role/status 和 effective matrix;见 [spec-v02-openfga-authorization.md](spec-v02-openfga-authorization.md)。 | -| `GET /v1/device-pods` 和 device-pod 操作 API | 按 actor role、OpenFGA relation 和 tool capability 过滤可见/可用 device pod。 | +| `GET /v1/device-pods` 和 HWPOD 操作 API | 按 actor role、OpenFGA relation 和 tool capability 过滤可见/可用 HWPOD。 | | `POST /v1/agent/chat` 及 result/trace/cancel | 必须校验 `agent_sessions.owner_user_id`;admin 可跨用户查看和取消。 | -`POST /v1/setup/first-admin` 的 device-pod 初始化只用于空库首次进入系统,不能作为长期 profile 管理入口。每个 seed 必须包含 `devicePodId` 和 object `profile`;cloud-api 会写入 `device_pods.profile_json/profile_hash`,并通过 Admin Access/OpenFGA relation 授权给首个 admin。响应只能返回脱敏 profile、profileHash 和授权摘要,不得返回 `gatewaySessionId`、`hostWorkspaceRoot`、password 或 session token 原文。 +`POST /v1/setup/first-admin` 的 HWPOD/profile 初始化只用于空库首次进入系统,不能作为长期 profile 管理入口。每个 seed 必须包含迁移期 `devicePodId` 和 object `profile`;cloud-api 会写入 `device_pods.profile_json/profile_hash`,并通过 Admin Access/OpenFGA relation 授权给首个 admin。响应只能返回脱敏 profile、profileHash 和授权摘要,不得返回 `gatewaySessionId`、`hostWorkspaceRoot`、password 或 session token 原文。 ## 微服务设计 -v0.2 不新增独立用户管理微服务。Keycloak 是独立身份提供方,不是 HWLAB 应用层授权服务;OpenFGA 是内部授权 PDP,不对用户暴露独立 API;用户映射、session/API key 消费、OpenFGA check/write、device pod relation、tool capability 和 code agent owner 校验全部放在 `hwlab-cloud-api` 内,理由是: +v0.2 不新增独立用户管理微服务。Keycloak 是独立身份提供方,不是 HWLAB 应用层授权服务;OpenFGA 是内部授权 PDP,不对用户暴露独立 API;用户映射、session/API key 消费、OpenFGA check/write、HWPOD relation、tool capability 和 code agent owner 校验全部放在 `hwlab-cloud-api` 内,理由是: - 当前权限入口必须和 `/v1/agent/*`、`/v1/device-pods/*`、AgentRun transient env 注入和 runtime store 保持强一致,拆出新的 HWLAB 用户微服务会增加网络、部署、Secret、迁移和一致性成本。 -- cloud-api 已经是 `/v1/agent/*`、`/v1/device-pods/*` 和 runtime store 的统一入口,最适合做应用层授权收口。 +- cloud-api 已经是 `/v1/agent/*`、迁移期 `/v1/device-pods/*` 和 runtime store 的统一入口,最适合做应用层授权收口。 - 后续若出现组织、计费、批量用户导入或跨产品用户中心,再把 cloud-api 内的 user/auth 模块抽成 `hwlab-user-api`;抽服务前接口和表结构仍以本文和 [spec-v02-auth.md](spec-v02-auth.md) 为准。 各服务职责如下: @@ -276,12 +290,12 @@ v0.2 不新增独立用户管理微服务。Keycloak 是独立身份提供方, | 服务 | v0.2 职责 | | --- | --- | | `hwlab-cloud-web` | Keycloak 登录入口、普通用户工作台、API key 管理入口和 Admin Access 授权 UI;浏览器 `/auth/*` 和 `/v1/admin/access*` 由 cloud-web 代理到 cloud-api。 | -| `hwlab-cloud-api` | 用户映射、Web session/API key 消费、OpenFGA 授权、device relation、tool capability、code agent owner 校验和对 device pod 的受控转发。 | +| `hwlab-cloud-api` | 用户映射、Web session/API key 消费、OpenFGA 授权、HWPOD relation、tool capability、code agent owner 校验和对 HWPOD 执行路径的受控转发。 | | OpenFGA | `hwlab-v02` 内部稳定授权服务,只接受 cloud-api 调用,不向普通用户或公网暴露。 | -| AgentRun v0.1 runner | 执行 code agent session;接收 cloud-api 提供的 owner/session/device 上下文方便观测,但不作为最终权限 authority。 | -| `hwlab-device-pod` | 暴露设备语义 API;不保存用户权限,不直接面向浏览器或普通用户 session Pod。 | +| AgentRun v0.1 runner | 执行 code agent session;接收 cloud-api 提供的 owner/session/HWPOD 上下文方便观测,但不作为最终权限 authority。 | +| HWPOD execution path | 执行受控硬件语义;不保存用户权限,不直接面向浏览器或普通用户 session Pod。 | | `hwlab-edge-proxy` | 公网/FRP 入口和 HTTP 转发;不做业务权限,只转发 cookie/header,不注入伪 actor。 | -| Postgres | v0.2 用户、session、授权、device pod 和既有 runtime durable state。 | +| Postgres | v0.2 用户、session、授权、HWPOD/profile 和既有 runtime durable state。 | ## Kubernetes 落点 @@ -291,8 +305,8 @@ Kubernetes 只做运行时隔离和资源兜底,不承载 HWLAB 用户权限 - 普通用户不直接持有 Kubernetes RBAC、ServiceAccount token 或 kubeconfig。 - `hwlab-v02` 优先拥有独立 Postgres StatefulSet/PVC,例如 `data-hwlab-v02-postgres-0`;不得把 `hwlab-dev/data-hwlab-g14-postgres-0` 当作 v0.2 权限数据源。 - code agent worker、session Pod/PVC/Job 必须带稳定 label,例如 `hwlab.pikastech.local/owner-user-id`、`hwlab.pikastech.local/session-id`。 -- device pod 工作负载必须带 `hwlab.pikastech.local/device-pod-id` label,并通过 Service 暴露稳定内部地址。 -- code agent 到 device pod 的访问应收敛到 `code agent -> cloud-api -> device-pod`,避免普通 session Pod 直接调用 device pod Service 绕过应用层授权。 +- 迁移期执行工作负载如果仍承载多 HWPOD,必须带能映射 HWPOD/profile 的稳定 label;普通用户不以该 label 作为授权来源。 +- code agent 到 HWPOD 的访问应收敛到 `code agent -> cloud-api -> HWPOD execution path`,避免普通 session Pod 直接调用残留执行 Service 绕过应用层授权。 - Keycloak 按 [spec-v02-auth.md](spec-v02-auth.md) 作为独立 `keycloak` namespace 的外部身份源接入;OpenFGA 按 [spec-v02-openfga-authorization.md](spec-v02-openfga-authorization.md) 部署在 `hwlab-v02` namespace 作为内部授权服务;Kubernetes 租户隔离第一轮仍不引入 Dex、oauth2-proxy、Capsule、vCluster、Kyverno 或 service mesh。 当前态、差距和迁移步骤已迁入 [pikasTech/HWLAB#531](https://github.com/pikasTech/HWLAB/issues/531) 评论。 @@ -305,7 +319,7 @@ Kubernetes 只做运行时隔离和资源兜底,不承载 HWLAB 用户权限 ## T2 -阅读 docs/reference/spec-user-access.md 和 docs/reference/spec-v02-openfga-authorization.md,然后用 cli 手动测试以下内容:用 admin 给普通用户授予某个 device pod 的 `viewer` 但不授予 `operator/job_submitter`,确认普通用户只能看到 device pod 摘要,提交 job 返回 403;授予 `operator/job_submitter` 后 job 可提交;撤销 relation 后同一用户不能再看到或使用该 device pod。 +阅读 docs/reference/spec-user-access.md 和 docs/reference/spec-v02-openfga-authorization.md,然后用 cli 手动测试以下内容:用 admin 给普通用户授予某个 HWPOD 的 `viewer` 但不授予 `operator/job_submitter`,确认普通用户只能看到 HWPOD 摘要,提交 job 返回 403;授予 `operator/job_submitter` 后 job 可提交;撤销 relation 后同一用户不能再看到或使用该 HWPOD。 ## T3 @@ -316,7 +330,7 @@ Kubernetes 只做运行时隔离和资源兜底,不承载 HWLAB 用户权限 | 规格项 | 状态 | 说明 | | --- | --- | --- | | admin/user 两角色模型 | 部分实现 | cloud-api 已实现 `/auth/*`、bootstrap admin、admin/user 创建和 admin-only 路由。 | -| `users`、`user_sessions`、OpenFGA relation 和 job 表 | 部分实现 | access-control bootstrap 覆盖 users、sessions、device_pods、access_tuples 和 jobs;Device Pod 强副作用 job 已接入 reason 校验,真实硬件执行仍依赖 gateway/device-host-cli 在线。 | +| `users`、`user_sessions`、OpenFGA relation 和 job 表 | 部分实现 | access-control bootstrap 覆盖 users、sessions、迁移期 device_pods、access_tuples 和 jobs;HWPOD 强副作用 job 已接入 reason 校验,真实硬件执行仍依赖 gateway/device-host-cli 在线。 | | Code Agent owner 绑定 | 已实现 | 已在 `agent_sessions` 写入 `owner_user_id`、conversation/thread/trace 和脱敏 session evidence;trace/result cache 也按 owner/admin 限制访问。 | -| OpenFGA 细粒度授权模型 | 核心已实现/持续约束 | v0.2 enforce runtime 已通过 Admin Access API 和同路径 CLI 管理 device pod relation 与 tool capability;后续扩展仍必须按 [spec-v02-openfga-authorization.md](spec-v02-openfga-authorization.md) 保持同一 authority。 | +| OpenFGA 细粒度授权模型 | 核心已实现/持续约束 | v0.2 enforce runtime 已通过 Admin Access API 和同路径 CLI 管理 HWPOD relation 与 tool capability;后续扩展仍必须按 [spec-v02-openfga-authorization.md](spec-v02-openfga-authorization.md) 保持同一 authority。 | | 不用 Kubernetes 表达用户权限 | 已实现/持续约束 | 规格明确禁止普通用户持有 kubeconfig 或直连 Service 权限。 | diff --git a/docs/reference/spec-v02-auth.md b/docs/reference/spec-v02-auth.md index 1b3edd30..4a1c67be 100644 --- a/docs/reference/spec-v02-auth.md +++ b/docs/reference/spec-v02-auth.md @@ -4,16 +4,24 @@ 基础设施实施跟踪见 [pikasTech/HWLAB#788](https://github.com/pikasTech/HWLAB/issues/788),Keycloak 到 HWLAB 的接入收口见 [pikasTech/HWLAB#814](https://github.com/pikasTech/HWLAB/issues/814)。用户角色、Code Agent session owner、OpenFGA relation 和资源授权矩阵见 [spec-user-access.md](spec-user-access.md);本文只定义“如何登录、如何恢复 actor、如何把请求归一成 actor”。 +## 当前实现状态 + +- Keycloak 已作为独立 `keycloak` namespace 的公网 HTTPS OIDC issuer 运行,HWLAB 侧浏览器 OIDC callback 和默认 Web 登录体验仍按 #814 收口;在该收口完成前,本地 bootstrap/Web session 只承担初始化和受控调试职责。 +- `hwlab-cloud-api` 已作为当前用户态 actor 恢复点:Web session、用户 API key、`/v1/users/me` 和 API key 管理接口都收敛到同一个 `users.id`、`role/status` 和 `AuthPrincipal` 摘要。 +- CLI、同路径验收和 AgentRun runner 的目标用户凭据都是用户级 `HWLAB_API_KEY`。API key 只延续用户身份,不授予额外 hwpod、tool、admin 或 Kubernetes 权限;后续授权仍交给 [spec-user-access.md](spec-user-access.md) 和 [spec-v02-openfga-authorization.md](spec-v02-openfga-authorization.md)。 +- `hwlab-v02-device-pod-internal` 一类内部 service token 只用于 cloud-api 到迁移期残留执行链路的服务间调用,不能恢复用户 actor,不能进入 CLI、Web、Code Agent runner env、API key 管理或 Admin Access 授权决策。 +- Keycloak realm role、Keycloak admin、OpenFGA token、Kubernetes ServiceAccount 和残留执行链路内部 token 都不是 HWLAB 用户权限来源。当前用户权限只从 `users.role/status`、Code Agent session owner、用户 API key 和 OpenFGA relation 组合得到。 + ## 设计目标 - Web 用户通过 Keycloak OIDC 登录或注册,HWLAB 不再把本地账号密码表单作为目标登录体验。 - Web 使用 `hwlab-cloud-api` 发行的 httpOnly `hwlab_session`,每个 session token 最长 24 小时;第一版不做复杂 refresh token 管理,但必须可撤销、可重新登录轮换。 - CLI 是纯 CLI 体验,不打开浏览器、不跳转 Web、不做 device-code flow,也不直接消费 Keycloak access token;标准凭据是环境变量 `HWLAB_API_KEY`。 -- AgentRun runner 内的 `hwpod` 也必须使用同一类用户 API key 认证,映射到发起 Code Agent session 的 `users.id`;device-pod 内部 service token 只允许 cloud-api 调用内部执行服务,不能恢复用户 actor。 +- AgentRun runner 内的 `hwpod` 也必须使用同一类用户 API key 认证,映射到发起 Code Agent session 的 `users.id`;内部执行 service token 只允许 cloud-api 调用受控执行链路,不能恢复用户 actor。 - 每个用户在首次登录后自动拥有一个默认 API key;用户也可以在 Web 中创建、查看、失效或重新生成 API key。 - API key 长效有效,除非用户或管理员手动 revoke/regenerate;它不跟 Web session 的 24 小时过期绑定。 - Keycloak 只做身份认证和账号注册;HWLAB 的 `users.role`、`users.status`、OpenFGA relation 和 Code Agent owner 仍是应用层授权 source of truth。 -- device-pod 权限只由 `users.role/status`、Code Agent session owner 和 OpenFGA relation 控制;runner 只接收当前 owner 的用户 API key。 +- hwpod 权限只由 `users.role/status`、Code Agent session owner 和 OpenFGA relation 控制;runner 只接收当前 owner 的用户 API key。 ## 系统边界 @@ -72,7 +80,7 @@ browser - 公网管理入口是 `https://auth.74-48-78-17.nip.io/admin/master/console/`,`/admin/` 可以重定向到该原生 console;不要在 HWLAB Cloud Web 中重做 Keycloak 管理 UI。 - 公网 issuer 是 `https://auth.74-48-78-17.nip.io/realms/hwlab`,discovery 与 JWKS 必须全部返回 HTTPS URL。 - Keycloak management 端口 `9000` 不作为公网入口暴露;健康和管理探测留在集群内或受控透传内完成。 -- `hwlab` realm 必须 enabled,并在小范围测试阶段允许 self-registration;新注册用户在 HWLAB 应用层只能默认成为普通 `user`,不自动拥有 device pod relation。 +- `hwlab` realm 必须 enabled,并在小范围测试阶段允许 self-registration;新注册用户在 HWLAB 应用层只能默认成为普通 `user`,不自动拥有 HWPOD relation。 - `hwlab-cloud-web` client 的 redirect URI 指向 `https://hwlab.74-48-78-17.nip.io/auth/oidc/callback`,web origin 指向 `https://hwlab.74-48-78-17.nip.io`,Direct Access Grants 必须关闭,避免把 Keycloak password grant 变成第三种 CLI 鉴权方式。 管理员凭据边界: @@ -92,9 +100,9 @@ browser 短期小范围测试允许 Keycloak 开启自助注册,并且不要求邮箱或手机校验。该策略只有在以下条件同时满足时成立: - 新注册用户默认只映射为 HWLAB `user` 角色,不自动成为 `admin`。 -- 新用户没有 device pod relation,也不能看到或使用任何 device pod,直到 `admin` 在 HWLAB 中授权。 +- 新用户没有 HWPOD relation,也不能看到或使用任何 HWPOD,直到 `admin` 在 HWLAB 中授权。 - Keycloak 账号状态必须能被管理员禁用;HWLAB `users.status='disabled'` 也必须能独立阻断 session 和 API key。 -- 如果公网注册出现垃圾账号或撞库迹象,第一优先级是关闭 Keycloak self-registration 或增加邀请码/管理员审核;不要把防滥用逻辑塞进 device pod 授权。 +- 如果公网注册出现垃圾账号或撞库迹象,第一优先级是关闭 Keycloak self-registration 或增加邀请码/管理员审核;不要把防滥用逻辑塞进 HWPOD 授权。 邮箱和手机字段可以作为 profile 信息保存,但第一版不要求验证。后续如果进入更大范围公网使用,应至少补充邮箱验证、注册限流或邀请制中的一种。 @@ -147,7 +155,7 @@ type AuthPrincipal = { 1. `Authorization: Bearer hwl_live_...` 或 `HWLAB_API_KEY` 映射出的 header 是用户 API key,适用于 CLI 和 AgentRun runner 内 `hwpod`。 2. `hwlab_session` cookie 是 Web session。 -本地 `/auth/login` 只用于空库 bootstrap 和 legacy/debug;它发行的 session 也必须走同一个 `hwlab_session` cookie,不再通过 `Authorization: Bearer ` 恢复用户。device-pod 内部服务 key 只能在 cloud-api 到 device-pod 的受控链路内使用,不出现在用户 API、CLI、AgentRun runner 或浏览器文档中。 +本地 `/auth/login` 只用于空库 bootstrap 和 legacy/debug;它发行的 session 也必须走同一个 `hwlab_session` cookie,不再通过 `Authorization: Bearer ` 恢复用户。残留执行链路内部服务 key 只能在 cloud-api 到内部执行壳的受控链路内使用,不出现在用户 API、CLI、AgentRun runner 或浏览器文档中。 ## Web 登录流程 @@ -190,7 +198,7 @@ bun tools/hwlab-cli/bin/hwlab-cli.ts client auth whoami - CLI 不跳转浏览器,不依赖 Web cookie,不要求 username/password 交互。 - `hwlab-cli` 默认从 `HWLAB_API_KEY` 读取 key,并发送 `Authorization: Bearer `。 -- AgentRun runner 的 transient env 只允许注入映射到当前 Code Agent session owner 的 `HWLAB_API_KEY`;可以使用该用户默认 key,也可以使用同一 `api_keys` 表中为该用户创建的 runner 专用 key,但绝不能使用跨用户共享的 device-pod 系统 key 或 Keycloak token。 +- AgentRun runner 的 transient env 只允许注入映射到当前 Code Agent session owner 的 `HWLAB_API_KEY`;可以使用该用户默认 key,也可以使用同一 `api_keys` 表中为该用户创建的 runner 专用 key,但绝不能使用跨用户共享的内部执行系统 key 或 Keycloak token。 - `client auth status` 必须显示 endpoint、是否检测到 `HWLAB_API_KEY`、key prefix 和用户摘要;不得输出完整 API key。 - `client auth whoami` 或 `client request GET /v1/users/me` 必须能用 API key 返回与 Web 同一用户的 `AuthPrincipal` 摘要。 - `client auth login --username ...` 和本地 cookie profile 属于 legacy 兼容入口;目标体验不再把它作为一等 CLI 登录。 @@ -246,10 +254,10 @@ API key 行为: 登录和认证只回答“请求是谁”。资源授权仍由 [spec-user-access.md](spec-user-access.md) 定义: -- `admin` 可以管理用户、device pod profile/relation,并跨用户查看或取消 Code Agent session。 -- `user` 只能访问自己的 Code Agent session 和被授权的 device pod。 -- Web session、CLI API key 和 AgentRun runner 内 `hwpod` API key 得到同一个 `users.id` 时,应看到相同 OpenFGA device pod relation 和账号 workspace。 -- Keycloak realm role、group 或 claim 不直接决定 HWLAB device pod 权限;最多作为创建/绑定用户时的输入线索。 +- `admin` 可以管理用户、HWPOD profile/relation,并跨用户查看或取消 Code Agent session。 +- `user` 只能访问自己的 Code Agent session 和被授权的 HWPOD。 +- Web session、CLI API key 和 AgentRun runner 内 `hwpod` API key 得到同一个 `users.id` 时,应看到相同 OpenFGA HWPOD relation 和账号 workspace。 +- Keycloak realm role、group 或 claim 不直接决定 HWLAB HWPOD 权限;最多作为创建/绑定用户时的输入线索。 ## 测试规格 @@ -267,11 +275,11 @@ API key 行为: ## T4 -阅读 docs/reference/spec-v02-auth.md,然后 revoke 或 regenerate API key,再用旧 `HWLAB_API_KEY` 请求 `/v1/users/me` 或 `/v1/device-pods`,必须返回 `401 api_key_invalid`;同一用户重新登录 Web session 不应恢复旧 key。 +阅读 docs/reference/spec-v02-auth.md,然后 revoke 或 regenerate API key,再用旧 `HWLAB_API_KEY` 请求 `/v1/users/me` 或迁移期 `/v1/device-pods`,必须返回 `401 api_key_invalid`;同一用户重新登录 Web session 不应恢复旧 key。 ## T5 -阅读 docs/reference/spec-v02-auth.md,然后分别使用 Web session 和同一用户的 CLI API key 访问 `/v1/device-pods`、创建 Code Agent session 和读取自己的 trace/result,确认授权结果一致;另一个普通用户的 API key 不能读取该 session,admin 可以跨用户查看。 +阅读 docs/reference/spec-v02-auth.md,然后分别使用 Web session 和同一用户的 CLI API key 访问迁移期 `/v1/device-pods`、创建 Code Agent session 和读取自己的 trace/result,确认授权结果一致;另一个普通用户的 API key 不能读取该 session,admin 可以跨用户查看。 ## T6 @@ -282,13 +290,13 @@ API key 行为: | 规格项 | 状态 | 说明 | | --- | --- | --- | | Keycloak 独立 namespace 与公网 HTTPS issuer | 部署已完成 | `keycloak` namespace、Caddy/FRP HTTPS、`hwlab` issuer、admin console 和 bootstrap Job 已形成部署基线;后续只按本文件继续硬化。 | -| Keycloak 自助注册且不强制邮箱/手机验证 | Keycloak 侧已完成 | 只适合小范围测试;HWLAB 应用层仍必须默认 `user`、无 device pod relation。 | +| Keycloak 自助注册且不强制邮箱/手机验证 | Keycloak 侧已完成 | 只适合小范围测试;HWLAB 应用层仍必须默认 `user`、无 HWPOD relation。 | | Web OIDC login/callback | 待 HWLAB 接入收口 | Keycloak client、Cloud API/Web rollout 和浏览器 callback 验收见 #814;redirect URI 必须使用 `https://hwlab.74-48-78-17.nip.io/auth/oidc/callback`。 | | Web session 24 小时轮换 | 待 HWLAB 接入收口 | 当前目标是 callback 成功后由 `hwlab-cloud-api` 发行 24 小时 `hwlab_session`;验收见 #814。 | -| CLI/AgentRun `HWLAB_API_KEY` 一等登录 | 待 HWLAB 接入收口 | 目标是统一 env API key 映射到用户,无浏览器跳转,无 Keycloak token,无跨用户 device-pod key;验收见 #814。 | -| API key 一次性显示和 revoke/regenerate | 待 HWLAB 接入收口 | 目标状态只在创建或 regenerate 时显示完整 key;列表和已存在默认 key 只返回 metadata。 | -| `AuthPrincipal` 归一 | 待 HWLAB 接入收口 | 后续实现必须把 Web session、CLI API key 和 AgentRun `hwpod` API key 都归一成同一用户 actor;legacy/internal key 不作为正式用户方法。 | -| `admin/user` 与 device pod relation 授权 | 部分实现 | 现有 cloud-api 已有本地用户、session、OpenFGA relation 和 Code Agent owner 绑定;资源授权继续按 spec-user-access 收敛。 | +| CLI/AgentRun `HWLAB_API_KEY` 一等登录 | 已实现/持续约束 | 当前 Cloud API 支持用户 API key 恢复 actor,CLI 从 env 读取并走 `/v1/users/me`;AgentRun runner 只能接收映射到当前 owner 的用户 key。 | +| API key 一次性显示和 revoke/regenerate | 已实现/持续约束 | 创建或 regenerate 时返回一次性完整 key;列表、默认状态和 CLI 默认输出只返回 metadata、prefix 和脱敏 actor;revoke 后旧 key 必须立即失效。 | +| `AuthPrincipal` 归一 | 部分实现/持续约束 | 用户 API key 和 Web session 已归一到 `AuthPrincipal`;浏览器 OIDC callback 仍按 #814 收口;内部 service token 不作为正式用户 auth method。 | +| `admin/user` 与 HWPOD relation 授权 | 部分实现 | 现有 cloud-api 已有本地用户、session、OpenFGA relation 和 Code Agent owner 绑定;资源授权继续按 spec-user-access 收敛。 | ## Keycloak 部署纪律 diff --git a/docs/reference/spec-v02-cicd.md b/docs/reference/spec-v02-cicd.md index d8672e59..18e13e54 100644 --- a/docs/reference/spec-v02-cicd.md +++ b/docs/reference/spec-v02-cicd.md @@ -126,9 +126,9 @@ devops-infra git mirror 仍是 PipelineRun 和 Argo CD 的集群内读写源。` 7. affected service 通过 BuildKit 发布到 G14 本地 registry;reused service 复用 catalog digest。 所有 selected service 的 build TaskRun 都只依赖 `plan-artifacts`,不按 service 串行排队,也不设置 8 并发或其他 Pipeline 级限流。 实际并发由 Tekton controller、G14 节点资源、PVC I/O、BuildKit sidecar 和本地 registry 承载能力决定。 - `hwlab-cloud-web` 和 `hwlab-device-pod` 必须使用 `env-reuse-git-mirror-checkout`。 + `hwlab-cloud-web` 和 HWPOD residual executor serviceId 必须使用 `env-reuse-git-mirror-checkout`。 只有 package/runtime/env 输入变化时才构建 `-env` 镜像。 - 纯前端源码、Device Pod 源码或 boot code 变化只更新三变量和 GitOps desired state, + 纯前端源码、HWPOD/迁移期执行源码或 boot code 变化只更新三变量和 GitOps desired state, 不再重新构建业务镜像。 8. promotion 刷新 `deploy/artifact-catalog.v02.json`,render `deploy/gitops/g14/runtime-v02/**`,只在本 PipelineRun 的 source commit 仍是当前 `origin/v0.2` head 时推送到 `devops-infra` mirror/relay 的 `v0.2-gitops`;若 source branch 已推进,本轮输出 superseded/no-op,写出 `runtime-ready-required=false`,不得回写旧 GitOps revision。 9. `hwlab-g14-v02` 从本地 mirror/relay 的 `v0.2-gitops:deploy/gitops/g14/runtime-v02` 同步到 `hwlab-v02`。 @@ -160,9 +160,9 @@ mirror 的 HTTP upload-pack 必须允许按精确 commit SHA 拉取已存在对 v0.2 最小校验的目标是拦截高确定性低级错误,不恢复旧重型门禁。`hwlab-cloud-web` 源码、模板或浏览器 bundle 输入发生变化时,CI 必须在镜像发布和 GitOps promotion 前执行 Cloud Web source check。该 check 至少包含实际 bundle 输入集合的 TypeScript 语义检查、自动发现的单元测试、bundle build 和 dist freshness 校验。 -语法检查和 Bun build 不能证明浏览器运行路径安全。`node --check` 只解析语法,`Bun.build()` 只转译和打包,二者都可能放过 `isRequestTraceEvent is not defined` 这类未绑定标识符;因此 Cloud Web check 必须用实际参与 bundle 的 `app.ts`、`app-device-pod.ts`、`app-conversation.ts`、`app-trace.ts` 和 `app-helpers.ts` 生成同一入口并运行 TypeScript semantic check,例如 `tsc --noEmit` 或等价 Bun/TS checker。该检查失败时不得继续发布 `hwlab-cloud-web` 镜像。 +语法检查和 Bun build 不能证明浏览器运行路径安全。`node --check` 只解析语法,`Bun.build()` 只转译和打包,二者都可能放过 `isRequestTraceEvent is not defined` 这类未绑定标识符;因此 Cloud Web check 必须用实际参与 bundle 的 `app.ts`、迁移期 `app-device-pod.ts`、`app-conversation.ts`、`app-trace.ts` 和 `app-helpers.ts` 生成同一入口并运行 TypeScript semantic check,例如 `tsc --noEmit` 或等价 Bun/TS checker。该检查失败时不得继续发布 `hwlab-cloud-web` 镜像。 -Cloud Web 单元测试必须自动发现并执行 repo-owned `web/hwlab-cloud-web/**/*.test.ts`,不能只依赖手写文件清单。新增 `app-trace`、markdown、auth、status 或 device-pod 前端纯逻辑测试后,应天然进入 `bun run --cwd web/hwlab-cloud-web check`。trace 渲染核心路径必须有不依赖浏览器、Playwright、公网或真实 provider 的轻量单测,直接构造 `runnerTrace.events` 并调用 trace row/render helper,确保请求事件、setup 事件、tool command summary、assistant markdown 和 completion row 不会因未定义 helper 或数据形态漂移在浏览器运行时崩溃。 +Cloud Web 单元测试必须自动发现并执行 repo-owned `web/hwlab-cloud-web/**/*.test.ts`,不能只依赖手写文件清单。新增 `app-trace`、markdown、auth、status 或 HWPOD 前端纯逻辑测试后,应天然进入 `bun run --cwd web/hwlab-cloud-web check`。trace 渲染核心路径必须有不依赖浏览器、Playwright、公网或真实 provider 的轻量单测,直接构造 `runnerTrace.events` 并调用 trace row/render helper,确保请求事件、setup 事件、tool command summary、assistant markdown 和 completion row 不会因未定义 helper 或数据形态漂移在浏览器运行时崩溃。 默认 v0.2 CI 不启动 Playwright、布局 smoke、移动端截图、旧 quick prompt 检查、旧 M3 evidence 检查或历史 DEV/D601 browser gate。这些检查只能作为显式人工诊断或专项验收命令存在,不能重新进入最小 CI/CD 关键路径。新增测试也必须只表达当前 v0.2 目标行为;发现旧 UI/旧路由/旧门禁断言阻碍当前目标时,删除旧断言而不是维护兼容分支。 @@ -191,7 +191,7 @@ G14 host、worktree、k3s 控制面或 pod 内的验证命令必须按短连接 | 剪裁 fast-path 探针 | 约 50s | `prepare-source` 约 11s;`gitops-promote` 约 7s;`runtime-ready` 约 17s | runtime 有实际变化时的合理预算。 | | P1 no-op runtime skip | 约 37-40s | 固定阶段预算见下文;`runtime-ready` 跳过 | source-only 且 runtime identity-only 变化时的目标预算;当前代表性实测为 38s。 | -当前预算判定:source-only、所有 service 都复用 artifact、GitOps runtime 只发生 source identity 变化时,总耗时应接近 40s;超过 50s 需要先查是否误触发 `runtime-ready`、是否发生 GitHub 直连、是否恢复了 `npm ci` 或无效 preflight。真正需要 rollout 的 code-only 变更允许约 50s,因为 `runtime-ready` 必须等待 Argo 与 workload 收敛。涉及 BuildKit publish、env image rebuild、registry push 或真实 runtime 滚动时,不适用 40s 预算,应按 affected service 的 build 耗时单独测量。混合变更必须按 service 作用域裁剪:只改 `package.json` 的 `scripts`、旧门禁入口、短连接 CLI、CLI 测试、非 runtime 文档或 device-pod host asset 时,不得触发无关 runtime service 全量 build;若同一变更确实同时改了 `internal/cloud/**`、device-pod code 和 skill bundle,则只允许对应 service build/rollout,其他 service 必须复用 catalog digest。 +当前预算判定:source-only、所有 service 都复用 artifact、GitOps runtime 只发生 source identity 变化时,总耗时应接近 40s;超过 50s 需要先查是否误触发 `runtime-ready`、是否发生 GitHub 直连、是否恢复了 `npm ci` 或无效 preflight。真正需要 rollout 的 code-only 变更允许约 50s,因为 `runtime-ready` 必须等待 Argo 与 workload 收敛。涉及 BuildKit publish、env image rebuild、registry push 或真实 runtime 滚动时,不适用 40s 预算,应按 affected service 的 build 耗时单独测量。混合变更必须按 service 作用域裁剪:只改 `package.json` 的 `scripts`、旧门禁入口、短连接 CLI、CLI 测试、非 runtime 文档或 HWPOD host asset 时,不得触发无关 runtime service 全量 build;若同一变更确实同时改了 `internal/cloud/**`、HWPOD/迁移期执行 code 和 skill bundle,则只允许对应 service build/rollout,其他 service 必须复用 catalog digest。 真实 rebuild 场景必须按全并行 fan-out 判定性能。 `plan-artifacts` 之后所有 affected service build task 应同时进入 Tekton 调度队列,`collect-artifacts` 只做 fan-in 等待全部 build task 写入 service report。 @@ -212,7 +212,7 @@ G14 host、worktree、k3s 控制面或 pod 内的验证命令必须按短连接 | `gitops-promote` no-op | 约 7-9s | 未输出 `skipped-runtime-unchanged`,或写入了 `v0.2-gitops`,说明 runtime 比对未命中。 | | `runtime-ready` | no-op 应跳过;真实 rollout 约 15-20s | no-op 场景出现 TaskRun 即为 P1 退化;真实 rollout 超时则按 Argo/workload 排障。 | -真实 rollout 若超过约 20s,先区分是 workload 本身启动慢,还是 `runtime-ready` 观察集合过大。正常日志应带 `observedCount`,且该值应接近本轮 `rolloutServices` 数量加上明确连带依赖;如果 `workloadCount` 很大但 `observedCount` 缺失,或 FRP、Postgres、device-pod 在无关 cloud-web/cloud-api 变更中重启,通常说明 render 把复用服务的 Pod template 绑定到了全局 source commit。修复方向是收窄 `runtime-ready` 观察集合,并把复用服务 template identity 改成 artifact commit、boot commit、config hash 或 migration hash,而不是加大 timeout 或恢复全 namespace 等待。 +真实 rollout 若超过约 20s,先区分是 workload 本身启动慢,还是 `runtime-ready` 观察集合过大。正常日志应带 `observedCount`,且该值应接近本轮 `rolloutServices` 数量加上明确连带依赖;如果 `workloadCount` 很大但 `observedCount` 缺失,或 FRP、Postgres、HWPOD/迁移期执行服务在无关 cloud-web/cloud-api 变更中重启,通常说明 render 把复用服务的 Pod template 绑定到了全局 source commit。修复方向是收窄 `runtime-ready` 观察集合,并把复用服务 template identity 改成 artifact commit、boot commit、config hash 或 migration hash,而不是加大 timeout 或恢复全 namespace 等待。 ## 性能优化原理 @@ -261,7 +261,7 @@ CI/CD 拓扑只表达依赖:`prepare-source` 输出 source/catalog,轻量检 正确做法是保持 source tree 只读、每个 service 独立 workdir 和 report、每个 service 独立 image repo。 先按容量节点定位真实瓶颈,再决定是否增加资源或修 sidecar/registry/mirror。 -planner 必须按 service component model 裁剪 build。`tools/` 下的短连接 CLI 源码不是 runtime service 输入;`skills/` 只有 agent runtime 或 skills bundle 真正消费的子目录才影响对应 runtime;device-pod host CLI asset 属于 skills bundle 或 host 侧分发输入,不得让 cloud-api、agent-worker、gateway、edge-proxy 等服务重建。CI 读取 artifact catalog 时支持 repo 相对路径和绝对路径,便于用真实 catalog 做本地热探测;catalog 缺失才允许 fail closed 到 rebuild,不得把“诊断命令没读到 catalog”误判为生产路径必须全量构建。 +planner 必须按 service component model 裁剪 build。`tools/` 下的短连接 CLI 源码不是 runtime service 输入;`skills/` 只有 agent runtime 或 skills bundle 真正消费的子目录才影响对应 runtime;HWPOD host CLI asset 属于 skills bundle 或 host 侧分发输入,不得让 cloud-api、agent-worker、gateway、edge-proxy 等服务重建。CI 读取 artifact catalog 时支持 repo 相对路径和绝对路径,便于用真实 catalog 做本地热探测;catalog 缺失才允许 fail closed 到 rebuild,不得把“诊断命令没读到 catalog”误判为生产路径必须全量构建。 P1 no-op runtime skip 只跳过无实际 runtime 变化的等待。planner 输出 `buildServices=[]` 且 `rolloutServices=[]` 后,`gitops-promote` 会在写入前对旧 `runtime-v02` 与新 render 结果做归一化比较:忽略 source commit、artifact source commit、boot commit 和等价 commit env/annotation 这类 identity-only 字段。如果归一化结果相同,promotion 输出 `skipped-runtime-unchanged`,把 Tekton result `runtime-ready-required=false` 写出,并跳过 GitOps commit、push、Argo hard refresh 和 `runtime-ready`。如果 workload spec、image digest、env image、SecretRef、Service、Ingress、FRP、ConfigMap 或 rollout service 有实际变化,必须保持 `runtime-ready-required=true` 并等待 runtime 收敛。 @@ -616,7 +616,7 @@ GitOps branch 已更新、source branch render 通过、PipelineRun 名称存在 | Argo v02 Application | 已实现 | `hwlab-g14-v02` 指向 v02 GitOps path 和 namespace。 | | FRP `19666/19667` 入口 | 已实现 | 由 `hwlab-v02-frpc` 与 master frps allowlist 共同提供。 | | SecretRef 独立与 provider 验收 | 已实现/持续约束 | SecretRef 已独立;验收必须做真实短连接聊天。 | -| env 容器复用三变量启动 | 已实现/持续约束 | device-pod fast lane 已由 CI/CD 自动推导 `HWLAB_BOOT_REPO`、`HWLAB_BOOT_COMMIT`、`HWLAB_BOOT_SH`;code-only rollout 复用 env image digest,只更新代码身份。 | +| env 容器复用三变量启动 | 已实现/持续约束 | HWPOD/迁移期执行 fast lane 已由 CI/CD 自动推导 `HWLAB_BOOT_REPO`、`HWLAB_BOOT_COMMIT`、`HWLAB_BOOT_SH`;code-only rollout 复用 env image digest,只更新代码身份。 | | `devops-infra` git mirror/relay 加速 | 已实现/持续约束 | source/catalog/runtime checkout 读路径和 GitOps promotion 写路径均使用独立基础设施集群 mirror/relay;Argo source 指向本地 mirror,GitHub flush 由 UniDesk CLI 手动触发,不设置 CronJob。runtime namespace 不持有 GitHub deploy key,registry 保持 G14 `hwlab-ci/hwlab-registry`。 | | `hwlab-cli` 不进 CI/CD service matrix | 已实现/持续约束 | CLI 是固定 repo 短连接 client,不发布镜像、不生成 artifact、不创建 `build-hwlab-cli` TaskRun;相关旧入口出现时直接删除。 | | CI/CD fast lane 性能预算 | 已实现/持续约束 | env-reuse no-op 目标约 40s;真实 runtime rollout 目标约 50s;不得恢复 `prepare-source` 依赖安装、GitHub 关键路径写入或 no-op runtime 等待。 | diff --git a/docs/reference/spec-v02-documentation-governance.md b/docs/reference/spec-v02-documentation-governance.md index 8b2fb92a..e711fd7b 100644 --- a/docs/reference/spec-v02-documentation-governance.md +++ b/docs/reference/spec-v02-documentation-governance.md @@ -32,6 +32,13 @@ 5. 迁移后的 reference 必须引用相关 issue,尤其是规格尚未完全实现、仍需要 issue 承接实施步骤时。 6. GitHub issue/PR 写入必须走 UniDesk CLI `bun scripts/cli.ts gh ...`,不能直接用原生 `gh` 或手写 GitHub API。 +## 当前状态沉淀规则 + +- 当当前实现状态和目标状态不同,`docs/reference/` 与 `spec-*.md` 必须分别标明:当前已经存在并应继续遵守的 runtime/source 权限链路,以及仍按 issue 收口的目标能力。不要把目标状态写成已完成,也不要把当前临时过程写成长期规则。 +- 当前状态可以记录稳定服务组成、authority 链路、用户入口、SecretRef 边界和可复用判定标准;不要写 commit、日期、PR 流水、临时命令输出或一次性实测全文。 +- 已移除的实现路径、断言、预检、兼容入口和门禁只保留在 issue/PR 证据中。长期参考中应替换为当前 authority 和删除规则,不维护已移除对象清单,也不把它们迁移成新的负向 gate。 +- SPEC 的测试规格只表达当前目标行为。发现测试只保护历史路径时,直接删除或改写为当前 Web session/API key/OpenFGA/Admin Access 行为,不另建 legacy mode 或双路径验收。 + ## D601/G14 口径处理 - 当前 HWLAB DEV/PROD 真相是 G14 k3s、`G14`/`G14-gitops` 和 `hwlab-dev`/`hwlab-prod`。 diff --git a/docs/reference/spec-v02-hwlab-cli.md b/docs/reference/spec-v02-hwlab-cli.md index 04f9fc82..f0691cc1 100644 --- a/docs/reference/spec-v02-hwlab-cli.md +++ b/docs/reference/spec-v02-hwlab-cli.md @@ -8,10 +8,17 @@ 登录鉴权目标见 [spec-v02-auth.md](spec-v02-auth.md):CLI 必须是一等纯 CLI 体验,默认从环境变量 `HWLAB_API_KEY` 读取用户 API key,并发送 `Authorization: Bearer hwl_live_...`。`client auth login --username ...`、本地 cookie session 和 profile cookie 只属于当前实现的 legacy 兼容入口;后续目标验收不得要求 CLI 打开浏览器、跳转 Web 或输入 Keycloak 密码。 -正式复现和验收必须通过运行时装配解析 endpoint,而不是在命令里手动传 URL。标准环境是 `HWLAB_RUNTIME_NAMESPACE=hwlab-v02`、`HWLAB_RUNTIME_LANE=v02`、`HWLAB_RUNTIME_ENDPOINT_LOCKED=1` 和 `HWLAB_CODE_AGENT_ASSEMBLED_RUNTIME=1`;CLI 输出必须包含 `runtimeEndpoint.source=runtime-namespace`、`runtimeEndpoint.explicitOverride=false` 和解析出的 `baseUrl`。`--base-url`、`--api-base-url`、`HWLAB_CLIENT_BASE_URL` 或等价显式 URL 只允许在本地 debug 且未设置 endpoint locked 时使用;issue 复现、最终验收、Web 等价 CLI、AgentRun runner、device-pod-cli 和 `hwpod` 都不得靠人工判断 17666/19666/19667。 +正式复现和验收必须通过运行时装配解析 endpoint,而不是在命令里手动传 URL。标准环境是 `HWLAB_RUNTIME_NAMESPACE=hwlab-v02`、`HWLAB_RUNTIME_LANE=v02`、`HWLAB_RUNTIME_ENDPOINT_LOCKED=1` 和 `HWLAB_CODE_AGENT_ASSEMBLED_RUNTIME=1`;CLI 输出必须包含 `runtimeEndpoint.source=runtime-namespace`、`runtimeEndpoint.explicitOverride=false` 和解析出的 `baseUrl`。`--base-url`、`--api-base-url`、`HWLAB_CLIENT_BASE_URL` 或等价显式 URL 只允许在本地 debug 且未设置 endpoint locked 时使用;issue 复现、最终验收、Web 等价 CLI、AgentRun runner、迁移期 `device-pod-cli` shim 和 `hwpod` 都不得靠人工判断 17666/19666/19667。 当前阶段的 Web 等价 CLI 验收默认使用 `admin` 的默认账号 workspace:不要为了避免污染而临时创建测试账号、切换 profile、指定临时 `projectId` 或隔离 workspace。需要清理上下文时直接通过 `client workbench restore/status/reset --confirm` 作用于 admin 默认 workspace,并在 issue 评论记录 reset、traceId、workspace revision 和恢复结果。只有用户明确要求多账号/多 profile 隔离验证,或目标功能本身就是账号隔离/profile 行为时,才使用 `--profile`、新增账号或非默认 `projectId`。 +## 当前实现状态 + +- `hwlab-cli client auth status|whoami` 当前以 `HWLAB_API_KEY` 和 `/v1/users/me` 恢复用户 actor;默认输出只显示 endpoint、key prefix 和脱敏用户摘要,不保存或打印完整 API key。 +- `client access ...` 当前是 Admin Access WebUI 的非视觉同路径入口,覆盖 summary、users、HWPOD/profile relation grant/revoke、tool grant/revoke 和 check;它只打 Cloud Web 同源 path,不直连 OpenFGA 或手动传 OpenFGA token。 +- `hwpod` 在 AgentRun runner 中使用 `HWLAB_RUNTIME_API_URL` 直达 cloud-api,并携带当前 owner 的用户级 `HWLAB_API_KEY`。内部执行凭据不属于 CLI auth 状态,也不得通过 CLI profile、state file 或 runner env 暴露。 +- CLI 变更是 source-only 短连接工具变更,不创建常驻 Service、镜像、Job template 或 GitOps 对象;测试和验收只表达当前 Cloud Web/API/OpenFGA 权限行为,不保留已移除路径的断言。 + ## Code Agent session 手动化 Code Agent session 是显式资源,不再由普通 `client agent send`、Workbench composer、`--from-trace` 或账号 workspace 自动创建、滚动或替换。账号 workspace 只能记录当前显式选中的 session、最近 trace 和展示状态;它不是隐式 session factory。 @@ -27,19 +34,19 @@ Code Agent session 是显式资源,不再由普通 `client agent send`、Workb ## 在系统中的职责划分 -- 提供 WEB 等价的非视觉业务入口:登录鉴权、显式 Code Agent session 管理、Device Pod 看板、Admin Access 授权管理、Code Agent 对话、trace/result 轮询、logout 和工作台 live summary。 +- 提供 WEB 等价的非视觉业务入口:登录鉴权、显式 Code Agent session 管理、HWPOD 看板、Admin Access 授权管理、Code Agent 对话、trace/result 轮询、logout 和工作台 live summary。 - 只走 Cloud Web 同源 API surface;正式运行时由 `HWLAB_RUNTIME_*` 装配出当前 lane 的 Web/API endpoint,失败时必须 fail closed,不能静默退回 legacy DEV 入口。 - Web/CLI 路径一致性优先于继续 Web 修复。Cloud Web 暴露 Code Agent、AgentRun、continuation、steer、trace/result 或 provider 问题后,必须先能用 runtime namespace/lane 装配出的 `bun tools/hwlab-cli/bin/hwlab-cli.ts client agent send/result/trace/inspect/steer ...` 对同一 Cloud Web origin、同一 `/v1/agent/chat*`、同一 `conversationId/sessionId/threadId/retryOf` 复现或解释,再继续修 Web 状态机。Cloud API 只用于显式 admin/setup/gateway 诊断,不得替代 WEB 同源路径验收。 - 从 Web trace 回放 Code Agent 问题时,优先用 `client agent inspect --trace-id ` 读取 Cloud Web 的 `/v1/agent/chat/inspect`,输出 trace 所属 `conversationId/sessionId/threadId`、session 状态和 `retryOf` 建议;`client agent send --from-trace ` 只能作为显式复现该 trace 所属 session 的入口,不能自动创建、滚动或替换 session。inspect 缺失或 session 已 failed/stale 时,CLI 必须返回结构化 blocker 和显式新建 session 建议。 -- 默认业务子命令不直连 Postgres、Kubernetes Service、Secret、device-pod 内部 Service、gateway RPC 或本地 fixture;需要鉴权的请求优先使用 `HWLAB_API_KEY` 生成的 `Authorization: Bearer hwl_live_...`,legacy/debug 才使用 `/auth/*` 返回的 cookie 或显式 `--cookie`。唯一例外是 `client gateway` 诊断族:它使用同一 runtime endpoint resolver 定位 Cloud API,用于短连接观测 gateway session、单次 shell invoke 和 transport 压测;该入口只验证底层传输稳定性,不替代 Web 用户流程授权,也不发布镜像或常驻服务。显式 API URL 只作为 unlocked local debug 入口。 +- 默认业务子命令不直连 Postgres、Kubernetes Service、Secret、残留执行 Service、gateway RPC 或本地 fixture;需要鉴权的请求优先使用 `HWLAB_API_KEY` 生成的 `Authorization: Bearer hwl_live_...`,legacy/debug 才使用 `/auth/*` 返回的 cookie 或显式 `--cookie`。唯一例外是 `client gateway` 诊断族:它使用同一 runtime endpoint resolver 定位 Cloud API,用于短连接观测 gateway session、单次 shell invoke 和 transport 压测;该入口只验证底层传输稳定性,不替代 Web 用户流程授权,也不发布镜像或常驻服务。显式 API URL 只作为 unlocked local debug 入口。 - Pod 内透传执行不放进 `hwlab-cli`;需要进入正在工作的 Code Agent/Cloud API pod 时,`hwlab-cli` 只查询并输出 UniDesk 标准 route,实际透传由 UniDesk `bun scripts/cli.ts ssh 'G14:k3s:hwlab-v02:pod::' ...` 完成。`pod:` 是 route 语法,`/` 只用于 pod 内文件系统路径。 - `client runtime routes` 必须按当前运行 profile/lane 的数据生成 UniDesk `pod:` route;实现不得硬编码 `dev`、`v0.2`、`v0.3`、namespace 或 catalog path。新增版本只允许通过 `deploy.json.lanes[profile]` 声明 namespace、artifact catalog 和 service overrides,不为每个版本新增代码分支。 - 运行时不做内部证明型校验、旧健康诊断或重断言;CI/CD 只保留能证明代码可构建、语法正确和最小冒烟可用的校验。功能正确性通过 `hwlab-cli client` 短连接真实业务 E2E 暴露和修复。 - 专用子命令覆盖高频用户工作台;`client request METHOD /path` 覆盖 WEB 同源代理允许的其他非视觉 API。`client request` 只接受以 `/` 开头的 Cloud Web 相对路径,禁止绝对 URL,避免绕过 Cloud Web 直接打内部服务。 - `client access ...` 是 Admin Access 页面的同路径 CLI,不直连 OpenFGA,不手动传 OpenFGA token,不把 `19667` Cloud API 当作 Web 等价验收路径。所有授权读写都必须输出 runtimeEndpoint、route、actor、mode、decision 和 effective matrix 摘要。 -- `client gateway pressure` 是 device-pod/gateway 高频故障的真实业务传输压测入口;必须覆盖 small stdout、大 stdout、长单行 stdout、stderr flood、结构化 timeout 和超出 gateway inflight 上限的并发请求。所有场景必须返回 JSON、HTTP/route/traceId/requestId、字节数、truncated 标记、sha256 和 bounded preview;失败必须明确是 `http_*`、`stdout_not_truncated`、`stderr_not_truncated`、`timeout_not_observed`、`structured_gateway_busy` 等可定位原因,禁止无输出、长时间黑洞或只靠 shell pipe 截断。 +- `client gateway pressure` 是 HWPOD/gateway 高频故障的真实业务传输压测入口;必须覆盖 small stdout、大 stdout、长单行 stdout、stderr flood、结构化 timeout 和超出 gateway inflight 上限的并发请求。所有场景必须返回 JSON、HTTP/route/traceId/requestId、字节数、truncated 标记、sha256 和 bounded preview;失败必须明确是 `http_*`、`stdout_not_truncated`、`stderr_not_truncated`、`timeout_not_observed`、`structured_gateway_busy` 等可定位原因,禁止无输出、长时间黑洞或只靠 shell pipe 截断。 - 输出默认是 JSON;任何失败都要有 `ok:false`、`action`、`status`、HTTP 状态、route 和可定位错误,不允许无 stdout 成功。可能返回大对象的 `client` 子命令默认返回紧凑摘要,避免高频排障输出爆炸;需要完整响应体时显式加 `--full`。 -- `device-pod-cli`/`hwpod` 在 AgentRun runner 中是设备 API 标准短入口,必须自动使用装配的 `HWLAB_RUNTIME_API_URL` 直达 `hwlab-cloud-api`,并使用映射到当前 Code Agent session owner 的 `HWLAB_API_KEY`;不能把 Cloud Web 同源代理当作设备 API 通道,也不能手动传 URL 或 session token。`job output` 默认也必须返回紧凑 JSON:保留 job/status/blocker/freshness/text/evidence 摘要,省略嵌套 gateway dispatch 和长命令;需要完整 payload 时显式加 `--full`。Code Agent 和人工不得用 `| head`、`grep` 或 shell 管道作为默认输出压缩方式,避免 stdout pipe、子进程信号转发或长输出造成 commandExecution 黑洞。 +- `hwpod` 是 AgentRun runner 中的设备 API 标准短入口,必须自动使用装配的 `HWLAB_RUNTIME_API_URL` 直达 `hwlab-cloud-api`,并使用映射到当前 Code Agent session owner 的 `HWLAB_API_KEY`;迁移期 `device-pod-cli` 只作为 shim,不是当前概念入口。设备 API 不能把 Cloud Web 同源代理当作通道,也不能手动传 URL 或 session token。`job output` 默认也必须返回紧凑 JSON:保留 job/status/blocker/freshness/text/evidence 摘要,省略嵌套 gateway dispatch 和长命令;需要完整 payload 时显式加 `--full`。Code Agent 和人工不得用 `| head`、`grep` 或 shell 管道作为默认输出压缩方式,避免 stdout pipe、子进程信号转发或长输出造成 commandExecution 黑洞。 - Code Agent 交互必须默认暴露 `traceId`、`resultUrl`、终态和 assistant 回复文本摘要;不能要求用户先拉全量 trace 再手工查找回复。 - CLI 本地登录态必须支持 `--profile NAME` 隔离,同一 base URL 下不同 profile 写入 `.state/hwlab-cli/profiles//.json`。切换到其他账号再切回原账号时,`client workbench restore/status` 必须从服务端账号 workspace 恢复之前的 `workspaceId`、`conversationId`、`sessionId`、`threadId`、`activeTraceId` 和 revision,而不是只依赖本地文件。 - `client workbench restore/status/watch/reset` 是账号 workspace 的非视觉入口:`restore/status` 对应 `GET /v1/workbench/workspace`,`watch` 对应 `/events?afterRevision=`,`reset --confirm` 对应服务端 reset。输出必须显示 workspace revision、selected conversation/session、active trace 和本地 state file,且不得保存 password、session token 原文以外的 Secret 值。 @@ -73,18 +80,18 @@ Code Agent session 是显式资源,不再由普通 `client agent send`、Workb | `hwlab-cli client auth session` | `GET /auth/session` | Web session debug。 | | `hwlab-cli client auth profiles` | 本地状态读取 | 列出同一 base URL 下的本地 profile state,用于账号切换可见性。 | | `hwlab-cli client auth logout` | `POST /auth/logout` | 撤销 server session 并清理本地 cookie。 | -| `hwlab-cli client access summary` | `GET /v1/admin/access/summary` | Admin Access 总览,显示 OpenFGA mode/readiness/store/model、用户/tool/device-pod 数量和 mismatch 摘要。 | +| `hwlab-cli client access summary` | `GET /v1/admin/access/summary` | Admin Access 总览,显示 OpenFGA mode/readiness/store/model、用户/tool/HWPOD 数量和 mismatch 摘要。 | | `hwlab-cli client access users list` | `GET /v1/admin/access/users` | 列出用户、role/status、Keycloak 绑定摘要和 effective capability 摘要。 | -| `hwlab-cli client access users inspect USER` | `GET /v1/admin/access/users/{userId}` | 查看单个用户的 device pod、agent session 和 tool 权限矩阵。 | +| `hwlab-cli client access users inspect USER` | `GET /v1/admin/access/users/{userId}` | 查看单个用户的 HWPOD/profile、agent session 和 tool 权限矩阵。 | | `hwlab-cli client access users set-role USER --role admin|user` | `PATCH /v1/admin/access/users/{userId}` | 更新用户 role/status,并同步 OpenFGA admin tuple。 | -| `hwlab-cli client access device-pods grant/revoke USER POD --relation REL` | `PUT/DELETE /v1/admin/access/users/{userId}/device-pods/{devicePodId}/{relation}` | 授予或撤销 device pod `viewer/operator/profile_editor/job_submitter` 等 relation。 | +| `hwlab-cli client access device-pods grant/revoke USER POD --relation REL` | `PUT/DELETE /v1/admin/access/users/{userId}/device-pods/{devicePodId}/{relation}` | 迁移期 API path;授予或撤销 HWPOD/profile 的 `viewer/operator/profile_editor/job_submitter` 等 relation。 | | `hwlab-cli client access tools grant/revoke USER TOOL` | `PUT/DELETE /v1/admin/access/users/{userId}/tools/{toolId}/can-use` | 授予或撤销 `hwpod`、`unidesk_ssh`、`trans_cmd`、GitHub 写工具等 capability。 | | `hwlab-cli client access check --user USER --relation REL --object OBJECT` | `POST /v1/admin/access/check` | 管理员调试单次 authorization check,输出 decision 和 redacted actor/object。 | | `hwlab-cli client provider-profiles list` | `GET /v1/admin/provider-profiles` | 管理页的非视觉状态入口;输出 actor、profile、SecretRef、resourceVersion、hash 后缀和最近验证结果,不输出 API Key 或 Secret data。 | | `hwlab-cli client provider-profiles set-key PROFILE --key-stdin` | `PUT /v1/admin/provider-profiles/{profile}/credential` | 从 stdin 写入 provider API Key,Cloud API 鉴权后委托 AgentRun;默认只输出 resourceVersion/hash 后缀和 failureKind。 | | `hwlab-cli client provider-profiles validate PROFILE --wait` | `POST /v1/admin/provider-profiles/{profile}/validate` + `GET /validations/{id}` | 触发 provider canary 并短连接轮询,输出 validationId、runId、commandId、jobName、traceId、status、failureKind 和 redacted bridge 摘要。 | -| `hwlab-cli client device-pods list` | `GET /v1/device-pods` | 对应右侧 Device Pod 列表。 | -| `hwlab-cli client device-pods status POD` | `GET /v1/device-pods/{pod}/status` | 对应 Device Pod summary/status。 | +| `hwlab-cli client device-pods list` | `GET /v1/device-pods` | 迁移期 HWPOD 列表 API path;CLI 当前命名残留,目标概念是 `hwpod`。 | +| `hwlab-cli client device-pods status POD` | `GET /v1/device-pods/{pod}/status` | 迁移期 HWPOD status API path;CLI 当前命名残留,目标概念是 `hwpod`。 | | `hwlab-cli client device-pods events POD` | `GET /v1/device-pods/{pod}/events` | 对应纯文本事件流。 | | `hwlab-cli client device-pods probe POD` | `/debug-probe/chip-id`、`/io-probe/uart/1`、`/tail` | 对应 Target/Debug/IO 摘要。 | | `hwlab-cli client runtime routes` | `GET /v1/live-builds` | 查询当前工作面 pod,并输出 UniDesk 标准 `pod:` route;不执行透传、不调用 kubectl、不内嵌 UniDesk。 | @@ -100,7 +107,7 @@ Code Agent session 是显式资源,不再由普通 `client agent send`、Workb | `hwlab-cli client agent cancel TRACE` | `POST /v1/agent/chat/cancel` | 取消当前 Code Agent 请求。 | | `hwlab-cli client harness submit` | `POST /v1/agent/chat` | G14 harness-ops 的短连接提交入口,默认 provider profile 为 `deepseek`,返回 trace/result URL;`harness-ops` 和 `harness-opt` 是同义别名。 | | `hwlab-cli client harness wait/result/trace` | `GET /v1/agent/chat/result/{trace}`、`GET /trace/{trace}` | 轮询或读取一次 Code Agent 结果和 trace;单次 wait 最长 60 秒。 | -| `hwlab-cli client harness audit` | `GET /v1/agent/chat/trace/{trace}` 或本地 trace file | 只输出工具摩擦信号,辅助发现应补的主 CLI/device-pod 操作;不是运行时 gate。 | +| `hwlab-cli client harness audit` | `GET /v1/agent/chat/trace/{trace}` 或本地 trace file | 只输出工具摩擦信号,辅助发现应补的主 CLI/HWPOD 操作;不是运行时 gate。 | | `hwlab-cli client workbench summary` | `/health/live`、`/v1`、`/v1/live-builds`、`/v1/device-pods*` | 汇总 Cloud Workbench 非视觉功能面。 | | `hwlab-cli client workbench restore/status/watch/reset` | `GET/PATCH /v1/workbench/workspace*` | 恢复、观察或重置账号级共享 workspace,支持 Web/CLI 和多 profile 共享同一账号状态。 | | `hwlab-cli client rpc METHOD [--full]` | `POST /json-rpc` | 像 Web `callRpc` 一样自动生成 `id`、`traceId` 和 `meta`,覆盖 `system.health`、`cloud.adapter.describe` 等 JSON-RPC 非视觉能力。 | @@ -117,7 +124,7 @@ Code Agent session 是显式资源,不再由普通 `client agent send`、Workb ## T2 -阅读 docs/reference/spec-v02-hwlab-cli.md,然后在 `G14:/root/hwlab-v02` 用 cli 手动测试以下内容:在 runtime endpoint locked 环境下运行 `client auth session`、`client device-pods list`、`client device-pods status device-pod-71-freq` 和 `client workbench summary --pod-id device-pod-71-freq`,确认全部由 runtime namespace 解析到 Cloud Web 同源 API,未登录时返回认证 blocker,登录后返回真实 Device Pod payload,不读取本地 fixture,也不需要手动传 URL。 +阅读 docs/reference/spec-v02-hwlab-cli.md,然后在 `G14:/root/hwlab-v02` 用 cli 手动测试以下内容:在 runtime endpoint locked 环境下运行 `client auth session`、迁移期 `client device-pods list`、`client device-pods status device-pod-71-freq` 和 `client workbench summary --pod-id device-pod-71-freq`,确认全部由 runtime namespace 解析到 Cloud Web 同源 API,未登录时返回认证 blocker,登录后返回真实 HWPOD/profile payload,不读取本地 fixture,也不需要手动传 URL。 ## T3 @@ -157,7 +164,7 @@ Code Agent session 是显式资源,不再由普通 `client agent send`、Workb ## T6 -阅读 docs/reference/spec-v02-hwlab-cli.md,然后在 `G14:/root/hwlab-v02` 用 cli 手动测试以下内容:在 runtime endpoint locked 环境下运行 `hwpod job output --pod-id D601-F103-V2 `,确认 `device-pod-cli` 自动定位当前 lane 的 Cloud API,默认输出包含 `body.compacted=true`、状态、job 摘要和 bounded text,且不包含嵌套 `dispatch.command`;再加 `--full` 确认完整 payload 可按需展开。通过 `client harness submit` 让 Code Agent 执行同一 `hwpod job output`,确认 trace 中 commandExecution 可以完成,不需要 `| head`,也不需要手动传 URL。 +阅读 docs/reference/spec-v02-hwlab-cli.md,然后在 `G14:/root/hwlab-v02` 用 cli 手动测试以下内容:在 runtime endpoint locked 环境下运行 `hwpod job output --pod-id D601-F103-V2 `,确认 `hwpod` 自动定位当前 lane 的 Cloud API,默认输出包含 `body.compacted=true`、状态、job 摘要和 bounded text,且不包含嵌套 `dispatch.command`;再加 `--full` 确认完整 payload 可按需展开。通过 `client harness submit` 让 Code Agent 执行同一 `hwpod job output`,确认 trace 中 commandExecution 可以完成,不需要 `| head`,也不需要手动传 URL。 ## T7 @@ -177,7 +184,7 @@ Code Agent session 是显式资源,不再由普通 `client agent send`、Workb | --- | --- | --- | | 固定 repo 短连接 client | 目标状态 | `hwlab-cli` 在 `G14:/root/hwlab-v02` 或当前 v0.2 worktree 直接用 Bun 运行,不作为 runtime service。 | | WEB 等价 API client | 目标状态 | `client` 子命令覆盖 Cloud Web 非视觉业务面。 | -| Admin Access 同路径 CLI | 目标状态 | `client access ...` 覆盖 OpenFGA summary、user matrix、grant/revoke、tool capability 和 check,必须走 Cloud Web 同源 path。 | +| Admin Access 同路径 CLI | 已实现/持续约束 | `client access ...` 覆盖 OpenFGA summary、user matrix、grant/revoke、tool capability 和 check,必须走 Cloud Web 同源 path。 | | Provider profile 管理同路径 CLI | 目标状态 | `client provider-profiles ...` 覆盖管理页状态、API Key 写入和 canary 验证,必须走 Cloud Web 同源 path 并委托 AgentRun。 | | 显式 Code Agent session 管理 | 目标状态 | `client agent session create|select|status|list` 是 `send` 前置;普通 `send` 不自动创建或滚动 session。 | | WEB composer 状态机等价 | 目标状态 | `client agent composer status|submit` 复用 Web composer policy,但必须显示 sessionRequired/sessionUsable,不能自动创建或滚动 session。 | @@ -185,8 +192,8 @@ Code Agent session 是显式资源,不再由普通 `client agent send`、Workb | 通用同源 API request | 目标状态 | `client request` 用于追平低频和新增 WEB API,禁止绝对 URL。 | | G14 harness-ops 短连接能力 | 目标状态 | `client harness` / `client harness-ops` / `client harness-opt` 覆盖 submit/result/trace/wait/audit,只作为业务 API client。 | | Gateway transport 压测 | 已实现 | `client gateway pressure` 只作为显式短连接诊断入口,覆盖大输出、timeout 和并发超容量的结构化返回。 | -| Device Pod job output 紧凑输出 | 已实现 | `hwpod job output` 默认省略嵌套 dispatch,`--full` 才展开完整 payload,防止 Code Agent 通过 shell pipe 压输出。 | -| 用户 API key 登录 | 目标状态 | `HWLAB_API_KEY` 是 CLI 一等登录入口;完整 key 不写入默认输出或本地 state。 | +| HWPOD job output 紧凑输出 | 已实现 | `hwpod job output` 默认省略嵌套 dispatch,`--full` 才展开完整 payload,防止 Code Agent 通过 shell pipe 压输出。 | +| 用户 API key 登录 | 已实现/持续约束 | `HWLAB_API_KEY` 是 CLI 一等登录入口;完整 key 不写入默认输出或本地 state。 | | 本地 cookie session | Legacy | `.state/hwlab-cli/session.json` 只作为现有 cookie/session 兼容,不再作为目标一等 CLI 登录体验。 | | 账号 profile 与共享 workspace | 已实现 | `--profile` 隔离本地登录态,`client workbench` 通过服务端 `account_workspaces` 恢复同账号共享 workspace。 | | 镜像/Service/Job template | 已废弃 | 相关 deploy、GitOps、artifact 和 Tekton 口径必须删除。 | diff --git a/docs/reference/spec-v02-hwlab-cloud-api.md b/docs/reference/spec-v02-hwlab-cloud-api.md index 432b0de3..f50640eb 100644 --- a/docs/reference/spec-v02-hwlab-cloud-api.md +++ b/docs/reference/spec-v02-hwlab-cloud-api.md @@ -5,17 +5,24 @@ Provider API Key 管理属于 Cloud API 的 authenticated admin surface:前端 ## 在系统中的职责划分 -- 承担 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、字节数或哈希指纹,不记录值。 +- 承担 runtime health、DB readiness、登录鉴权、`AuthPrincipal`、OpenFGA 授权 check/write、用户/session/API key 权限、Code Agent 对话、trace/result 轮询、gateway outbound registry、M3 IO 控制、HWPOD/profile/job authority 和 live build inventory。 +- 是 `hwlab-cloud-web`、Code Agent session、HWPOD 用户态操作、Admin Access API、AgentRun 工具注入和 gateway outbound poll 的唯一应用层收口点;普通用户不直接访问内部执行壳、OpenFGA Service 或 HWPOD node。 +- 读取 `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`,HWPOD 用户态授权只能从该表恢复到用户 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。 +## 当前实现状态 + +- 当前 v0.2 runtime 的应用层收口由 `hwlab-cloud-api` 承担,相关稳定服务包含 Cloud Web、HWPOD 相关实现、OpenFGA、v0.2 Postgres、FRP 入口和 Keycloak 外部 issuer。普通用户入口最终都必须恢复成 `AuthPrincipal` 后再执行授权。 +- Cloud API 当前已经承载本地 bootstrap/Web session、用户 API key、API key create/revoke/regenerate、`/v1/users/me`、Admin Access API、OpenFGA check/write、迁移期 HWPOD/profile/job authority、Code Agent owner binding 和 AgentRun transient env 装配。Keycloak 浏览器 OIDC callback 仍按 [spec-v02-auth.md](spec-v02-auth.md) 的 #814 收口。 +- OpenFGA token、残留执行链路内部 token、provider key、GitHub token 和 UniDesk SSH token 都是服务或工具凭据,不是用户 actor。Cloud API 可以消费其中一部分完成内部调用,但不得把它们写入用户 API 响应、CLI 默认输出、runner `HWLAB_API_KEY` 或 Admin Access 授权矩阵。 +- OpenFGA 不可达、store/model 未就绪或写入失败时,权限写操作必须结构化失败;不得用本地 role/status、access 摘要或 runtime cache 替代 OpenFGA 放行。 + ## 内部架构 - `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/server.ts` 负责 HTTP route、REST/RPC bridge、health、live-builds、HWPOD/profile 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` 负责 `/auth/*`、OIDC callback、Web session、用户 API key、admin/user、HWPOD profile/relation、HWPOD 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。 @@ -38,14 +45,14 @@ Provider API Key 管理属于 Cloud API 的 authenticated admin surface:前端 | --- | --- | | `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 能力摘要。 | +| `GET /v1` | REST adapter 索引、RPC 方法、runtime readiness 和 HWPOD/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 /v1/device-pods...` | 迁移期 HWPOD API path;经 cloud-api 鉴权后读取服务端 profile/OpenFGA relation/job authority;probe GET 会创建只读 job 并经当前可用执行链路执行或返回同源 blocker,不会回退到 fake 数据。 | | `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 的入口。 | +| `POST /v1/admin/users`、`POST/PUT /v1/admin/device-pods` | `admin` 管理用户和 HWPOD/profile 的入口;`device-pods` path 是迁移期实现名。 | | `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。 | @@ -54,7 +61,7 @@ Provider API Key 管理属于 Cloud API 的 authenticated admin surface:前端 | `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/internal/device-pod/gateway-dispatch` | 迁移期内部残留 path;仅接受内部服务凭据,用于把已授权 job dispatch 到 gateway poll/result;普通用户和 Code Agent 不可调用,后续应收敛到 HWPOD node-ops。 | | `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` 语义 @@ -63,7 +70,7 @@ Provider API Key 管理属于 Cloud API 的 authenticated admin surface:前端 需要验证 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)。 +登录鉴权 API 的最终规格见 [spec-v02-auth.md](spec-v02-auth.md);用户、权限、HWPOD 管理 API 的最终规格见 [spec-user-access.md](spec-user-access.md) 和 [spec-hwpod-harness.md](spec-hwpod-harness.md),旧 path/table 对照见 [spec-device-pod.md](spec-device-pod.md)。 OpenFGA、Access API、工具能力和 shadow/enforce 策略的最终规格见 [spec-v02-openfga-authorization.md](spec-v02-openfga-authorization.md)。 ## 测试规格 @@ -82,11 +89,11 @@ OpenFGA、Access API、工具能力和 shadow/enforce 策略的最终规格见 [ ## 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。 +阅读 docs/reference/spec-v02-hwlab-cloud-api.md,然后用 cli 手动测试以下内容:在同一个显式 `conversationId/sessionId/threadId` 中先完成一轮“看看 HWPOD 可用性?”这类 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。 +阅读 docs/reference/spec-v02-hwlab-cloud-api.md,然后用 cli 手动测试以下内容:未登录访问迁移期 `/v1/device-pods` 必须返回认证错误;登录后访问 HWPOD list/status 时必须显示当前 authority contract 和 `fake=false` 来源,不得出现 fake fallback。 ## T4 @@ -109,9 +116,9 @@ OpenFGA、Access API、工具能力和 shadow/enforce 策略的最终规格见 [ | 显式 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 过滤。 | +| HWPOD/profile/job authority | 迁移期实现/持续收敛 | profile/relation/list/status/job 持久化在 cloud-api;用户态 probe GET 已收敛为只读 job;内部 gateway dispatch route 仍是残留 path,后续应收敛到 HWPOD node-ops。 | +| v0.2 登录鉴权与 admin/user 权限模型 | 部分实现/持续约束 | 本地 bootstrap/Web session、用户 API key、`AuthPrincipal`、admin user/HWPOD relation API 和 Code Agent owner binding 已接入;Keycloak OIDC 浏览器 callback 仍按 spec-v02-auth 收口。 | +| OpenFGA 细粒度授权与 Admin Access API | 已实现/持续约束 | OpenFGA client/bootstrap、`enforce`、`/v1/admin/access*`、tool capability check 和 runner env 过滤已成为当前权限 authority;后续扩展不得新增第二条授权写路径。 | | 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 层处理。 | diff --git a/docs/reference/spec-v02-hwlab-cloud-web.md b/docs/reference/spec-v02-hwlab-cloud-web.md index d066fd7d..de1f06e0 100644 --- a/docs/reference/spec-v02-hwlab-cloud-web.md +++ b/docs/reference/spec-v02-hwlab-cloud-web.md @@ -5,15 +5,15 @@ Provider API Key 配置入口也归属 Cloud Web:左侧顶级导航必须提 ## 在系统中的职责划分 -- 向用户提供 Cloud Workbench、Code Agent 对话、live status、device-pod 右侧面板、trace 展示和帮助内容。 -- 只消费 `hwlab-cloud-api`,不直接访问 Postgres、gateway、device-pod Service、FRP、Kubernetes 或 provider Secret。 +- 向用户提供 Cloud Workbench、Code Agent 对话、live status、HWPOD 右侧面板、trace 展示和帮助内容。 +- 只消费 `hwlab-cloud-api`,不直接访问 Postgres、gateway、残留执行 Service、FRP、Kubernetes 或 provider Secret。 - 为浏览器提供同源代理,避免前端直接跨域调用内部 ClusterIP。 - Web 登录按 [spec-v02-auth.md](spec-v02-auth.md) 走 Keycloak OIDC;未登录用户进入 Keycloak 登录/注册,callback 后由 cloud-api 发行 24 小时 `hwlab_session`。 - Cloud Web 提供 API key 管理入口,让用户查看默认 API key、创建新 key、revoke 或 regenerate;浏览器日常请求仍使用 Web session,不要求用户手动输入 API key。 -- Cloud Web 提供 admin-only Access 页面,让管理员按用户管理 role/status、device pod relation、Code Agent session 可见性和工具 capability;页面只调用 cloud-api `/v1/admin/access*` 同源 API,不直接访问 OpenFGA、Postgres、Kubernetes 或 Keycloak admin API。 +- Cloud Web 提供 admin-only Access 页面,让管理员按用户管理 role/status、HWPOD relation、Code Agent session 可见性和工具 capability;页面只调用 cloud-api `/v1/admin/access*` 同源 API,不直接访问 OpenFGA、Postgres、Kubernetes 或 Keycloak admin API。 - Cloud Web 提供 admin-only Provider 管理页面,让管理员通过 cloud-api `/v1/admin/provider-profiles*` 同源 API 配置 AgentRun provider API Key;页面不得直接访问 AgentRun、Kubernetes Secret、Moon Bridge 或 provider upstream。 -- Cloud Web 与 `hwlab-cli client` 必须共享同一组非视觉业务 API。浏览器遇到的 Code Agent continuation、trace/result、device-pod list/status 和 device-pod job 问题,必须能通过 `hwlab-cli client` 走同一 `19666` Cloud Web path 复现;不能让 CLI 长期绕到 `19667` Cloud API 后把 Web 路径缺口误判为业务已通过。 -- Cloud Web 只承担浏览器 UI 和 `hwlab-cli client` 的同源代理。AgentRun runner 内的 `hwpod` 不走 Cloud Web;runner 使用映射到发起用户的 `HWLAB_API_KEY` 直连 Cloud API,Cloud Web 不保留 device-pod lease 路由。 +- Cloud Web 与 `hwlab-cli client` 必须共享同一组非视觉业务 API。浏览器遇到的 Code Agent continuation、trace/result、HWPOD list/status 和 HWPOD job 问题,必须能通过 `hwlab-cli client` 走同一 `19666` Cloud Web path 复现;不能让 CLI 长期绕到 `19667` Cloud API 后把 Web 路径缺口误判为业务已通过。 +- Cloud Web 只承担浏览器 UI 和 `hwlab-cli client` 的同源代理。AgentRun runner 内的 `hwpod` 不走 Cloud Web;runner 使用映射到发起用户的 `HWLAB_API_KEY` 直连 Cloud API,Cloud Web 不保留 HWPOD lease 路由。 - 浏览器启动后必须从 `GET /v1/workbench/workspace` hydrate 账号 workspace;同一个账号在多个浏览器标签页、多个浏览器或 CLI profile 中应看到同一个 `workspaceId`、selected conversation/session/thread、provider profile 和 active trace。浏览器 localStorage 只能作为短期缓存,并必须绑定 actor,不能作为 workspace authority。 - Code Agent session 管理必须全部手动化。Workbench 可以从账号 workspace 恢复“已显式选中”的 session,但不能在普通发送、页面刷新、trace replay、失败恢复或 provider resume 失败时自动创建、滚动或替换 session。没有已选 session 时,composer 必须展示“新建 session/选择 session”的显式动作;session failed/stale/canceled 后必须保留失败证据,继续前由用户显式新建或选择另一个 session。 - 显式 session 的 `providerProfile` 优先于账号 workspace provider profile。Workbench 可以展示 workspace 默认 provider,但对已选 session 发送 turn 时必须使用该 session 的 provider profile;用户想切换 provider 时,应显式创建或选择对应 provider 的 session,不能把旧 workspace provider 静默套到当前 session 上。 @@ -80,7 +80,7 @@ Provider API Key 配置入口也归属 Cloud Web:左侧顶级导航必须提 - #795 PR #797 修了一版,但留了 `max(totalTimeoutMs * 4, totalTimeoutMs + 60s)` 兜底 cap。用户反馈"硬上限等于又引入 #795 同一类回归",第二轮把 `for(;;)` + 删 `attempt > TRACE_HARD_CAP_ATTEMPTS` + 删 `startedAt` 累计计时 + per-poll 传 `totalTimeoutMs`(不缩小),实现"完全无 total / 轮询上限"。 - Round 10 起 commit / spec / 测试同步固化为本节。 -- `web/hwlab-cloud-web/app.ts` 是浏览器端主入口,和 `app-device-pod.ts`、`app-conversation.ts`、`app-trace.ts`、`app-helpers.ts` 共同组成实际 bundle 输入集合,组织 Workbench 状态、Code Agent 会话缓存、trace 轮询和 device-pod 面板。 +- `web/hwlab-cloud-web/app.ts` 是浏览器端主入口,和迁移期 `app-device-pod.ts`、`app-conversation.ts`、`app-trace.ts`、`app-helpers.ts` 共同组成实际 bundle 输入集合,组织 Workbench 状态、Code Agent 会话缓存、trace 轮询和 HWPOD 面板。 - `internal/dev-entrypoint/http.mjs` 提供静态服务、health 和 HTTP proxy 基础能力。 - `internal/dev-entrypoint/cloud-web-routes.mjs` 定义可代理到 cloud-api 的同源 API route 和认证边界。 - `web/hwlab-cloud-web/auth.ts` 管理工作台登录态、Keycloak redirect/callback 状态和 API key 管理 UI 调用;真正的登录鉴权和用户权限 authority 仍应收敛到 cloud-api。 @@ -95,12 +95,12 @@ Provider API Key 配置入口也归属 Cloud Web:左侧顶级导航必须提 | `GET /help` | 返回可用 route 摘要。 | | `GET /auth/oidc/login`、`GET /auth/oidc/callback`、`GET /auth/session`、`POST /auth/logout` | 同源代理到 cloud-api 的 Keycloak/Web session 入口;登录鉴权最终规格见 [spec-v02-auth.md](spec-v02-auth.md)。 | | `GET/POST /v1/api-keys...` | 同源代理到 cloud-api 的 API key 管理入口;短期测试允许当前用户重复查看默认 key 明文。 | -| `GET/PATCH/PUT/DELETE /v1/admin/access...` | 同源代理到 cloud-api 的 Admin Access API;用于 Access 页面读取 summary/user matrix、授予/撤销 device pod relation、tool capability 和 role/status。 | +| `GET/PATCH/PUT/DELETE /v1/admin/access...` | 同源代理到 cloud-api 的 Admin Access API;用于 Access 页面读取 summary/user matrix、授予/撤销 HWPOD relation、tool capability 和 role/status。 | | `GET /v1`、`GET /v1/...` | 同源代理到 `hwlab-cloud-api`;公开的 Code Agent result/trace 轮询按 route policy 处理。 | | `GET/PATCH /v1/workbench/workspace...` | 同源代理到 cloud-api 的账号 workspace authority,用于 Web/CLI 共享工作区和 revision 冲突保护。 | | `POST/GET/PATCH /v1/agent/sessions...` | 同源代理到 cloud-api 的显式 Code Agent session 生命周期入口;Web 不在普通 send 中隐式创建 session。 | | `POST /v1/agent/chat`、`POST /v1/agent/chat/steer`、`POST /v1/agent/chat/cancel` | 同源代理到 cloud-api 的 Code Agent 入口;steer 必须走同一个 `19666` Web path,由 cloud-api/AgentRun 判断目标 turn 是否可接收。 | -| `POST /v1/device-pods/...` | 受控同源代理到 cloud-api 的 Device Pod job/操作入口;只要 Cloud API 已提供对应能力,Cloud Web 不能只代理 list/status 而让 job POST 在 `19666` 返回 404。 | +| `POST /v1/device-pods/...` | 受控同源代理到 cloud-api 的迁移期 HWPOD job/操作 path;只要 Cloud API 已提供对应能力,Cloud Web 不能只代理 list/status 而让 job POST 在 `19666` 返回 404。 | | `POST /v1/web-performance` | 浏览器 RUM 上报入口;只允许低基数性能事件和数值,Cloud API 聚合后进入 Prometheus,详见 [spec-v02-observability-monitoring.md](spec-v02-observability-monitoring.md)。 | | `GET /v1/web-performance/summary` | 性能监控顶级页读取的同源摘要接口;返回低基数 WebUI 体感性能 JSON,包含样本数、route p95、Web Vitals、long task 和错误/超时问题队列,不返回 Prometheus 原始文本或高基数 trace/session/conversation/thread/user 标识。 | | `POST /v1/m3/io`、`POST /json-rpc` | 同源代理到受控 API;不能绕过 cloud-api 直连硬件服务。 | @@ -115,7 +115,7 @@ Browser/layout/live smoke 属于显式专项诊断,不进入默认 Cloud Web c WebUI 性能监控 issue 的 live closeout 不能只检查页面可见或 sidecar 存活;必须用 `19666` Web 入口触发真实浏览器 RUM 上报,再按 [spec-v02-observability-monitoring.md](spec-v02-observability-monitoring.md) 查询 `hwlab_webui_*` Prometheus 指标。LCP、Navigation Timing、业务 API timing、Long Task、CLS/INP/FID 近似只表达用户感知性能趋势,不替代 trace/result/inspect 的高基数排障证据。 -Cloud Web 顶级性能页是观测面,不是业务工作台。访问 `/performance` / `#/performance` 时不得初始化 Workbench store 的 workspace hydrate 或 live refresh,也不得触发 device-pod events、agent conversations、live-builds、system.health 等 workspace-only 请求。性能页只允许主动请求 `GET /v1/web-performance/summary` 和静态资源;如果需要调查 workspace 或 device-pod 性能,必须从 workspace 原入口触发样本,而不是让性能页自己制造业务流量。 +Cloud Web 顶级性能页是观测面,不是业务工作台。访问 `/performance` / `#/performance` 时不得初始化 Workbench store 的 workspace hydrate 或 live refresh,也不得触发 HWPOD events、agent conversations、live-builds、system.health 等 workspace-only 请求。性能页只允许主动请求 `GET /v1/web-performance/summary` 和静态资源;如果需要调查 workspace 或 HWPOD 性能,必须从 workspace 原入口触发样本,而不是让性能页自己制造业务流量。 Live smoke 登录前必须等待前端 auth bootstrap 结束(`body[data-auth-state]` 不再是 `checking`,且 login submit 已可见/可用)再填表;登录后必须断言 URL query 不含 `username` 或 `password`,防止原生 form submit 泄漏凭据并伪装成 layout 超时。 @@ -153,11 +153,11 @@ Cloud Web check 通过后仍需执行 bundle build 和 dist freshness 校验, ## T3 -阅读 docs/reference/spec-v02-hwlab-cloud-web.md,然后用 cli 手动测试以下内容:打开 Workbench device-pod 面板,确认 status/freshness/blocker 显示来自 `/v1/device-pods`,未登录或未授权时必须显示认证/授权 blocker,不得把 fixture 或 blocked fallback 写成真实硬件 DEV-LIVE。 +阅读 docs/reference/spec-v02-hwlab-cloud-web.md,然后用 cli 手动测试以下内容:打开 Workbench HWPOD 面板,确认 status/freshness/blocker 显示来自迁移期 `/v1/device-pods` path,未登录或未授权时必须显示认证/授权 blocker,不得把 fixture 或 blocked fallback 写成真实硬件 DEV-LIVE。 ## T3.1 -阅读 docs/reference/spec-v02-hwlab-cloud-web.md,然后用 cli 手动测试以下内容:通过 `19666` Cloud Web 同源 path 对当前允许的 device-pod job/操作 POST 做只读或 dry-run 级验证,确认与 `19667` Cloud API 的 route policy 对齐;如果 Cloud API 返回业务级 4xx,Cloud Web 也应透传业务错误,不应在 Web 层直接 404。 +阅读 docs/reference/spec-v02-hwlab-cloud-web.md,然后用 cli 手动测试以下内容:通过 `19666` Cloud Web 同源 path 对当前允许的 HWPOD job/操作 POST 做只读或 dry-run 级验证,确认与 `19667` Cloud API 的 route policy 对齐;如果 Cloud API 返回业务级 4xx,Cloud Web 也应透传业务错误,不应在 Web 层直接 404。 ## T4 @@ -169,7 +169,7 @@ Cloud Web check 通过后仍需执行 bundle build 和 dist freshness 校验, ## T6 -阅读 docs/reference/spec-v02-hwlab-cloud-web.md 和 docs/reference/spec-v02-openfga-authorization.md,然后用 cli 手动测试以下内容:登录 admin 后打开 Cloud Web Access 页面,确认 ActivityRail 显示 Access 入口,页面加载 `/v1/admin/access/summary` 和用户权限矩阵;普通用户访问同一路由必须显示 authorization blocker。通过页面授予/撤销一次 device pod relation 后,`hwlab-cli client access users inspect ` 必须看到同一 effective matrix。 +阅读 docs/reference/spec-v02-hwlab-cloud-web.md 和 docs/reference/spec-v02-openfga-authorization.md,然后用 cli 手动测试以下内容:登录 admin 后打开 Cloud Web Access 页面,确认 ActivityRail 显示 Access 入口,页面加载 `/v1/admin/access/summary` 和用户权限矩阵;普通用户访问同一路由必须显示 authorization blocker。通过页面授予/撤销一次 HWPOD relation 后,`hwlab-cli client access users inspect ` 必须看到同一 effective matrix。 ## Session state 持久化与 eviction reset @@ -204,11 +204,11 @@ runner pod 或 runner Job 丢失但 PVC 仍存在时,下一轮必须继续使 | 规格项 | 状态 | 说明 | | --- | --- | --- | | Workbench 首屏 | 已实现 | 当前页面直接进入工作台,不是 landing page。 | -| cloud-api 同源代理 | 已实现 | 受 route policy 控制;device-pod job POST 必须与 Cloud API route policy 对齐。 | +| cloud-api 同源代理 | 已实现 | 受 route policy 控制;HWPOD job POST 必须与 Cloud API route policy 对齐。 | | Code Agent UI/trace/result | 已实现 | 支持 provider profile、timeout、trace 轮询和取消。 | | Code Agent 无锁 composer | 已实现 | Web/CLI 共享 composer policy;运行中输入框保持可编辑并自动走 steer。 | | 账号 workspace hydrate/sync | 已实现 | 启动读取 `account_workspaces`,Code Agent 请求携带 workspace revision,终态再同步 workspace。 | -| device-pod 面板 | 未完全实现 | 当前主要消费 fake/只读 device-pod payload。 | +| HWPOD 面板 | 未完全实现 | 当前仍有迁移期 path/payload 残留;目标只展示 HWPOD 当前概念和真实授权 payload。 | | Admin Access 授权页面 | 目标状态 | 新增 admin-only ActivityRail 页面,管理 OpenFGA relation/tool capability/role status,并与 CLI `client access` 共用同一路径。 | | 完整多用户 admin/user UI | 未完全实现 | 登录态存在;Keycloak/Web session/API key 需按 spec-v02-auth 收敛,权限 authority 仍需按 spec-user-access 收敛到 cloud-api。 | diff --git a/docs/reference/spec-v02-hwlab-device-pod-service.md b/docs/reference/spec-v02-hwlab-device-pod-service.md deleted file mode 100644 index 657f7575..00000000 --- a/docs/reference/spec-v02-hwlab-device-pod-service.md +++ /dev/null @@ -1,53 +0,0 @@ -# v0.2 hwlab-device-pod 服务规格 - -本文描述 v02 中 `hwlab-device-pod` 微服务的部署和接口实现。逻辑 device-pod、profile authority、OpenFGA relation 和 job 模型的权威规格见 [spec-device-pod.md](spec-device-pod.md)。 - -## 在系统中的职责划分 - -- 承接 `cloud-api -> hwlab-device-pod -> gateway/device-host-cli` 的内部执行服务位置。 -- 当前阶段只暴露 device-pod executor 边界;用户、profile、OpenFGA relation、用户 API key 和 job authority 都在 `hwlab-cloud-api`,不能由该 Service 伪造或兜底。 -- 普通用户和 Code Agent 不应直接调用该 Service;正式路径必须经过 `hwlab-cloud-api` 鉴权、OpenFGA relation/API key 和 mutating job reason 校验。 - -## 内部架构 - -- `cmd/hwlab-device-pod/main.ts` 提供 HTTP server,端口默认 `7601`。 -- `cmd/hwlab-device-pod/main.ts` 返回 `contractVersion=device-pod-executor-v1`,并声明 `authority=hwlab-cloud-api`、`fake=false`。 -- `internal/device-pod/fake-data.mjs` 只保留为 legacy 前端/smoke fixture,不是 `hwlab-device-pod` 服务运行契约。 -- `HWLAB_DEVICE_POD_ID` 可指定默认 executor devicePodId;正式 profile 仍由 `hwlab-cloud-api` 的 `device_pods.profile_json` 管理。 -- `HWLAB_CLOUD_API_INTERNAL_URL` 配置后,executor 可用内部服务凭据回调 `hwlab-cloud-api` 的 gateway dispatch route,把已授权 job 下发到 gateway/device-host-cli;内部 job 和 dispatch 必须同时携带 `x-hwlab-internal-service` 与 `HWLAB_DEVICE_POD_INTERNAL_TOKEN` 对应的 `x-hwlab-internal-token`。未配置内部 token、profile route 缺失或 gateway 不在线时返回 blocker/拒绝执行。 -- `hwlab-cloud-api` 的用户态 device-pod job/probe 必须经 `HWLAB_DEVICE_POD_URL` 指向的 `hwlab-device-pod` executor;未配置 executor 时只创建 blocked job 并返回 `device_pod_executor_unavailable`,不得直接绕过 executor 调用 gateway/device-host-cli。 - -## API 接口说明 - -| 接口 | 说明 | -| --- | --- | -| `GET /health`、`GET /health/live` | 返回 `contractVersion=device-pod-executor-v1`、`authority=hwlab-cloud-api` 和 `fake=false`。 | -| `GET /v1/device-pods` | 返回 executor boundary 摘要,不表达用户可见 device pod authority。 | -| `GET /v1/device-pods/{devicePodId}/status` | 返回 blocked executor status;真实 profile/status 以 cloud-api `/v1/device-pods/{devicePodId}/status` 为准。 | -| `GET /v1/device-pods/{devicePodId}/events` | 返回 bounded executor boundary event,不伪造硬件事件。 | -| `POST /v1/device-pods/{devicePodId}/jobs` | 只接受带内部 token 的 `hwlab-cloud-api` 调用;创建内部 executor job 并返回 job、freshness、output/cancel URL;有可用 profile route 和 `HWLAB_CLOUD_API_INTERNAL_URL` 时通过 cloud-api gateway dispatch 下发到 device-host-cli,否则返回 `gateway_dispatch_unavailable`。 | -| `GET /v1/device-pods/{devicePodId}/jobs/{jobId}`、`GET /output`、`POST /cancel` | 只接受带内部 token 的 `hwlab-cloud-api` 调用,用于查询内部 job、最大 12000 bytes 的 bounded output 和取消非终态 job;普通用户仍必须走 cloud-api 用户态 API。 | - -用户态 `POST /jobs`、job output/cancel、admin profile/API key/OpenFGA relation API 和正式 `device-pod-cli` REST 调用由 `hwlab-cloud-api` 实现,应以 [spec-device-pod.md](spec-device-pod.md) 为目标。`hwlab-device-pod` 不接受 CLI、浏览器或 Code Agent 直接上传 profile snapshot。 - -## 测试规格 - -## T1 - -阅读 docs/reference/spec-v02-hwlab-device-pod-service.md,然后用 cli 手动测试以下内容:访问 `/health/live` 和 `/v1/device-pods`,确认响应包含 `contractVersion=device-pod-executor-v1`、`authority=hwlab-cloud-api` 和 `fake=false`。 - -## T2 - -阅读 docs/reference/spec-v02-hwlab-device-pod-service.md,然后用 cli 手动测试以下内容:通过 `hwlab-cloud-api /v1/device-pods` 访问 device pod authority,确认未登录返回认证错误,登录后只返回 actor 可见 device pod。 - -## 规格的实现情况 - -| 规格项 | 状态 | 说明 | -| --- | --- | --- | -| health 和 executor boundary REST | 已实现 | 服务声明内部 executor、cloud-api authority 和非 fake 来源。 | -| bounded events/status | 已实现 | 只返回 executor boundary/blocker,不伪造硬件事件。 | -| 正式 profile authority | 已在 cloud-api 实现 | `hwlab-device-pod` 不读取或覆盖 `device_pods.profile_json`。 | -| job lifecycle | 部分实现 | 用户态 job 由 cloud-api 鉴权、授权、reason 校验和持久化;executor 侧已提供内部 job create/get/output/cancel lifecycle,并能把 job dispatch 结果写回 bounded output。 | -| gateway/device-host-cli adapter | 部分实现 | executor 已通过 cloud-api internal dispatch 接入 gateway poll/result 和 device-host-cli 命令映射;真实执行仍依赖 profile route、在线 gateway 和 host CLI。 | -| device-pod-cli REST authority | 已实现 | `tools/device-pod-cli.ts` 默认只走 cloud-api REST,不读取 `.device-pod/*.json` 作为 profile authority;旧 `.mjs` 入口只负责启动 TypeScript CLI。 | - diff --git a/docs/reference/spec-v02-observability-monitoring.md b/docs/reference/spec-v02-observability-monitoring.md index 53a02911..c69b26b4 100644 --- a/docs/reference/spec-v02-observability-monitoring.md +++ b/docs/reference/spec-v02-observability-monitoring.md @@ -34,7 +34,7 @@ Cloud Web 用户感知性能必须进入同一套 Prometheus 查询面。浏览 Cloud Web 顶级性能页通过同源 `GET /v1/web-performance/summary` 读取低基数 JSON 摘要,用于展示用户可感知的 WebUI 样本数、慢 API route p95、Web Vitals、long task 和错误/超时问题队列。该接口不得返回 Prometheus 原始文本、Secret、prompt/assistant 正文或 trace/session/conversation/thread/user 等高基数标识;CLI 同路径验收使用 `hwlab-cli client request GET /v1/web-performance/summary`,不直连 Prometheus 公网地址。 -观测面不得污染被观测对象。Cloud Web 性能页自身(`/performance` / `#/performance`)不得启动 workspace hydrate、live refresh、device-pod events、agent conversations 等业务请求,也不得把性能页自身的 LCP、Navigation Timing、Long Task 或 `/v1/web-performance*` 请求写入 WebUI 业务性能样本。Cloud API 在 `/v1/web-performance` 入库时必须丢弃来自性能页或观测 API 的样本,作为旧 bundle、缓存客户端或探针误上报的兜底。 +观测面不得污染被观测对象。Cloud Web 性能页自身(`/performance` / `#/performance`)不得启动 workspace hydrate、live refresh、HWPOD events、agent conversations 等业务请求,也不得把性能页自身的 LCP、Navigation Timing、Long Task 或 `/v1/web-performance*` 请求写入 WebUI 业务性能样本。Cloud API 在 `/v1/web-performance` 入库时必须丢弃来自性能页或观测 API 的样本,作为旧 bundle、缓存客户端或探针误上报的兜底。 ## API 接口说明 @@ -47,7 +47,7 @@ Cloud Web 顶级性能页通过同源 `GET /v1/web-performance/summary` 读取 | `hwlab-cloud-api` | HTTP request count/latency、DB readiness/query latency、Code Agent submit/result/trace latency、AgentRun dispatch status、provider profile terminal status | 核心业务 authority,优先接入。 | | `hwlab-cloud-web` | HTTP request count/latency、static asset/proxy latency、upstream timeout/error count | 只记录同源代理与静态服务指标。 | | `hwlab-edge-proxy` | HTTP proxy request count/latency、upstream status、timeout/error count | 证明公网 edge 到 cloud-api 的性能。 | -| `hwlab-device-pod` | executor request count/latency、job terminal status、gateway dispatch latency | 不记录 device output text。 | +| HWPOD residual executor serviceId | executor request count/latency、job terminal status、gateway dispatch latency | serviceId 可暂时保留旧实现命名;不记录 device output text,不作为当前产品概念。 | | `hwlab-agent-skills` | health/list/upload/tree/file request count/latency、error count | 技能包服务指标。 | | `hwlab-deepseek-proxy` | bridge request count/latency、upstream status、model/readiness probe result | 不记录 prompt、response 或 upstream token。 | @@ -109,7 +109,7 @@ HWLAB v0.2 可声明 `PrometheusRule`,但规则只表达当前 v0.2 目标行 - Code Agent submit/result terminal latency。 - AgentRun dispatch failure rate by provider profile。 - edge-proxy upstream 5xx/timeout rate。 -- device-pod executor job failure rate。 +- HWPOD job failure rate;如果 metric label 仍出现残留 executor serviceId,只作为实现残留维度。 规则命名和 label 必须能定位 lane、namespace、service 和 operation;不要把单个 trace/run/job 写入规则。 @@ -164,7 +164,7 @@ HWLAB v0.2 可声明 `PrometheusRule`,但规则只表达当前 v0.2 目标行 | --- | --- | --- | | 应用侧监控接入边界 | 已实现 | HWLAB 只负责 metrics sidecar、ServiceMonitor/PrometheusRule 和受控查询;共享 Prometheus control-plane 不在 `hwlab-v02`。 | | G14 共享监控控制面 | 已实现 | 由 UniDesk/G14 基础设施规格定义,默认位于 `devops-infra`。 | -| v0.2 服务 `/metrics` | 已实现 | 第一阶段接入 `hwlab-cloud-api`、`hwlab-cloud-web`、`hwlab-edge-proxy`、`hwlab-device-pod`、`hwlab-agent-skills`、`hwlab-deepseek-proxy`。 | +| v0.2 服务 `/metrics` | 已实现/持续收敛 | 第一阶段接入 `hwlab-cloud-api`、`hwlab-cloud-web`、`hwlab-edge-proxy`、HWPOD residual executor serviceId、`hwlab-agent-skills`、`hwlab-deepseek-proxy`;残留 serviceId 不作为当前产品概念。 | | ServiceMonitor / PrometheusRule | 已实现 | 已进入 v0.2 GitOps desired state;规则用于观测,不作为发布旧门禁。 | | 受控查询与验收 | 已实现 | 通过 UniDesk `hwlab g14 observability status|query` 和集群内边界检查验证 target discovered、health probe 可用和公网不暴露。 | | Cloud Web 用户感知性能 | 已实现 | 浏览器 RUM 经 `/v1/web-performance` 聚合为 `hwlab_webui_*` 指标,由 `hwlab-cloud-api` sidecar 附加导出,关闭 issue 前必须用 `19666` Web 入口触发并在 Prometheus 中看到样本。 | diff --git a/docs/reference/spec-v02-openfga-authorization.md b/docs/reference/spec-v02-openfga-authorization.md index 71a6ef2f..987b02ea 100644 --- a/docs/reference/spec-v02-openfga-authorization.md +++ b/docs/reference/spec-v02-openfga-authorization.md @@ -2,7 +2,7 @@ 本文是 HWLAB `v0.2` 细粒度资源授权、OpenFGA 接入、管理员 Access WebUI 和同路径 CLI 的长期规格。Keycloak 只回答“用户是谁”;OpenFGA 回答“该用户能不能对该对象做该动作”;`hwlab-cloud-api` 仍是唯一应用层 enforcement point 和授权写入口。 -本规格补充 [spec-v02-auth.md](spec-v02-auth.md) 和 [spec-user-access.md](spec-user-access.md):前者定义 Keycloak/Web session/API key 到 `AuthPrincipal` 的认证归一;后者定义用户、session owner、device pod 和工具能力的业务权限口径;本文定义 OpenFGA 如何在 `hwlab-v02` namespace 内以 Kubernetes 原生方式落地,并如何被 Cloud Web 与 `hwlab-cli client` 管理和验证。 +本规格补充 [spec-v02-auth.md](spec-v02-auth.md) 和 [spec-user-access.md](spec-user-access.md):前者定义 Keycloak/Web session/API key 到 `AuthPrincipal` 的认证归一;后者定义用户、session owner、HWPOD 和工具能力的业务权限口径;本文定义 OpenFGA 如何在 `hwlab-v02` namespace 内以 Kubernetes 原生方式落地,并如何被 Cloud Web 与 `hwlab-cli client` 管理和验证。 实施跟踪 issue 必须记录 spec、GitOps、cloud-api、Cloud Web、CLI、测试、PR/CI/CD 和原入口验收进展。过程记录写 issue 评论,本文只保留稳定目标和验收口径。 @@ -20,6 +20,14 @@ OpenFGA 是 HWLAB 应用层授权基础设施,不是 Kubernetes RBAC、ServiceAccount、NetworkPolicy 或 Keycloak realm role 的替代。普通用户不获得 kubeconfig、内部 Service 直连、OpenFGA token 或 Keycloak admin 权限。 +## 当前实现状态 + +- 当前 v0.2 desired state 和 runtime 都包含 `hwlab-openfga` ClusterIP 服务、Postgres backend、migration/bootstrap 过程和 `hwlab-v02-openfga` SecretRef。`hwlab-cloud-api` 在 `/health/live` 和 Admin Access summary 中只公开 mode、readiness、store/model 摘要和 degraded reason。 +- `enforce` 是当前正式授权口径。Admin Access 写入 OpenFGA 失败时必须 fail closed;本地 role/status、effective matrix 或 access 摘要只能作为 domain state 和可见性材料,不能在 OpenFGA 不可用时单独放行。 +- 当前授权写入口只有 cloud-api Admin Access API;Cloud Web Access 和 `hwlab-cli client access ...` 都调用同一路 API。浏览器、CLI、AgentRun runner 和普通用户都不直连 OpenFGA,也不持有 OpenFGA token。 +- `tool:trans_cmd#can_use` 的当前含义是允许在 agent/tool registry 中暴露受控 UniDesk passthrough 命令能力;它不改变 UniDesk route/operation 边界,不授予 Kubernetes Secret 读取、裸控制面写入、残留执行链路内部 token 或主 server SSH key。 +- hwpod/profile 修改当前仍由迁移期 `POST/PUT /v1/admin/device-pods` path 和 `profile_editor`/admin 权限控制;OpenFGA 管 relation,cloud-api 管 profile authority,执行节点只执行内部受控 job。该 API path 名称是实现残留,不是当前概念命名。 + ## Kubernetes 和 GitOps 落点 OpenFGA 固定部署在 `hwlab-v02` namespace,作为稳定外部服务进入 v0.2 GitOps desired state: @@ -42,7 +50,7 @@ OpenFGA 官方 Helm chart 可以作为 YAML 来源,但 `hwlab-v02` desired sta | --- | --- | --- | | 用户 | `user:usr_123` | HWLAB `users.id`,不是 Keycloak `sub`。 | | 系统 | `system:hwlab` | 平台级 admin、access 管理和全局工具授权。 | -| Device Pod | `device_pod:device-pod-71-freq` | 服务端 profile authority 对象。 | +| HWPOD | `device_pod:device-pod-71-freq` | 服务端 HWPOD/profile authority 对象;`device_pod` 是迁移期 OpenFGA object type 名称,不是当前产品概念。 | | Code Agent session | `agent_session:ags_123` | 会话 owner/collaborator/viewer 判定。 | | 工具 | `tool:hwpod`、`tool:unidesk_ssh`、`tool:github_pr`、`tool:trans_cmd` | AgentRun runner 可注入或可调用的功能能力。 | @@ -89,9 +97,9 @@ type tool | 功能 | Check | | --- | --- | | 管理用户和权限 | `user: access_manager system:hwlab` 或 `admin system:hwlab` | -| 查看 device pod | `viewer device_pod:` | -| 提交 device pod job / 使用 `hwpod` | `operator` 或 `job_submitter device_pod:`,并要求 `can_use tool:hwpod` | -| 修改 device pod profile | `profile_editor device_pod:` 或 `admin system:hwlab` | +| 查看 HWPOD | `viewer device_pod:` | +| 提交 HWPOD job / 使用 `hwpod` | `operator` 或 `job_submitter device_pod:`,并要求 `can_use tool:hwpod` | +| 修改 HWPOD profile | `profile_editor device_pod:` 或 `admin system:hwlab` | | 查看自己的 Code Agent session | `viewer agent_session:` | | 继续/取消 Code Agent session | `operator agent_session:` | | 注入 UniDesk SSH 透传能力 | `can_use tool:unidesk_ssh` | @@ -120,13 +128,13 @@ authenticate -> AuthPrincipal -> load domain object -> openfga check -> reason c 模式语义: -- `enforce` 是唯一正式运行模式:device pod、agent session 和工具能力以 OpenFGA check 为准。OpenFGA 不可达、store/model 未就绪或 check 超时时,高风险写操作 fail closed;低风险只读可以返回 degraded blocker,不得静默放行。 +- `enforce` 是唯一正式运行模式:HWPOD、agent session 和工具能力以 OpenFGA check 为准。OpenFGA 不可达、store/model 未就绪或 check 超时时,高风险写操作 fail closed;低风险只读可以返回 degraded blocker,不得静默放行。 - `off` / `shadow` 不作为 v0.2 runtime 目标路径;文档、测试或 render 如再次把它们作为业务 allow source,应优先删除而不是兼容。 tuple 写入必须由 cloud-api admin API 统一完成,并和 Postgres domain state 保持事务级或可恢复一致: - 用户创建、禁用、角色提升/降级时同步写 `users` 和 `system:hwlab` tuples。 -- device pod 创建/更新/删除时同步 `device_pods` 和相关 tuples;删除对象时清理 tuple 或标记不可用。 +- HWPOD/profile 创建/更新/删除时同步迁移期 `device_pods` 表和相关 tuples;删除对象时清理 tuple 或标记不可用。 - Code Agent session 创建时写 `agent_session:#owner@user:`;取消/归档不删除 owner tuple,便于审计和 trace 回放。 - API key `scopes_json` 只能作为用户权限的收窄条件,不能授予超过 OpenFGA 的能力。 @@ -136,19 +144,19 @@ Cloud Web 和 CLI 只能通过 cloud-api 的 admin API 管理授权。第一版 | 接口 | 说明 | | --- | --- | -| `GET /v1/admin/access/summary` | 返回 mode、OpenFGA readiness、storeId/modelId、user/tool/device-pod 数量和最近 mismatch 摘要。 | +| `GET /v1/admin/access/summary` | 返回 mode、OpenFGA readiness、storeId/modelId、user/tool/HWPOD 数量和最近 mismatch 摘要。 | | `GET /v1/admin/access/users` | 列出用户、role/status、Keycloak 绑定摘要、API key 数量和 effective capability 摘要。 | -| `GET /v1/admin/access/users/{userId}` | 返回单个用户的 device pod、agent session 和 tool 权限矩阵。 | +| `GET /v1/admin/access/users/{userId}` | 返回单个用户的 HWPOD、agent session 和 tool 权限矩阵。 | | `PATCH /v1/admin/access/users/{userId}` | 更新用户 `role/status`,并同步 OpenFGA admin tuple。 | | `PUT /v1/admin/access/users/{userId}/device-pods/{devicePodId}/{relation}` | 授予 `viewer/operator/profile_editor/job_submitter` 等 relation。 | -| `DELETE /v1/admin/access/users/{userId}/device-pods/{devicePodId}/{relation}` | 撤销指定 device pod relation。 | +| `DELETE /v1/admin/access/users/{userId}/device-pods/{devicePodId}/{relation}` | 撤销指定 HWPOD relation;URL path 仍是迁移期实现名。 | | `PUT /v1/admin/access/users/{userId}/tools/{toolId}/can-use` | 授予工具能力,例如 `hwpod`、`unidesk_ssh`、`github_pr`、`trans_cmd`。 | | `DELETE /v1/admin/access/users/{userId}/tools/{toolId}/can-use` | 撤销工具能力。 | | `POST /v1/admin/access/check` | 管理员调试单次 authorization check;响应必须标明 actor/object/relation/mode,但不得泄漏 token。 | 所有 write API 必须要求当前 actor 具备 `access_manager system:hwlab` 或 admin tuple;普通 `user` 不可调用。响应必须包含结构化 `authorization` 字段:`mode`、`allowed`、`decisionSource`、`storeId`、`modelId`、`relation`、`object` 和 redacted actor。 -Cloud Web 必须把 Admin Access 读写 API 作为同源代理路径转发给 cloud-api,包括 `POST /v1/admin/access/check`、`PATCH /v1/admin/access/users/{userId}`、device pod relation 的 `PUT/DELETE` 和 tool capability 的 `PUT/DELETE`。Cloud Web 不保存授权副本,也不直接访问 OpenFGA。 +Cloud Web 必须把 Admin Access 读写 API 作为同源代理路径转发给 cloud-api,包括 `POST /v1/admin/access/check`、`PATCH /v1/admin/access/users/{userId}`、HWPOD relation 的 `PUT/DELETE` 和 tool capability 的 `PUT/DELETE`。Cloud Web 不保存授权副本,也不直接访问 OpenFGA。 ## Admin Access WebUI @@ -157,7 +165,7 @@ Cloud Web 新增 ActivityRail 顶层入口 `Access`,只对具备 access manage 页面布局: - 左栏:用户列表、搜索、role/status 筛选、Keycloak 绑定摘要。 -- 中栏:选中用户的权限矩阵,按 `Device Pods`、`Agent Sessions`、`Tools`、`Platform` 分组;权限用 checkbox/toggle 表达,危险工具用显式确认。 +- 中栏:选中用户的权限矩阵,按 `HWPOD`、`Agent Sessions`、`Tools`、`Platform` 分组;权限用 checkbox/toggle 表达,危险工具用显式确认。 - 右栏:effective permission 预览、最近变更、OpenFGA mode/readiness、写入 blocker 和保存结果。 交互规则: @@ -177,26 +185,32 @@ Cloud Web 新增 ActivityRail 顶层入口 `Access`,只对具备 access manage | `client access users list` | `GET /v1/admin/access/users` | | `client access users inspect USER` | `GET /v1/admin/access/users/{userId}` | | `client access users set-role USER --role admin|user` | `PATCH /v1/admin/access/users/{userId}` | -| `client access device-pods grant USER POD --relation viewer|operator|profile_editor|job_submitter` | `PUT /v1/admin/access/users/{userId}/device-pods/{devicePodId}/{relation}` | -| `client access device-pods revoke USER POD --relation ...` | `DELETE /v1/admin/access/users/{userId}/device-pods/{devicePodId}/{relation}` | +| `client access device-pods grant USER POD --relation viewer|operator|profile_editor|job_submitter` | `PUT /v1/admin/access/users/{userId}/device-pods/{devicePodId}/{relation}`;迁移期 path,业务对象是 HWPOD | +| `client access device-pods revoke USER POD --relation ...` | `DELETE /v1/admin/access/users/{userId}/device-pods/{devicePodId}/{relation}`;迁移期 path,业务对象是 HWPOD | | `client access tools grant USER TOOL` | `PUT /v1/admin/access/users/{userId}/tools/{toolId}/can-use` | | `client access tools revoke USER TOOL` | `DELETE /v1/admin/access/users/{userId}/tools/{toolId}/can-use` | | `client access check --user USER --relation REL --object OBJECT` | `POST /v1/admin/access/check` | CLI 输出必须是 JSON,包含 `runtimeEndpoint`、HTTP route、actor 摘要、mode、decision 和变更后的 effective matrix 摘要。它不能直接调用 OpenFGA API,不能手动传 OpenFGA token,不能把 `--base-url 19667` 作为 WebUI 等价验收路径。 -权限变更的真实入口验收必须以 Cloud Web 同源 origin 为准。最小闭环是:`client access summary` 确认 `openfga.mode=enforce` 且 ready;对一个普通用户执行某个 device pod relation 的 `check false -> grant -> check true -> revoke -> check false`;对至少一个工具能力执行同样闭环,`trans_cmd` 必须覆盖;最后 `users inspect` 确认测试 tuple 已撤回。验收报告必须记录 `baseUrl`、method/path、actor、relation/object、HTTP status、OpenFGA decision 和最终 effective matrix 摘要。 +权限变更的真实入口验收必须以 Cloud Web 同源 origin 为准。最小闭环是:`client access summary` 确认 `openfga.mode=enforce` 且 ready;对一个普通用户执行某个 HWPOD relation 的 `check false -> grant -> check true -> revoke -> check false`;对至少一个工具能力执行同样闭环,`trans_cmd` 必须覆盖;最后 `users inspect` 确认测试 tuple 已撤回。验收报告必须记录 `baseUrl`、method/path、actor、relation/object、HTTP status、OpenFGA decision 和最终 effective matrix 摘要。 ## AgentRun 工具能力边界 Cloud API 在创建 AgentRun command/runner 时必须按 OpenFGA 决策装配 transient env 和工具说明: -- 用户没有 `can_use tool:hwpod` 时,不注入 `HWLAB_API_KEY` 给 `hwpod`,也不在 prompt/tools 中声明 device pod 操作可用。 -- 用户没有目标 device pod 的 `operator`/`job_submitter` 时,即使拥有 `tool:hwpod`,具体 device pod job 也必须被 cloud-api 拒绝。 +- 用户没有 `can_use tool:hwpod` 时,不注入 `HWLAB_API_KEY` 给 `hwpod`,也不在 prompt/tools 中声明 HWPOD 操作可用。 +- 用户没有目标 HWPOD 的 `operator`/`job_submitter` 时,即使拥有 `tool:hwpod`,具体 HWPOD job 也必须被 cloud-api 拒绝。 - 用户没有 `can_use tool:unidesk_ssh` 时,不注入 UniDesk SSH client token、workspace route 或相关 alias。 - `tool:trans_cmd` 只代表允许通过受控 UniDesk route 调用透传命令;它不绕过 UniDesk CLI 的 route/operation 安全边界,不允许 pod 内任意 Secret 读取或 Kubernetes 写操作。 - GitHub issue/PR 写入能力必须单独由工具对象授权;拥有 Code Agent session 不等于拥有 GitHub 写权限。 +## 授权残留处理 + +- 授权残留指任何让用户或工具能力绕过当前 `AuthPrincipal -> OpenFGA/Admin Access` 判定的代码、文档、测试、render 或 runtime 路径,也包括共享用户绕过凭据、兼容写分支和只为已移除路径存在的断言。 +- 处理方式是删除或改写为当前合同,不迁移为 legacy mode、兼容表、feature flag、负向 gate 或长期分叉。快速测试应断言当前 Web session/API key/OpenFGA/Admin Access 的 allow/deny 行为,而不是保留被移除路径的名字。 +- 运行面排查可以临时扫描 source、GitOps 和 live schema 证明当前 authority 收敛;长期 SPEC 只保留当前 authority、当前入口和判定规则,不维护已移除对象清单。 + ## 测试规格 ## T1 @@ -209,7 +223,7 @@ Cloud API 在创建 AgentRun command/runner 时必须按 OpenFGA 决策装配 tr ## T3 -阅读 docs/reference/spec-v02-openfga-authorization.md,然后用 admin 通过 CLI 或 WebUI 给普通用户授予某个 device pod 的 `viewer` 但不授予 `operator`,确认该用户可以看到 device pod 摘要,但提交 device job 返回 403;再授予 `operator/job_submitter` 后 job 可提交。 +阅读 docs/reference/spec-v02-openfga-authorization.md,然后用 admin 通过 CLI 或 WebUI 给普通用户授予某个 HWPOD 的 `viewer` 但不授予 `operator`,确认该用户可以看到 HWPOD 摘要,但提交 HWPOD job 返回 403;再授予 `operator/job_submitter` 后 job 可提交。 ## T4 @@ -217,7 +231,7 @@ Cloud API 在创建 AgentRun command/runner 时必须按 OpenFGA 决策装配 tr ## T5 -阅读 docs/reference/spec-v02-openfga-authorization.md,然后在 `enforce` 运行面授予和撤销普通用户的 device pod relation,确认 `/v1/device-pods`、status、job 和 Access summary 都按 OpenFGA 结果执行;OpenFGA 写失败时 Admin Access API 必须返回结构化 5xx blocker,不能更新本地 access matrix。 +阅读 docs/reference/spec-v02-openfga-authorization.md,然后在 `enforce` 运行面授予和撤销普通用户的 HWPOD relation,确认迁移期 `/v1/device-pods`、status、job 和 Access summary 都按 OpenFGA 结果执行;OpenFGA 写失败时 Admin Access API 必须返回结构化 5xx blocker,不能更新本地 access matrix。 ## T6 @@ -229,7 +243,7 @@ Cloud API 在创建 AgentRun command/runner 时必须按 OpenFGA 决策装配 tr ## T8 -阅读 docs/reference/spec-v02-openfga-authorization.md 和 docs/reference/spec-user-access.md,然后检查 source schema、`v0.2-gitops` rendered ConfigMap 和 live Postgres:确认 source、render 和 live schema 都只保留 OpenFGA/Admin Access 当前授权结构;live Postgres 中不应存在任何已退出的 device pod 授权表或兼容写入口。 +阅读 docs/reference/spec-v02-openfga-authorization.md 和 docs/reference/spec-user-access.md,然后检查 source schema、`v0.2-gitops` rendered ConfigMap 和 live Postgres:确认当前授权状态能由 `AuthPrincipal`、users/session/API key、HWPOD profile、Admin Access API 和 OpenFGA tuple 完整表达。检查中发现其他授权 source 时,必须删除或改写到当前合同,不新增兼容分支或负向 gate。 ## 规格的实现情况 @@ -237,8 +251,8 @@ Cloud API 在创建 AgentRun command/runner 时必须按 OpenFGA 决策装配 tr | --- | --- | --- | | OpenFGA 作为 v0.2 内部授权服务 | 已实现/持续约束 | v0.2 runtime 以 `enforce` 模式运行,summary 暴露 redacted store/model 和 readiness;OpenFGA 不向公网、浏览器或 CLI 暴露。 | | Cloud API OpenFGA client/bootstrap/check/write | 已实现 | Admin Access API 可 check/write tuple,响应包含 structured decision 和 redacted OpenFGA 状态。 | -| 细粒度 device pod / session / tool 授权 | 核心已实现/持续扩展 | device pod relation 和 tool capability 统一写 OpenFGA tuple;后续 session/tool 扩展必须继续保持同一 authority。 | +| 细粒度 HWPOD / session / tool 授权 | 核心已实现/持续扩展 | HWPOD relation 和 tool capability 统一写 OpenFGA tuple;后续 session/tool 扩展必须继续保持同一 authority。 | | Admin Access WebUI | 部分实现/持续约束 | Cloud Web Access 页面使用同一 Admin Access API;浏览器交互深测可作为专项验收,但不得新增第二条授权路径。 | -| 同路径 CLI | 已实现 | `client access ...` 走 Cloud Web 同源 path,已覆盖 summary、users、check、device pod relation grant/revoke 和 tool grant/revoke。 | +| 同路径 CLI | 已实现 | `client access ...` 走 Cloud Web 同源 path,已覆盖 summary、users、check、HWPOD relation grant/revoke 和 tool grant/revoke。 | | AgentRun 工具注入按用户权限过滤 | 部分实现/持续约束 | `hwpod`、`unidesk_ssh`、`trans_cmd`、GitHub 写工具必须独立授权;runner 中的 `HWLAB_API_KEY` 必须映射到当前 Code Agent session owner。 | diff --git a/docs/reference/spec-v02-services.md b/docs/reference/spec-v02-services.md index adaa319d..ef7c32a0 100644 --- a/docs/reference/spec-v02-services.md +++ b/docs/reference/spec-v02-services.md @@ -6,9 +6,10 @@ 细节权威出处: -- 用户、权限、session 归属和 device pod relation:见 [spec-user-access.md](spec-user-access.md)。 +- 用户、权限、session 归属和 hwpod relation/capability:见 [spec-user-access.md](spec-user-access.md)。 - OpenFGA 细粒度授权、Admin Access 管理页和同路径 CLI:见 [spec-v02-openfga-authorization.md](spec-v02-openfga-authorization.md)。 -- device pod profile authority、REST/job 和 gateway 执行边界:见 [spec-device-pod.md](spec-device-pod.md)。 +- HWPOD 当前概念、workspace-local spec、compiler、node-ops 和 node 边界:见 [spec-hwpod-harness.md](spec-hwpod-harness.md)。 +- Device Pod 迁移对照和残留命名识别:见 [spec-device-pod.md](spec-device-pod.md)。 - `v0.2` branch、namespace、GitOps、FRP、SecretRef 和发布验收:见 [spec-v02-cicd.md](spec-v02-cicd.md)。 - Code Agent provider 真实聊天验收:见 [code-agent-chat-readiness.md](code-agent-chat-readiness.md)。 - G14 GitOps、Tekton、Argo CD、registry 和外部稳定中间件边界:见 [g14-gitops-cicd.md](g14-gitops-cicd.md)。 @@ -18,10 +19,10 @@ `hwlab-v02` 是独立 runtime namespace,公网只暴露 `19666/19667`。浏览器进入 `hwlab-cloud-web`,API、agent、device 和 gateway 请求收敛到 `hwlab-cloud-api`,内部硬件与 agent 能力由专门服务承接,稳定外部服务只提供数据库、模型桥、provider 通道和 FRP 入口。 -- `hwlab-cloud-api` 是 v0.2 应用层 authority:用户身份、`admin/user`、Code Agent session owner、OpenFGA relation、profile authority 和用户态 REST 都在这里判定。 +- `hwlab-cloud-api` 是 v0.2 应用层 authority:用户身份、`admin/user`、Code Agent session owner、OpenFGA relation、hwpod/profile authority 和用户态 REST 都在这里判定。 - OpenFGA 是 `hwlab-v02` 内部稳定授权服务,只作为 cloud-api 的 PDP/relationship store;Keycloak、Cloud Web、CLI、AgentRun runner 和普通用户都不能直接调用 OpenFGA。 - `hwlab-cloud-web` 只作为用户入口和 API proxy,不拥有业务 authority;CLI 可以旁路 UI,但不能旁路 `cloud-api` 的授权。 -- `hwlab-device-pod` 是正式设备业务承载点;用户态请求必须走 `cloud-api -> hwlab-device-pod -> hwlab-gateway -> device-host-cli -> hardware`。 +- 当前设备研发概念是 HWPOD:用户态请求必须先经 `cloud-api` 完成身份和 OpenFGA/Admin Access 判定,再按 HWPOD 目标进入 `hwpod-node-ops` / `hwpod-node`。source/runtime 中仍存在的旧 executor workload 或 API path 只属于实现命名残留,不是当前服务概念。 - Code Agent session 归属、鉴权、trace 和用户态 API 收敛在 `hwlab-cloud-api`;执行调度接入 AgentRun v0.1 共享基础设施。`hwlab-agent-mgr`、`hwlab-agent-worker` 和 repo-owned codex-stdio supervisor 不是 v0.2 runtime service matrix,不再生成 Deployment、Job template、Service、artifact 或 GitOps desired state。 - `hwlab-gateway` 是 transport,不理解用户权限、不保存 profile authority;用户端已经验证稳定,v0.2 第一阶段先不改造它。 - Code Agent provider 通道分为 `codex-api` loopback forwarder 和 `deepseek` bridge/Moon Bridge;自研 bridge/forwarder 属于 HWLAB 常驻服务,Moon Bridge 和 hyueapi/DeepSeek upstream 是稳定外部依赖。 @@ -41,11 +42,10 @@ browser ``` ```text -cloud-web or device-pod-cli or code agent tool +cloud-web or hwpod-cli or code agent tool -> hwlab-cloud-api --> hwlab-device-pod --> hwlab-gateway --> device-host-cli +-> hwpod-node-ops +-> hwpod-node -> Keil / pyOCD / UART / target ``` @@ -89,7 +89,7 @@ origin/v0.2 | API/live 公网入口 | `http://74.48.78.17:19667/health/live` 和同源 API | [spec-v02-hwlab-edge-proxy.md](spec-v02-hwlab-edge-proxy.md)、[spec-v02-hwlab-cloud-api.md](spec-v02-hwlab-cloud-api.md) | | 用户、session、授权 | `/auth/*`、`/v1/admin/*`、`/v1/agent/chat*` | [spec-user-access.md](spec-user-access.md)、[spec-v02-hwlab-cloud-api.md](spec-v02-hwlab-cloud-api.md) | | OpenFGA 授权管理 | `/v1/admin/access*`、`hwlab-cli client access ...`、Cloud Web Access 页面 | [spec-v02-openfga-authorization.md](spec-v02-openfga-authorization.md) | -| Device Pod | `/v1/device-pods*`、正式 job/admin API | [spec-device-pod.md](spec-device-pod.md)、[spec-v02-hwlab-device-pod-service.md](spec-v02-hwlab-device-pod-service.md) | +| HWPOD | `hwpod-cli`、`hwpod-ctl`、`hwpod-node-ops`、迁移期 `/v1/device-pods*` API | [spec-hwpod-harness.md](spec-hwpod-harness.md)、[spec-device-pod.md](spec-device-pod.md) | | Gateway transport | `cloud-api /v1/gateway/poll`、`/v1/gateway/result`、gateway `/status` | [spec-v02-hwlab-gateway.md](spec-v02-hwlab-gateway.md) | | Code Agent provider | `deepseek` 和 `codex-api` provider profile | [spec-v02-deepseek-proxy.md](spec-v02-deepseek-proxy.md)、[spec-v02-codex-api-forwarder.md](spec-v02-codex-api-forwarder.md) | | Code Agent AgentRun 调度 | `hwlab-cloud-api` 会话 owner/auth/trace -> AgentRun v0.1 dispatch | [agentrun-code-agent-dispatch.md](agentrun-code-agent-dispatch.md)、[spec-v02-hwlab-agent-skills.md](spec-v02-hwlab-agent-skills.md) | @@ -104,10 +104,10 @@ origin/v0.2 | 对象 | 类型 | v0.2 处理 | Bun + TS | 细节出处 | | --- | --- | --- | --- | --- | -| `hwlab-cloud-api` | HWLAB 自研常驻服务 | 保留并核心化 | 是,P0 | [spec-v02-hwlab-cloud-api.md](spec-v02-hwlab-cloud-api.md)、[spec-user-access.md](spec-user-access.md)、[spec-device-pod.md](spec-device-pod.md) | +| `hwlab-cloud-api` | HWLAB 自研常驻服务 | 保留并核心化 | 是,P0 | [spec-v02-hwlab-cloud-api.md](spec-v02-hwlab-cloud-api.md)、[spec-user-access.md](spec-user-access.md)、[spec-hwpod-harness.md](spec-hwpod-harness.md) | | `hwlab-cloud-web` runtime wrapper | HWLAB 自研常驻 web/proxy wrapper | 保留 | 是,P0 | [spec-v02-hwlab-cloud-web.md](spec-v02-hwlab-cloud-web.md)、[cloud-workbench.md](cloud-workbench.md) | | `hwlab-edge-proxy` | HWLAB 自研常驻服务 | 保留 | 是,P0 | [spec-v02-hwlab-edge-proxy.md](spec-v02-hwlab-edge-proxy.md) | -| `hwlab-device-pod` | HWLAB 自研常驻服务 | 保留并增强 | 是,P0 | [spec-v02-hwlab-device-pod-service.md](spec-v02-hwlab-device-pod-service.md)、[spec-device-pod.md](spec-device-pod.md) | +| HWPOD harness / node-ops / node | HWLAB 硬件研发执行能力 | 当前概念保留并增强;旧 executor 命名作为残留清理 | 视入口而定 | [spec-hwpod-harness.md](spec-hwpod-harness.md)、[spec-device-pod.md](spec-device-pod.md) | | `hwlab-codex-api-responses-forwarder` | HWLAB 自研常驻 sidecar | 保留 | 是,P1 | [spec-v02-codex-api-forwarder.md](spec-v02-codex-api-forwarder.md) | | `hwlab-deepseek-responses-bridge` / `hwlab-deepseek-proxy` | HWLAB 自研 bridge + Moon Bridge 外部依赖 | 保留 | 是,P1 for bridge | [spec-v02-deepseek-proxy.md](spec-v02-deepseek-proxy.md) | | AgentRun v0.1 runner | 共享 Agent 执行基础设施 | 作为外部基础设施接入,不进 HWLAB service/artifact matrix | 否 | [agentrun-code-agent-dispatch.md](agentrun-code-agent-dispatch.md) | @@ -120,7 +120,8 @@ origin/v0.2 | `hwlab-box-simu` | HWLAB 自研模拟服务 | 裁撤 | 否 | 本文即裁撤权威,不保留单独 spec | | `hwlab-patch-panel` | HWLAB 自研接线盘服务 | 裁撤 | 否 | 本文即裁撤权威,不保留单独 spec | | `hwlab-cli` | 固定 repo 短连接 client | 保留为 WEB 等价非视觉业务入口,不进 runtime service inventory | 是,CLI 自身 | [spec-v02-hwlab-cli.md](spec-v02-hwlab-cli.md) | -| `device-pod-cli` | CLI 工具 | 保留并改 REST 调用 | 否 | [spec-device-pod.md](spec-device-pod.md) | +| `hwpod-cli` / `hwpod-ctl` / `hwpod-compiler-cli` | CLI 工具 | 保留并作为当前 HWPOD 入口 | 否 | [spec-hwpod-harness.md](spec-hwpod-harness.md) | +| `device-pod-cli` | CLI shim/迁移残留 | 仅作过渡,不作为当前概念入口 | 否 | [spec-device-pod.md](spec-device-pod.md) | | render/publish/smoke scripts | 一次性脚本 | 保留现状 | 否 | [spec-v02-cicd.md](spec-v02-cicd.md)、[g14-gitops-cicd.md](g14-gitops-cicd.md) | | browser-side Cloud Web JS | HWLAB 自研前端浏览器代码 | 保留并 TS 化 | 是,P0 | [spec-v02-hwlab-cloud-web.md](spec-v02-hwlab-cloud-web.md)、[cloud-workbench.md](cloud-workbench.md) | | Moon Bridge | 外部稳定服务 | 保留 | 否 | [spec-v02-deepseek-proxy.md](spec-v02-deepseek-proxy.md) | @@ -150,7 +151,7 @@ v0.2 服务语言统一约束 HWLAB 仓库内自研、会以 Deployment、sideca hwlab-cloud-api hwlab-cloud-web runtime wrapper hwlab-edge-proxy -hwlab-device-pod +HWPOD harness / node-ops / node hwlab-codex-api-responses-forwarder hwlab-deepseek-responses-bridge browser-side Cloud Web JS @@ -166,7 +167,8 @@ hwlab-agent-skills wrapper ```text hwlab-gateway -device-pod-cli +hwpod-cli / hwpod-ctl / hwpod-compiler-cli +device-pod-cli residual shim scripts and tools stable external services ``` @@ -207,10 +209,11 @@ hwlab-agent-worker | 规格项 | 状态 | 说明 | | --- | --- | --- | -| v0.2 总体依赖方向 | 已实现 | 本文定义浏览器、API、device、provider 和 CI/CD 链路。 | +| v0.2 总体依赖方向 | 已实现/持续收敛 | 本文定义浏览器、API、HWPOD、provider 和 CI/CD 链路;旧 executor 命名只作为残留清理对象。 | | 保留服务均有 spec | 已实现 | 本文服务总表列出当前保留服务和 spec 文件。 | | 稳定外部服务纳入 spec | 已实现 | Postgres、Codex API forwarder/hyueapi、DeepSeek/Moon Bridge、FRP 已独立成文或交叉引用权威规格。 | | OpenFGA 授权服务纳入 spec | 目标状态 | 需要按 [spec-v02-openfga-authorization.md](spec-v02-openfga-authorization.md) 完成 GitOps、cloud-api、WebUI 和 CLI。 | +| HWPOD 当前概念收敛 | 目标状态 | 新开发和长期文档只使用 HWPOD 概念;旧 `device-pod` / `hwlab-device-pod` 命名从服务总表、权限职责和新测试中移除。 | | `simu`、接线盘、router、tunnel-client 裁撤 | 已实现 | 本文记录裁撤口径;v0.2 render、artifact catalog、Tekton build service set 和 cloud-api 运行时 env 不再包含裁撤对象。 | | Bun + TypeScript 迁移边界 | 已实现 | 本文区分第一阶段、后续、暂不迁移和裁撤集合。 | | spec 作为开发和测试权威 | 已实现 | AGENTS.md 规格区提供顶级索引。 |