feat(ql3): add secure console cron scheduling

This commit is contained in:
whyour
2026-08-29 17:13:09 +08:00
parent 951de26ffd
commit b970e2aede
30 changed files with 1916 additions and 31 deletions
+2
View File
@@ -1,5 +1,7 @@
# QingLong 3.0 Architecture RFC
- D-423/ADR-0518(已实现源码候选,待远程阶段实物验收):Local Console 复用既有 immutable Trigger revision、固定 Task revision/content digest、semantic cron validation、durable schedule cursor 与原子 Run admission,新增有界 `GET /api/v3/projects/:projectId/triggers[/:triggerId]` 和强认证 `PUT /api/v3/projects/:projectId/triggers/:triggerId`。列表只返回低敏摘要,精确读取才返回完整 spec/Task digest;读取走 `task.read`mutation 要求 User credential、`task.update`、两分钟一次性 owner-private exact-content proof、credential reconfirm,并由 request-scoped SQLite Trigger repository 在同一事务重验 Policy/RoleBinding/Task pin/credential fence、追加 immutable revision、初始化或更新 schedule 与 durable audit。Console 仅支持冻结的 `qinglong/cron@v1` expression/timezone/`skip|fire_once`,编辑、启停均追加 revision,不提供删除或通用 provider 编辑。Fresh/adopted Profile 共用现有 database close fence;不新增 package、migration、connection、daemon、watcher、每 Trigger timer 或第二 scheduler。默认 headless 不携带 API/Console 资产与 listenerCluster 不复用 Local POSIX proof/SQLite authority。18-package clean build/test `3,044 total / 3,022 pass / 22 conditional skip / 0 fail`,完整 backend `1,653 total / 1,651 pass / 2 Linux conditional skip / 0 fail`Local API `70/70`package boundary、122-module Edge import 与精确 Cluster dependency audit compatible。默认 Edge 2,754,742 bytes/331 files/58 modulesopt-in Edge/Standalone Console 4,150,439/4,150,583 bytes、479 files/101 modules,三资产 84,401 bytes。该结论目前只证明可进入远程验收;最新可下载实物仍是 D-422,必须等 D-423 exact commit 的 CI、Kubernetes、双架构 Console milestone 与下载离线复核闭合后再升级。
- RFC ID: QL-RFC-0001
- 标题:QingLong 3.0 运行时、工作流与 AI 自动化架构
- 状态:Draft
@@ -0,0 +1,71 @@
# ADR-0518Local Console cron Trigger 管理
- 状态:Accepted
- 日期:2026-08-29
- 对应 RFC 切片:D-423
- 关联:ADR-0094、ADR-0103、ADR-0512、ADR-0516、ADR-0517
## 背景
Local Console 已能创建、无损编辑和手动运行内建 command Task,但部署者仍需转到受信 CLI 才能把 Task 变成周期自动化。QingLong 3.0 已有 immutable Trigger revision、固定 Task revision/content digest、语义化 cron 校验、durable schedule cursor 和原子 Run admission;缺口是一个不复制这些 authority、适合低配单机的安全产品入口。
本切片只开放既有本机 Trigger authority。它不能把 Local POSIX presence proof 搬到 Cluster,也不能为每个 Trigger 新建内存 timer,或让默认 headless 设备承担 Console 资产和 HTTP listener。
## 决策
### 1. Local API 暴露有界 Trigger 读取与强认证 mutation
新增固定路由:
```text
GET /api/v3/projects/:projectId/triggers
GET /api/v3/projects/:projectId/triggers/:triggerId
PUT /api/v3/projects/:projectId/triggers/:triggerId
```
列表按 `triggerId` 使用稳定 keysetEdge 默认 16 条、Standalone 默认 32 条、硬上限 64 条。列表只返回 identity、revision、Task pin、schema、enabled、content digest 和时间戳;完整 cron spec 与 Task content digest 只在精确详情读取中返回。读取复用 `task.read` Policy、credential reconfirm 与 durable audit,存储损坏或不可用时失败关闭。
PUT 的 body 必须精确包含 `expectedRevision``mutationId`、Task ID/revision/content digest、spec、enabled 与发生时间。请求只接受 User credential,并要求 `task.update` Policy;服务为 canonical exact body 签发两分钟、一次性的 owner-private presence challengeproof 消费后才形成 `local_console` principal。事务前再次确认 exact credential,随后由既有 Trigger administration service 和 request-scoped SQLite repository 重验 Project/RoleBinding/credential/Policy fence,原子写 immutable revision、head、schedule cursor 与 durable audit。
### 2. Trigger 始终固定当前 Task 内容
Console 在创建或更新 Trigger 前精确读取关联 Task,提交其当前 revision 与 content digest。既有 repository 在事务内重新验证 pin;Task 已变化时拒绝写入,不猜测重绑。
创建使用 `expectedRevision=null`,更新使用当前 revision。Trigger ID 与 Task ID 创建后不能改绑;启用、停用、cron/timezone/misfire 修改都追加新 revision。当前阶段不提供删除,停用是可审计、可恢复的替代操作。
### 3. 首个 UI 只支持冻结的内建 cron schema
Console 的“定时”工作区支持创建、查看、编辑、启用和停用 `qinglong/cron@v1`,字段为 cron expression、显式 timezone 与 `skip|fire_once` misfire policy。它不实现通用 provider/schema 编辑器,不接受浏览器自行扩展未知 Trigger kind。
Credential、presence proof 与编辑快照仍只保存在页面内存,不进入 Cookie、Web Storage、URL、日志或遥测。成功写入后由现有 durable Scheduler 在既有单一有界 lifecycle 中发现;Console 不轮询、不注册每 Trigger callback,也不新增 daemon、watcher、timer 或数据库连接。
### 4. Fresh 与 adopted Profile 使用同一窄 authority
Fresh SQLite runtime 和 adopted Profile 都在现有数据库 close fence 上暴露 `TriggerSource``triggerAdministrationForCredential`。每次 mutation 取得绑定当前 credential fence 的 request-scoped repository;常驻 Application 不持有一个可绕过认证的裸 append authority。
`@qinglong/local-api` 只有 `triggerPutRoute.ts` 被 package dependency audit 精确允许导入 `@qinglong/local-admin/trigger-administration`。同目录或其他文件不能借此扩大依赖;Cluster dependency audit 继续拒绝 Local SQLite/POSIX authority 越界。
### 5. 部署档位保持分层
- 默认 Edge/Standalone headless 不包含 Local API、Console 资产或 listenerSQLite runtime 只增加复用既有 Trigger mutation 的窄装配代码。
- opt-in `edge-application-api``standalone-application-api` 承担同源 Console/API 增量,仍只有一个 Application 进程、一个 SQLite connection 和现有 Scheduler lifecycle。
- Cluster 不复用 Local proof、SQLite repository 或 Local admin service;后续 Cluster Console mutation 必须走 TLS、共享 PostgreSQL authority、RBAC/Approval 与 HA fence。
## 不采用的方案
- 不在浏览器或 HTTP 层实现第二套 cron scheduler:调度事实必须留在 durable Trigger/schedule authority。
- 不允许 Bearer 单因子直接写 Trigger:周期自动化会持续产生执行,必须保留本机 presence proof。
- 不让列表返回完整 spec 或 Task digest:列表应是低敏、有界的浏览面。
- 不通过删除实现“关闭定时”:不可变 revision 与停用记录更可审计。
- 不新建 `ql3-local-trigger-api` 微包:现有 Local API capability 内的两个路由文件不足以形成独立部署/依赖边界。
- 不为路由器注册每 Trigger timer:现有有界 durable scheduler 已覆盖该职责。
## 结果与验证边界
定向测试覆盖列表投影不泄漏 spec/digest、精确详情、坏存储失败关闭、presence challenge、exact body/credential fence、Policy/audit、Task pin、创建/更新冲突与 proof 漂移。真实 SQLite/loopback 旅程已完成 Task 创建/更新 → Trigger challenge/proof/create → bounded list → exact read → 新 revision disable,并验证 create/get/list/update audit。
本地 18-package clean build/test 为 `3,044 total / 3,022 pass / 22 conditional、platform 或 external-service skip / 0 fail`,其中 Local API `70/70`、Local SQLite `248/248`、Local Admin `96/96`Local Application `55 total / 51 pass / 4 platform skip / 0 fail`;完整 backend 为 `1,653 total / 1,651 pass / 2 Linux conditional skip / 0 fail`。package boundary 保持 18 packages、`singleSourcePackages=[]``shallowSourcePackages=[]`122-module Edge import 与精确 Cluster dependency audit 均 compatible。
三项 Console 静态资产合计 84,401 bytes,仍低于 192 KiB 总闭包与 96 KiB 单文件门。默认 Edge 为 2,754,742 bytes/331 files/3 packages/58 modulesRSS delta 11,108,352 bytesopt-in Edge/Standalone Console 为 4,150,439/4,150,583 bytes、479 files/12 packages/101 modulesRSS delta 17,088,512/17,055,744 bytes,均低于既有门。
这些结果证明 D-423 是可进入远程验收的源码候选,不自动等于新的下载产物。当前最新可下载实物仍是 D-422 绑定的 Console v5 milestone;只有 D-423 exact commit 的普通 CI、Kubernetes gate 与显式双架构 Local Console milestone 全部成功,且下载索引通过 checksum/离线 auditor 后,才能把本 ADR 的阶段状态升级为“已交付实物”。
+2 -1
View File
@@ -520,7 +520,8 @@
| [ADR-0514](./ADR-0514-stage-usable-first-automation-journey.md) | 阶段可用的首个自动化旅程 | AcceptedConsole v5 双架构实物已由 D-420 闭合) |
| [ADR-0515](./ADR-0515-bounded-local-console-run-log.md) | Local Console 的有界 Run 日志观察面 | Accepted(首份实际 Console v5 双架构 milestone 已交付) |
| [ADR-0516](./ADR-0516-request-scoped-local-console-task-mutation.md) | request-scoped Local Console Task mutation | Accepted(新双架构 Trial Kit 待本阶段 milestone |
| [ADR-0517](./ADR-0517-strong-local-console-task-authoring-lease.md) | 强认证 Local Console Task authoring lease | Accepted源码候选,待 CI 与新双架构 milestone |
| [ADR-0517](./ADR-0517-strong-local-console-task-authoring-lease.md) | 强认证 Local Console Task authoring lease | AcceptedD-422 双架构 milestone 已交付 |
| [ADR-0518](./ADR-0518-local-console-cron-trigger-administration.md) | Local Console cron Trigger 管理 | Accepted(源码候选,待 CI 与新双架构 milestone |
## 规则
+3
View File
@@ -27,11 +27,14 @@
| D-420 Console Run 日志观察面 | 选择 Run 后经既有认证/Policy/Audit 链读取 latest Attempt 首个 32 KiB,展示 range、truncation、pending/retired 等明确状态 | 不自动轮询、不提供整文件下载;Web Task 创建/修订仍待独立强认证事务切片 |
| D-421 Console Task 创建切片 | request-scoped credential fence、两分钟一次性本机 proof、同事务 Policy/Audit/Task mutation 已完成;Console 可创建 command Task;同源双架构 Console v5 Trial Kit 与 milestone 已生成并验真 | Web update 等待 authoring read/leaseCluster 不复用 Local proof;仍不是生产或公开发布 |
| D-422 Console Task 安全编辑切片 | 强认证完整定义读取、10 分钟一次性 authoring lease、第二份 exact save proof 与 revision/content/credential fence 已完成;Console 可无损编辑内建 argv command Task;同源双架构 Console v5 Trial Kit 与 milestone 已生成并验真 | Cluster 不复用 Local proof;尚无通用 workflow 编辑器、2.x 升级或生产远程管理;仍不是正式发布 |
| D-423 Console cron Trigger 管理切片 | 源码候选已复用既有 immutable Trigger、Task pin、durable schedule 与原子 audit authorityConsole/API 可 list/read/create/update/enable/disable `qinglong/cron@v1`,真实 SQLite/loopback 已通过 | 尚未绑定新的远程 CI 与双架构 milestone;当前下载物仍是 D-422Cluster 不复用 Local proof;不提供删除或通用 Trigger schema 编辑 |
D-421 已关闭 D-420 记录的“Web Task mutation 必须独立设计”缺口,而且没有改名复用 run `33173769047` 的旧 archive。修复提交 `dc1686bd6fb3505174dd9a14098ae5c2c92a1a7f` 的普通主 CI [run 33229592307](https://github.com/whyour/qinglong/actions/runs/33229592307) 为 41 success/3 expected artifact-finalizer skip/0 fail,同源 Kubernetes deployment [run 33229592293](https://github.com/whyour/qinglong/actions/runs/33229592293) 成功;随后显式 Local Console milestone [run 33230227006](https://github.com/whyour/qinglong/actions/runs/33230227006) 为 42 success/2 scope skip/0 fail。由此 Web 创建能力已进入新的阶段实物,而不再只是候选源码。
D-422 已从“源码候选”升级为阶段实物:本地真实 SQLite/loopback 已证明读取、租约、第二次 proof、更新与新围栏启动闭环;18-package clean build/test 为 `3,038 total / 3,016 pass / 22 conditional skip / 0 fail`Local API `64/64`,默认 Edge 与 opt-in Edge/Standalone Console 资源门及真实 Chromium 双证明编辑均通过。修复提交 `f28bf74d1bd29e9b8a8727915de19509f4bda9cf` 的普通主 CI [run 33236204273](https://github.com/whyour/qinglong/actions/runs/33236204273) 为 41 success/3 expected artifact-finalizer skip/0 fail,同源 Kubernetes deployment [run 33236204254](https://github.com/whyour/qinglong/actions/runs/33236204254) 为 1/1;随后显式 Local Console milestone [run 33237026187](https://github.com/whyour/qinglong/actions/runs/33237026187) 为 42 success/2 scope skip/0 fail。阶段产物绑定新的 exact commit/run/artifact digest,没有沿用或改名复用 D-421 archive。
D-423 当前是已通过本地门禁的源码候选,不冒充阶段实物:18-package clean build/test `3,044 total / 3,022 pass / 22 conditional skip / 0 fail`,完整 backend `1,653 total / 1,651 pass / 2 Linux conditional skip / 0 fail`Local API `70/70`18-package boundary、122-module Edge import 与 Cluster dependency audit 均 compatible。默认 headless Edge 为 2,754,742 bytes/331 files/58 modulesopt-in Edge/Standalone Console 为 4,150,439/4,150,583 bytes、479 files/101 modules,三资产合计 84,401 bytes。只有 D-423 exact commit 的完整 CI/Kubernetes 与显式 Console milestone 成功并完成下载复核后,下面的“当前最新可交付”才允许替换为 D-423;在此之前部署者应继续下载 D-422。
D-418 防止把“20 天代码和测试”冒充“用户已经能下载并完整操作”:源码与普通 CI 已具备生成、审计和实跑两种 Trial Kit 的能力,但只有显式 artifact run 生成且被同 run 的双架构 milestone 收录后,才是可下载阶段产物。操作说明见 [Local Alpha Trial Kit](./ql3-local-alpha-trial-kit.md) 与 [Local Web Console](./ql3-local-web-console.md)。
当前最新可交付 Local Console v5 绑定提交 `f28bf74d1bd29e9b8a8727915de19509f4bda9cf` 与 [GitHub Actions run 33237026187](https://github.com/whyour/qinglong/actions/runs/33237026187),保留至 2026-09-28
+14 -11
View File
@@ -1,24 +1,24 @@
# QingLong 3.0 Local Web Console
Local Web Console 是 `@qinglong/local-api` 的 opt-in 操作界面,用来创建 command Task、查看 Task/Run/执行事件,并显式启动或取消一次运行。它由 Console Local Alpha Trial Kit 交付,但不进入默认 headless 变体,也不是 2.x Web UI 的完整替代品。
Local Web Console 是 `@qinglong/local-api` 的 opt-in 操作界面,用来创建和编辑 command Task、配置 cron Trigger、查看 Task/Run/执行事件,并显式启动或取消一次运行。它由 Console Local Alpha Trial Kit 交付,但不进入默认 headless 变体,也不是 2.x Web UI 的完整替代品。
## 选择部署档位
| 场景 | 建议 |
| --- | --- |
| 内存很小、只需后台自动化的路由/NAS | 继续使用默认 `edge` headless ApplicationConsole 零增量 |
| 内存很小、只需后台自动化的路由/NAS | 继续使用默认 `edge` headless Application不携带 Console/API 资产与 listener |
| 路由/NAS 需要临时人工查看与操作 | 选择 `edge-application-api`,只通过 loopback 或 SSH tunnel 访问 |
| 普通单节点服务器 | 选择 `standalone-application-api` |
| Kubernetes/Cluster 节点 | 不使用本 Local Console;继续使用 Cluster Control/Console 路径 |
D-418 已闭合独立 Console image/Trial KitD-419 的 v5 quickstart 进一步安装可直接使用的 Owner credential presentation,并创建默认不自动运行的 `alpha-first-automation`。D-420 又把该 Run 的 latest Attempt 首个 32 KiB 日志带到 Console。D-421 增加 request-scoped strong-auth Task PUT 与 Console command Task 创建器;它不复用 CLI 的进程级 active credential,也不让单因子 Bearer 直接写 Task。绑定提交 `dc1686bd6fb3505174dd9a14098ae5c2c92a1a7f` 的双架构实物已由 [milestone run 33230227006](https://github.com/whyour/qinglong/actions/runs/33230227006) 生成;普通 push 的源码和 CI 仍不是公开下载物
D-418 已闭合独立 Console image/Trial KitD-419 的 v5 quickstart 进一步安装可直接使用的 Owner credential presentation,并创建默认不自动运行的 `alpha-first-automation`。D-420 又把该 Run 的 latest Attempt 首个 32 KiB 日志带到 Console。D-421/D-422 依次增加 request-scoped strong-auth Task 创建与双 proof 无损编辑;绑定提交 `f28bf74d1bd29e9b8a8727915de19509f4bda9cf`最新双架构实物已由 [milestone run 33237026187](https://github.com/whyour/qinglong/actions/runs/33237026187) 生成。D-423 源码候选继续开放既有 immutable Trigger/cron authority;在新的 exact-commit milestone 成功前,当前下载物仍不包含该能力
## 前置条件
- 已完成 Local fresh setup,并有受支持的 Application config
- Owner pepper keyring 与 SQLite active pepper 一致;
- 已通过 [`ql3-identity`](./ql3-local-identity-credential.md) 为 active Identity 签发 API credential
- credential 对目标 Project 至少有读取 Task/Run 的权限;创建 Task、启动和取消分别还需要 `task.create``run.start``run.stop`
- credential 对目标 Project 至少有读取 Task/Run 的权限;创建 Task、修改 Task/Trigger、启动和取消分别还需要 `task.create``task.update``run.start``run.stop`
- config、keyring、database 和 credential delivery 保持既有 `0700/0600`、no-symlink 和同 UID authority。
## 启动
@@ -57,22 +57,25 @@ ssh -L 5701:127.0.0.1:5701 router.example
2. 选择“创建任务”,填写 Task ID、名称、argv 可执行文件和逐行参数,再选择“保存并生成本机证明”。
3. 在部署设备上以 QingLong 数据目录 owner 读取 `<deploymentRoot>/console-presence/<页面显示的 basename>`;把 JSON 的完整 `ql3p_…` proof 值粘贴回页面。文件为 `0600`、两分钟有效且只能用于这份 exact 操作一次。不要通过聊天、日志或 URL 转发 proof。
4. 编辑现有内建 command Task 时先选择“编辑任务”,完成第一次本机证明以读取完整定义并取得 10 分钟一次性编辑租约。保存新内容时页面会要求第二份 proof;第一份只授权读取,不能复用来保存。Task ID 只读,未展示的 command config 与 labels 会原样保留。
5. 创建或更新成功后核对 revision/content fence,再选择“运行一次”。fresh Console Trial Kit 也可直接使用 `alpha-first-automation`
6. 在“运行”中选择 durable Run,按 Event sequence 判断实际进度;Bounded log 只显示 latest Attempt 的首个 32 KiB,后续内容仍需通过 API 分页读取
7. 日志 pending 时使用“刷新”显式重读;retired 表示内容已按保留策略清理,不代表 Run/Event 事实丢失
8. “请求取消”只提交 durable cancellation intent;界面出现 `cancelled|failed|succeeded|timed_out` 终态前,不要认为进程已经停止
9. 完成后选择“断开并清除凭据”,再关闭页面
5. 需要周期运行时进入“定时”,选择“新建定时”,填写 Trigger ID、已存在的 Task ID、cron expression、显式 timezone、`skip|fire_once` misfire policy 与 enabled。页面会先读取当前 Task revision/content digest,再要求一份绑定这次 exact Trigger 内容的本机 proof
6. 编辑、启用或停用 Trigger 都会追加 immutable revision;当前阶段没有删除。Task 已被其他操作更新时,旧 pin 会失败关闭,应刷新后重新确认,不能猜测重绑
7. 创建或更新 Task 成功后核对 revision/content fence,再选择“运行一次”。fresh Console Trial Kit 也可直接使用 `alpha-first-automation`enabled Trigger 则由已有 durable Scheduler 自动产生 Run,不依赖浏览器保持打开
8. 在“运行”中选择 durable Run,按 Event sequence 判断实际进度;Bounded log 只显示 latest Attempt 的首个 32 KiB,后续内容仍需通过 API 分页读取
9. 日志 pending 时使用“刷新”显式重读;retired 表示内容已按保留策略清理,不代表 Run/Event 事实丢失
10. “请求取消”只提交 durable cancellation intent;界面出现 `cancelled|failed|succeeded|timed_out` 终态前,不要认为进程已经停止。
11. 完成后选择“断开并清除凭据”,再关闭页面。
Credential 只存在当前页面内存,不进入 URL、Cookie 或 Web Storage。页面刷新会丢失 credential,需要重新输入;这是当前安全边界,不是缺陷。
## 当前阶段可用边界
当前可操作闭环是内建 argv command Task create/list/read/update/enable/disable/start Run list/read/events/steps/log/cancel。编辑器只修改当前展示字段并保留完整快照中的其他 config/labels;其他 kind/schema 继续使用受信管理入口。页面暂不负责:
D-423 源码候选的可操作闭环是内建 argv command Task create/list/read/update/enable/disable/start`qinglong/cron@v1` Trigger list/read/create/update/enable/disable,以及 Run list/read/events/steps/log/cancel。Task 编辑器只修改当前展示字段并保留完整快照中的其他 config/labels;其他 Task kind 或 Trigger schema 继续使用受信管理入口。页面暂不负责:
- Identity、Policy、Secret、Plugin Package 或 AI 配置管理;
- Trigger 删除、通用 Trigger provider/schema 编辑或 Cluster Trigger 管理;
- 日志整文件下载、终端、文件管理或 2.x 数据迁移;
- LAN/public 暴露、TLS termination、多用户 Web session 或 Cluster 管理。
三项静态资产总计 69,723 bytes,不依赖 CDN、网络字体或前端框架,仍低于 192 KiB 总闭包和单文件 96 KiB 门。`edge-application-api|standalone-application-api` 为 4,077,890 / 4,078,034 bytes、473 files、12 packages、95 loaded modules,仍低于 6 MiB/640-file 门;本机 RSS delta 为 16,269,312 / 16,318,464 bytes,低于 28 MiB。基础 headless Edge 保持 2,737,205 bytes、329 files、3 packages、58 modules不携带这些资产
三项静态资产总计 84,401 bytes,不依赖 CDN、网络字体或前端框架,仍低于 192 KiB 总闭包和单文件 96 KiB 门。`edge-application-api|standalone-application-api` 为 4,150,439 / 4,150,583 bytes、479 files、12 packages、101 loaded modules,仍低于 6 MiB/640-file 门;本机 RSS delta 为 17,088,512 / 17,055,744 bytes,低于 28 MiB。默认 headless Edge 2,754,742 bytes、331 files、3 packages、58 modulesRSS delta 11,108,352 bytes;它不携带 Console/API 资产、listener 或第二个 scheduler,但包含复用既有 SQLite Trigger mutation authority 的小幅装配代码增量
停止 Local API 进程走与 Application 相同的 drain/shutdown 路径。Console 没有独立数据库、后台任务或需要额外清理的持久状态。