Cron 定时任务系统
wesgine 定时任务:创建、调度、Actor 边界与执行身份保护。
核心 API
任务管理
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /cron | 任务列表(含 states) |
| POST | /cron | 添加定时任务 |
| GET | /cron/stats | 调度器统计 |
| DELETE | /cron/{id} | 删除任务 |
| PATCH | /cron/{id} | 更新任务 |
| POST | /cron/{id}/trigger | 立即触发 |
| POST | /cron/{id}/enable | 启用 |
| POST | /cron/{id}/disable | 禁用 |
| GET | /cron/{id}/runs | 运行记录 |
SDK API
cell.Cron().Add(ctx, job)
cell.Cron().Update(ctx, id, patch)
cell.Cron().Remove(ctx, id)
cell.Cron().Enable(ctx, id)
cell.Cron().Disable(ctx, id)
cell.Cron().Trigger(ctx, id)
cell.Cron().ListAllStates(ctx)
cell.Cron().ListRuns(ctx, id)
cell.Cron().Stats(ctx)
Actor 边界(INV-CRON-10)
Cron 是 Actor 边界的第四个面,泄漏的不是数据而是执行身份。
核心风险
executeJob 把 entry.Actor 抄进 JobRequest,job 以所有者身份跑。
不查主的 Trigger = 以别人的身份跑代码。
两个判据单点
| 单点 | 用途 |
|---|---|
jobVisible(actor) | 可见性(actor == "" 是 admin 视图) |
ownedJob(ctx, id, actor) | by-ID 解析(行身份 (id, actor)) |
每个方法都收 actor
wcron.Manager 的每个方法都收 actor 参数。少一个 = 完整越权路径。
错误响应
不存在与不属于你返回同一个 wcron.ErrJobNotFound——不给 ID 探针。
工具侧
cron_manage 工具全 action 取 tc.Actor。
add 不可冒名(jobInput 无 Actor 字段),但 update/remove/trigger 缺 actor 时只需一个 ID 即可越权。
无主 Job
读侧 fail-closed 但 ListDue 不带 actor
无主 job:
- UI 列不出 ✗
- by-ID 全
ErrJobNotFound✗ - 每到点照跑照发消息 ✗
「能跑、关不掉、查不到」比「看不到」坏得多。
ClaimOwnerless
Manager.ClaimOwnerless(ctx, actor)
只动 actor = '' 的行。只给单 actor 部署用。
多 actor Cell 盲调 = 把某员工的任务判给另一个人。
种子声明(INV-SEED-01)
CellSpec.Cron 是出生声明,不是常驻状态。
首次 boot → 播种进 wes_cron_jobs
此后 → 存储是唯一权威
标记在探测成功后写,含声明为空。
配额(INV-QUOTA-05)
MaxCronJobs 判据在唯一增长点 cron.Scheduler.Add。
覆盖已有名字 delta=0(upsert 是编辑手势)。
相关文档
- Actor 边界总览 →
actor-boundary.md - CellSpec 总览 →
cellspec-overview.md - 治理模型 →
governance-model.md