Files
pikasTech-HWLAB/docs/reference/spec-user-access.md
T
2026-06-05 14:00:13 +08:00

324 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# v0.2 用户和权限管理规格
本文是 HWLAB `v0.2` 用户和权限管理的规格说明。目标是用最少概念支持真实用户使用 code agent session,并让管理员能按用户独立调整 device pod、Code Agent session 和工具功能权限,同时避免把用户体系、Kubernetes 租户、设备授权、硬件证据链和审计系统混成一套复杂门禁。
本规格与 [spec-device-pod.md](spec-device-pod.md) 配套:用户和权限规格定义谁可以看见、创建和使用 device poddevice-pod 规格定义 profile authority、REST/job 和硬件执行边界。
登录入口、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 两类。
实施跟踪见 [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。
Postgres 是用户、session、业务对象和迁移 ledger 的持久化边界;OpenFGA 是细粒度授权关系与授权判定边界。Kubernetes namespace、ServiceAccount、Service 直连和 gateway route 都不能替代用户权限模型;普通用户不获得 kubeconfig、内部 Service 直连能力、OpenFGA token 或长期 Secret。
`v0.2` 本地 bootstrap 管理员账号固定为 `admin`,默认登录密码固定为 `hwlab2026`,只作为空库初始化和 legacy fallback。Keycloak 接入后的目标 Web 登录以 OIDC 为准,CLI 以 `HWLAB_API_KEY` 为准。
运行时仍只通过 `hwlab-v02-bootstrap-admin/password-hash` SecretRef 注入本地 fallback password hashPostgres、API 响应、日志、CLI session 和文档不得保存或输出 password hash、session token 原文或 Secret 值。
如果 live Secret 需要重建或旋转,必须保持 `/auth/login` 这个 legacy/bootstrap 入口可用,但不得把它重新写成目标登录体验。
## 规格目标
- 只保留两类角色:`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``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 应用层完成。
- v0.2 权限数据必须与 `hwlab-dev`/`hwlab-prod` 运行数据隔离。优先在 `hwlab-v02` namespace 内使用独立 Postgres StatefulSet/PVC;若未来显式复用共享 Postgres 实例,也必须使用独立 database 或 schema、独立 Secret 和独立 migration ledger,不得直接复用 `hwlab-dev` 的 pgdata。
## 简化边界
| 模块 | 不采用的复杂方案 | v0.2 规格方案 |
| --- | --- | --- |
| 用户角色 | `platform_admin``device_admin``developer``viewer` | `admin``user` |
| 用户组 | `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 表达 |
| 设备权限粒度 | `io.read``io.write` 等硬件寄存器级 capability | `viewer``operator``profile_editor``job_submitter` 等产品级 relation |
| Viewer | 单独只读角色 | 不引入 |
| Audit | 独立用户审计表 | 不引入 |
| Lease | 权限和互斥可能混用 | 只作为设备互斥锁 |
## 表结构
v0.2 多用户实现复用 cloud-api 和现有 Postgres runtime store,不新增独立用户管理微服务。推荐新增一个 `0002_multi_user_access_v1` 数据库迁移,保持现有 `0001_cloud_core_skeleton.sql` 的硬件 runtime 表不被误用为用户权限表。
### `users`
用户身份和角色 source of truth。
```sql
CREATE TABLE IF NOT EXISTS users (
id TEXT PRIMARY KEY,
username TEXT NOT NULL UNIQUE,
display_name TEXT NOT NULL DEFAULT '',
role TEXT NOT NULL CHECK (role IN ('admin', 'user')),
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'disabled')),
password_hash TEXT,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
```
- bootstrap 阶段必须至少有一个 `admin`
- `password_hash` 只用于 v0.2 本地 bootstrap/legacy 账号;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。
### `user_sessions`
浏览器 server session/cookie 的持久状态。cookie 中只保存不可逆 session token;数据库保存 token hash。
```sql
CREATE TABLE IF NOT EXISTS user_sessions (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL REFERENCES users(id),
session_token_hash TEXT NOT NULL UNIQUE,
created_at TEXT NOT NULL,
last_seen_at TEXT NOT NULL,
expires_at TEXT NOT NULL,
revoked_at TEXT
);
```
- `/auth/session`、OIDC callback、API key 认证和 `/auth/logout` 的最终 authority 是 cloud-api;本地 `/auth/login` 只作为 bootstrap/legacy fallback。`/v1/auth/session``/v1/users/me``/v1/access/status``/v1/setup/status` 只作为同一 authority 的 REST 状态或兼容入口。cloud-web 可以保留同名浏览器路由,但只能作为静态 UI 或代理层。
- logout 是设置 `revoked_at`,不是仅删除浏览器本地状态。
### `agent_sessions`
复用现有 `agent_sessions` 作为 code agent session 归属记录,不再新增同义的 `code_agent_sessions` 表。v0.2 迁移应在现有表上增加 owner 和 chat/session 绑定字段:
```sql
ALTER TABLE agent_sessions ADD COLUMN IF NOT EXISTS owner_user_id TEXT REFERENCES users(id);
ALTER TABLE agent_sessions ADD COLUMN IF NOT EXISTS conversation_id TEXT;
ALTER TABLE agent_sessions ADD COLUMN IF NOT EXISTS thread_id TEXT;
ALTER TABLE agent_sessions ADD COLUMN IF NOT EXISTS last_trace_id TEXT;
ALTER TABLE agent_sessions ADD COLUMN IF NOT EXISTS session_json TEXT NOT NULL DEFAULT '{}';
ALTER TABLE agent_sessions ADD COLUMN IF NOT EXISTS updated_at TEXT;
CREATE INDEX IF NOT EXISTS idx_agent_sessions_owner ON agent_sessions(owner_user_id);
CREATE INDEX IF NOT EXISTS idx_agent_sessions_conversation ON agent_sessions(conversation_id);
```
- 新建 code agent session 必须写入 `owner_user_id`
- 历史 ownerless session 在迁移时可以一次性归属 bootstrap `admin` 或直接标记为 `expired`;迁移完成后不再允许 ownerless 活跃 session。
### `device_pods`
设备能力单元的管理表;正式 profile authority 和执行语义以 [spec-device-pod.md](spec-device-pod.md) 为准。profile 定义 device pod,因此 profile 必须由 `admin` 通过 cloud-api 管理,不能由 code agent 本地 `.device-pod/` 文件决定。
```sql
CREATE TABLE IF NOT EXISTS device_pods (
id TEXT PRIMARY KEY,
name TEXT NOT NULL DEFAULT '',
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'disabled')),
profile_json TEXT NOT NULL DEFAULT '{}',
profile_hash TEXT NOT NULL DEFAULT '',
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
```
`profile_json` 中的 gateway route、host workspace、probe UID、串口端口和 host CLI 都是服务端权威字段;普通用户响应只能看到脱敏 profile 摘要和 `profile_hash`
## 权限矩阵
| 操作 | `admin` | `user` |
| --- | --- | --- |
| 管理用户和权限 | 可以;应镜像为 `system:hwlab#admin``access_manager` | 只有被授予 `access_manager` 才可以 |
| 创建自己的 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 |
| 调用 UniDesk SSH / trans cmd / GitHub 写工具 | 可以,但仍受工具边界约束 | 需要对应 `tool:*#can_use` |
## 请求链路
cloud-api 每个用户态请求都按同一顺序处理:
```text
authenticate -> actor -> authorize(actor, action, resource) -> reason check for mutating device jobs -> execute
```
### 登录、API key 和 session 恢复
```text
browser
-> Keycloak OIDC login/callback
-> cloud-api auth controller
-> users issuer+sub/status/role mapping
-> user_sessions insert token hash
-> Set-Cookie httpOnly sameSite
-> browser
cli
-> Authorization: Bearer hwl_live_...
-> cloud-api api_keys lookup
-> users status/role check
-> AuthPrincipal(authMethod='api-key')
```
登录、Keycloak issuer、CLI API key 和 24 小时 Web session 规则见 [spec-v02-auth.md](spec-v02-auth.md)。资源授权模块只消费已经恢复出的 actor/AuthPrincipal。`/auth/session` 使用 cookie 查 `user_sessions`,再查 `users` 得到 `actor`API key 认证直接从 `api_keys -> users` 得到同一 actor。`/auth/logout` 标记 `user_sessions.revoked_at`。Keycloak access token、refresh token、realm role 和内部 service token 都不能绕过这里的 actor 恢复与资源授权。
### admin 创建用户
```text
browser admin UI
-> cloud-web proxy
-> cloud-api POST /v1/admin/users
-> authenticate admin
-> insert users(role='user')
-> return redacted user record
```
普通用户请求该接口必须返回 `403`。响应不得返回 `password_hash`、session token 或 Secret 值。
### admin 授权 device pod
```text
browser admin Access UI
-> cloud-api PUT /v1/admin/access/users/{userId}/device-pods/{devicePodId}/{relation}
-> authenticate admin
-> validate users.status='active'
-> validate device_pods.status='active'
-> write OpenFGA tuple
-> return effective permission matrix
```
撤销授权走 `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。
### 用户列出 device pod
```text
browser or code agent tool
-> cloud-api GET /v1/device-pods
-> 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
```
未授权普通用户看到空列表或对单个未授权 device pod 收到 `403`;不得回退到 fake default device pod。
### 用户创建或继续 code agent session
```text
browser
-> cloud-api POST /v1/agent/chat
-> authenticate actor
-> find or create agent_sessions(owner_user_id=actor.id, conversation_id)
-> run code agent turn
-> persist last_trace_id/thread_id/session_json
-> return 202 trace/result polling pointer
```
`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
```text
code agent turn
-> cloud-api device operation route
-> authenticate actor from owning session or user API key in runner
-> 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
-> 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`
## 内部架构
`hwlab-cloud-api` 内部应按 auth/session/API key、authorization、agent session owner、device-pod 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 authorityOpenFGA tuple 表示用户对 device pod、agent session 和工具的细粒度能力。cloud-api 调用 device-pod 内部执行服务使用内部 service token,该 token 不参与用户鉴权、不写入 runner env,也不产生 actor。
## API 接口说明
| 接口 | 说明 |
| --- | --- |
| `GET /auth/oidc/login``GET /auth/oidc/callback` | Web 登录入口;按 [spec-v02-auth.md](spec-v02-auth.md) 接入 Keycloak 并写入 24 小时 `user_sessions` token hash。 |
| `POST /auth/login` | 本地账号密码 bootstrap/legacy fallback;不作为目标 Web/CLI 登录体验。 |
| `GET /auth/session` | 从 cookie 恢复 actor、role 和 session 状态;API key actor 摘要见 `/v1/users/me`。 |
| `GET /v1/auth/session``GET /v1/users/me``GET /v1/access/status``GET /v1/setup/status` | REST 状态和兼容入口;不得读取或返回 password hash、session token 原文或 Secret 值。 |
| `GET/POST /v1/api-keys...` | 用户 API key 管理入口;CLI `HWLAB_API_KEY` 规则见 [spec-v02-auth.md](spec-v02-auth.md)。 |
| `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。 |
| `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。 |
| `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 原文。
## 微服务设计
v0.2 不新增独立用户管理微服务。Keycloak 是独立身份提供方,不是 HWLAB 应用层授权服务;OpenFGA 是内部授权 PDP,不对用户暴露独立 API;用户映射、session/API key 消费、OpenFGA check/write、device pod 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 内的 user/auth 模块抽成 `hwlab-user-api`;抽服务前接口和表结构仍以本文和 [spec-v02-auth.md](spec-v02-auth.md) 为准。
各服务职责如下:
| 服务 | 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 的受控转发。 |
| OpenFGA | `hwlab-v02` 内部稳定授权服务,只接受 cloud-api 调用,不向普通用户或公网暴露。 |
| AgentRun v0.1 runner | 执行 code agent session;接收 cloud-api 提供的 owner/session/device 上下文方便观测,但不作为最终权限 authority。 |
| `hwlab-device-pod` | 暴露设备语义 API;不保存用户权限,不直接面向浏览器或普通用户 session Pod。 |
| `hwlab-edge-proxy` | 公网/FRP 入口和 HTTP 转发;不做业务权限,只转发 cookie/header,不注入伪 actor。 |
| Postgres | v0.2 用户、session、授权、device pod 和既有 runtime durable state。 |
## Kubernetes 落点
Kubernetes 只做运行时隔离和资源兜底,不承载 HWLAB 用户权限模型:
- `v0.2` 使用 `hwlab-v02` namespace,不按用户创建 namespace。
- 普通用户不直接持有 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 绕过应用层授权。
- 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) 评论。
## 测试规格
## T1
阅读 docs/reference/spec-user-access.md 和 docs/reference/spec-v02-auth.md,然后用 Web session 或 `HWLAB_API_KEY` 访问 `/v1/users/me`,确认响应包含 actor、role 和 active auth method,不包含 password hash、session token 原文、完整 API key 或 Secret 值。空库 bootstrap fallback 可额外验证 `/auth/login`,但不得把它作为目标登录体验。
## 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。
## T3
阅读 docs/reference/spec-user-access.md,然后用 cli 手动测试以下内容:普通用户创建 Code Agent session 后,只能读取、继续和取消自己的 trace/result;另一个普通用户访问该 session 必须失败,admin 可以跨用户查看或取消。
## 规格的实现情况
| 规格项 | 状态 | 说明 |
| --- | --- | --- |
| 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 和 jobsDevice Pod 强副作用 job 已接入 reason 校验,真实硬件执行仍依赖 gateway/device-host-cli 在线。 |
| Code Agent owner 绑定 | 已实现 | 已在 `agent_sessions` 写入 `owner_user_id`、conversation/thread/trace 和脱敏 session evidencetrace/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。 |
| 不用 Kubernetes 表达用户权限 | 已实现/持续约束 | 规格明确禁止普通用户持有 kubeconfig 或直连 Service 权限。 |