路线图

July 27, 2026 · View on GitHub

当前版本 v0.5.0。本文说明下一个版本要做什么、为什么是这些、以及每项的代价与风险。 已完成的版本保留在下方作为记录。

判断标准:一项工作能不能进下一个版本,看它是否阻挡「把星云放进一个真实生产 环境」。功能是否好看、是否对标竞品,都不是理由。

v0.4.0 与 v0.5.0 都与本文原先的规划不一致,而两次的原因不同。

v0.4.0 原本规划的是「规模与接入」,实际发的是「补齐文档写了但实现没有的地方」—— 核实文档承诺时接连撞出四处「说了没做」,其中一处是缺陷(测试态策略会真的拦线上 流量)。没被规划,但比规划里的事更该先做。

v0.5.0 则把顺延过来的三项都做完了,其中一项是推翻了自己的判断:此前多次说 「这台机器起不了 k8s」,实际能起 —— 受限的是估计,不是机器。

记这两笔是想说明本文的用法:它是当前的判断,不是承诺。撞到更该先做的事时 就改,但要说清楚为什么改。


0. 当前还有没有「规定了但没做」

没有了。 隐私设计里最后一处(数据主体权利接口)在 v0.4.0 补齐。

这一节此前写的是「pii 级别的数据目前是明文存储的」,那是 v0.3.0 要解决的第一 优先级问题,现已解决(HMAC 存储)。保留这段沿革是因为它说明了本文的用法: 这一节永远写「文档承诺了而实现没有」的最要紧那一处,而不是写下一个功能。

v0.4.0 的经验值得记下来:那一版一半的内容不在任何规划里,而是核实文档承诺时接连 撞出来的 —— 包括一个真缺陷(测试态策略会真的拦线上流量)。所以下一版开工前, 先做的事不是挑功能,而是再核一遍文档里的承诺

当前已知的差距不在「说了没做」,而在「明确说了不做」—— 见威胁模型第四节:采集器入口无认证、控制面不做 TLS 终结、管理接口无全局限流。它们的处理方在部署侧,但采集器认证那条应当由项目解决, 理由见下方第 8 项。


v0.3.0 —— 主题:能进生产环境

四项,都属于「不做就不能上线」而不是「做了更好」。

1. 存储层的 pii 保护

做什么

  • ClickHouse:pii 列改为存 HMAC-SHA256(密钥从 NEBULA_HMAC_KEY 注入,该变量 gen-env.sh 已经在生成,但目前只有采集器的 hash 动作在用)
  • 保留可查询性:风控要按 uid 聚合,HMAC 后等值比较仍然成立,范围查询本来也不需要
  • 告警表的 subject_key 是运营要看的,不做 HMAC,继续靠分级脱敏 在读取时按角色控制
  • PostgreSQL 的审计表已经只存掩码值,不需要改

为什么这样分

HMAC 是单向的,存了就查不回原值。对事件明细这是对的:它的用途是聚合统计, 不需要还原到人。对告警不行:运营处置告警时必须能看到是哪个账号,还原不了就没法处置。 所以两张表的策略必须不同 —— 一刀切会要么废掉风控,要么废掉隐私。

风险:密钥轮换会让新旧数据无法关联。需要在文档里写清楚这是不可逆的操作, 并提供「双写过渡期」的做法。

估算:schema 改动小,难点在迁移既有数据与文档。约 1–2 天。

2. 策略热更新

做什么:改完策略不重启 Flink 作业即可生效。

难点不在轮询,在于换策略时变量状态不能丢。

如果重载时重建变量图,所有窗口计数归零 —— 「IP 5 分钟内登录失败次数」在改完阈值的 那一刻变回 0,攻击正好在那个窗口里溜过去。改一个阈值的代价不该是丢掉全部在途状态。

设计:

VariableGraph.extendTo(newDefs, neededNames)
  · 已存在的节点连同累积状态原样保留(定义可以换,Node 持有的是累积值,与定义字面无关)
  · 新引入的变量建新节点 —— 必然冷启动,它此前没在算,没有历史可继承
  · 不再被引用的变量**不删**:策略在 test / online 之间来回切是常见操作,
    每切一次清空状态会让热更新失去意义。变量总数有上界(253),保留全部代价有限
  · 重做拓扑排序(新变量可能插在已有变量之间),order 重建、nodes 不动

下发用 Flink 的 broadcast state:一个 source 轮询 /api/v2/metadata/version, 版本变了才拉全量并广播。用 broadcast 而不是每个算子各自轮询,是因为后者会让并行实例 在一小段时间内按不同版本的策略判定 —— 同一批事件的结果取决于它落在哪个实例上。

必须证明而不是声称:测试要构造「窗口内已累积 4 次 → 热更新 → 第 5 次事件」的场景, 断言计数是 5 而不是 1。

运维需要知道:新引入的变量要经过一个完整窗口期才给出有意义的值。这不是缺陷, 但不说清楚会被当成 bug。

估算:3–5 天。这是这个版本里最难的一项。

3. 控制面的限流与登录失败锁定

做什么

  • 登录失败计数与锁定(按账号 + 按来源 IP 双维度,避免单一维度被绕过)
  • /checkRisk 按服务令牌限流 —— 它是延迟敏感路径,一个失控的调用方能拖垮整条链路
  • 管理接口的全局限流

为什么现在做:认证已经落地,但没有任何暴力破解防护。Argon2id 让离线爆破变得 昂贵,但在线爆破只受限于网络带宽。SECURITY.md 里已经把这条列为「规划中」, 它是认证这块最后一个缺口。

估算:1–2 天。Redis 已经在,计数器直接用它。

4. 可观测性

做什么

  • 引擎:变量计算耗时、各策略命中数、去重抑制数、迟到事件数,走 Flink 的 metrics
  • 控制面:接口延迟与错误率(actuator 已在,补 Micrometer + Prometheus 端点)
  • 采集器:补 Prometheus 端点 ✅ 已完成
  • 补齐 docs/operations/monitoring.md(当前 13 行占位)

为什么:出问题时现在没有任何手段判断「哪个变量算慢了」「哪条策略命中率突然异常」。 风控系统的失效往往是静默的 —— 策略不再命中不会报错,只是告警变少。没有指标就没人 会发现。

估算:2–3 天。


v0.4.0 —— 主题:补齐文档写了但实现没有的地方(已完成)

发出来的内容与原规划不同,原因见开头。四项都是同一个来源:去核实文档里的某句 承诺,发现不成立。

说明
序列检测(A 之后 N 秒内没有 B)参考引擎实现了、Java 引擎没有 —— 跨引擎语义不一致
采集器的 Prometheus 端点监控文档如实标注过「尚未实现」
数据主体权利接口隐私文档里最后一处「规定了但没做」
测试态策略不影响线上决策(缺陷)四处文档都写了这条语义,而 /checkRisk 从未过滤

另外还做了账号与令牌管理界面、表单化策略编辑器、威胁模型文档。


v0.5.0 —— 主题:规模与接入

5. Kubernetes 上的高可用

Helm chart 已完成,在单节点 k3s 上实测装通并验证了出厂资产导入。 容量规划也已按实测补齐。

剩下的是高可用,而这一项刻意留在 chart 之外:存储组件目前是单副本 StatefulSet, 生产上应当换成各自的 Operator(CloudNativePG、Altinity ClickHouse Operator、 Redpanda Operator)或托管服务 —— 在 Kubernetes 里自己维护有状态集群是另一个量级的 工作,不该由一份应用 chart 顺带承担。

引擎同理:当前是 Flink 的 session 集群,不是 Flink Kubernetes Operator,没有作业级的 自动恢复与滚动升级。

chart 的 README 里逐条写了「验证过什么 / 没验证过什么」。

6. Kafka 作为数据源

Kafka 输出已完成(franz-go,按主体分区)。当时那条「零依赖 vs 引入客户端库」的 取舍由项目决定为引入,理由与代价记在 internal/sink/kafka.goCHANGELOG

剩下的是把 Kafka 当输入:从既有主题消费事件,而不是只往里写。这样采集器能接在 别人已有的数据管道后面,而不要求上游改成推给采集器。

「A 之后 N 秒内没有 B」(延迟判定)与多步序列(A → B → C)都已实现,两个引擎共用 金标准向量对照。

本条原本写的是「引入 flink-cep」,查下来这条路走不通:

flink-cep 1.20.5 里没有 dynamic 包也没有 PatternProcessor —— 模式在构图时 编译进作业图。而 2.0 的策略是热更新的,改一条序列策略就得重启作业,连带丢掉全部 已累积的窗口状态。这与 v0.3.0 落地的热更新直接冲突。

更根本的一条:参考引擎(JS)必须实现同样的语义供金标准向量对照,它不可能用 Flink CEP。也就是说无论如何都要手写一份,引入 CEP 只是多一份需要与手写实现保持 一致的东西。

仍然没做的是分支(A 之后 B 或 C)。需要时写成多条策略 —— 那样每条的命中量还能 分别看到,而分支写法看不到。重复(A 出现 3 次以上)用内联计数器就能表达,不需要 序列构造。

8. 接入方式 —— 已完成

syslog(RFC 3164 / 5424,UDP 与 TCP)、HTTP 入口的共享令牌校验、 OpenResty 埋点都已落地。

Zeek 不需要单独的驱动:配成 JSON 日志后走 -source file-source syslog 即可。

剩下的是把 Kafka 当输入(见上一条)。


不打算做的

写下来是为了让人不必反复问。

不做规则的可视化拖拽编排。 风控策略的表达力来自条件树的嵌套与算子语义, 拖拽界面要么表达不了复杂逻辑,要么复杂到不如直接写。表单化的编辑器足够。

不做自己的图表库。 1.x 有一个基于 D3 v3 的 5600 行单文件 BChart,这类东西的 维护成本远超收益。

不追求覆盖所有风控场景。 内置 170 条策略来自 1.x 某个站点的经验,它们是起点 而不是标准答案。项目的价值在于领域模型与引擎语义的正确性,不在于策略条数。

不做多租户。 会侵入领域模型的每一层。有这个需求时应该跑多套实例。


版本号与兼容性

0.x 阶段升 MINOR 就要预期可能需要改配置或改数据,详见发布流程 §1.2。

上面几项中会带来破坏性变更的:

  • 存储层 pii 保护 —— 既有数据需要迁移,或接受新旧数据无法关联
  • 策略热更新 —— 元数据下发接口可能调整

离开 0.x 的条件见发布流程 §1.2,当前还差:CEP、第三方完成过端到端部署。