Files
qinglong/docs/adr/ADR-0256-owner-authenticated-local-task-definition-management.md
T

104 lines
7.2 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.
# ADR-0256Owner-authenticated Local TaskDefinition Management
- 状态:Accepted
- 日期:2026-08-01
- 关联 RFCQL-RFC-0001 D-85、D-175、D-207、D-239
- 关联 ADRADR-0074、ADR-0085、ADR-0089、ADR-0091、ADR-0092、ADR-0217
## 背景
Fresh Edge/Standalone 已具备版本化 TaskDefinition、内建 `qinglong/command@v1`
语义注册表、原子 execution revision 物化、Owner credential 和 Project Policy,但产品入口
仍只能通过测试或直接 Repository 写入 TaskDefinition。部署者无法用受支持命令完成
create/update/enable/disable/inspect/list,也就无法在 fresh 安装后建立第一个可调度任务。
这个缺口不能靠向常驻 application 暴露写 Repository 解决。路由设备不应因此增加管理
daemon、第二数据库连接、timer 或新的 workspace packageCluster 也不能复用本机文件
credential 和 SQLite authority 来绕过 PostgreSQL/RBAC/多副本管理边界。
## 决策
1. 在既有 `@qinglong/local-owner-cli` 增加唯一短生命周期 binary `ql3-task`,只接受
`run --command-file /absolute/private-command.json`。command file 必须位于 deployment
root 内、由当前 UID 持有、模式 `0600`、不是 symlink;不接受参数拼装的 Task spec。
2. v1 只开放 `task.put|task.inspect|task.list``task.put`
`expectedRevision=null` 创建,以当前 revision 更新;`enabled=false|true` 分别表达
disable/enable 的新 revision,不提供 update-in-place 或 delete。
3. 写入只接受已冻结 registry 中的 Task spec;当前 production registry 仍只有
`qinglong/command@v1`。CLI 不因为 envelope 支持 `script|workflow|agent|tool` 就猜测其语义。
4. 每次命令建立短生命周期 SQLite connection,使用现有 Owner pepper/credential
authentication 和 Project Policy。create/update 分别要求 `task.create|task.update`,查询
要求 `task.read`principal 必须是 strong Userowner/admin/operator 可写,viewer 只读。
5. 写事务在同一 `BEGIN IMMEDIATE` 内重新验证 credential/Identity/pepper fence、Project
version/status、RoleBinding version/state,并原子提交 allowed audit、Task head、immutable
revision、mutation replay 与适用的 local execution revision。任何 fence、semantic、revision
或 audit replay 冲突整体回滚。
6. `mutationId` 同时是 allowed audit event ID`requestId`、actor、Policy fence、operation 和
immutable `occurredAtMs` 都属于 exact replay 语义。调用结果未知时只能原样重放 command
file。复用 mutation 但改变 Task 或 audit 语义必须失败关闭。
7. inspect/list 使用当前 head、最多 256 条的稳定 `taskId` keyset;每页使用新的
request/audit identity。输出只包含 Project/task、revision、name、kind/schema、enabled、
content digest 与时间,不回显 spec/config/command 参数、SecretRef、credential、pepper、
数据库路径或 command-file 路径。
8. 实现只增加现有 `runtime-core``local-sqlite``local-admin``local-owner-cli` 的显式
subpathworkspace 保持 19 个 package,不新增第三方或 production dependency、migration、
daemon、timer、watcher、listener、cache、Pool 或端口。Edge/Standalone 空闲成本为零,只有
operator 发起命令时产生一个短进程和一个 SQLite connection。
9. `ql3-task` 明确不支持 Cluster。Cluster TaskDefinition 管理必须后续通过 PostgreSQL 管理
repository、独立 credential/RBAC、强认证 transport、持久审计和多副本 fence 提供;不能
挂载本机 Owner credential 或把 SQLite command file 当集群管理协议。
## 失败与恢复
- 配置、路径、权限或 Task semantic 失败:修正后创建全新的 command identity
- 认证、授权或 fence 失败:恢复 credential/RoleBinding 后创建新命令,不复用已改变语义的
mutation
- revision conflict:先 inspect 当前 revision,再以新 mutation/时间提交明确更新;
- COMMIT 结果未知:保留原 `0600` command file 并逐字重放;`existing` 表示 durable semantic
已存在,不重新创建 revision;
- audit 与 Task replay 不一致:停止自动重试并人工检查,禁止直接 SQL 修补或删除历史。
## 被拒绝的替代方案
1. **新建 `task-admin` workspace package**:没有独立部署、依赖或权限域,只会让 19 包拓扑
再次碎片化,拒绝。
2. **把写入口放进常驻 local application**:扩大空闲 authority 和攻击面,也让路由设备为偶发
管理持续付费,拒绝。
3. **直接使用 SQLite/Repository 脚本**:绕过 authentication、Policy、audit、semantic registry
和 revision fence,拒绝。
4. **用 `task.enable|disable` 修改 head bool**:破坏 append-only 历史、pinned Run 解释和 exact
replay,拒绝;状态变化必须是新 revision。
5. **让 Cluster 复用本机 CLI**SQLite 单 authority 和本机 credential 不具备 PostgreSQL HA、
多副本竞争或集群 RBAC 语义,拒绝。
## 验证
- `ql3-task` 真实 SQLite 纵切面覆盖 create、exact replay、disable、inspect、keyset list、
operator allow、viewer deny、credential/Policy race rollback、Task semantic drift、audit semantic
drift、私有 command file 与低敏输出;当前 5/5
- `@qinglong/local-owner-cli` 完整 dependency closure strict TypeScript 通过;
- Owner CLI 83/83、local-admin 81/81、local-sqlite 189/189、backend 956 pass/2 条件
skip/0 fail19 个 QL3 package 清理后顺序构建并完成全量测试,所有已执行 suite 零失败;
- dependency boundary 40/40 且 repository audit `findings=[]`Edge import 与 local image
静态 inventory audit 均 compatibleGitNexus compare-to-develop 为 LOW、0 条受影响执行流;
- PostgreSQL 18.4 arm64 physical HA 同轮重新通过 `remote_apply`、timeline 1→2、旧主先
fencing、`pg_rewind` 只读同步重入、两个 fresh control replica 与全部业务 gate
`gates.passed=true`;测试容器、网络、卷零残留。此项只证明 Cluster 基线未回归,不把
本机 `ql3-task` 宣称为 Cluster 产品入口;
- 完整 package/backend/dependency/artifact 回归是合入前门禁。
D-239 验证时曾发现 storage-only 超过 512 files、adopted assembly 漏收 `local-secret`,且
application 为 741 files、约 6.40 MiB、import RSS delta 约 20.2--20.35 MiB,因此本 ADR
当时没有把 Profile gate 标记为通过。后续 ADR-0257/D-240 已通过内部运行制品裁剪、精确 adopted
闭包和 eager import 收口修复该 blocker;十档门现已全部通过。D-239 没有自行放宽预算,完整
application 的 24 MiB import delta 分层也不改变 Edge 96 MiB 总物理进程预算。
## 后续约束
后续 ADR-0258/D-241 已完成 Trigger create/update/enable/disable/inspect/list 和
Task→Trigger→Run 的可操作 fresh 用户闭环,并在 Trigger 写入、scheduler candidate 与最终 Run
commit 三处执行 Task current-head fence。下一步为 Cluster 实现 PostgreSQL/RBAC 对等管理入口。
HTTP/UI 只能调用同一服务语义,不能复制 Task/Trigger semantic、Policy、current-head fence 或
replay 规则。非 command kind 仍须各自完成 semantic、权限、编译与执行评审后才能进入
production registry。