mirror of
https://github.com/FunnyWolf/agentic-soc-platform.git
synced 2026-08-22 13:12:56 +02:00
add TODO
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
# v0.6.0 release scope
|
||||
|
||||
Status: Confirmed
|
||||
|
||||
## 1. Release objective
|
||||
|
||||
v0.6.0 面向单组织私有化部署,目标是让一个小型 SOC 团队完成稳定的告警分诊、案件调查、响应执行和日常运维。该版本以核心功能闭环为优先,不以 SaaS、多租户或大规模集群为目标。
|
||||
|
||||
## 2. Supported deployment
|
||||
|
||||
- 唯一正式支持的部署拓扑是单主机 Docker Compose。
|
||||
- 不承诺 Kubernetes、横向扩容、多节点高可用或自动故障切换。
|
||||
- 每种后台 Worker 正式支持一个实例;重复实例必须按 Worker Health Spec 拒绝启动。
|
||||
- 数据库降级不受支持。回滚依赖升级前备份恢复。
|
||||
|
||||
## 3. Upgrade contract
|
||||
|
||||
- 必须支持从 v0.5.2 直接升级到 v0.6.0。
|
||||
- 升级必须保留所有业务数据、用户、LDAP 配置、集成配置、API Key、附件和自定义脚本。
|
||||
- 所有数据库变更必须提供 Django migration。
|
||||
- migration 必须适用于已有数据,不得要求清空数据库。
|
||||
- 升级流程继续先运行 migration,再启动应用服务和 Worker。
|
||||
- 不承诺从 v0.5.1 或更早版本直接升级,也不提供 v0.6.0 到 v0.5.2 的数据库 downgrade。
|
||||
|
||||
## 4. Capacity baseline
|
||||
|
||||
正式验收使用现有 `generate_perf_data --scale medium` 数据规模:
|
||||
|
||||
| Resource | Baseline |
|
||||
| --- | ---: |
|
||||
| Users | 20 |
|
||||
| Cases | 10,000 |
|
||||
| Alerts | 100,000 |
|
||||
| Artifacts | 50,000 |
|
||||
| Alert-artifact links | 300,000 |
|
||||
| Enrichments | 30,000 |
|
||||
| Playbook runs | 10,000 |
|
||||
| Knowledge records | 2,000 |
|
||||
| Audit logs | 100,000 |
|
||||
|
||||
`large` 和 `extreme` 数据档位可用于开发压测,但不属于 v0.6.0 响应时间承诺。
|
||||
|
||||
## 5. Official compatibility matrix
|
||||
|
||||
| Area | Officially supported |
|
||||
| --- | --- |
|
||||
| SIEM | Splunk, ELK |
|
||||
| LLM | OpenAI-compatible Chat Completions endpoints |
|
||||
| Threat intelligence | AlienVault OTX, OpenCTI |
|
||||
| Authentication | Local account, LDAP |
|
||||
| Object storage | 当前 Compose 分发所配置的 S3-compatible storage |
|
||||
| Cache/stream | 当前 Compose 分发所配置的 Redis |
|
||||
|
||||
代码枚举或 UI 示例中出现其他厂商名称,不代表官方连接器或兼容承诺。
|
||||
|
||||
## 6. Roles
|
||||
|
||||
v0.6.0 保持三个固定角色:
|
||||
|
||||
| Role | Meaning |
|
||||
| --- | --- |
|
||||
| Admin | 系统管理和全部业务写操作 |
|
||||
| User / Analyst | 分析师业务写操作 |
|
||||
| Viewer | 只读访问 |
|
||||
|
||||
不实现自定义角色、权限编辑器或团队级数据隔离。每个新功能必须在自己的 Spec 中定义三种角色的具体权限。
|
||||
|
||||
## 7. Language
|
||||
|
||||
- 正式 UI 只支持英文。
|
||||
- 项目用户文档保持中英文版本,先完成中文再同步英文。
|
||||
- v0.6.0 不引入前端 i18n 框架。
|
||||
|
||||
## 8. API compatibility
|
||||
|
||||
- v0.6.0 允许破坏性修改现有前端 API 和 `/api/agent/v1/`。
|
||||
- 不创建 Agent API v2 作为兼容层。
|
||||
- 不要求旧 CLI 或旧插件拒绝连接,也不维护其兼容性。
|
||||
- 新 API 仍应有明确的 DRF schema,避免无意的响应漂移。
|
||||
|
||||
## 9. Confirmed functional domains
|
||||
|
||||
1. Case 批量分诊。
|
||||
2. 案件合并。
|
||||
3. Playbook 执行可观测性和控制。
|
||||
4. Custom Variables。
|
||||
5. Worker Health。
|
||||
6. Integration Health。
|
||||
7. SLA 管理,待详细讨论。
|
||||
8. AI 质量评估,待详细讨论。
|
||||
9. 抑制规则,待详细讨论。
|
||||
10. Operations 页面整合,待详细讨论。
|
||||
|
||||
SLA、AI 质量评估和抑制规则都是 v0.6.0 正式发布的阻断项,但必须限制为最小可用闭环。
|
||||
|
||||
## 10. Explicit exclusions
|
||||
|
||||
- 多租户、组织/Workspace 隔离。
|
||||
- Kubernetes 和高可用部署。
|
||||
- OIDC、SAML 或其他 SSO。
|
||||
- 自定义角色。
|
||||
- Jira、ServiceNow 等专用连接器。
|
||||
- 可视化或表单式 Playbook 编排器。
|
||||
- Playbook 中途人工审批。
|
||||
- 通用 HTTP/Webhook Connector 或统一厂商动作抽象。
|
||||
- 全站 UI 国际化。
|
||||
- 旧 CLI/插件兼容层。
|
||||
- 自动 Unmerge。
|
||||
|
||||
## 11. Cross-domain invariants
|
||||
|
||||
- merged source Case 默认不得进入 Dashboard、SLA、案件数量和正常列表统计。
|
||||
- Case 单条编辑和批量编辑必须调用同一服务端状态机。
|
||||
- Playbook、Module 和 Worker 错误不得向 API 暴露原始凭据、响应正文或 traceback。
|
||||
- Admin 管理动作按各 Spec 写 AuditLog;高频健康遥测不写 AuditLog。
|
||||
- 所有时间使用 timezone-aware UTC 存储,前端按浏览器时区显示。
|
||||
- 所有列表型 API 必须分页;动态 Stage 数量不设上限,因此 Stage API 尤其不得全量返回。
|
||||
- 业务写操作不得通过前端限制替代服务端权限和状态校验。
|
||||
@@ -0,0 +1,323 @@
|
||||
# Bulk Case Triage
|
||||
|
||||
Status: Confirmed
|
||||
|
||||
## 1. Purpose
|
||||
|
||||
允许分析师在 Case 列表中明确勾选一组 Case,一次修改相同的分诊字段。该功能优化重复人工操作,不替代案件合并、抑制规则、AI 分析或 Playbook。
|
||||
|
||||
## 2. Scope
|
||||
|
||||
### Included
|
||||
|
||||
- 仅 Case 支持批量分诊。
|
||||
- 一次可组合修改 Assignee、Status、Severity、Verdict。
|
||||
- 最多 100 个显式 Case ID。
|
||||
- 支持跨分页选择。
|
||||
- 同步执行并返回逐 Case 成功或失败结果。
|
||||
- 允许部分成功。
|
||||
|
||||
### Excluded
|
||||
|
||||
- Alert 批量分诊。
|
||||
- Priority 和 Tags 批量修改。
|
||||
- 对当前筛选结果全部执行。
|
||||
- 后台批量任务。
|
||||
- 服务端幂等键。
|
||||
- updated_at 乐观锁。
|
||||
- 批次级 AuditLog 实体。
|
||||
|
||||
## 3. Permission matrix
|
||||
|
||||
| Action | Admin | User | Viewer |
|
||||
| --- | --- | --- | --- |
|
||||
| Select cases | Yes | Yes | No |
|
||||
| Bulk update any case | Yes | Yes | No |
|
||||
| Bulk assign to any valid user | Yes | Yes | No |
|
||||
| Bulk close | Yes | Yes | No |
|
||||
|
||||
权限必须在服务端检查。User 不受 assignee 所有权限制。
|
||||
|
||||
## 4. Shared Case state machine
|
||||
|
||||
单条 PATCH 与 bulk triage 必须调用同一个 domain service,不得分别维护状态逻辑。
|
||||
|
||||
### Allowed transitions
|
||||
|
||||
| Current | Allowed next |
|
||||
| --- | --- |
|
||||
| New | In Progress, Closed |
|
||||
| In Progress | On Hold, Resolved, Closed |
|
||||
| On Hold | In Progress, Resolved, Closed |
|
||||
| Resolved | In Progress, Closed |
|
||||
| Closed | In Progress |
|
||||
|
||||
相同状态写入可以作为无变化字段跳过,不应产生状态错误。
|
||||
|
||||
### Transition side effects
|
||||
|
||||
- Case 第一次离开 `New` 时设置 `acknowledged_time=now()`。
|
||||
- `acknowledged_time` 设置后永久保留,Reopen 不重置。
|
||||
- 进入 `Closed` 时设置 `closed_time=now()`。
|
||||
- 进入 `Closed` 必须在最终状态上存在非空 Verdict,并要求本次请求提供 disposition note。
|
||||
- `Closed → In Progress` 是 Reopen:
|
||||
- 清空 `closed_time`。
|
||||
- 清空 `verdict`。
|
||||
- 保留 Summary、acknowledged_time 和历史审计。
|
||||
- 其他离开 Closed 的路径不存在。
|
||||
|
||||
状态机应作为可复用服务,例如 `apps.cases.services.transition_case()`;Serializer 和 bulk endpoint 都调用它。
|
||||
|
||||
## 5. Editable field semantics
|
||||
|
||||
### Assignee
|
||||
|
||||
- 字段未包含:保持不变。
|
||||
- 提供有效用户 ID:设置新 assignee。
|
||||
- 显式 `null`:取消分配。
|
||||
- 不允许设置不存在或不可用的用户。
|
||||
|
||||
### Status
|
||||
|
||||
- 字段未包含:保持不变。
|
||||
- 必须是 CaseStatus 枚举。
|
||||
- 必须满足共享状态机。
|
||||
- Status 不允许清空或设为 null。
|
||||
|
||||
### Severity
|
||||
|
||||
- 字段未包含:保持不变。
|
||||
- 必须是 CaseSeverity 枚举。
|
||||
- 不使用空字符串表达清空;使用 `Unknown`。
|
||||
|
||||
### Verdict
|
||||
|
||||
- 字段未包含:保持不变。
|
||||
- 必须是 CaseVerdict 枚举。
|
||||
- 非 Closed Case 可以显式设为 null/空值以清空 Verdict。
|
||||
- 请求结束后的 Case 若为 Closed,Verdict 必须非空。
|
||||
- 不允许只清空 Closed Case 的 Verdict。
|
||||
|
||||
### Multi-field evaluation
|
||||
|
||||
校验应基于本次请求全部字段应用后的最终 Case 状态,而不是按 JSON 字段顺序执行。例如同一次请求可以把 New Case 设为 Closed 并提供 Verdict。
|
||||
|
||||
## 6. Bulk close note
|
||||
|
||||
进入 Closed 时 `reason` 必填。该文本同时用于审计和 Summary 追加。
|
||||
|
||||
Summary 追加格式:
|
||||
|
||||
```markdown
|
||||
|
||||
### Bulk disposition · 2026-07-28 10:44 UTC · alice
|
||||
|
||||
Confirmed as false positive after campaign review.
|
||||
```
|
||||
|
||||
规则:
|
||||
|
||||
- 保留现有 Summary。
|
||||
- 现有 Summary 与新段落之间插入空行。
|
||||
- 标题时间使用 UTC 的稳定格式。
|
||||
- actor 使用当前用户名。
|
||||
- 对每个成功关闭的 Case 追加相同说明。
|
||||
- 普通分配、Severity、Status 或 Verdict 修改的 reason 可选;如果提供,只写审计,不追加 Summary。
|
||||
|
||||
## 7. API
|
||||
|
||||
### Endpoint
|
||||
|
||||
`POST /api/cases/bulk-triage/`
|
||||
|
||||
这是 Case 专用 collection action,不实现通用 bulk PATCH。
|
||||
|
||||
### Request
|
||||
|
||||
```json
|
||||
{
|
||||
"case_ids": [
|
||||
"6387d62e-4ed6-45b4-b83a-522cd7b5e845",
|
||||
"b109ce5c-956a-42d5-b53c-bc883d19dbfc"
|
||||
],
|
||||
"changes": {
|
||||
"assignee": "e0602519-1cd0-4750-80af-aac269a98722",
|
||||
"status": "In Progress",
|
||||
"severity": "High",
|
||||
"verdict": "Suspicious"
|
||||
},
|
||||
"reason": "Campaign triage"
|
||||
}
|
||||
```
|
||||
|
||||
Validation:
|
||||
|
||||
- `case_ids` 必须是非空数组。
|
||||
- 去重后最多 100 个。
|
||||
- ID 必须是合法 UUID;请求结构中的非法 UUID 是请求级 400,而不存在的合法 UUID 是逐项失败。
|
||||
- `changes` 至少包含一个允许字段。
|
||||
- 不接受 Priority、Tags 或其他字段。
|
||||
- `reason` 去除首尾空白后校验。
|
||||
- 如果请求的最终目标状态是 Closed,reason 必填。
|
||||
|
||||
### Success and partial success response
|
||||
|
||||
只要请求结构有效,返回 HTTP 200:
|
||||
|
||||
```json
|
||||
{
|
||||
"operation_id": "a143bc8b-df7b-40d8-b0ee-e5df4e7efdc4",
|
||||
"requested": 2,
|
||||
"succeeded_count": 1,
|
||||
"failed_count": 1,
|
||||
"succeeded": [
|
||||
{
|
||||
"id": "6387d62e-4ed6-45b4-b83a-522cd7b5e845",
|
||||
"case_id": "case_000123",
|
||||
"updated_at": "2026-07-28T02:44:00Z"
|
||||
}
|
||||
],
|
||||
"failed": [
|
||||
{
|
||||
"id": "b109ce5c-956a-42d5-b53c-bc883d19dbfc",
|
||||
"case_id": "case_000124",
|
||||
"code": "invalid_transition",
|
||||
"detail": "Case cannot transition from New to Resolved."
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
允许的安全失败码至少包括:
|
||||
|
||||
- `not_found`
|
||||
- `permission_denied`
|
||||
- `invalid_transition`
|
||||
- `invalid_final_state`
|
||||
- `invalid_assignee`
|
||||
- `update_failed`
|
||||
|
||||
不得在 `detail` 返回 traceback 或数据库错误。
|
||||
|
||||
### Request-level errors
|
||||
|
||||
以下返回 400,不处理任何 Case:
|
||||
|
||||
- 空 case_ids。
|
||||
- 超过 100 个 ID。
|
||||
- 非法 UUID。
|
||||
- changes 为空。
|
||||
- 未知字段。
|
||||
- 非法枚举值。
|
||||
- 目标 Closed 但 reason 缺失。
|
||||
|
||||
401/403 使用现有认证和权限响应。
|
||||
|
||||
## 8. Transaction and failure behavior
|
||||
|
||||
- 整批不使用一个全局原子事务。
|
||||
- 每个 Case 在自己的短事务中读取、应用状态机、保存并写 AuditLog。
|
||||
- 一个 Case 失败不得回滚已成功 Case。
|
||||
- 使用 last-write-wins,不比较客户端看到的 updated_at。
|
||||
- 在事务中读取最新 Case;只覆盖请求明确包含的字段。
|
||||
- 前端请求进行中禁用提交按钮,平台不提供服务端 idempotency key。
|
||||
|
||||
## 9. Notifications
|
||||
|
||||
只有 assignee 实际变化时产生 assignment notification。
|
||||
|
||||
- 按最终 assignee 分组。
|
||||
- 每位接收人一次批量请求最多收到一条 Inbox 消息。
|
||||
- 消息包含成功分配给该用户的 Case 数量和可打开的 Case ID 列表。
|
||||
- 失败 Case 不进入通知。
|
||||
- 取消分配不发送通知。
|
||||
- 如果 assignee 未改变,不发送通知。
|
||||
- 通知在对应 Case 事务成功后统一生成;通知失败不得把已完成业务更新伪装为失败,应按现有通知错误策略显式记录。
|
||||
|
||||
## 10. Audit
|
||||
|
||||
每个成功 Case 写一条现有 `updated` AuditLog:
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "updated",
|
||||
"changes": {
|
||||
"status": {"from": "New", "to": "In Progress"},
|
||||
"severity": {"from": "Medium", "to": "High"}
|
||||
},
|
||||
"metadata": {
|
||||
"source": "bulk_triage",
|
||||
"operation_id": "a143bc8b-df7b-40d8-b0ee-e5df4e7efdc4",
|
||||
"reason": "Campaign triage"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- 后端为每次请求生成一个 UUID operation_id。
|
||||
- 同一请求的所有成功 Case 共享 operation_id。
|
||||
- 不创建单独的批次 AuditLog。
|
||||
- 无实际字段变化的 Case 可以视为成功,但不得写空 changes 审计;响应应包含 `unchanged: true`。
|
||||
- 失败 Case 不写业务更新审计。
|
||||
|
||||
## 11. Frontend
|
||||
|
||||
### Selection
|
||||
|
||||
- 仅 Case 主列表启用批量分诊。
|
||||
- 翻页保留选择。
|
||||
- Search、普通 Filter 或 Advanced Filter 发生变化时清空选择。
|
||||
- 不支持“Select all filtered results”。
|
||||
- 最多选择 100 条;达到上限后其他 checkbox disabled,并显示上限提示。
|
||||
- Toolbar 始终显示选中数量和 Clear selection。
|
||||
|
||||
### Bulk triage modal
|
||||
|
||||
- 用户点击 Bulk Triage 打开 Modal,不立即执行。
|
||||
- 每个字段有独立“修改此字段”开关;未启用字段不进入 changes。
|
||||
- Assignee 支持选择用户和 Unassigned。
|
||||
- Status、Severity、Verdict 使用现有选项与 Tag 表现。
|
||||
- reason 默认可选;Status 选择 Closed 后立即变为必填并解释会追加到 Summary。
|
||||
- 显示选中数量、字段变更预览和关闭副作用。
|
||||
- 提交期间禁用所有操作。
|
||||
|
||||
### Result handling
|
||||
|
||||
- 成功项从 selection 移除。
|
||||
- 失败项保持选中。
|
||||
- 显示成功/失败汇总。
|
||||
- 失败列表显示 Case ID、失败码对应的用户可读说明。
|
||||
- 不自动重试。
|
||||
- 刷新当前列表数据,但不得因刷新清除失败 selection。
|
||||
|
||||
## 12. Backend implementation surfaces
|
||||
|
||||
预期涉及:
|
||||
|
||||
- `backend/apps/cases/services.py`:共享状态机和字段应用。
|
||||
- `backend/apps/cases/serializers.py`:bulk request/response serializer。
|
||||
- `backend/apps/cases/views.py`:collection action。
|
||||
- `backend/apps/inbox/notifications.py`:汇总 assignment notification。
|
||||
- `frontend/src/components/DataTable.tsx`:受控跨页 selection 和自定义 bulk action context。
|
||||
- Case resource 页面:Bulk Triage modal 和结果展示。
|
||||
|
||||
不得把 Case 业务状态机写进通用 DataTable。
|
||||
|
||||
## 13. Acceptance criteria
|
||||
|
||||
1. Admin/User 可对 1–100 个 Case 组合修改四个允许字段。
|
||||
2. Viewer 看不到操作并且 API 返回 403。
|
||||
3. 跨页选择保留,筛选变化清空。
|
||||
4. 非法状态转换只失败对应 Case,其他 Case 成功。
|
||||
5. Closed 必须有最终 Verdict 和 reason。
|
||||
6. Bulk close 正确追加带时间和 actor 的 Markdown Summary。
|
||||
7. Reopen 清空 closed_time/verdict,保留 acknowledged_time/Summary。
|
||||
8. assignment notification 按用户汇总。
|
||||
9. 每个成功 Case 有 source、operation_id 和 changes 审计。
|
||||
10. 请求级错误不产生任何 Case 更新。
|
||||
11. medium 数据集下 100 条同步请求可在正式验收设定的超时内完成。
|
||||
|
||||
## 14. Known tradeoffs
|
||||
|
||||
- last-write-wins 可能覆盖并发编辑,这是已接受行为。
|
||||
- 没有服务端 idempotency,客户端网络重试可能重复追加 close note;前端必须避免自动重试 POST。
|
||||
- 部分成功使一次 operation_id 不代表全量成功;响应和逐 Case 审计是事实来源。
|
||||
@@ -0,0 +1,424 @@
|
||||
# Case Merge
|
||||
|
||||
Status: Confirmed
|
||||
|
||||
## 1. Purpose
|
||||
|
||||
允许 Admin 将最多 20 个重复源 Case 原子地合并到一个明确目标 Case。业务对象迁移到目标,源 Case 永久保留为只读历史入口,确保 correlation routing、调查证据和审计可追溯。
|
||||
|
||||
## 2. Scope
|
||||
|
||||
### Included
|
||||
|
||||
- 多源到单目标合并。
|
||||
- 服务端预检。
|
||||
- 原子执行和行锁复检。
|
||||
- operation_id 幂等。
|
||||
- 关联 Alert、Enrichment、Comment、Playbook 和 AI Job 迁移。
|
||||
- merged source 只读 API 和 UI。
|
||||
- 后续 correlation_uid 路由到最终目标。
|
||||
- 合并关系扁平化。
|
||||
|
||||
### Excluded
|
||||
|
||||
- 自动重复检测。
|
||||
- 自动选择目标。
|
||||
- Unmerge。
|
||||
- 非 Admin 合并。
|
||||
- 自动合并字段、Tags、Summary、AI 报告或 Knowledge。
|
||||
- 活动任务自动取消。
|
||||
|
||||
## 3. Permission matrix
|
||||
|
||||
| Action | Admin | User | Viewer |
|
||||
| --- | --- | --- | --- |
|
||||
| View merged source | Yes | Yes | Yes |
|
||||
| View Merged Sources tab | Yes | Yes | Yes |
|
||||
| Run merge preflight | Yes | No | No |
|
||||
| Execute merge | Yes | No | No |
|
||||
| Edit merged source | No | No | No |
|
||||
|
||||
User 可以普通编辑 Verdict 为 Duplicate,但不能迁移关系。
|
||||
|
||||
## 4. Data model
|
||||
|
||||
### Case additions
|
||||
|
||||
```python
|
||||
merged_into = models.ForeignKey(
|
||||
"self",
|
||||
null=True,
|
||||
blank=True,
|
||||
on_delete=models.PROTECT,
|
||||
related_name="merged_sources",
|
||||
)
|
||||
```
|
||||
|
||||
约束:
|
||||
|
||||
- `merged_into_id != id`。
|
||||
- merged source 的 merged_into 必须直接指向最终目标。
|
||||
- 最终目标的 merged_into 必须为空。
|
||||
- 应为 `merged_into` 建索引。
|
||||
- 使用 PROTECT 防止目标删除;业务 API 本身也不应允许删除有历史关系的 Case。
|
||||
|
||||
### CaseMergeOperation
|
||||
|
||||
目标模型字段:
|
||||
|
||||
| Field | Notes |
|
||||
| --- | --- |
|
||||
| id | UUID primary key |
|
||||
| operation_id | UUID, unique, client supplied |
|
||||
| target | FK Case, PROTECT |
|
||||
| actor | nullable FK User, SET_NULL |
|
||||
| reason | required text |
|
||||
| source_snapshot | JSON list of explicit and flattened source metadata |
|
||||
| target_changes | JSON of explicit target edits |
|
||||
| moved_counts | JSON counters |
|
||||
| result | JSON stable idempotent response |
|
||||
| created_at | immutable timestamp |
|
||||
|
||||
Operation 创建后不可修改或删除。`source_snapshot` 至少保存源 UUID、readable ID、title、原 status、原 verdict、原 assignee、原 merged_into 和本次是否显式选择。
|
||||
|
||||
不单独创建可变 join table;Case.merged_into 是当前关系来源,Operation 是历史事实。
|
||||
|
||||
## 5. Eligibility
|
||||
|
||||
### Target
|
||||
|
||||
- 必须存在。
|
||||
- 必须是未合并最终 Case,即 `merged_into IS NULL`。
|
||||
- 不能同时出现在 source_ids。
|
||||
- Status 不能是 Closed;用户必须先显式 Reopen 到 In Progress。
|
||||
- 可以已经拥有 merged sources。
|
||||
|
||||
### Explicit sources
|
||||
|
||||
- 1–20 个唯一 ID。
|
||||
- 必须存在。
|
||||
- 可以是任何 Status,包括 Closed。
|
||||
- 必须 `merged_into IS NULL`,即已经作为 source 的 Case 不能再次显式合并。
|
||||
- 不能等于 target。
|
||||
- 可以本身拥有历史 merged sources;执行时这些 inbound source 一并扁平化到新目标。
|
||||
|
||||
### Active job blocker
|
||||
|
||||
任一显式 source 或其需要迁移的任务存在以下状态时阻止整次合并:
|
||||
|
||||
- Playbook Pending 或 Running。
|
||||
- CaseAnalysisJob Pending 或 Running。
|
||||
|
||||
系统不自动取消任务。用户必须等待结束或通过对应合法操作取消后重试。
|
||||
|
||||
## 6. Preflight API
|
||||
|
||||
### Endpoint
|
||||
|
||||
`POST /api/cases/merge/preflight/`
|
||||
|
||||
### Request
|
||||
|
||||
```json
|
||||
{
|
||||
"target_id": "ef235eb5-a0d8-48d2-a568-741909ce2e06",
|
||||
"source_ids": [
|
||||
"87cc7cf4-66df-4f4e-bb96-66c1155c0219",
|
||||
"aa3d9963-8338-4401-b92d-3eeaa9fcbfde"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Response
|
||||
|
||||
预检返回:
|
||||
|
||||
- target 摘要及可编辑字段。
|
||||
- 每个 source 的字段对比。
|
||||
- source Tags 差异。
|
||||
- 将迁移的 Alert、Case-level Enrichment、Comment、Playbook、AI Job 数量。
|
||||
- 保留在 source 的 Knowledge、Summary 和 AI report 标记。
|
||||
- active job blocker。
|
||||
- target Closed、source already merged、循环或其他 blocker。
|
||||
- 需要扁平化重新指向的历史 source 数量。
|
||||
- 不可撤销提示。
|
||||
|
||||
示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"eligible": true,
|
||||
"target": {
|
||||
"id": "ef235eb5-a0d8-48d2-a568-741909ce2e06",
|
||||
"case_id": "case_000100",
|
||||
"status": "In Progress"
|
||||
},
|
||||
"sources": [],
|
||||
"move_counts": {
|
||||
"alerts": 17,
|
||||
"case_enrichments": 4,
|
||||
"comments": 8,
|
||||
"playbooks": 3,
|
||||
"analysis_jobs": 2
|
||||
},
|
||||
"retained_counts": {
|
||||
"knowledge": 1,
|
||||
"source_reports": 2
|
||||
},
|
||||
"flattened_source_count": 3,
|
||||
"blockers": [],
|
||||
"warnings": ["This merge cannot be undone."]
|
||||
}
|
||||
```
|
||||
|
||||
Preflight 不获取长期锁,不保证执行时仍可合并。执行必须复检。
|
||||
|
||||
## 7. Merge API
|
||||
|
||||
### Endpoint
|
||||
|
||||
`POST /api/cases/merge/`
|
||||
|
||||
### Request
|
||||
|
||||
```json
|
||||
{
|
||||
"operation_id": "a00384a1-bca5-471f-95e2-02fc8f419a3a",
|
||||
"target_id": "ef235eb5-a0d8-48d2-a568-741909ce2e06",
|
||||
"source_ids": [
|
||||
"87cc7cf4-66df-4f4e-bb96-66c1155c0219"
|
||||
],
|
||||
"reason": "Duplicate cases generated by the same campaign",
|
||||
"target_changes": {
|
||||
"severity": "High",
|
||||
"tags": ["phishing", "campaign-2026-07"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
规则:
|
||||
|
||||
- operation_id 必填 UUID。
|
||||
- reason trim 后必填。
|
||||
- target_changes 可选,只允许 Case 正常可编辑业务字段。
|
||||
- target_changes 必须通过共享 Case serializer/state machine;不能在 merge 中把 target 设为 Closed。
|
||||
- target 默认字段全部保留,source 永不自动覆盖 target。
|
||||
- Tags 只有在 target_changes 明确给出时改变,不自动并集。
|
||||
|
||||
### Idempotency
|
||||
|
||||
- operation_id unique。
|
||||
- 已存在相同 operation_id 时,不再次执行;返回 Operation 保存的原始 result。
|
||||
- 如果相同 operation_id 携带不同 target/source/reason,也返回原 result,不重新解释请求;可以附 `replayed: true`。
|
||||
- Operation 与业务迁移在同一数据库事务提交。
|
||||
|
||||
### Success response
|
||||
|
||||
返回目标摘要、显式 source、扁平化 source、moved_counts、保留项和 operation_id。
|
||||
|
||||
### Failure
|
||||
|
||||
- 结构错误返回 400。
|
||||
- 权限错误返回 403。
|
||||
- target/source 不存在返回 404 或统一 400;实现必须保持 schema 一致。
|
||||
- 执行时状态变化、active job 或已合并冲突返回 409。
|
||||
- 任一失败整次事务回滚。
|
||||
- 不返回原始数据库异常。
|
||||
|
||||
## 8. Execution algorithm
|
||||
|
||||
在一个 `transaction.atomic()` 中:
|
||||
|
||||
1. 按稳定 UUID 顺序 `select_for_update()` 锁定 target 和显式 sources,避免死锁。
|
||||
2. 查找显式 source 已有的 inbound merged sources,并锁定这些 Case。
|
||||
3. 重新校验 target、source、merged relation 和活动任务。
|
||||
4. 应用显式 target_changes。
|
||||
5. 保存 Operation 初始事实。
|
||||
6. 迁移业务关系。
|
||||
7. 把所有显式 source 和其 inbound source 的 `merged_into` 设为 target,形成一层关系。
|
||||
8. 显式 source 设置 Closed、Duplicate、closed_time;保留 Summary、AI 字段和 acknowledged_time。
|
||||
9. 写 target 和每个显式 source 的 merge AuditLog。
|
||||
10. 保存 moved_counts 和稳定 result。
|
||||
11. transaction commit 后发送汇总通知。
|
||||
|
||||
任何步骤失败全部回滚。
|
||||
|
||||
## 9. Relationship migration
|
||||
|
||||
| Relation | Behavior |
|
||||
| --- | --- |
|
||||
| Alert.case | 全部改为 target |
|
||||
| Alert.artifacts | 不变 |
|
||||
| Alert-level Enrichment | 不变,随 Alert 可见 |
|
||||
| Artifact-level Enrichment | 不变 |
|
||||
| Case-level Enrichment | case_id 改为 target;不去重 |
|
||||
| Completed/Failed Playbook | case_id 改为 target |
|
||||
| Completed/Failed CaseAnalysisJob | case_id 改为 target |
|
||||
| Pending/Running jobs | 阻止合并 |
|
||||
| Comment on source Case | content object 改为 target |
|
||||
| Comment reply tree | 保持 parent/mentions/attachments |
|
||||
| Knowledge.case | 保持 source |
|
||||
| Existing AuditLog | 保持原 object_id |
|
||||
| Source Summary/report | 保持 source |
|
||||
|
||||
### Comment origin
|
||||
|
||||
评论迁移后必须能显示来源。推荐在 Comment 增加 nullable `origin_case`,或创建等价不可变 metadata。不得修改评论正文来注入来源文本。
|
||||
|
||||
## 10. Source final state
|
||||
|
||||
显式 source:
|
||||
|
||||
- `status = Closed`
|
||||
- `verdict = Duplicate`
|
||||
- `closed_time = merge time`
|
||||
- `merged_into = target`
|
||||
- 保留 title、description、summary、tags、AI 字段、acknowledged_time 和 correlation_uid。
|
||||
- 记录合并前 status/verdict 到 Operation snapshot 和 AuditLog。
|
||||
|
||||
历史 inbound source 已经是 merged source,只更新 merged_into 到最终 target,不重写其原 merge 时间或历史。
|
||||
|
||||
## 11. Correlation routing
|
||||
|
||||
Alert ingestion 按 correlation_uid 找到 Case 后:
|
||||
|
||||
1. 如果 Case.merged_into 为空,正常使用。
|
||||
2. 如果不为空,使用 merged_into 最终 target。
|
||||
3. 数据约束要求最多一跳;服务仍可防御性解析并检测循环。
|
||||
4. source 保留 correlation_uid,作为目标的历史 alias。
|
||||
|
||||
这项逻辑必须放在统一 Case correlation service,不能只修某个 Module。
|
||||
|
||||
## 12. Merged source API behavior
|
||||
|
||||
### Read
|
||||
|
||||
- GET list 默认排除 `merged_into IS NOT NULL`。
|
||||
- 提供 `include_merged=true` Admin/User/Viewer 筛选。
|
||||
- 精确 Case ID 搜索必须能找到 merged source。
|
||||
- GET detail 返回 200,并包含:
|
||||
- `is_merged: true`
|
||||
- target UUID/readable ID/title
|
||||
- merge actor/time/reason
|
||||
- 原只读字段和历史 Summary/report。
|
||||
|
||||
### Write
|
||||
|
||||
对 merged source 的所有业务写操作返回 409:
|
||||
|
||||
- PATCH/PUT/DELETE Case。
|
||||
- 添加/删除评论。
|
||||
- 启动 Playbook 或 AI 分析。
|
||||
- 新建 Case-level Enrichment/Knowledge。
|
||||
- 任何其他以 Case 为目标的 mutation。
|
||||
|
||||
409 响应包含安全目标摘要:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "case_merged",
|
||||
"detail": "This case was merged and is read-only.",
|
||||
"merged_into": {
|
||||
"id": "...",
|
||||
"case_id": "case_000100"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 13. Visibility and metrics
|
||||
|
||||
默认排除 merged source:
|
||||
|
||||
- Case 主列表。
|
||||
- Dashboard Case 数量、状态分布和 SLA。
|
||||
- 任何“当前案件”统计。
|
||||
- Assignee 活跃工作量。
|
||||
|
||||
保留:
|
||||
|
||||
- Audit 搜索。
|
||||
- 精确 ID 搜索。
|
||||
- `include_merged` 筛选。
|
||||
- target 的 Merged Sources tab。
|
||||
|
||||
## 14. Audit
|
||||
|
||||
现有历史 AuditLog 永不迁移或改写。
|
||||
|
||||
本次合并:
|
||||
|
||||
- target 一条 `merged` AuditLog。
|
||||
- 每个显式 source 一条 `merged` AuditLog。
|
||||
- 共享 operation_id。
|
||||
- metadata 包含 target、显式 sources、actor、reason、moved_counts、flattened count。
|
||||
- source changes 包含 status/verdict/merged_into 的 before/after。
|
||||
- 不为每个 Alert 外键变化生成独立业务审计,避免日志爆炸;迁移数量由 merge event 覆盖。
|
||||
|
||||
## 15. Notifications
|
||||
|
||||
通知目标:
|
||||
|
||||
- target 当前 assignee。
|
||||
- 每个显式 source 合并前 assignee。
|
||||
- 去重用户。
|
||||
- null assignee 忽略。
|
||||
|
||||
每个用户只收到一条 Inbox 消息,包含 target、source 数量、source IDs 和 reason。通知在 commit 后发送,通知失败不回滚合并。
|
||||
|
||||
## 16. Frontend
|
||||
|
||||
### Entry
|
||||
|
||||
- Case 列表 Admin 批量操作增加 Merge。
|
||||
- Detail 页面 Admin 可发起“Merge into another case”。
|
||||
- 非 Admin 不显示入口。
|
||||
|
||||
### Modal
|
||||
|
||||
1. 选择或确认最多 20 个 source。
|
||||
2. 通过 Case ID/title 搜索明确选择任意有效 target。
|
||||
3. 调用 preflight。
|
||||
4. 展示 target/source 字段对比、Tags 差异、迁移数量、保留数据和 blocker。
|
||||
5. 允许手动编辑 target 字段。
|
||||
6. reason 必填。
|
||||
7. 显示不可撤销警告。
|
||||
8. 生成 operation_id 后提交;网络重试复用同一 operation_id。
|
||||
|
||||
### Merged source
|
||||
|
||||
- Detail 顶部显示明显 Warning banner。
|
||||
- 提供 Open target case。
|
||||
- 所有编辑控件、评论、Playbook 等 mutation 隐藏或 disabled。
|
||||
|
||||
### Target
|
||||
|
||||
新增 `Merged Sources` tab:
|
||||
|
||||
- source Case ID、title。
|
||||
- 合并前 status/verdict。
|
||||
- merge time、actor、reason。
|
||||
- 打开只读 source。
|
||||
- 使用分页。
|
||||
|
||||
## 17. Acceptance criteria
|
||||
|
||||
1. Admin 可把 1–20 个 source 原子合并到 target。
|
||||
2. User/Viewer 无法 preflight 或 merge。
|
||||
3. Closed target 被拒绝,Closed source 可合并。
|
||||
4. active Playbook/AI Job 阻止整次操作。
|
||||
5. 所有规定关系迁移或保留正确。
|
||||
6. 任一迁移异常整次回滚。
|
||||
7. 重复 operation_id 不产生重复迁移、审计或通知。
|
||||
8. 现有审计 object identity 不变。
|
||||
9. merged source GET 可读、所有 mutation 409。
|
||||
10. 后续 correlation_uid Alert 路由到最终 target。
|
||||
11. 二次合并扁平化,不形成多跳。
|
||||
12. 默认列表/Dashboard/SLA 排除 merged source。
|
||||
13. Merged Sources tab 可追溯原 Summary 和 AI report。
|
||||
|
||||
## 18. Known tradeoffs
|
||||
|
||||
- Merge 不可撤销;误操作只能人工修复或恢复备份。
|
||||
- Knowledge 不迁移,target 与 source 可能分别保留知识。
|
||||
- target 字段默认胜出,不自动选择“更严重”的值。
|
||||
- 合并后的评论失去 source 详情页独立讨论入口,但通过 origin metadata 可追溯。
|
||||
@@ -0,0 +1,388 @@
|
||||
# Playbook Execution
|
||||
|
||||
Status: Confirmed
|
||||
|
||||
## 1. Purpose
|
||||
|
||||
保留低心智成本的 Python `run()` 编程模型,同时增加可持久化的结构化执行阶段、明确时间、取消、完整重试、崩溃恢复和只读运行历史。
|
||||
|
||||
该设计不是工作流引擎。Stage 是观测事件,不是独立调度、恢复或重试的 Step。
|
||||
|
||||
## 2. Authoring model
|
||||
|
||||
Playbook 继续由 Python 代码定义:
|
||||
|
||||
```python
|
||||
class Playbook(BasePlaybook):
|
||||
NAME = "Contain Endpoint"
|
||||
DESC = "Contain the endpoint associated with the case."
|
||||
TAGS = ["EDR", "Response"]
|
||||
RISK_LEVEL = "High"
|
||||
|
||||
def run(self):
|
||||
with self.stage("collect", "Collect endpoint context") as stage:
|
||||
endpoints = collect_endpoints(self.case)
|
||||
stage.summary = f"Collected {len(endpoints)} endpoint(s)."
|
||||
|
||||
for endpoint in endpoints:
|
||||
with self.stage("contain", f"Contain {endpoint.hostname}") as stage:
|
||||
contain(endpoint)
|
||||
stage.summary = "Containment request accepted."
|
||||
|
||||
return f"Contained {len(endpoints)} endpoint(s)."
|
||||
```
|
||||
|
||||
规则:
|
||||
|
||||
- `run()` 仍是唯一执行入口。
|
||||
- Stage 完全可选;现有 v0.5.2 Playbook 无需修改即可运行。
|
||||
- Stage 不保存 Python 返回值或跨阶段输入。
|
||||
- Stage 不独立调度、不暂停、不恢复、不单步重试。
|
||||
- `run()` 未捕获异常导致整个 Run Failed。
|
||||
- 开发者可以在 Stage 内捕获可容忍错误,并显式写安全 summary。
|
||||
- Playbook 可直接使用 `httpx` 或厂商 SDK;平台不提供通用 Connector。
|
||||
|
||||
## 3. Explicit exclusions
|
||||
|
||||
- 可视化/表单式编排器。
|
||||
- DAG、分支、并行 Step。
|
||||
- 中途人工审批;点击 Run 即授权整个 Playbook。
|
||||
- Running hard cancel 或 cooperative cancel。
|
||||
- 单步 retry/resume。
|
||||
- 源码、hash 或定义版本锁定。
|
||||
- 结构化 input schema。
|
||||
- HTTP/Webhook Connection profile。
|
||||
- 自动轮询或 WebSocket 进度。
|
||||
|
||||
## 4. Definition metadata
|
||||
|
||||
定义扫描继续读取:
|
||||
|
||||
- `NAME`
|
||||
- `DESC`
|
||||
- `TAGS`
|
||||
- 新增 `RISK_LEVEL`
|
||||
|
||||
风险枚举:
|
||||
|
||||
- Low
|
||||
- Medium
|
||||
- High
|
||||
- Critical
|
||||
|
||||
默认 Low。风险只用于 UI 展示,不改变权限、确认或执行流程。
|
||||
|
||||
Run 不保存 DESC/TAGS/RISK_LEVEL 快照。历史页面按 name 解析当前定义:
|
||||
|
||||
- 定义仍存在:展示当前 metadata。
|
||||
- 定义已删除或改名:只展示 Run 保存的 name,并标记 definition unavailable。
|
||||
|
||||
Pending/Retry 执行时始终加载当前最新 Python 代码。
|
||||
|
||||
## 5. Run model
|
||||
|
||||
现有 `Playbook` 记录继续作为 Run,可考虑重命名 Python 类但不要求修改 db_table。
|
||||
|
||||
新增/调整字段:
|
||||
|
||||
| Field | Type | Semantics |
|
||||
| --- | --- | --- |
|
||||
| job_status | enum | Pending/Running/Success/Failed/Cancelled |
|
||||
| job_id | string/UUID | 当前执行标识 |
|
||||
| retry_of | nullable self FK | Failed Run 的重试来源 |
|
||||
| started_at | nullable datetime | claim 成功时间 |
|
||||
| finished_at | nullable datetime | terminal 时间 |
|
||||
| cancelled_by | nullable FK User | Pending cancel actor |
|
||||
| remark | text | 终态安全摘要 |
|
||||
|
||||
保留:
|
||||
|
||||
- case
|
||||
- name
|
||||
- user
|
||||
- user_input
|
||||
- created_at/updated_at
|
||||
|
||||
### Status transitions
|
||||
|
||||
| Current | Allowed next |
|
||||
| --- | --- |
|
||||
| Pending | Running, Cancelled |
|
||||
| Running | Success, Failed |
|
||||
| Success | none |
|
||||
| Failed | none; Retry creates new Run |
|
||||
| Cancelled | none |
|
||||
|
||||
不允许直接修改 job_status。所有状态变化通过 domain service。
|
||||
|
||||
### Timing
|
||||
|
||||
- Pending 创建时 started_at/finished_at 为空。
|
||||
- Pending→Running 设置 started_at。
|
||||
- Running→Success/Failed 设置 finished_at。
|
||||
- Pending→Cancelled 设置 finished_at,不设置 started_at。
|
||||
- duration_seconds 由 started_at 和 finished_at 计算;Running 使用 now-started_at。
|
||||
|
||||
## 6. Stage model
|
||||
|
||||
建议模型 `PlaybookStage`:
|
||||
|
||||
| Field | Type | Notes |
|
||||
| --- | --- | --- |
|
||||
| id | UUID | primary key |
|
||||
| playbook_run | FK | CASCADE at DB level, though Run API cannot delete |
|
||||
| sequence | positive bigint | per Run append order |
|
||||
| key | string | developer supplied, may repeat |
|
||||
| label | string | human-readable |
|
||||
| status | enum | Running/Success/Failed |
|
||||
| summary | text | explicit safe summary |
|
||||
| error_type | string | sanitized exception class |
|
||||
| error_message | text | sanitized length-limited message |
|
||||
| started_at | datetime | context enter |
|
||||
| finished_at | nullable datetime | context exit |
|
||||
| duration_ms | nullable bigint | derived/persisted |
|
||||
|
||||
Constraints/indexes:
|
||||
|
||||
- unique `(playbook_run, sequence)`.
|
||||
- index `(playbook_run, sequence)`.
|
||||
- index `(playbook_run, status)`.
|
||||
- key/label 长度必须有限,但 Stage 数量不限。
|
||||
|
||||
### Stage context behavior
|
||||
|
||||
进入 `with self.stage(key, label)`:
|
||||
|
||||
1. 原子分配下一个 sequence。
|
||||
2. 创建 Running Stage。
|
||||
3. 返回可设置 `summary` 的 context object。
|
||||
|
||||
正常退出:
|
||||
|
||||
1. 保存显式 summary。
|
||||
2. 设置 Success、finished_at、duration。
|
||||
|
||||
异常退出:
|
||||
|
||||
1. 设置 Failed。
|
||||
2. 保存异常类型。
|
||||
3. 保存经过敏感字段过滤和长度限制的安全错误。
|
||||
4. 完整 traceback 只进入 Worker log。
|
||||
5. 原异常继续抛出,使 Run Failed。
|
||||
|
||||
Stage key/label 可以重复,支持循环动态生成。Stage 是扁平 sequence,不支持 parent。
|
||||
|
||||
### Output safety
|
||||
|
||||
- 不自动 `str()` 或 JSON serialize 任意函数输出。
|
||||
- 不自动保存 HTTP response、LLM output、SIEM records 或变量值。
|
||||
- summary 由自定义代码显式提供。
|
||||
- error_message 使用统一 sanitizer,至少屏蔽 password/token/api_key/secret/authorization 等值。
|
||||
- API 不返回 traceback。
|
||||
|
||||
## 7. Queue and Worker behavior
|
||||
|
||||
### Supported topology
|
||||
|
||||
- 正式支持一个 Playbook Worker。
|
||||
- 全局 FIFO,按 created_at/id claim。
|
||||
- 不按 Case Severity 或用户优先级排序。
|
||||
|
||||
### Per-Case concurrency
|
||||
|
||||
同一 Case 最多一个 Running Run:
|
||||
|
||||
- claim 时跳过已经存在 Running Run 的 Case。
|
||||
- 同 Case 的其他 Pending 保持队列。
|
||||
- 不同 Case 在未来多 Worker 实现中可并行,但 v0.6.0 不支持多 Worker。
|
||||
|
||||
### Duplicate launch
|
||||
|
||||
Run endpoint 不提供 idempotency。重复请求可以创建多条 Pending Run,这是已接受行为。
|
||||
|
||||
### Worker loss
|
||||
|
||||
Playbook Worker 使用 Worker Health 心跳。检测到前一实例丢失后:
|
||||
|
||||
- 遗留 Running Run 标记 Failed。
|
||||
- 遗留 Running Stage 标记 Failed。
|
||||
- remark 使用固定安全文本,说明 Worker stopped before completion。
|
||||
- 不自动重置 Pending 或重跑。
|
||||
- 用户检查后手动 Retry。
|
||||
|
||||
实现可在 Worker 成功获取 singleton lease 后执行 orphan recovery。不得仅按运行时长把合法长任务判失败。
|
||||
|
||||
## 8. Cancellation
|
||||
|
||||
只有 Pending 可取消。
|
||||
|
||||
`POST /api/playbooks/{id}/cancel/`
|
||||
|
||||
- Admin/User 可取消任意 Pending Run。
|
||||
- Viewer 403。
|
||||
- 非 Pending 返回 409。
|
||||
- 设置 Cancelled、finished_at、cancelled_by 和安全 remark。
|
||||
- 写 AuditLog。
|
||||
- 不发送 completion notification。
|
||||
|
||||
Running 无取消入口。Playbook 中每个外部调用必须自行设置超时。
|
||||
|
||||
## 9. Retry
|
||||
|
||||
`POST /api/playbooks/{id}/retry/`
|
||||
|
||||
- 仅 Failed Run。
|
||||
- Admin/User 可重试任意 Failed Run。
|
||||
- Viewer 403。
|
||||
- 创建新的 Pending Run。
|
||||
- `retry_of` 指向原 Run。
|
||||
- 复制 case、name、user_input。
|
||||
- 新 Run 的 user 是执行 Retry 的当前用户。
|
||||
- 使用当前 Case 数据和最新 Playbook 代码。
|
||||
- 原 Run/Stage 不修改。
|
||||
- 写 retry AuditLog,关联新旧 Run。
|
||||
- Retry 不受幂等保护;重复点击可能创建多个 Run。
|
||||
|
||||
## 10. Launch
|
||||
|
||||
`POST /api/playbooks/run/`
|
||||
|
||||
- Admin/User 可运行,Viewer 403。
|
||||
- 任何未合并 Case 均可运行,包括 Closed。
|
||||
- merged source 返回 409 和 target 摘要。
|
||||
- name 必须能在当前定义扫描中找到。
|
||||
- user_input 是可选自由文本。
|
||||
- 点击 Run 直接创建 Pending,不增加确认。
|
||||
- risk level 只展示。
|
||||
|
||||
## 11. API shape
|
||||
|
||||
Playbook Run 资源改为 read-only:
|
||||
|
||||
- GET list。
|
||||
- GET retrieve。
|
||||
- GET definitions。
|
||||
- POST run。
|
||||
- POST cancel。
|
||||
- POST retry。
|
||||
- GET stages(detail action 或独立 nested endpoint)。
|
||||
|
||||
禁止:
|
||||
|
||||
- 普通 POST create。
|
||||
- PUT/PATCH。
|
||||
- DELETE。
|
||||
|
||||
Stage API:
|
||||
|
||||
- 只读。
|
||||
- 必须按 sequence 分页。
|
||||
- 支持 status 筛选可选,但不得一次返回无限 Stage。
|
||||
|
||||
### Run response additions
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "...",
|
||||
"playbook_id": "playbook_000123",
|
||||
"job_status": "Running",
|
||||
"retry_of": null,
|
||||
"started_at": "2026-07-30T08:00:00Z",
|
||||
"finished_at": null,
|
||||
"duration_seconds": 42,
|
||||
"current_stage": {
|
||||
"sequence": 8,
|
||||
"key": "contain",
|
||||
"label": "Contain endpoint-7",
|
||||
"status": "Running"
|
||||
},
|
||||
"stage_count": 8,
|
||||
"definition": {
|
||||
"available": true,
|
||||
"risk_level": "High",
|
||||
"tags": ["EDR", "Response"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 12. Remark semantics
|
||||
|
||||
| Terminal state | Remark |
|
||||
| --- | --- |
|
||||
| Success | `str(run() return value)`,经长度限制和安全处理 |
|
||||
| Failed | 固定安全摘要,可引用失败 Stage label/sequence |
|
||||
| Cancelled | 固定文本并记录取消 actor |
|
||||
|
||||
过程日志不得拼接到 remark。原始异常只进服务器日志。
|
||||
|
||||
## 13. Notifications
|
||||
|
||||
遵循发起用户现有 `notify_on_playbook_completion` 偏好:
|
||||
|
||||
- Success 通知。
|
||||
- Failed 通知。
|
||||
- Cancelled 不通知。
|
||||
- Stage 状态变化不通知。
|
||||
- Retry 新 Run 按新发起用户偏好处理。
|
||||
|
||||
## 14. Audit
|
||||
|
||||
只记录用户动作:
|
||||
|
||||
- launch
|
||||
- cancel
|
||||
- retry
|
||||
|
||||
Worker 自动状态变化不写全局 AuditLog,因为 Run/Stage 已是状态事实。
|
||||
|
||||
Audit metadata 不包含 user_input 全文、Stage summary 或任何 Secret。
|
||||
|
||||
## 15. Frontend
|
||||
|
||||
### Definition selection
|
||||
|
||||
- 显示 name、description、tags、risk level。
|
||||
- 点击 Run 直接排队。
|
||||
- 保留自由文本 user_input。
|
||||
|
||||
### Run list/detail
|
||||
|
||||
- 状态、Case、发起人、时间、duration、retry relation。
|
||||
- Pending 显示 Cancel。
|
||||
- Failed 显示 Retry。
|
||||
- Run/Stage 无 Delete/Edit。
|
||||
- Stage 使用分页的扁平时间线或表格。
|
||||
- 页面不自动轮询、不使用 WebSocket;提供 Refresh。
|
||||
- definition 删除后显示 unavailable,而不是报页面错误。
|
||||
|
||||
## 16. Migration
|
||||
|
||||
- 现有四状态数据直接保留。
|
||||
- 新 Cancelled 只用于 v0.6.0 后记录。
|
||||
- 已有 Success/Failed Run 的 started_at 可为空,不伪造历史时间。
|
||||
- 旧 Running Run 在升级后由首次 Worker recovery 处理。
|
||||
- retry_of、timing、cancel actor 均 nullable。
|
||||
|
||||
## 17. Acceptance criteria
|
||||
|
||||
1. v0.5.2 旧 Playbook 不修改即可运行。
|
||||
2. 可选 Stage 正确保存动态重复 key 和 sequence。
|
||||
3. Stage 异常导致 Stage/Run Failed,API 不泄露 traceback。
|
||||
4. Pending 可取消,Running 不可取消。
|
||||
5. Failed Retry 创建新 Run并保留原历史。
|
||||
6. Run/Stage 所有普通 mutation/delete 被拒绝。
|
||||
7. 同一 Case 不同时 Running 两个 Run。
|
||||
8. FIFO claim 可预测。
|
||||
9. Worker 崩溃后遗留 Running 标记 Failed且不自动重跑。
|
||||
10. Closed Case 可运行,merged source 409。
|
||||
11. Success/Failed 通知符合用户偏好。
|
||||
12. Stage 数量大时 API 正确分页。
|
||||
|
||||
## 18. Known tradeoffs
|
||||
|
||||
- 最新代码执行使 Pending Run 语义可能在排队期间变化。
|
||||
- 不保存 metadata 快照,历史 risk/tags 会随定义变化。
|
||||
- 重复 launch/retry 可产生重复外部副作用。
|
||||
- Running 不可取消。
|
||||
- 动态无限 Stage 可能产生大量数据,开发者需自律;平台只通过分页保护读取。
|
||||
- 直接 httpx 调用的重试、幂等和 Secret 安全由自定义代码负责。
|
||||
@@ -0,0 +1,316 @@
|
||||
# Custom Variables
|
||||
|
||||
Status: Confirmed
|
||||
|
||||
## 1. Purpose
|
||||
|
||||
提供一个由 Admin 通过 UI 管理、由自定义 Playbook 和 Agentic Module 在后端运行时读取的字符串变量仓库。它类似受控的数据库版环境变量,但不会修改真实 `os.environ`,也不影响 Django 全局配置。
|
||||
|
||||
典型用途:
|
||||
|
||||
- 外部系统 base URL。
|
||||
- API token、username/password。
|
||||
- tenant/project ID。
|
||||
- 自定义 JSON 字符串。
|
||||
- Module 或 Playbook 的运行参数。
|
||||
|
||||
## 2. Scope
|
||||
|
||||
### Consumers
|
||||
|
||||
- `BasePlaybook.get_variable(key)`
|
||||
- `BaseModule.get_variable(key)`
|
||||
|
||||
### Not consumers
|
||||
|
||||
- Agent API。
|
||||
- CLI 或插件。
|
||||
- 前端运行时。
|
||||
- Django template。
|
||||
- 其他任意后端模块。
|
||||
- 系统 Runtime Settings。
|
||||
|
||||
### Excluded
|
||||
|
||||
- 写入真实进程环境变量。
|
||||
- 全局配置覆盖。
|
||||
- per-Playbook/per-Module scope。
|
||||
- 变量继承或 override。
|
||||
- value 类型系统。
|
||||
- `.env` 导入导出。
|
||||
- REQUIRED_VARIABLES 声明。
|
||||
- Secret 加密、版本历史或自动泄露防护。
|
||||
|
||||
## 3. Data model
|
||||
|
||||
建议模型 `CustomVariable`:
|
||||
|
||||
| Field | Type | Rules |
|
||||
| --- | --- | --- |
|
||||
| id | UUID | primary key |
|
||||
| key | CharField(128) | unique, immutable |
|
||||
| value | TextField | non-empty, max 65,536 UTF-8 bytes |
|
||||
| is_secret | Boolean | controls API masking only |
|
||||
| description | TextField | optional |
|
||||
| enabled | Boolean | default true |
|
||||
| created_at | DateTime | auto |
|
||||
| updated_at | DateTime | auto |
|
||||
|
||||
### Key validation
|
||||
|
||||
Regex:
|
||||
|
||||
```text
|
||||
[A-Z][A-Z0-9_]{0,127}
|
||||
```
|
||||
|
||||
- 全局唯一。
|
||||
- 大小写敏感,但合法输入只有大写。
|
||||
- 创建后不可改名。
|
||||
- 修改 key 的 PATCH/PUT 返回 validation error。
|
||||
|
||||
### Value validation
|
||||
|
||||
- 必须至少一个字符。
|
||||
- 以 UTF-8 bytes 计算,最多 65,536 bytes。
|
||||
- 可包含换行。
|
||||
- 不支持 binary。
|
||||
- 不做 trim;空格可能是有意值,但零长度禁止。
|
||||
- 更新 Secret 时省略 value 表示保留旧值。
|
||||
- 显式 `""` 始终拒绝。
|
||||
|
||||
## 4. Storage security
|
||||
|
||||
已确认:
|
||||
|
||||
- Secret 和非 Secret 均以 PostgreSQL 明文保存。
|
||||
- 数据库管理员、数据库泄露和未加密备份可以读取 Secret。
|
||||
- `is_secret` 只控制 API/UI 显示与审计,不提供 cryptographic protection。
|
||||
- 用户文档必须明确该限制。
|
||||
|
||||
不得在 AuditLog、普通 API response、异常文本或日志中复制 value。
|
||||
|
||||
## 5. Runtime API
|
||||
|
||||
Base class helper:
|
||||
|
||||
```python
|
||||
def get_variable(self, key):
|
||||
...
|
||||
```
|
||||
|
||||
返回:
|
||||
|
||||
- Enabled 且存在:原始 `str`。
|
||||
- 不存在:`None`。
|
||||
- Disabled:`None`。
|
||||
- 已删除:`None`。
|
||||
|
||||
行为:
|
||||
|
||||
- 每次调用都查询 PostgreSQL。
|
||||
- 不做 per-run、per-message 或 Worker 全局缓存。
|
||||
- Admin 在 Playbook/Module 运行中修改、Disabled 或删除变量,下一次读取立即看到变化。
|
||||
- 不抛 missing variable 异常。
|
||||
- 不支持 default 参数作为平台约定;自定义代码自行使用 `or` 或显式 None 处理。
|
||||
- 不返回 is_secret/description 等 metadata。
|
||||
- 不记录 read audit 或 usage relation。
|
||||
|
||||
建议共享 service 位于 `apps.settings.custom_variables` 或等价窄模块,BasePlaybook/BaseModule 只做代理,避免重复 ORM 代码。
|
||||
|
||||
## 6. Permissions
|
||||
|
||||
| Action | Admin | User | Viewer | Playbook/Module Worker |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| List metadata | Yes | No | No | No |
|
||||
| Read non-secret value via Admin API | Yes | No | No | N/A |
|
||||
| Reveal secret | Yes | No | No | N/A |
|
||||
| Create/update/delete | Yes | No | No | No |
|
||||
| get_variable runtime | No direct API | No direct API | No direct API | Yes |
|
||||
|
||||
Worker 读取不根据发起 Playbook 的用户角色过滤。User 发起的 Playbook 仍可读取所有 Custom Variables,因此 Admin 必须只安装可信自定义代码。
|
||||
|
||||
## 7. Admin API
|
||||
|
||||
建议资源:
|
||||
|
||||
`/api/settings/custom-variables/`
|
||||
|
||||
### List/retrieve default representation
|
||||
|
||||
非 Secret:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "...",
|
||||
"key": "EDR_BASE_URL",
|
||||
"value": "https://edr.internal.example",
|
||||
"is_secret": false,
|
||||
"description": "Production EDR API",
|
||||
"enabled": true
|
||||
}
|
||||
```
|
||||
|
||||
Secret:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "...",
|
||||
"key": "EDR_API_TOKEN",
|
||||
"value": "",
|
||||
"value_configured": true,
|
||||
"is_secret": true,
|
||||
"description": "Production EDR token",
|
||||
"enabled": true
|
||||
}
|
||||
```
|
||||
|
||||
### Reveal
|
||||
|
||||
`POST /api/settings/custom-variables/{id}/reveal/`
|
||||
|
||||
- 仅 active Admin session。
|
||||
- 不要求重新输入本地或 LDAP 密码。
|
||||
- 返回一次明文 value。
|
||||
- 每次请求写 AuditLog。
|
||||
- 不应支持 bulk reveal。
|
||||
- response 应使用 no-store cache headers。
|
||||
|
||||
### Update
|
||||
|
||||
- key 不可改。
|
||||
- Secret update 未包含 value:保留旧值。
|
||||
- Secret→non-secret:API 需要显式确认字段,例如 `confirm_secret_exposure=true`。
|
||||
- 没有确认返回 400。
|
||||
- non-secret→secret 正常允许。
|
||||
- 所有 changes 审计不包含 value。
|
||||
|
||||
### Delete
|
||||
|
||||
- 允许硬删除。
|
||||
- UI 必须二次确认。
|
||||
- API 不负责扫描 Python 代码引用。
|
||||
- 删除后运行时返回 None。
|
||||
- 删除 AuditLog 保存 key、description、is_secret、enabled,不保存 value。
|
||||
|
||||
## 8. Audit
|
||||
|
||||
写 AuditLog:
|
||||
|
||||
- create
|
||||
- update
|
||||
- enable/disable(作为 update)
|
||||
- reveal
|
||||
- delete
|
||||
|
||||
不写:
|
||||
|
||||
- get_variable runtime read。
|
||||
- Admin 普通 list/retrieve。
|
||||
|
||||
Secret 和非 Secret value 均不得进入 changes。可使用:
|
||||
|
||||
```json
|
||||
{
|
||||
"changes": {
|
||||
"value": {"from": "***", "to": "***"},
|
||||
"enabled": {"from": true, "to": false}
|
||||
},
|
||||
"metadata": {
|
||||
"key": "EDR_API_TOKEN",
|
||||
"value_changed": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
不保留旧 value,不支持 rollback。
|
||||
|
||||
## 9. Frontend
|
||||
|
||||
位置:System Settings 中新增 `Custom Variables`。
|
||||
|
||||
### List
|
||||
|
||||
- Key。
|
||||
- Description。
|
||||
- Secret 标记。
|
||||
- Enabled。
|
||||
- Value configured。
|
||||
- Updated time。
|
||||
- Edit/Delete actions。
|
||||
- Secret 默认不可见。
|
||||
|
||||
### Create/edit modal
|
||||
|
||||
- Key 创建时可编辑,编辑时只读。
|
||||
- Value 使用 multiline input;Secret 使用 password input。
|
||||
- is_secret switch。
|
||||
- enabled switch。
|
||||
- description。
|
||||
- value byte limit 提示。
|
||||
- 编辑 Secret 时 value 留空表示不修改,UI 必须明确说明。
|
||||
|
||||
### Reveal
|
||||
|
||||
- Secret 行显示 Reveal。
|
||||
- active Admin session 直接调用。
|
||||
- 明文只显示在临时 Modal。
|
||||
- Modal 关闭后从前端 state 清除。
|
||||
- 不复制到列表 state、URL、localStorage 或 console。
|
||||
|
||||
### Secret downgrade
|
||||
|
||||
从 Secret 改为普通变量时显示额外确认:
|
||||
|
||||
> This value will become visible in normal Admin API responses and UI.
|
||||
|
||||
## 10. Interaction with custom code
|
||||
|
||||
示例:
|
||||
|
||||
```python
|
||||
class Playbook(BasePlaybook):
|
||||
def run(self):
|
||||
base_url = self.get_variable("EDR_BASE_URL")
|
||||
token = self.get_variable("EDR_API_TOKEN")
|
||||
if not base_url or not token:
|
||||
raise ValueError("EDR custom variables are not configured.")
|
||||
```
|
||||
|
||||
自定义代码责任:
|
||||
|
||||
- 检查 None。
|
||||
- 解析 JSON/boolean/number。
|
||||
- 不把 Secret 写入 log、Stage summary、remark、Enrichment 或异常。
|
||||
- 为 HTTP 调用设置 timeout、TLS、proxy、重试和幂等。
|
||||
|
||||
平台不静态分析 key,也不展示“变量被哪些脚本使用”。
|
||||
|
||||
## 11. Migration and backup
|
||||
|
||||
- 新表 migration,无旧数据迁移。
|
||||
- v0.5.2 现有 LLM/SIEM/LDAP/TI Secret 不自动复制到 Custom Variables。
|
||||
- 备份/恢复自然包含明文 value。
|
||||
- Compose 文档需提醒保护数据库备份。
|
||||
|
||||
## 12. Acceptance criteria
|
||||
|
||||
1. 只有 Admin 能访问资源和 Reveal。
|
||||
2. key regex、唯一和不可改名规则生效。
|
||||
3. 空 value 和超过 65,536 bytes 被拒绝。
|
||||
4. Secret 默认 response 不含明文。
|
||||
5. Admin Reveal 返回明文并写不含 value 的审计。
|
||||
6. Secret→普通无确认被拒绝。
|
||||
7. Playbook 和 Module 每次读取当前数据库值。
|
||||
8. Disabled/missing/deleted 返回 None。
|
||||
9. Agent API、CLI 和普通用户没有读取端点。
|
||||
10. CRUD AuditLog 永不包含 old/new value。
|
||||
11. 删除无需引用检查且立即影响运行时读取。
|
||||
|
||||
## 13. Known tradeoffs
|
||||
|
||||
- PostgreSQL 和备份保存明文 Secret。
|
||||
- active Admin session 无需重新认证即可 Reveal。
|
||||
- User 发起的可信 Playbook 可以间接使用全部 Secret。
|
||||
- 平台无法阻止恶意或错误自定义代码泄露 Secret。
|
||||
- 每次 ORM 查询增加少量开销,这是为即时一致性接受的成本。
|
||||
@@ -0,0 +1,362 @@
|
||||
# Worker Health
|
||||
|
||||
Status: Confirmed
|
||||
|
||||
## 1. Purpose
|
||||
|
||||
让 Admin 不依赖 Docker CLI 或日志文件,就能判断后台 Worker 是否存活、正在做什么、是否失败以及业务积压情况。
|
||||
|
||||
v0.6.0 最终监控六个逻辑 Worker:
|
||||
|
||||
1. Agentic Module。
|
||||
2. Case Analysis。
|
||||
3. Playbook。
|
||||
4. ELK Action。
|
||||
5. Dashboard Cache。
|
||||
6. Integration Health。
|
||||
|
||||
监控粒度是逻辑进程类型,不是每个 Module/Playbook definition,也不是 Docker container ID。
|
||||
|
||||
## 2. Architecture
|
||||
|
||||
Worker Health 基础设施集成进 `apps.common.worker_runner.run_worker()`。
|
||||
|
||||
每个 Worker:
|
||||
|
||||
- 启动时获取 Redis singleton lease。
|
||||
- 启动 daemon heartbeat thread。
|
||||
- 主线程在 iteration 边界更新执行状态与计数。
|
||||
- 优雅退出时释放 lease并留下安全退出原因。
|
||||
|
||||
Admin API 从 Redis 读取当前状态,并按 Worker 类型附加 backlog diagnostics。
|
||||
|
||||
## 3. Redis keys
|
||||
|
||||
建议:
|
||||
|
||||
```text
|
||||
worker-health:v1:{worker_type}:lease
|
||||
worker-health:v1:{worker_type}:state
|
||||
worker-health:v1:{worker_type}:last-exit
|
||||
```
|
||||
|
||||
### Lease
|
||||
|
||||
- value 至少包含随机 instance_id。
|
||||
- TTL 30 秒。
|
||||
- heartbeat 每 10 秒 compare-and-refresh,只有 owner instance_id 可续租。
|
||||
- 第二个同类型 Worker 发现有效 lease 后拒绝启动。
|
||||
- lease 过期后新实例可获取。
|
||||
- 不允许 last-writer-wins 覆盖。
|
||||
|
||||
### State
|
||||
|
||||
state 可与 lease 同一个 JSON key,也可独立;必须保证 owner 才能更新。
|
||||
|
||||
建议字段:
|
||||
|
||||
| Field | Meaning |
|
||||
| --- | --- |
|
||||
| worker_type | stable logical identifier |
|
||||
| instance_id | random UUID per process |
|
||||
| hostname | safe host/container hostname |
|
||||
| state | Starting/Idle/Running/Degraded |
|
||||
| started_at | process start |
|
||||
| heartbeat_at | last heartbeat |
|
||||
| iteration_started_at | current/last iteration start |
|
||||
| last_iteration_success_at | any successful iteration, including idle |
|
||||
| last_processed_at | last iteration that processed work |
|
||||
| last_failure_at | last failed/partial-failed iteration |
|
||||
| last_duration_ms | last iteration duration |
|
||||
| consecutive_failures | reset on successful iteration |
|
||||
| iteration_count | process-lifetime count |
|
||||
| processed_iteration_count | iterations with processed=true |
|
||||
| success_count | successful iterations |
|
||||
| failure_count | failed or partial-failed iterations |
|
||||
| last_message | safe WorkerIterationResult message |
|
||||
| last_error | normalized safe error |
|
||||
| log_role | log file role |
|
||||
|
||||
PID、完整 command、环境变量和 Secret 不通过 API 暴露。
|
||||
|
||||
### Last exit
|
||||
|
||||
优雅退出时记录短 TTL 或非租约型安全信息:
|
||||
|
||||
- instance_id
|
||||
- exited_at
|
||||
- reason=`graceful`
|
||||
|
||||
意外退出没有 last-exit update,API 通过 lease expired 判定。
|
||||
|
||||
## 4. Heartbeat
|
||||
|
||||
- daemon thread 每 10 秒刷新 lease。
|
||||
- 主线程运行长任务或 sleep 时仍持续刷新。
|
||||
- heartbeat Redis 写失败应记录安全日志;如果长期失败 lease 会过期。
|
||||
- heartbeat thread 不改变业务 iteration 状态。
|
||||
- Running 不设最大时长,也没有 Stalled。
|
||||
- 只要 heartbeat 存活,任意长任务保持 Running。
|
||||
|
||||
线程退出规则:
|
||||
|
||||
- Worker 正常 SIGTERM/KeyboardInterrupt 时停止 heartbeat。
|
||||
- 释放仅属于自身 instance_id 的 lease。
|
||||
- 不能删除新实例已取得的 lease。
|
||||
|
||||
## 5. Singleton startup
|
||||
|
||||
Worker 启动:
|
||||
|
||||
1. 生成 instance_id。
|
||||
2. 原子 SET NX 获取 lease。
|
||||
3. 失败则抛 CommandError 并退出,Compose 可记录重启失败。
|
||||
4. 成功写 Starting state。
|
||||
5. 启动 heartbeat thread。
|
||||
6. 进入主循环。
|
||||
|
||||
这适用于所有六个正式 Worker。`--once` 是运维命令,不应长期占用 singleton lease;如果它会与正式 Worker 并行影响同一数据,需要显式决定是否短暂获取 lease,默认建议获取以避免并发。
|
||||
|
||||
## 6. States
|
||||
|
||||
API 状态:
|
||||
|
||||
| State | Definition |
|
||||
| --- | --- |
|
||||
| Starting | live lease exists, first iteration not completed |
|
||||
| Idle | live lease, last iteration successful, currently no work |
|
||||
| Running | live lease, iteration currently executing |
|
||||
| Degraded | live lease, latest iteration has any failure |
|
||||
| Down | expected Worker has no live lease |
|
||||
|
||||
### Never reported
|
||||
|
||||
系统知道六种 expected worker type。没有 lease 且没有 state/last-exit 时:
|
||||
|
||||
- state=Down
|
||||
- reason=`never_reported`
|
||||
- UI 显示 Never reported
|
||||
|
||||
### Graceful and expired
|
||||
|
||||
- 正常退出:Down / graceful。
|
||||
- 意外崩溃或进程被杀:30 秒后 Down / heartbeat_expired。
|
||||
- Redis 本身不可用不是 Unknown/Down;整个 Worker Health API 返回 503。
|
||||
|
||||
### Degraded recovery
|
||||
|
||||
- 一次完整失败立即 Degraded。
|
||||
- `WorkerIterationResult.failure_count > 0` 的部分失败也立即 Degraded。
|
||||
- consecutive_failures 增加。
|
||||
- 下一次无失败的 iteration 清零并恢复 Idle 或完成后的健康状态。
|
||||
|
||||
## 7. WorkerIterationResult contract
|
||||
|
||||
扩展当前 dataclass:
|
||||
|
||||
```python
|
||||
@dataclass(frozen=True)
|
||||
class WorkerIterationResult:
|
||||
processed: bool = False
|
||||
message: str = ""
|
||||
failure_count: int = 0
|
||||
```
|
||||
|
||||
语义:
|
||||
|
||||
- `processed` 表示至少完成一个业务工作项。
|
||||
- `failure_count` 表示 iteration 内被捕获的业务失败数。
|
||||
- 抛异常是完整 iteration failure。
|
||||
- `message` 必须是安全摘要。
|
||||
|
||||
### Time semantics
|
||||
|
||||
- 正常 idle polling 更新 `last_iteration_success_at`。
|
||||
- 只有 `processed=True` 更新 `last_processed_at`。
|
||||
- partial failure 不更新 success timestamp;是否同时有 processed 由结果如实记录。
|
||||
|
||||
## 8. Backlog diagnostics
|
||||
|
||||
不得强制统一为 queue_depth。
|
||||
|
||||
### Playbook
|
||||
|
||||
- Pending Run count。
|
||||
- oldest Pending age。
|
||||
- Running count。
|
||||
|
||||
### Case Analysis
|
||||
|
||||
- Pending job count。
|
||||
- oldest eligible scheduled job age。
|
||||
- Running count。
|
||||
|
||||
### Module
|
||||
|
||||
复用 Redis Stream health:
|
||||
|
||||
- stream length。
|
||||
- consumer group pending。
|
||||
- consumer count。
|
||||
- last-delivered-id。
|
||||
- 可计算的 lag。
|
||||
- 每个 Module definition 仍在现有 Custom 页面展示;Worker Health 可给汇总和跳转。
|
||||
|
||||
### ELK Action
|
||||
|
||||
外部索引完整积压不可可靠得知,只显示:
|
||||
|
||||
- last poll time。
|
||||
- last poll actions/sent/skipped。
|
||||
- last successful processed time。
|
||||
- current configured interval。
|
||||
|
||||
### Dashboard Cache
|
||||
|
||||
- 24h/7d/30d 各缓存 age。
|
||||
- generated/refreshed time。
|
||||
- configured interval。
|
||||
- cache missing 标记。
|
||||
|
||||
### Integration Health
|
||||
|
||||
- enabled target count。
|
||||
- Healthy/Unhealthy/Disabled counts。
|
||||
- current serial check progress(如果可安全获得)。
|
||||
|
||||
Backlog 查询失败不应使 heartbeat 消失。API 行级 diagnostics 可返回 safe warning。
|
||||
|
||||
## 9. Error safety
|
||||
|
||||
`last_error` 只包含:
|
||||
|
||||
- exception type。
|
||||
- 标准 reason code 或固定安全摘要。
|
||||
- failure time。
|
||||
- log role。
|
||||
|
||||
不包含:
|
||||
|
||||
- 原始 exception message。
|
||||
- traceback。
|
||||
- HTTP response。
|
||||
- SIEM query/event。
|
||||
- username、password、token、API key。
|
||||
|
||||
完整排查信息留在 Worker 日志。
|
||||
|
||||
## 10. API
|
||||
|
||||
建议:
|
||||
|
||||
`GET /api/settings/operations/workers/`
|
||||
|
||||
仅 Admin。
|
||||
|
||||
正常返回六行:
|
||||
|
||||
```json
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"worker_type": "playbook",
|
||||
"display_name": "Playbook Worker",
|
||||
"state": "Idle",
|
||||
"reason": "",
|
||||
"instance_id": "...",
|
||||
"hostname": "asp-worker-playbook",
|
||||
"started_at": "...",
|
||||
"heartbeat_at": "...",
|
||||
"last_iteration_success_at": "...",
|
||||
"last_processed_at": "...",
|
||||
"last_failure_at": null,
|
||||
"last_duration_ms": 8,
|
||||
"consecutive_failures": 0,
|
||||
"counters": {},
|
||||
"last_message": "",
|
||||
"last_error": null,
|
||||
"log_role": "agentic-playbook-worker",
|
||||
"backlog": {
|
||||
"pending_count": 0,
|
||||
"oldest_pending_age_seconds": null,
|
||||
"running_count": 0
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Redis health storage 无法访问:
|
||||
|
||||
- HTTP 503。
|
||||
- 固定信息:`Worker health monitoring is unavailable.`
|
||||
- 不返回六个伪造 Down 状态。
|
||||
|
||||
API 不提供:
|
||||
|
||||
- Restart。
|
||||
- Run Now。
|
||||
- Stop。
|
||||
- Tail/download logs。
|
||||
- 历史趋势。
|
||||
|
||||
## 11. Permissions and audit
|
||||
|
||||
- 仅 Admin 可访问 API/UI。
|
||||
- User/Viewer 403。
|
||||
- Health read 不写 AuditLog。
|
||||
- heartbeat/state change 不写 AuditLog。
|
||||
- 没有 Worker health notification。
|
||||
|
||||
## 12. Frontend
|
||||
|
||||
最终位置:`System Settings → Operations → Worker Health`。
|
||||
|
||||
行为:
|
||||
|
||||
- 每 10 秒自动刷新。
|
||||
- 提供手动 Refresh。
|
||||
- 503 显示页面级 monitoring unavailable,不显示全红 Down。
|
||||
- 状态 Tag:Starting、Idle、Running、Degraded、Down。
|
||||
- 每行显示心跳、最后处理、最后失败、错误摘要、积压和 log reference。
|
||||
- Worker-specific backlog 使用不同 detail 展示,不强行同列 JSON。
|
||||
- 不提供任何 mutation 按钮。
|
||||
- 可展示运维命令文本,但 Operations Center TODO 尚未确认具体命令 UX。
|
||||
|
||||
## 13. History and retention
|
||||
|
||||
- Redis 只保存当前状态。
|
||||
- 计数从进程启动开始。
|
||||
- Worker 重启后计数重置。
|
||||
- 不提供 uptime 百分比、趋势图或历史事件。
|
||||
- 日志是历史排查来源。
|
||||
|
||||
## 14. Compose changes
|
||||
|
||||
- 新增 Integration Health Worker 服务。
|
||||
- 六个 Worker 使用明确 command 和 log role。
|
||||
- 不挂载 Docker socket。
|
||||
- 不通过 Web app 重启容器。
|
||||
- Worker singleton 冲突应在日志中明确显示。
|
||||
|
||||
## 15. Acceptance criteria
|
||||
|
||||
1. 六类 Worker 正常显示 Starting→Idle/Running。
|
||||
2. 第二个同类型实例拒绝启动。
|
||||
3. heartbeat 10 秒、30 秒过期行为正确。
|
||||
4. 长 Running 任务保持 heartbeat,不被判 Stalled/Down。
|
||||
5. 进程被强杀后约 30 秒显示 Down/heartbeat expired。
|
||||
6. 优雅退出显示 Down/graceful。
|
||||
7. 第一次 iteration/partial failure 立即 Degraded,后续成功恢复。
|
||||
8. idle success 与 actual processed 时间分离。
|
||||
9. Redis 不可用 API 503。
|
||||
10. 错误 API 不泄露原始异常或 Secret。
|
||||
11. 每类 backlog 数据符合专属 schema。
|
||||
12. User/Viewer 403,Admin 页面每 10 秒刷新。
|
||||
|
||||
## 16. Known tradeoffs
|
||||
|
||||
- Redis 故障时无法独立判断 Worker 健康。
|
||||
- 当前状态和计数不会跨进程重启保留。
|
||||
- daemon thread 增加少量线程复杂度,但解决了长任务 liveness。
|
||||
- Running 永不按时长 Stalled,业务死循环只要 heartbeat thread 活着就仍显示 Running。
|
||||
@@ -0,0 +1,355 @@
|
||||
# Integration Health
|
||||
|
||||
Status: Confirmed
|
||||
|
||||
## 1. Purpose
|
||||
|
||||
通过独立定时 Worker 主动验证正式支持的外部集成和关键依赖,并在 Admin Operations 页面展示每个配置实例的当前状态。
|
||||
|
||||
该功能表达“最近一次轻量检查是否成功”,不保证所有业务查询、数据权限或具体动作都可用。
|
||||
|
||||
## 2. Targets
|
||||
|
||||
定时检查:
|
||||
|
||||
- Splunk singleton config。
|
||||
- ELK singleton config。
|
||||
- 每个 enabled OpenAI-compatible LLM Provider。
|
||||
- AlienVault OTX。
|
||||
- OpenCTI。
|
||||
- LDAP。
|
||||
- Redis。
|
||||
- S3-compatible object storage。
|
||||
|
||||
不检查 PostgreSQL;Web/API 本身已依赖 PostgreSQL。
|
||||
|
||||
大量 EnrichmentProvider 枚举值不等于可检查的官方集成。
|
||||
|
||||
## 3. Dedicated Worker
|
||||
|
||||
新增 `run_integration_health_worker`:
|
||||
|
||||
- 使用公共 `run_worker()`。
|
||||
- 作为第六类 singleton Worker 出现在 Worker Health。
|
||||
- 固定每 5 分钟检查一次。
|
||||
- 串行检查目标。
|
||||
- 单目标超时 10 秒。
|
||||
- 单项失败后继续其他目标。
|
||||
- iteration 中任一失败使 Integration Health Worker 本轮 `failure_count > 0`,因此 Worker 状态 Degraded。
|
||||
|
||||
页面加载不直接 fan-out 外部请求。
|
||||
|
||||
## 4. States
|
||||
|
||||
持久状态只有:
|
||||
|
||||
- Healthy
|
||||
- Unhealthy
|
||||
- Disabled
|
||||
|
||||
附加 reason 表达细节:
|
||||
|
||||
| Situation | State | Reason |
|
||||
| --- | --- | --- |
|
||||
| enabled, never checked | Unhealthy | never_checked |
|
||||
| check running | 保持原状态 | transient `checking=true` |
|
||||
| last check succeeded | Healthy | ok |
|
||||
| last check failed | Unhealthy | normalized failure code |
|
||||
| config disabled | Disabled | disabled |
|
||||
| config incomplete/not configured | Disabled | not_configured |
|
||||
|
||||
不使用 Unknown/Degraded/Checking 作为持久 state。
|
||||
|
||||
未配置和 Disabled 是中性状态,不算健康也不算故障。页面不计算整体 platform/integration status。
|
||||
|
||||
## 5. Data model
|
||||
|
||||
建议模型 `IntegrationHealth`:
|
||||
|
||||
| Field | Type | Notes |
|
||||
| --- | --- | --- |
|
||||
| id | UUID | primary key |
|
||||
| integration_type | string enum | splunk/elk/llm/otx/opencti/ldap/redis/storage |
|
||||
| config_object_id | nullable string | LLM Provider UUID;singleton/dependency 使用稳定 sentinel |
|
||||
| display_name | string | safe display label |
|
||||
| state | enum | Healthy/Unhealthy/Disabled |
|
||||
| reason_code | string | normalized |
|
||||
| message | string | fixed safe message |
|
||||
| checking_started_at | nullable datetime | transient check marker |
|
||||
| last_checked_at | nullable datetime | any completed check |
|
||||
| last_success_at | nullable datetime | successful check |
|
||||
| last_failure_at | nullable datetime | failed check |
|
||||
| duration_ms | nullable integer | latest check |
|
||||
| consecutive_failures | nonnegative integer | reset on success |
|
||||
| updated_at | datetime | auto |
|
||||
|
||||
Constraints:
|
||||
|
||||
- unique `(integration_type, config_object_id)`,null/sentinel 处理必须稳定。
|
||||
- index `(state, integration_type)`。
|
||||
- 不保存完整 config、URL、username、Secret、response body。
|
||||
|
||||
只保存当前状态,不保存历史行或趋势。
|
||||
|
||||
## 6. Configuration synchronization
|
||||
|
||||
### Create
|
||||
|
||||
- 新 enabled config:创建 Unhealthy/never_checked。
|
||||
- 新 disabled config:创建 Disabled/disabled。
|
||||
|
||||
### Update
|
||||
|
||||
相关配置字段改变:
|
||||
|
||||
- Enabled:立即 Unhealthy/configuration_changed。
|
||||
- Disabled:Disabled/disabled。
|
||||
- 不保留旧 Healthy 到下一个检查。
|
||||
|
||||
现有 Settings 保存逻辑应在同一 transaction commit 后同步 health 状态。
|
||||
|
||||
### Delete
|
||||
|
||||
- 多实例配置(例如 LLM Provider)删除时同步硬删除 health row。
|
||||
- singleton config 不删除;Disabled 时保留 Disabled row。
|
||||
- 配置 CRUD 自身继续由现有 AuditLog 追溯。
|
||||
|
||||
## 7. Check depth by target
|
||||
|
||||
### LLM
|
||||
|
||||
- 对每个 enabled Provider 发最小 Chat Completions 请求。
|
||||
- 验证 base URL、认证、model 和实际推理路径。
|
||||
- 限制最大 token,接受每 5 分钟极低但非零费用。
|
||||
- 不保存 prompt/response content。
|
||||
|
||||
### Splunk
|
||||
|
||||
- 使用轻量认证管理/信息接口。
|
||||
- 不执行 SPL。
|
||||
- 不依赖 index、time field 或数据存在。
|
||||
|
||||
### ELK
|
||||
|
||||
- 使用集群 info/health 或等价轻量认证接口。
|
||||
- 不搜索用户 index。
|
||||
- 不将 yellow 等集群业务状态自动扩展为复杂 Degraded;检查器应按能否满足现有连接需求返回 Healthy/Unhealthy,并在 reason code 中规范化。
|
||||
|
||||
### LDAP
|
||||
|
||||
- 连接服务器。
|
||||
- 使用配置的 bind DN/password 进行 service bind。
|
||||
- 验证 search base 可访问。
|
||||
- 不保存或使用专用普通用户测试密码。
|
||||
- 没有 bind DN 时执行当前支持模式下可行的基础连接/search check。
|
||||
|
||||
### OTX/OpenCTI
|
||||
|
||||
- 调用轻量身份/账户 endpoint。
|
||||
- 验证 Token。
|
||||
- 不执行 IOC 搜索。
|
||||
|
||||
### Redis
|
||||
|
||||
- PING。
|
||||
- 读取安全的基础 INFO:版本、内存、连接状态。
|
||||
- 不写测试 key。
|
||||
- 不重复 Module stream backlog diagnostics。
|
||||
|
||||
### Object storage
|
||||
|
||||
- 验证 endpoint、凭据和目标 bucket metadata/list 权限。
|
||||
- 不写入或删除 probe object。
|
||||
|
||||
## 8. Check service contract
|
||||
|
||||
统一内部结果:
|
||||
|
||||
```python
|
||||
@dataclass(frozen=True)
|
||||
class IntegrationCheckResult:
|
||||
success: bool
|
||||
reason_code: str
|
||||
message: str
|
||||
duration_ms: int
|
||||
```
|
||||
|
||||
规范 reason_code 至少包括:
|
||||
|
||||
- ok
|
||||
- never_checked
|
||||
- configuration_changed
|
||||
- not_configured
|
||||
- disabled
|
||||
- authentication_failed
|
||||
- timeout
|
||||
- tls_error
|
||||
- connection_refused
|
||||
- dns_error
|
||||
- invalid_response
|
||||
- permission_denied
|
||||
- service_unavailable
|
||||
- unknown_error
|
||||
|
||||
message 必须来自固定映射,不直接使用 exception message。
|
||||
|
||||
## 9. State update rules
|
||||
|
||||
检查开始:
|
||||
|
||||
- 设置 checking_started_at。
|
||||
- 不改变当前 state。
|
||||
|
||||
成功:
|
||||
|
||||
- state=Healthy。
|
||||
- reason_code=ok。
|
||||
- last_checked_at/last_success_at=now。
|
||||
- consecutive_failures=0。
|
||||
- 清空 checking_started_at。
|
||||
|
||||
失败:
|
||||
|
||||
- 第一次失败立即 state=Unhealthy。
|
||||
- last_checked_at/last_failure_at=now。
|
||||
- consecutive_failures += 1。
|
||||
- 保存 normalized reason/message。
|
||||
- 清空 checking_started_at。
|
||||
|
||||
Worker 崩溃遗留 checking_started_at 时,下次 Worker 可覆盖;持久 state 仍是上一次结果。
|
||||
|
||||
## 10. Manual Settings tests
|
||||
|
||||
保留现有各配置页 Test:
|
||||
|
||||
- 用于测试未保存或已保存配置。
|
||||
- 已保存配置的 Test 结果更新对应 IntegrationHealth。
|
||||
- 未保存临时配置的 Test 不更新 health。
|
||||
- Manual Test 继续写现有 AuditLog。
|
||||
- Unified Integration Health 页面不提供 Check Now。
|
||||
|
||||
普通业务调用的成功/失败永不更新 IntegrationHealth,避免查询输入、限流或业务权限错误污染集成状态。
|
||||
|
||||
## 11. API
|
||||
|
||||
建议:
|
||||
|
||||
`GET /api/settings/operations/integrations/`
|
||||
|
||||
仅 Admin。
|
||||
|
||||
返回每个目标/配置实例的独立状态:
|
||||
|
||||
```json
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"integration_type": "llm",
|
||||
"config_object_id": "...",
|
||||
"display_name": "OpenAI Production",
|
||||
"state": "Healthy",
|
||||
"reason_code": "ok",
|
||||
"message": "The provider responded successfully.",
|
||||
"checking": false,
|
||||
"checking_started_at": null,
|
||||
"last_checked_at": "...",
|
||||
"last_success_at": "...",
|
||||
"last_failure_at": null,
|
||||
"duration_ms": 821,
|
||||
"consecutive_failures": 0,
|
||||
"settings_route": "/system/llm-providers/..."
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- 不返回 overall status。
|
||||
- 不返回 Secret、完整 endpoint、username 或 response preview。
|
||||
- API 只读取 PostgreSQL,不执行检查。
|
||||
- Redis target 的健康状态也是定时检查结果;与 Worker Health Redis 503 是不同语义。
|
||||
|
||||
## 12. Error safety
|
||||
|
||||
UI/API 只显示:
|
||||
|
||||
- normalized reason code。
|
||||
- 固定安全 message。
|
||||
- check time/duration。
|
||||
|
||||
禁止:
|
||||
|
||||
- 原始 exception。
|
||||
- traceback。
|
||||
- response body。
|
||||
- URL query。
|
||||
- username。
|
||||
- API key/token/password。
|
||||
|
||||
完整异常进入 Integration Health Worker log,仍需遵循现有日志 Secret 规范。
|
||||
|
||||
## 13. Permissions and audit
|
||||
|
||||
- 仅 Admin 可看 API/UI。
|
||||
- User/Viewer 403。
|
||||
- Scheduled check 不写 AuditLog。
|
||||
- 定时状态变化不写 AuditLog。
|
||||
- Manual saved-config Test 写现有 AuditLog。
|
||||
- 不发送 Inbox/Webhook 健康通知。
|
||||
|
||||
## 14. Frontend
|
||||
|
||||
位置:`System Settings → Operations → Integration Health`。
|
||||
|
||||
- 与 Worker Health 独立表格/API。
|
||||
- 每 30 秒读取 PostgreSQL 当前状态。
|
||||
- 提供页面手动 Refresh,但不触发外部检查。
|
||||
- 展示 integration type/name/state/reason/last check/duration/consecutive failures。
|
||||
- Disabled/Not configured 可见且使用中性样式。
|
||||
- enabled never checked 显示 Unhealthy + Never checked。
|
||||
- checking 时保留原 state Tag并显示 Checking 辅助标记。
|
||||
- 行可跳转对应 Settings 页面。
|
||||
- 不显示 overall status。
|
||||
- 不提供 Check Now。
|
||||
|
||||
## 15. Worker Health interaction
|
||||
|
||||
Integration Health Worker 自身:
|
||||
|
||||
- 使用第六个 Worker type。
|
||||
- heartbeat/lease 遵循 Worker Health Spec。
|
||||
- 本轮任一集成失败时 WorkerIterationResult.failure_count > 0,Worker Health 显示 Degraded。
|
||||
- Integration 表显示具体失败目标。
|
||||
- Redis health target 失败可能同时导致 Worker 心跳不可用;此时 Worker Health API 可能 503,但 PostgreSQL 中最后 Integration Health 结果仍可显示。
|
||||
|
||||
## 16. Migration
|
||||
|
||||
- 创建 IntegrationHealth 表。
|
||||
- 为现有配置生成初始行:
|
||||
- enabled/configured → Unhealthy/never_checked。
|
||||
- disabled/incomplete → Disabled。
|
||||
- 不在 migration 中访问外部服务。
|
||||
- 添加 Integration Health Worker Compose service。
|
||||
- 更新 log role mapping。
|
||||
|
||||
## 17. Acceptance criteria
|
||||
|
||||
1. Worker 每 5 分钟串行检查所有 eligible targets。
|
||||
2. 每项超时 10 秒,失败不阻止后续目标。
|
||||
3. 各目标使用已确认的轻量检查深度。
|
||||
4. enabled 首次检查前 Unhealthy/never_checked。
|
||||
5. 一次失败立即 Unhealthy,一次成功立即恢复。
|
||||
6. config 修改立即 configuration_changed。
|
||||
7. saved-config Test 更新状态,unsaved Test 不更新。
|
||||
8. 业务请求不更新状态。
|
||||
9. 删除 LLM Provider 同步删除 health row。
|
||||
10. API 不执行外部请求、不返回敏感信息。
|
||||
11. Admin-only 和 30 秒 UI 刷新正确。
|
||||
12. Worker 自身出现在 Worker Health。
|
||||
|
||||
## 18. Known tradeoffs
|
||||
|
||||
- 没有历史趋势和整体状态。
|
||||
- 没有统一 Check Now。
|
||||
- LLM 检查产生持续小额调用成本。
|
||||
- 一次瞬时失败立即 Unhealthy,可能产生短暂抖动,但当前不发送通知。
|
||||
- 轻量管理接口 Healthy 不等于所有索引、模型能力或业务查询均正常。
|
||||
@@ -0,0 +1,391 @@
|
||||
# SLA Management
|
||||
|
||||
Status: Confirmed
|
||||
|
||||
## 1. Purpose
|
||||
|
||||
为升级后新建的 Case 建立按 Severity 配置的 TTD、TTA、TTR 时限,向负责人提示即将超时和已经超时的工作,并在 Case 和 Dashboard 中提供与现有平均时间指标完全一致的达标统计。
|
||||
|
||||
## 2. Terminology and formulas
|
||||
|
||||
单个 Case 使用不带 Mean 前缀的名称:
|
||||
|
||||
| Per-Case | Formula | Dashboard aggregate |
|
||||
| --- | --- | --- |
|
||||
| TTD, Time to Detect | earliest valid Alert.first_seen_time → Case.created_at | MTTD |
|
||||
| TTA, Time to Acknowledge | Case.created_at → Case.acknowledged_time | MTTA |
|
||||
| TTR, Time to Resolve | Case.acknowledged_time → Case.closed_time | MTTR |
|
||||
|
||||
规则:
|
||||
|
||||
- `M` 仅表示多个样本的 Mean。
|
||||
- Dashboard 现有 MTTD/MTTA/MTTR 公式必须与上表一致。
|
||||
- created_at→closed_time 可以作为总耗时展示,但没有独立 SLA target。
|
||||
- 全部使用 24×7 elapsed seconds,不使用工作时间、节假日或暂停时钟。
|
||||
|
||||
## 3. Policy
|
||||
|
||||
每个 CaseSeverity 一行全局策略:
|
||||
|
||||
| Severity | TTD | TTA | TTR |
|
||||
| --- | ---: | ---: | ---: |
|
||||
| Critical | 300s | 900s | 14,400s |
|
||||
| High | 900s | 1,800s | 28,800s |
|
||||
| Medium | 1,800s | 7,200s | 86,400s |
|
||||
| Low | 7,200s | 28,800s | 259,200s |
|
||||
| Informational | 28,800s | 86,400s | 604,800s |
|
||||
| Unknown | 3,600s | 14,400s | 172,800s |
|
||||
|
||||
- v0.6.0 中 SLA 始终启用。
|
||||
- 六行和三项目标都必填。
|
||||
- 每个目标为整数 seconds,范围 60 秒到 365 天。
|
||||
- 不要求 TTD ≤ TTA ≤ TTR,因为三者覆盖不同阶段。
|
||||
- 不按 Category、Tag、Assignee 或业务组配置。
|
||||
|
||||
### Policy model
|
||||
|
||||
建议 `SlaPolicy`:
|
||||
|
||||
- severity,unique。
|
||||
- ttd_target_seconds。
|
||||
- tta_target_seconds。
|
||||
- ttr_target_seconds。
|
||||
- created_at/updated_at。
|
||||
|
||||
Settings API 一次提交全部六行,并在一个事务中全部保存。任一值非法则全部拒绝。
|
||||
|
||||
## 4. Snapshot semantics
|
||||
|
||||
每个阶段开始时快照当时 Severity 和 target:
|
||||
|
||||
- TTD:Case 创建时。
|
||||
- TTA:Case 创建时。
|
||||
- TTR:首次 acknowledged_time 设置时。
|
||||
|
||||
之后修改 Case Severity 不改变已经开始或完成的阶段。
|
||||
|
||||
策略修改只影响未来创建的阶段快照:
|
||||
|
||||
- 新 Case 的 TTD/TTA 使用新策略。
|
||||
- 旧但尚未 acknowledge 的 Case,其 TTR 在未来开始时使用新策略。
|
||||
- 不批量重算已有快照。
|
||||
|
||||
## 5. CaseSla model
|
||||
|
||||
每个参与 SLA 的 Case 一条 OneToOne `CaseSla`,不用 JSON 或通用 metric child table。
|
||||
|
||||
每项指标明确保存:
|
||||
|
||||
- severity_snapshot。
|
||||
- target_seconds。
|
||||
- started_at。
|
||||
- ended_at。
|
||||
- deadline_at。
|
||||
- elapsed_seconds。
|
||||
|
||||
补充字段:
|
||||
|
||||
- case OneToOne。
|
||||
- created_at/updated_at。
|
||||
|
||||
建议索引:
|
||||
|
||||
- tta_deadline_at,配合 acknowledged_time/null 或等价 active marker。
|
||||
- ttr_deadline_at,配合 closed_time/null。
|
||||
- CaseSla.case unique。
|
||||
|
||||
状态不周期写入数据库,按快照、当前时间和完成时间动态计算。
|
||||
|
||||
## 6. Metric states
|
||||
|
||||
通用状态:
|
||||
|
||||
- Pending。
|
||||
- Warning。
|
||||
- Met。
|
||||
- Breached。
|
||||
- Not applicable。
|
||||
|
||||
未完成指标:
|
||||
|
||||
- elapsed < 80% target:Pending。
|
||||
- 80% ≤ elapsed < 100%:Warning。
|
||||
- elapsed ≥ target:Breached。
|
||||
|
||||
已完成指标:
|
||||
|
||||
- elapsed ≤ target:Met。
|
||||
- elapsed > target:Breached。
|
||||
- 完成后超时仍保持 Breached,不增加 Completed late。
|
||||
|
||||
### TTD subset
|
||||
|
||||
TTD 在被观察时已经完成,因此只可能:
|
||||
|
||||
- Met。
|
||||
- Breached。
|
||||
- Not applicable。
|
||||
|
||||
TTD 不进入 Pending 或 Warning。
|
||||
|
||||
### TTR before acknowledgement
|
||||
|
||||
尚未 acknowledge:
|
||||
|
||||
- UI state 显示 Pending。
|
||||
- started/deadline/elapsed 为空。
|
||||
- 标注 `Starts after acknowledgement`。
|
||||
- SLA Worker 不扫描 Warning/Breach。
|
||||
|
||||
不计算 Case overall SLA 状态。TTD、TTA、TTR 始终独立展示和筛选。
|
||||
|
||||
## 7. TTD behavior
|
||||
|
||||
### Valid source
|
||||
|
||||
使用当前 Case 下最早的有效 Alert.first_seen_time,要求:
|
||||
|
||||
- 非空。
|
||||
- `first_seen_time <= Case.created_at`。
|
||||
|
||||
没有有效时间时:
|
||||
|
||||
- TTD=Not applicable。
|
||||
- 不进入达标率分母。
|
||||
- 不使用 Alert.created_at、Case.created_at 或 0 秒兜底。
|
||||
|
||||
### Recalculation
|
||||
|
||||
以下情况重算 TTD start、elapsed 和 result:
|
||||
|
||||
- Alert 创建并关联 Case。
|
||||
- Alert.first_seen_time 修改。
|
||||
- Alert 移动到其他 Case。
|
||||
- Case merge 迁移 Alert。
|
||||
|
||||
TTD target 和 Severity snapshot 不变。更早 Alert 可能使 Met 变为 Breached。
|
||||
|
||||
## 8. TTA behavior
|
||||
|
||||
- Case 创建时立即启动。
|
||||
- deadline=created_at + snapshotted target。
|
||||
- Case 第一次离开 New 时设置 acknowledged_time 并结束。
|
||||
- acknowledged_time 永久保留,Reopen 不重置。
|
||||
- On Hold 不暂停。
|
||||
|
||||
## 9. TTR behavior
|
||||
|
||||
- 首次 acknowledged_time 设置时启动并快照当时 Severity/target。
|
||||
- deadline=acknowledged_time + target。
|
||||
- closed_time 设置时结束。
|
||||
- On Hold 不暂停。
|
||||
- Closed→In Progress Reopen 清空 closed_time 后,从原 acknowledged_time 恢复同一时钟。
|
||||
- 再次 Closed 后使用新的 closed_time 作为当前最终结果。
|
||||
- 原关闭历史只通过 AuditLog 保留。
|
||||
|
||||
### Direct New→Closed
|
||||
|
||||
状态机同时设置 acknowledged_time 和 closed_time:
|
||||
|
||||
- TTA=created_at→transition timestamp。
|
||||
- TTR=0 秒,Met。
|
||||
|
||||
## 10. Upgrade boundary
|
||||
|
||||
- 只有 SLA migration/feature 启用后新建的 Case 创建 CaseSla。
|
||||
- v0.5.2 已存在 Case 不回填,不展示 SLA 状态、不筛选、不通知、不进入达标率。
|
||||
- 旧 Case 继续进入现有 MTTD/MTTA/MTTR mean,只要满足原查询条件。
|
||||
- Dashboard 必须显示 mean 和 compliance 各自 sample count,因为样本集合不同。
|
||||
|
||||
## 11. Case merge
|
||||
|
||||
- merged source 排除 SLA、达标率、active count 和通知。
|
||||
- target 保留自己的 TTA/TTR snapshot 和时钟。
|
||||
- Alert 迁入后 target 重算 TTD。
|
||||
- target 不继承 source target、Severity snapshot、状态或通知记录。
|
||||
|
||||
## 12. Notifications
|
||||
|
||||
### Recipient
|
||||
|
||||
- 只通知当前 Assignee。
|
||||
- 未分配 Case 不通知。
|
||||
- Admin 不作为 fallback recipient。
|
||||
- 用户不能关闭 SLA 通知。
|
||||
|
||||
### Events
|
||||
|
||||
- TTA Warning。
|
||||
- TTA Breached。
|
||||
- TTR Warning。
|
||||
- TTR Breached。
|
||||
- TTD Breached。
|
||||
- 不发送 TTD Warning。
|
||||
- 不发送 Met、恢复或完成通知。
|
||||
|
||||
Warning 和 Breached 分别通知。同一 recipient 最多各一次。Worker 首次看到已 Breached 时只发 Breached,不补发 Warning。
|
||||
|
||||
### Reassignment
|
||||
|
||||
按 CaseSla + metric + state + recipient 去重。当前状态已经 Warning/Breached 后重新分配,新 Assignee 在下一次扫描收到一次当前状态通知;原 Assignee不收到撤销消息。
|
||||
|
||||
### Notification storage
|
||||
|
||||
`CaseSlaNotification`:
|
||||
|
||||
- case_sla FK。
|
||||
- metric enum TTD/TTA/TTR。
|
||||
- state enum Warning/Breached。
|
||||
- recipient nullable FK User,删除用户后 SET_NULL。
|
||||
- sent_at。
|
||||
- unique(case_sla, metric, state, recipient);nullable recipient 的历史处理需保证不会影响实际去重。
|
||||
|
||||
通知记录不保存消息 Secret 或完整 Case 内容。
|
||||
|
||||
## 13. SLA Worker
|
||||
|
||||
新增单实例 `run_sla_worker`:
|
||||
|
||||
- 每 60 秒扫描。
|
||||
- 接入 Worker Health,成为第 7 个 Worker。
|
||||
- 只负责发现需通知状态并去重发送。
|
||||
- API 状态仍动态计算,不依赖 Worker 更新状态。
|
||||
- 对适用且未完成的 TTA/TTR 使用 deadline 索引扫描。
|
||||
- 对 TTD Breached 和 reassignment 使用未通知查询。
|
||||
- 一个通知失败不得吞掉;按 WorkerIterationResult.failure_count 进入 Degraded。
|
||||
- 不发送外部 Webhook 或邮件。
|
||||
|
||||
## 14. API
|
||||
|
||||
Case list/detail 增加嵌套 SLA:
|
||||
|
||||
```json
|
||||
{
|
||||
"sla": {
|
||||
"applicable": true,
|
||||
"ttd": {
|
||||
"state": "Met",
|
||||
"severity": "High",
|
||||
"target_seconds": 900,
|
||||
"elapsed_seconds": 420,
|
||||
"started_at": "...",
|
||||
"ended_at": "...",
|
||||
"deadline_at": "..."
|
||||
},
|
||||
"tta": {},
|
||||
"ttr": {
|
||||
"state": "Pending",
|
||||
"started": false
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
旧 Case:
|
||||
|
||||
```json
|
||||
{"sla": {"applicable": false}}
|
||||
```
|
||||
|
||||
Case list 支持每项 state filter 和 deadline ordering。不得用 Python 全量计算后分页;查询必须可在数据库层筛选。
|
||||
|
||||
Settings SLA API:
|
||||
|
||||
- Admin GET。
|
||||
- Admin 原子 PUT/PATCH 全部六行。
|
||||
- User/Viewer 403。
|
||||
- seconds 为唯一 API 单位。
|
||||
|
||||
## 15. Frontend
|
||||
|
||||
### Case list
|
||||
|
||||
- TTA state 默认显示。
|
||||
- TTR state 默认显示。
|
||||
- TTD state 默认隐藏但可选。
|
||||
- 三项分别筛选。
|
||||
- TTA/TTR 支持 deadline 排序。
|
||||
- 不显示 overall Tag。
|
||||
|
||||
### Case detail
|
||||
|
||||
SLA 区块分别显示:
|
||||
|
||||
- TTD/TTA/TTR。
|
||||
- state。
|
||||
- target。
|
||||
- elapsed。
|
||||
- start/end/deadline。
|
||||
- Severity snapshot。
|
||||
- TTR 未开始提示。
|
||||
|
||||
### Settings
|
||||
|
||||
`System Settings → SLA`:
|
||||
|
||||
- 六个 Severity 行。
|
||||
- 三个目标列。
|
||||
- UI 使用分钟/小时可读输入,提交转换为精确 seconds。
|
||||
- 一次 Save 全部原子提交。
|
||||
- 仅 Admin。
|
||||
|
||||
## 16. Dashboard
|
||||
|
||||
保留现有:
|
||||
|
||||
- MTTD mean/sample。
|
||||
- MTTA mean/sample。
|
||||
- MTTR mean/sample。
|
||||
|
||||
新增:
|
||||
|
||||
- TTD compliance rate/sample。
|
||||
- TTA compliance rate/sample。
|
||||
- TTR compliance rate/sample。
|
||||
- 当前 TTA Warning count。
|
||||
- 当前 TTA Breached count。
|
||||
- 当前 TTR Warning count。
|
||||
- 当前 TTR Breached count。
|
||||
|
||||
取样:
|
||||
|
||||
- TTD compliance:Case.created_at 在窗口。
|
||||
- TTA compliance:acknowledged_time 在窗口。
|
||||
- TTR compliance:closed_time 在窗口。
|
||||
- 当前 Warning/Breached:所有活动、未合并且有 CaseSla 的 Case,不受创建时间窗口限制。
|
||||
|
||||
TTD 没有当前 Warning/Breach count。
|
||||
|
||||
## 17. Audit
|
||||
|
||||
- SLA policy 修改写 AuditLog,记录六行三项目标的 before/after seconds。
|
||||
- 时间推移导致的 Pending/Warning/Breached/Met 不写 Case AuditLog。
|
||||
- SLA Worker 扫描不写 AuditLog。
|
||||
- CaseSlaNotification 提供发送历史。
|
||||
|
||||
## 18. Acceptance criteria
|
||||
|
||||
1. 单案 TTD/TTA/TTR 与 Dashboard MTTD/MTTA/MTTR公式一致。
|
||||
2. 六个 Severity 默认值与范围正确,Admin 原子保存。
|
||||
3. 每阶段按当时 Severity 快照,后续修改不追溯。
|
||||
4. TTD 缺失时间为 NA,Alert 变化可重算。
|
||||
5. TTA first-exit-New 结束,TTR acknowledge 开始。
|
||||
6. On Hold 不暂停,Reopen 从原 acknowledge 继续。
|
||||
7. Direct close 的 TTR=0 Met。
|
||||
8. 80% Warning、100% Breached,完成后 late 仍 Breached。
|
||||
9. 旧 Case 无 SLA,新 Case有 SLA。
|
||||
10. merged source 排除,target 仅重算 TTD。
|
||||
11. SLA Worker 每分钟运行并进入 Worker Health。
|
||||
12. 仅当前 Assignee 强制接收去重 Warning/Breach。
|
||||
13. 新 Assignee 可收到当前状态,未分配不通知。
|
||||
14. Case list/detail、Dashboard 和 Settings 行为符合 Spec。
|
||||
15. medium 数据规模下 active deadline 扫描使用索引,不全表 Python 计算。
|
||||
|
||||
## 19. Known tradeoffs
|
||||
|
||||
- 旧 Case 不进入达标率,升级初期样本较少。
|
||||
- On Hold 持续计时,不反映净工作时间。
|
||||
- 重开 Case 会持续拉长同一 TTR。
|
||||
- Severity 变化不追溯,单 Case 不同阶段可能使用不同策略版本。
|
||||
- TTD 可因迟到或合并 Alert 从 Met 变为 Breached。
|
||||
- 未分配 Case 不产生 SLA 通知。
|
||||
@@ -0,0 +1,36 @@
|
||||
# ASP v0.6.0 specification index
|
||||
|
||||
本目录是 v0.6.0 的跨会话实施依据。已完成讨论的功能使用实施级 Spec 固化;尚未完成讨论的功能只记录 TODO 和待决策问题,不得把 TODO 中的推荐项当作已经确认的需求。
|
||||
|
||||
## 已确认 Spec
|
||||
|
||||
| 文档 | 状态 | 内容 |
|
||||
| --- | --- | --- |
|
||||
| [00-release-scope.md](00-release-scope.md) | Confirmed | 版本目标、部署边界、兼容矩阵、容量、权限与排除项 |
|
||||
| [01-bulk-case-triage.md](01-bulk-case-triage.md) | Confirmed | Case 批量分诊、共享状态机、通知与审计 |
|
||||
| [02-case-merge.md](02-case-merge.md) | Confirmed | 多源案件合并、数据迁移、只读源案件与幂等 |
|
||||
| [03-playbook-execution.md](03-playbook-execution.md) | Confirmed | Playbook Run、结构化 Stage、取消、重试与 Worker 语义 |
|
||||
| [04-custom-variables.md](04-custom-variables.md) | Confirmed | Playbook/Module 可读取的 UI 管理变量 |
|
||||
| [05-worker-health.md](05-worker-health.md) | Confirmed | Redis 心跳、Worker 状态、积压指标与 Admin API |
|
||||
| [06-integration-health.md](06-integration-health.md) | Confirmed | 定时集成检查、当前状态持久化与安全错误 |
|
||||
| [07-sla-management.md](07-sla-management.md) | Confirmed | TTD/TTA/TTR 时限、Severity 策略、通知和 Dashboard 达标率 |
|
||||
|
||||
## 待讨论
|
||||
|
||||
[TODO-remaining-domains.md](TODO-remaining-domains.md) 记录 AI 质量评估、抑制规则、Operations 页面整合和版本验收。继续讨论时应逐项把决定写回独立 Spec。
|
||||
|
||||
## 实施顺序
|
||||
|
||||
1. 先完成 Case 状态机,再实现批量分诊和案件合并。
|
||||
2. 完成 Playbook Run/Stage 后实现 Custom Variables。
|
||||
3. 完成通用 Worker Health 基础设施,再接入六类 Worker。
|
||||
4. 完成 Integration Health 数据层和 Worker,再实现 Operations 页面。
|
||||
5. 待剩余三个业务域定稿后,统一补齐 v0.6.0 验收规范。
|
||||
|
||||
## Spec 使用规则
|
||||
|
||||
- `Confirmed` 表示产品决策已确认,实施不得自行改变行为。
|
||||
- Spec 中的模型名和 URL 是目标设计;若代码库已有命名约束冲突,可以做等价调整,但外部行为必须一致。
|
||||
- 每项功能必须同时覆盖后端、前端、权限、审计、迁移和失败行为。
|
||||
- v0.6.0 允许破坏性 API 调整,不需要兼容旧 CLI 或插件。
|
||||
- 不得把本目录复制到 `asp-doc` 作为用户文档;用户文档应在功能实现定型后另行编写。
|
||||
@@ -0,0 +1,114 @@
|
||||
# v0.6.0 remaining domain TODO
|
||||
|
||||
Status: Not discussed
|
||||
|
||||
本文件用于在另一台电脑或新会话中继续产品讨论。以下内容仅是讨论清单,不是已确认需求。
|
||||
|
||||
## TODO 1: AI quality evaluation
|
||||
|
||||
Release blocking: Yes
|
||||
|
||||
Goal: 用人工最终判断评估 AI 严重度、置信度、影响、优先级和 Verdict 的质量,形成可解释的反馈闭环。
|
||||
|
||||
需要逐项决定:
|
||||
|
||||
- 评估对象是所有 AI 字段,还是先只做 Verdict/Severity。
|
||||
- 何时生成评价记录:字段修改、Case Close 或定时快照。
|
||||
- AI 输出与人工输出为空时如何处理。
|
||||
- 一次 Case 多次 AI 分析如何选择被评估版本。
|
||||
- 是否必须保存 model、provider、prompt slug/version 和 analysis job。
|
||||
- 指标使用 agreement rate、confusion matrix、precision/recall 还是更简单统计。
|
||||
- Verdict 多分类如何归并,Unknown/Insufficient Data 是否排除。
|
||||
- 分析师是否提供显式 thumbs up/down 和错误原因。
|
||||
- 是否允许抽样复核和争议标记。
|
||||
- 时间窗口、Provider、Model、Playbook/Trigger、Case category 等筛选维度。
|
||||
- 数据保留、历史不可变性和模型配置删除后的展示。
|
||||
- Dashboard/独立页面的信息架构。
|
||||
- 权限和匿名化要求。
|
||||
- 是否将反馈用于自动 prompt 优化;建议 v0.6.0 排除自动学习。
|
||||
- 审计、迁移和验收案例。
|
||||
|
||||
输出目标:`08-ai-quality-evaluation.md`。
|
||||
|
||||
## TODO 2: suppression rules
|
||||
|
||||
Release blocking: Yes
|
||||
|
||||
Goal: 在告警进入 Case 流程前降低已知噪声,同时保留可审计的命中记录,避免简单删除安全数据。
|
||||
|
||||
需要逐项决定:
|
||||
|
||||
- Rule 匹配对象是原始 Webhook、标准化 Alert 字段还是 Module 输出。
|
||||
- 可匹配字段白名单以及 text/exact/list/CIDR/regex 运算符。
|
||||
- 多条件 AND/OR 能力和是否允许嵌套。
|
||||
- Rule 作用域:全局、Module、rule_id、产品或数据源。
|
||||
- 动作是 drop、mark suppressed、route to existing Case 还是不创建 Case。
|
||||
- 被抑制事件是否持久化;存储多少原始数据。
|
||||
- Rule 优先级、first-match 或 all-match。
|
||||
- Enabled、有效期、自动过期和命中计数。
|
||||
- 模拟/预览如何在历史样本上运行,是否必须先模拟再启用。
|
||||
- 如何防止一条宽泛 Rule 隐藏大量真实告警。
|
||||
- Admin/User/Viewer 权限;建议至少创建/修改仅 Admin。
|
||||
- 命中 AuditLog、Rule 变更审计和 Secret/PII 脱敏。
|
||||
- Module correlation 与 merged source routing 的执行顺序。
|
||||
- 列表、详情、命中历史和筛选 UX。
|
||||
- 数据库索引、性能目标和验收案例。
|
||||
|
||||
输出目标:`09-suppression-rules.md`。
|
||||
|
||||
## TODO 3: Operations Center
|
||||
|
||||
Release blocking: Depends on final scope
|
||||
|
||||
已确认的固定边界:
|
||||
|
||||
- 位于 `System Settings → Operations`。
|
||||
- 包含 Worker Health 和 Integration Health 两个独立区块。
|
||||
- 仅 Admin 可访问。
|
||||
- Worker 每 10 秒刷新,Integration 每 30 秒刷新。
|
||||
- 不提供 Worker Restart、Run Now、日志读取或健康通知。
|
||||
- Integration 不提供统一 Check Now;保留各 Settings 页的 Test。
|
||||
- 不计算整体 Integration Health。
|
||||
|
||||
仍需决定:
|
||||
|
||||
- 页面布局、过滤、排序、状态颜色和移动端是否考虑。
|
||||
- Worker backlog 的不同数据结构如何展示。
|
||||
- 如何从 Integration 行跳转到对应 Settings。
|
||||
- Redis 监控不可用时页面局部还是整体错误。
|
||||
- Operations 导航徽标是否显示异常数量。
|
||||
- 是否显示 Compose 修复命令,以及具体安全文本。
|
||||
- 空状态、加载状态和权限拒绝 UX。
|
||||
- API 聚合还是前端调用两个独立 API。
|
||||
- 前端组件复用和验收案例。
|
||||
|
||||
输出目标:`10-operations-center.md`。
|
||||
|
||||
## TODO 4: v0.6.0 acceptance
|
||||
|
||||
Release blocking: Yes
|
||||
|
||||
需要在所有功能 Spec 完成后定义:
|
||||
|
||||
- 从 v0.5.2 生产备份副本升级的完整演练。
|
||||
- 全新 Docker Compose 安装。
|
||||
- medium 数据规模下的关键 API/页面性能阈值。
|
||||
- Admin/User/Viewer 权限矩阵回归。
|
||||
- Case 单条和批量状态机一致性。
|
||||
- 部分成功批量分诊、原子合并、幂等重试和 merged source 路由。
|
||||
- Playbook Pending/Running/终态、Cancel、Retry、Stage 和 Worker 崩溃恢复。
|
||||
- Custom Variables 的 Secret masking、Reveal audit 和 Module/Playbook 读取。
|
||||
- Worker Redis 故障、重复实例、优雅退出和心跳过期。
|
||||
- 所有正式集成的 Healthy/Unhealthy/Disabled 行为。
|
||||
- SLA、AI 质量评估和抑制规则端到端场景。
|
||||
- 备份、恢复和回滚演练。
|
||||
- 文档、release notes、已知限制和功能冻结条件。
|
||||
- P0/P1 缺陷门槛和 RC 观察周期。
|
||||
|
||||
输出目标:`11-release-acceptance.md`。
|
||||
|
||||
## Suggested continuation prompt
|
||||
|
||||
在新电脑上可从下面的提示继续:
|
||||
|
||||
> 阅读 `docs/specs/v0.6.0/README.md` 和 `TODO-remaining-domains.md`。已确认 Spec 不要重新讨论。从 AI quality evaluation 开始,一次只问一个决策问题,每个问题给出推荐答案;确认完成后依次处理 suppression rules、Operations Center 和 release acceptance。
|
||||
Reference in New Issue
Block a user