feat(ql3): establish 3.0 incubation baseline

This commit is contained in:
whyour
2026-08-12 00:25:26 +08:00
parent 4bf92dcfeb
commit c699c32461
2817 changed files with 779642 additions and 653 deletions
+286
View File
@@ -0,0 +1,286 @@
# `ql3-policy` 本机 Project 与 RoleBinding 管理
`ql3-policy` 是一次命令、一次进程的 Owner-only 管理入口,不启动管理 daemon,也不把
credential token、pepper、数据库路径或 command 路径写入成功输出。
## 前置条件
- 已完成 Fresh Setup
- 当前 credential 属于实例 authority Project 的 active User Owner
- deployment root 和子目录为当前 UID 所有、目录 `0700`、文件 `0600`
- SQLite 与 Owner pepper keyring 已通过 readiness
- 所有 UUID、Project、subject 和路径均替换为部署自己的值。
执行形式:
```sh
ql3-policy run --command-file /srv/qinglong3/commands/project.json
```
command file 必须是 deployment root 内的规范、非 symlink、当前 UID `0600` 文件。
响应不确定时只能原样重放同一个 command。
## 创建 Project
```json
{
"schemaVersion": 1,
"operation": "policy.project.create",
"options": {
"deploymentRoot": "/srv/qinglong3",
"databasePath": "/srv/qinglong3/qinglong3.sqlite",
"profile": "edge",
"ownerPepperKeyringDirectory": "/srv/qinglong3/owner-keys",
"credentialFilePath": "/srv/qinglong3/owner-credential.json"
},
"request": {
"authorityProjectId": "default",
"projectId": "automation",
"name": "Automation",
"slug": "automation",
"expectedCurrentVersion": 0,
"mutationId": "11111111-1111-4111-8111-111111111111",
"requestId": "project-create-automation",
"failureAuditEventId": "11111111-1111-4111-8111-111111111112"
}
}
```
创建会在同一 SQLite 事务内建立 active Project、调用者自己的首个 active Owner
RoleBinding、allowed audit 和 immutable mutation ledger。成功示例:
```json
{
"schemaVersion": 1,
"operation": "policy.project.create",
"status": "inserted",
"projectId": "automation",
"name": "Automation",
"slug": "automation",
"projectStatus": "active",
"version": 1
}
```
Edge 最多 16 个 ProjectStandalone 最多 128 个;archived Project 仍占容量。
## 归档与恢复
归档使用 Project 当前版本:
```json
{
"schemaVersion": 1,
"operation": "policy.project.archive",
"options": {
"deploymentRoot": "/srv/qinglong3",
"databasePath": "/srv/qinglong3/qinglong3.sqlite",
"profile": "edge",
"ownerPepperKeyringDirectory": "/srv/qinglong3/owner-keys",
"credentialFilePath": "/srv/qinglong3/owner-credential.json"
},
"request": {
"authorityProjectId": "default",
"projectId": "automation",
"expectedCurrentVersion": 1,
"mutationId": "22222222-2222-4222-8222-222222222221",
"requestId": "project-archive-automation",
"failureAuditEventId": "22222222-2222-4222-8222-222222222222"
}
}
```
归档后 Project Policy 默认拒绝该 Project 的业务权限,但不删除 RoleBinding、Task、
Run、Secret、Package 或审计历史。恢复把 operation 改为
`policy.project.restore`,并把 `expectedCurrentVersion` 改为归档结果的版本。
实例 authority Project 不能归档。不要通过直接 SQL 修改其状态,也不要把 archive
当成 hard delete。
## 查询 Project
查询一个 Project current head
```json
{
"schemaVersion": 1,
"operation": "policy.project.inspect",
"options": {
"deploymentRoot": "/srv/qinglong3",
"databasePath": "/srv/qinglong3/qinglong3.sqlite",
"profile": "edge",
"ownerPepperKeyringDirectory": "/srv/qinglong3/owner-keys",
"credentialFilePath": "/srv/qinglong3/owner-credential.json"
},
"request": {
"authorityProjectId": "default",
"projectId": "automation",
"requestId": "project-inspect-automation",
"auditEventId": "44444444-4444-4444-8444-444444444441"
}
}
```
已授权但不存在时返回 `found:false`。active 和 archived Project 都可查询;查询 archived
Project 不会恢复其业务权限。
有界列出 Project
```json
{
"schemaVersion": 1,
"operation": "policy.project.list",
"options": {
"deploymentRoot": "/srv/qinglong3",
"databasePath": "/srv/qinglong3/qinglong3.sqlite",
"profile": "edge",
"ownerPepperKeyringDirectory": "/srv/qinglong3/owner-keys",
"credentialFilePath": "/srv/qinglong3/owner-credential.json"
},
"request": {
"authorityProjectId": "default",
"limit": 16,
"status": "all",
"requestId": "project-list-first-page",
"auditEventId": "44444444-4444-4444-8444-444444444442"
}
}
```
`limit` 必须为 164`status` 只能为 `active``archived``all`。若响应中的
`nextCursor` 非 null,下一条 command 使用新的 request/audit ID,并把该值原样放入
request
```json
{
"after": {
"slug": "automation",
"projectId": "automation"
}
}
```
不要自行修改 cursor,也不要使用上一次 query command 的 audit event ID。列表按
`slug, projectId` 正序稳定翻页,不支持 offset、任意排序或模糊搜索。
每一页都是独立的 current-head snapshot;若翻页期间发生 create/archive/restore
需要严格同一时点清单时应从第一页重新查询。
## 管理 RoleBinding
授予或更新 RoleBinding
```json
{
"schemaVersion": 1,
"operation": "policy.role-binding.put",
"options": {
"deploymentRoot": "/srv/qinglong3",
"databasePath": "/srv/qinglong3/qinglong3.sqlite",
"profile": "edge",
"ownerPepperKeyringDirectory": "/srv/qinglong3/owner-keys",
"credentialFilePath": "/srv/qinglong3/owner-credential.json"
},
"request": {
"projectId": "automation",
"target": { "type": "user", "id": "operator-user" },
"role": "operator",
"expectedCurrentVersion": 0,
"mutationId": "33333333-3333-4333-8333-333333333331",
"requestId": "role-put-automation-operator",
"failureAuditEventId": "33333333-3333-4333-8333-333333333332"
}
}
```
撤销时使用 `policy.role-binding.revoke`,移除 `role`,并填写 target 当前 binding
version。Owner 只能授予 User;授予新 Owner 前,目标必须有 active Identity 和当前
有效 credential。撤销或降级当前 Owner 时必须保留另一位可登录的 active User Owner。
## 查询 RoleBinding
取得一个 subject 的 current binding
```json
{
"schemaVersion": 1,
"operation": "policy.role-binding.inspect",
"options": {
"deploymentRoot": "/srv/qinglong3",
"databasePath": "/srv/qinglong3/qinglong3.sqlite",
"profile": "edge",
"ownerPepperKeyringDirectory": "/srv/qinglong3/owner-keys",
"credentialFilePath": "/srv/qinglong3/owner-credential.json"
},
"request": {
"projectId": "automation",
"target": { "type": "user", "id": "operator-user" },
"requestId": "role-inspect-automation-operator",
"auditEventId": "55555555-5555-4555-8555-555555555551"
}
}
```
已授权但从未存在时返回 `found:false`。当前 binding 已撤销时返回 `state:"revoked"`
不返回 `role`;历史 active revision 不会被当成当前状态。
有界列出每个 subject 的最新 binding
```json
{
"schemaVersion": 1,
"operation": "policy.role-binding.list",
"options": {
"deploymentRoot": "/srv/qinglong3",
"databasePath": "/srv/qinglong3/qinglong3.sqlite",
"profile": "edge",
"ownerPepperKeyringDirectory": "/srv/qinglong3/owner-keys",
"credentialFilePath": "/srv/qinglong3/owner-credential.json"
},
"request": {
"projectId": "automation",
"limit": 32,
"state": "active",
"role": "all",
"requestId": "role-list-automation-first-page",
"auditEventId": "55555555-5555-4555-8555-555555555552"
}
}
```
`limit` 必须为 164`state` 只能为 `active|revoked|all``role` 只能为
`owner|admin|operator|viewer|all`。下一页使用新的 request/audit ID,并把响应的
`nextCursor` 原样放入 request
```json
{
"after": {
"subjectType": "api_app",
"subjectId": "deployment-bot"
}
}
```
每一页都是独立的 current-head snapshot。若翻页期间发生 put/revoke,需要严格同一
时点清单时从第一页重新查询。RoleBinding query 由目标 Project 自己的当前 Owner
授权,不要求实例 authority Project;但 Owner 不能用一个 Project 的权限查询另一个
Project。
## Authority 与失败处理
- Project lifecycle command 的 `authorityProjectId` 必须是 ADR-0211 定义的实例
authority Project
- Project inspect/list 也必须使用该 authority Projectsecondary Project Owner
不能枚举实例 Project 拓扑;
- secondary Project Owner 可以管理自身 RoleBinding,但不能创建、归档或恢复其他
Project
- RoleBinding inspect/list 与 put/revoke 一样,只允许目标 Project 的当前 Owner
- `current_version_conflict`:重新取得受支持的当前版本后创建新 command;
- `mutation_conflict`:同一 mutation ID 已用于不同语义,停止重试并人工调查;
- `project_identity_conflict`Project ID 或 slug 已存在;
- `project_capacity_exceeded`:已达到 Profile 硬上限;
- `authority_project_protected`:试图归档实例 authority Project
- `credential_or_policy_fence_rejected`:认证后 credential、authority 或 Owner fence
已变化,重新认证并使用新 mutation。
当前没有 Project rename、authority transfer 或 hard-delete 命令。不要用直接 SQLite
查询或修改模拟这些产品能力。