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

7.2 KiB
Raw Blame History

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.listtask.putexpectedRevision=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.readprincipal 必须是 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 IDrequestId、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-corelocal-sqlitelocal-adminlocal-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 复用本机 CLISQLite 单 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。