依据文档:dev_docs/proposal-v4.md (需求规格 v4.1)、dev_docs/feasibility-report.md (可行性分析 v4.1)
版本:v1.0 | 状态:评审稿(供 M0 定稿与编码)
阅读对象:后端(Rust)、SDK(TS/Go/Python)、前端(Admin UI)工程师
本文约定:默认值即推荐值;标注"待定"的为开放决策点(见 §16)
术语
含义
项目 Project
顶级作用域(如 order-service)
分支 Branch
环境维度,默认 dev/test/prod + 自定义
分组 Group / item
分支内配置组织;结构(分组+item 集合)在项目级定义
结构 Structure
项目级唯一事实源:分组集合 + 每组的 item 定义(key/类型/校验/secret)
草稿 Draft
未发布修改:分支值草稿 / 项目结构草稿 / 共享库草稿
版本 Version
不可变快照(结构 + 该分支值),每 (项目, 分支) 一条递增链
活动版本 ActiveVersion
当前客户端可见的已发布版本(版本链最新)
发布 Publish
原子操作:固化草稿 → 新版本 → 推进指针 → 生成 diff → 通知 SDK
回滚 Rollback
基于历史版本内容创建新版本(历史不可变)
共享库 SharedLibrary
集群级共享项(分组+item),项目分组可引用
单一管理员会话
同一时刻仅一个有效 admin 登录(R10/AC14.10)
revision
Raft 状态机内部单调序号(日志序);对外暴露版本号
单二进制分布式配置服务:Rust 主服务(Raft 集群 + 状态机 + API + 内嵌 Admin UI)+ 三语言 SDK 。
配置按 项目→分支→分组→item 组织;修改走"草稿 → 版本 → 发布 → 通知"闭环。
#
不变量
保证机制
I1
写请求线性一致:返回成功即已复制到多数派并持久化
Raft 单领导 + 多数派提交
I2
防脑裂:任意时刻至多一个 leader 接受写
Raft 选举安全(任期 + 日志匹配)
I3
结构恒等:项目下所有分支分组/item 完全一致
结构项目级单点定义,结构草稿发布全分支生效
I4
草稿隔离:未发布修改对 SDK 不可见
SDK 只读活动版本;草稿只存在于状态机
I5
发布原子性:一次 Raft 写入完成 固化+版本+指针+diff+事件
发布引擎单 proposal
I6
版本不可变:已发布版本内容永不被修改
版本存快照/diff,指针只前移
I7
单一管理员会话:同一时刻仅一个有效 admin 登录
会话记录在状态机内强制
I8
敏感项静态加密:secret 值任何持久化形态均为密文
加密层在写入前/读取后
I9
回滚可审计:回滚=创建新版本,不删除历史
版本链只增
┌─────────────────────────────────────────────────────────┐
│ Rust 单二进制(每节点) │
│ ┌───────────────────────────────────────────────────┐ │
│ │ API 层 │ │
│ │ ├ gRPC :8383 ── 数据面(SDK:Get/Watch/Members) │ │
│ │ ├ HTTP :8384 ── 管理面 + 渲染 + 健康检查 │ │
│ │ └ /admin ── 内嵌 Admin UI(静态资源,同源) │ │
│ ├───────────────────────────────────────────────────┤ │
│ │ 应用层 │ │
│ │ 发布引擎 / 分支服务(diff,promotion) / 渲染引擎 │ │
│ │ / 加密层(AEAD) / 审计 / 会话(单管理员) / 共享库级联 │ │
│ ├───────────────────────────────────────────────────┤ │
│ │ 状态机(确定性应用) │ │
│ │ 项目/分支/分组/item + 草稿 + 版本链 + 共享库 + 会话 │ │
│ ├───────────────────────────────────────────────────┤ │
│ │ 共识层 openraft(日志/快照/成员变更) │ │
│ ├───────────────────────────────────────────────────┤ │
│ │ 存储 RocksDB(日志、快照、状态机 KV,内嵌) │ │
│ └───────────────────────────────────────────────────┘ │
│ 节点间:Raft 端口 :8385(成员复制) │
└─────────────────────────────────────────────────────────┘
模块
职责
关键依赖
raft-node
openraft 集成:日志、快照、成员变更、选举
openraft, rocksdb
core
状态机与数据模型:项目/分支/结构/草稿/版本/共享库
serde
publish
发布引擎:固化、diff、事件、回滚、级联
core
api
gRPC + HTTP 接口、错误码、鉴权
tonic, axum
watch
订阅管理与事件扇出、断线重放
tokio
crypto
加密层:AEAD、信封、轮换、脱敏
aes-gcm/ring
render
规范化 IR → YAML/TOML/JSON,引用解析
serde_yaml, toml, serde_json
admin-ui
前端产物内嵌与静态托管
rust-embed, axum
observability
healthz/readyz、指标、结构化日志、审计
tower-http, tracing
角色:bootstrap 首节点 / 普通成员 / learner(追赶中)→ voter。
状态:follower → candidate → leader(Raft 标准)。
新节点启动:--join http://host:8384(任意已有实例的管理面端点)。
新节点调用 JoinCluster{join_addr, node_id}(写请求,需经 leader)。
leader 校验后:将新节点加入成员表为 learner ,返回集群信息(leader 地址、其他成员)。
learner 从 leader 拉取快照 + 日志追赶(限速,默认 64MB/s 可配)。
追平后,管理员(或配置 --auto-promote)将 learner 提升为 voter。
新节点成为 voter 后开始服务读写。
新节点 ──join──▶ 任意节点 ──Raft写──▶ leader 加 learner
◀──集群信息── │
──快照+日志追赶──▶ leader(限速)
◀──追平确认── │
──promote──▶ voter(此后参与投票与服务)
openraft 快照:由状态机定期生成(默认每 10k 条日志或 64MB,可配),存 RocksDB。
新节点/落后节点从 leader 拉快照 → 应用 → 追日志 → 追平。
日志压缩:快照之后截断旧日志(保留最近 N 条用于追赶,可配)。
心跳默认 500ms,选举超时 1.5~3s 随机(可配);多数派存活才可选出 leader。
少数派存活:拒绝写(I1/I2),读可选(follower 读已提交数据)。
线性一致读 :默认走 leader(或 ReadIndex 校验后本地读),保证读已提交。
follower 读 :可配置(--read-mode=stale),返回可能稍旧的已提交数据。
写请求到达非 leader:返回 ERR_LEADER_REDIRECT + leader 地址(SDK 自动跟随)。
参数
说明
默认
--bootstrap
首节点自举
无(二选一必填)
--join <endpoint>
加入集群(指定任一实例 HTTP 端点)
无
--data-dir
数据目录
./data
--grpc-addr
数据面 gRPC
:8383
--http-addr
管理面 HTTP
:8384
--raft-addr
节点间 Raft
:8385
--advertise-addr
对外公告地址(NAT/容器场景)
自动推断
--read-mode
linear
stale
环境变量
全部参数可用 DSH_* 环境变量覆盖
优先级:env > file > flag
Project { id, name, created_at }
├─ Structure { project_id, version, groups[] } // 已发布结构(不可变,版本递增)
│ groups[] = { name, items[] }
│ items[] = { key, type, required, secret, validate }
├─ StructureDraft { project_id, base_version, groups[] } // 未发布结构修改
├─ Branch { project_id, name, created_at }
│ └─ BranchState { project_id, branch,
│ active_version, // 活动版本号
│ value_draft, // (group,key) → 草稿值(未发布)
│ versions: Version[] } // 版本链(不可变)
│ Version { no, structure_version, created_at, operator, comment,
│ snapshot_ref, diff }
└─ RefBinding { project_id, group, item_key? , shared_group, shared_key } // 引用绑定
SharedLibrary
├─ SharedDraft { group, key, ... } // 未发布共享修改
├─ SharedItem { group, key, type, secret, required, validate, value, version }
└─ (共享版本链:可选保留,MVP 保留最近 N 个共享版本)
AdminSession { token_hash, issued_at, expires_at, device_id } // 全局唯一活动会话
AuditLog { id, ts, operator, action, target, detail, version? }
3.2 存储模型(状态机 KV 前缀布局,RocksDB)
Key 前缀
内容
p/{pid}
项目元数据
p/{pid}/struct
已发布结构(含结构版本号)
p/{pid}/struct-draft
结构草稿
p/{pid}/b/{branch}/state
分支状态(active_version、value_draft)
p/{pid}/b/{branch}/v/{no}
版本(快照或 diff 引用)
p/{pid}/refs/{g}/{k?}
共享库引用绑定
sh/{g}/{k}
共享项(含版本)
sh-draft/{g}/{k}
共享草稿
sess/admin
活动会话记录(单会话强制)
audit/{seq}
审计日志
meta/cluster
集群元数据(成员等,openraft 管理)
类型
校验
存储
string
长度/正则(validate 可配)
明文
int / float
范围校验
明文
bool
—
明文
json
合法 JSON(存规范化文本)
明文
array
元素类型约束
明文
secret
强制加密(I8)
密文(信封加密,§7)
item 定义:{ key, type, required(bool), secret(bool), validate? };
值可为空(未填);发布时校验 required (默认阻断,可配警告,见 §4.6)。
结构(分组+item 定义)只存在项目级:p/{pid}/struct 一份。
结构修改 → p/{pid}/struct-draft;发布结构 = 一次 Raft 写入:新结构版本 + 全部分支
各推进一个版本(值不变、结构版本号同步)→ 所有分支结构恒等(I3)。
新建分支:继承当前已发布结构 + 活动版本值(从活动版本物化出初始值草稿)。
3.5 版本链:快照 + diff(checkpoint)
活动版本 :存全量快照(读快)。
历史版本 :默认存 diff(相对前一版本);每 N 个版本(默认 100)生成一个 checkpoint 全量快照。
读取历史版本:从最近 checkpoint 重建(应用 diff)。
保留策略 :默认全量保留;可配 --version-retention-count / --version-retention-days;
裁剪只删历史、不动活动版本与 checkpoint 保底(至少保留最近 1 个 checkpoint 之后的历史)。
版本物化(materialization) :发布时把共享库引用解析后的值写入版本快照 ,
版本自包含、不可变(共享库后续变更不影响历史版本)。
编辑(API/UI) ──▶ 值草稿 value_draft
│
Publish{comment}(一次 Raft 写入)
├─ 1. 校验:权限、required、validate、引用解析
├─ 2. 快照草稿 → 解析共享引用 → 物化
├─ 3. 生成 diff(vs 活动版本)
├─ 4. 创建版本 no+1(快照/diff 写入)
├─ 5. 推进 active_version
├─ 6. 清空已发布草稿(记录"已消费")
└─ 7. 事件入队 → 异步扇出给订阅 SDK
以上 1–6 全部在单个 Raft proposal 的处理函数内 完成:要么整体生效,要么整体失败。
事件扇出(第 7 步)在提交后异步执行(不阻塞写入路径)。
版本号:(项目, 分支) 内单调递增 int64,从 1 开始,永不复用。
触发:PublishStructure{comment};写入:新结构版本 + 每个分支各推进一个版本
(diff 为结构变化,值不变)→ 一次 Raft 写入完成,原子。
发布前在 UI 展示影响预览:波及分支列表、受影响 item 列表。
Rollback{project, branch, to_version, comment}:
读取 to_version 的快照(checkpoint 重建,见 §3.5)
以该内容创建新版本 no+1(operator/comment/rollback_of=to_version 入审计)
推进活动版本,事件照常推送(SDK 无需特殊处理)
历史版本永不被修改(I6/I9);"再回滚"就是又一次 Rollback。
4.5 并发控制(v4.1 决策:单一管理员会话)
人工并发 :单会话强制(I7)——第二个登录被拒(ERR_SESSION_IN_USE),会话状态在
状态机内(sess/admin),集群范围线性一致地保证唯一。
程序化并发 (CI 令牌发布):发布本身是单 Raft 写入 + 快照语义,天然串行;
企业版可加"发布锁"实现绝对串行。
发布与编辑竞争:编辑在发布后继续进入下一版草稿;UI 提示"发布时刻的草稿内容"。
校验项:required 未填、类型/规则不符、引用解析失败、循环引用(共享库)。
策略:默认阻断发布 (返回 ERR_PUBLISH_BLOCKED + 明细);--publish-policy=warn 可降级为警告并发布。
数据面:gRPC(protobuf,含 Watch 服务端流);错误经 gRPC status + 自定义错误码。
管理面:REST/JSON(axum);渲染与健康检查也走 HTTP。
Admin UI:同源 /admin(免 CORS)。
5.2 数据面 API(SDK 用,proto 伪代码)
service Config {
rpc GetConfig (GetConfigRequest ) returns (ConfigSnapshot );
rpc GetItem (GetItemRequest ) returns (ItemValue );
rpc Watch (WatchRequest ) returns (stream WatchEvent ); // 服务端流
rpc ListMembers (ListMembersRequest ) returns (ListMembersResponse );
}
message GetConfigRequest { string project; string branch; int64 version = 0 ; } // version=0 取活动版本
message ConfigSnapshot { int64 version; int64 structure_version; map<string, GroupData> groups; }
message WatchRequest { string project; string branch; int64 after_version = 0 ; }
message WatchEvent { int64 version; EventType type; string comment;
repeated Change changes; } // Change{group, key, new_value}
enum EventType { VALUE_PUBLISH = 0 ; STRUCTURE_PUBLISH = 1 ; SHARED_CASCADE = 2 ; ROLLBACK = 3 ; }
after_version>0:重放该版本之后的所有已发布事件(断线续传,§6)。
5.3 管理面 API(REST,Admin/CLI 用,节选)
方法
路径
说明
POST
/api/login
登录(单会话强制)
POST
/api/logout / /api/heartbeat
登出 / 心跳续期
CRUD
/api/projects, /api/projects/{p}/branches
项目/分支(分支新建继承结构)
CRUD
/api/projects/{p}/structure-draft
结构草稿(含影响预览)
POST
/api/projects/{p}/structure-draft/publish
发布结构(全分支生效)
CRUD
/api/projects/{p}/branches/{b}/draft
值草稿编辑
POST
/api/projects/{p}/branches/{b}/publish
发布版本(body: {comment})
GET
/api/projects/{p}/branches/{b}/versions
版本历史
POST
/api/projects/{p}/branches/{b}/rollback
回滚(body: {to_version, comment})
GET
/api/projects/{p}/diff?branch_a&branch_b
分支对比
POST
/api/projects/{p}/promote
值提升(body: {from, to, items?},写入目标分支草稿)
CRUD
/api/shared, /api/shared-draft, POST /api/shared/publish
共享库(发布即级联)
GET
/api/audit
审计查询
GET
/healthz /readyz
健康检查
GET
/v1/projects/{p}/branches/{b}/config?format=&version=
渲染输出(YAML/TOML/JSON)
错误码
含义
客户端动作
ERR_LEADER_REDIRECT
非 leader
SDK 跟随 leader_hint
ERR_NOT_FOUND
不存在
—
ERR_VALIDATION
校验失败(带明细)
提示用户
ERR_PUBLISH_BLOCKED
完整性校验阻断
展示明细
ERR_VERSION_PRUNED
续传起点已被裁剪
重新拉全量快照
ERR_SESSION_IN_USE
已有管理员在线
提示等待/强制下线
ERR_SESSION_EXPIRED
会话过期
重新登录
ERR_FORBIDDEN
无权限
—
ERR_CYCLE_REF
共享引用成环
提示
ERR_CONFLICT
乐观锁冲突
刷新重试
proto 包名带 major(config.v1);破坏性变更升 major;SDK 声明支持的最低版本。
管理面 API 前缀 /api/v1。
事件只由发布 产生(I4):{version, type, comment, changes[]}。
changes:变更 item 的 {group, key, new_value}(结构发布为新增/删除的 item 定义;级联为引用共享项的 item)。
Watch{project, branch, after_version=0}:服务端先回当前版本号(+可选全量,SDK 用 GetConfig)。
服务端持续推送后续发布事件。
断线:SDK 记录 last_version;重连 after_version=last_version。
服务端重放 last_version 之后的版本事件(从版本链/事件日志),再转实时流。
若起点已被裁剪(ERR_VERSION_PRUNED):SDK 拉全量快照重置。
每 (项目, 分支) 订阅表:HashMap<(pid, branch), Vec<WatchStream>>(每节点本地;事件在
leader 提交后广播到所有节点,各节点扇出给本地订阅者——容错 follower 上的订阅)。
事件日志保留:最近 N 个事件(默认 10k)供重放;重放优先用版本链 diff。
限流:单订阅事件 QPS 上限、订阅数上限(默认 10k/节点,可配)。
首选 gRPC 服务端流(低延迟、类型化、TLS)。
备选长轮询(代理穿透场景);TS 补充 SSE。三通道语义一致(同事件模型)。
静态威胁:磁盘/备份/快照被读 → 密文。
传输威胁:TLS 保护(gRPC/HTTP 均支持,默认提示开启)。
不在范围:内存侧信道、恶意管理员(可解密)、密钥管理系统(KMS 为企业版)。
明文仅存在于解密瞬间 (I8)。
数据加密:AEAD AES-256-GCM (首选,硬件加速)或 ChaCha20-Poly1305 (软件快)。
层次(信封加密):
主密钥 KEK(来自 env/file/KMS,仅内存)
└─ 每 item 随机数据密钥 DEK(AES-256,随密文存储)
└─ 数据密文 = AEAD_enc(DEK, nonce, 明文)
附:encrypted_DEK = AEAD_enc(KEK, nonce2, DEK)(存于 item 元数据)
写(草稿/发布/共享库):secret 值在进入 Raft 提案前 加密(密文进状态机)。
读(SDK Get/渲染):状态机内为密文,解密发生在出站前瞬间;管理面脱敏展示(***)。
版本快照/diff 中同样只存密文。
抽象 KeyProvider trait:env(DSH_MASTER_KEY)/ 密钥文件(--master-key-file)/ KMS(企业版插件)。
主密钥不明文落盘 ;密钥文件权限建议 0400;提供 --gen-master-key 生成指引。
DEK 版本化:item 元数据含 dek_version;轮换 = 用新 KEK 重加密所有 DEK(不重加密数据)。
主密钥轮换:dsh admin rotate-master-key → 后台任务重包 DEK;轮换期间新写用新 KEK,旧数据可解(保留旧 KEK 列表直到全部重包完成)。
管理界面/导出:secret 默认掩码(redis://***@host:6379);?reveal=true 需二次确认并审计。
解密/导出/回滚含 secret 的版本 → 审计日志(操作者、时间、目标、版本)。
IR = 活动版本(或指定版本)物化后的 map<group, map<key, value>>(共享引用已解析)。
渲染前统一校验:类型、TOML 表达力(见 8.2)。
以 TOML 为表达力下限 :键限合法标识符或引号串;顶层必须是表(分组→顶层表);
json 类型值在 TOML 中输出为内联表/数组或字符串(规则文档化);无 null(secret 未填 → 输出占位注释)。
YAML:serde_yaml(1.x 稳定);JSON:serde_json;TOML:toml crate。
三格式语义等价 :由等价性测试保证(§14)。
渲染/发布时解析 RefBinding → 共享项值;解析失败 → 阻断(ERR_VALIDATION 明细)。
循环引用检测:构建引用图,DFS 判环,发布与渲染前执行。
属性测试:随机 IR → 三格式 → 各自解析 → 语义等价(规范化比较)。
前端:React + TypeScript + Vite(产物 ≤5MB 基准);构建后由 rust-embed 编译进二进制。
托管:axum /admin 静态 + SPA fallback;与 API 同源(免 CORS);CSP 头、无外链。
登录页(单会话)、项目列表、项目详情(分支 Tab)、分支编辑页
(树形:分组→item 值编辑器 + 待发布变更视图 + 发布按钮 (校验结果 + 影响预览))、
版本历史页(版本列表 + 版本 diff + 回滚按钮 )、分支对比页(diff + promote )、
共享库页(CRUD + 引用绑定 + 级联预览)、审计页、设置页(主密钥/保留策略/成员)。
登录成功 → 签发会话(token 存内存;状态机记 sess/admin:token_hash、expires_at、device_id)。
第二个登录 → ERR_SESSION_IN_USE:UI 提示"已有管理员在线(设备/时间)",提供"强制下线"按钮
(需再次确认,写状态机替换会话)。
心跳:前端每 5min 调 /api/heartbeat 续期;TTL 默认 24h(可配);服务端过期即清除。
CLI 兜底:dsh admin force-logout(管理员忘退时恢复)。
数据面 API(SDK)与管理面 API 鉴权独立:SDK 用 API 令牌(只读/watch);管理面用会话(读写)。
MVP 无 RBAC;企业版:分支级权限、SSO、MFA。
CI:前端构建 → 产物哈希固定 → 内嵌 → 单元测试含静态资源存在性检查。
体积基准:前端 ≤5MB;总二进制 ≤50MB。
10. SDK 契约(TS / Go / Python)
端点池与 failover :ConfigClient(endpoints: string[]);按序/权重连接;失败自动切换 +
指数退避(base 500ms,cap 30s,抖动);ERR_LEADER_REDIRECT 自动跟随 leader_hint。
订阅 :watch(project, branch, listener);首次连接后缓存活动版本(GetConfig);
收到 WatchEvent → 增量更新本地缓存 → 回调 listener(事件含 version,保证顺序)。
断线续传 :重连携带 after_version;ERR_VERSION_PRUNED → 重拉全量。
API :get(project, branch) / getItem(project, branch, group, key) / watch(...) / close()。
TLS :支持 ConfigClient(endpoints, {tls});令牌 Authorization: Bearer。
TS:浏览器(fetch + EventSource/WebSocket)与 Node(gRPC 流)双运行时;类型化 WatchEvent<T>。
Go:gRPC client + goroutine 扇出 listener;context.Context 贯穿;sync.Once 关闭。
Python:async(asyncio + grpc.aio);listener 为 async 回调;线程安全本地缓存。
三语言 SDK 对同一 mock 服务 (Rust 测试桩,协议 golden)跑同一套用例:
端点池切换、leader 重定向、watch 顺序、断线重放、VERSION_PRUNED 重置、secret 脱敏、多格式获取。
项
设计
传输
gRPC/HTTP 均支持 TLS;默认启动时提示开启;生产建议强制
认证
管理面:会话(单会话强制);数据面:API 令牌(只读/watch)
鉴权
MVP 全局管理员;企业版分支级 RBAC
会话
TTL + 心跳 + 强制下线 + 登录失败限次(防爆破)+ 设备绑定
密钥
§7 信封加密;主密钥不明文落盘
Web
CSP、XSS/CSRF 防护、初始凭证强制修改、无外链资源
审计
登录/登出、发布/回滚/结构发布/共享发布、解密与导出、promote、会话强制下线
供应链
cargo deny + RustSec;前端 lockfile + SBOM;产物哈希固定
健康检查:/healthz(进程存活)、/readyz(已加入集群且可服务:leader 或 follower 可读)。
Prometheus 指标(/metrics):
raft_role / raft_leader / raft_term / raft_committed_index / raft_snapshot_size、
api_qps / api_latency{grpc,http} / watch_conns / watch_events_total、
publish_total / rollback_total / versions_total / drafts_pending / shared_refs_total、
storage_bytes / session_active。
结构化日志:JSON 可选;含 request_id、操作者、版本号;审计独立表(audit/{seq})。
追踪:OpenTelemetry(企业版)。
极简启动:dsh --bootstrap(首节点)/ dsh --join http://host:8384(加入)。
配置优先级:环境变量(DSH_*)> 配置文件(--config dsh.yaml)> 命令行。
Docker:dsh:latest 镜像(静态编译,scratch/alpine);docker-compose.yml 一键三节点
(bootstrap + 2×join + 3 个数据卷)。
数据目录:./data(RocksDB + 快照 + 日志);备份:快照 + 日志归档;恢复指引文档。
层
内容
单元
数据模型、校验器、diff、加密(KAT 向量)、渲染器
集成
API 全链路、发布引擎状态机、共享级联、回滚
Raft 故障注入
分区(leader 隔离/少数派)、丢包、乱序、节点 kill/重启、快照追赶
watch 测试
断线重放、VERSION_PRUNED、事件顺序、fan-out 压力
加密测试
轮换、DEK 版本化、旧 KEK 兼容、脱敏
等价性测试
随机 IR → YAML/TOML/JSON 语义等价(属性测试)
契约测试
三 SDK vs mock 服务同一套用例
混沌
集群级混沌演练(配合故障注入)
基准
写 QPS ≥10k、watch 连接 ≥10k、发布→SDK ≤1s、单机内存 ≤128MB、二进制 ≤50MB
阶段
交付物
依赖
M0(2 周)
协议规范定稿(proto/REST 契约)、数据模型文档、CI 脚手架、决策点关闭
—
M1(4~6 周)
raft-node(bootstrap/join/快照)、core(CRUD + 草稿)、storage、极简配置
openraft, rocksdb
M2(4~6 周)
publish 引擎(发布/结构发布/回滚/校验)、共享库+级联、diff/promote、crypto、render
core
M2.5(3~4 周)
admin-ui(树形/草稿/发布/历史/回滚/单会话)、watch 服务端、可观测性
api
M3(4~6 周)
三 SDK + failover + watch + 续传 + 契约测试
api/watch
M4(持续)
混沌、加固、文档、发布、compose 示例
全部
MVP 收敛 :M1+M2 子集(不含共享库、多格式 TOML/YAML、promote 完整版)+基础 Admin UI+
watch(发布通知)+secret 加密。共享库、多格式、三 SDK 完整版按里程碑推进。
16. 开放决策点(推荐默认已给出,M0 前关闭)
#
决策
推荐默认
备注
D1
回滚粒度
整版本回滚 (§4.4)
单 item 回滚列后续
D2
发布完整性校验默认
阻断 (--publish-policy=block)
可配 warn
D3
版本存储
快照 + diff(checkpoint,每 100 版)
见 §3.5
D4
灰度发布
企业版第一期
不进开源 MVP
D5
会话 TTL/心跳
TTL 24h / 心跳 5min / 空闲可配
见 §9.3
D6
单会话语义
拒绝第二个登录 (可加"强制下线"按钮)
v4.1 已决策,细节可调
D7
共享库级联
自动级联 + 显式发布开关(防风暴)
见 §4 发布引擎
D8
存储引擎
RocksDB(rust-rocksdb)
sled 已停维护,不用
D9
共识库
openraft (备用 raft-rs)
见 §2
D10
分组嵌套
v4 默认平铺一层
嵌套列后续
repo/
server/ # Rust 工作区
crates/core/ # 数据模型 + 状态机
crates/raft-node/# openraft 集成
crates/storage/ # rocksdb
crates/publish/ # 发布引擎
crates/api/ # tonic + axum
crates/watch/ # 订阅与扇出
crates/crypto/ # 加密层
crates/render/ # 多格式渲染
crates/admin-ui/ # 内嵌前端产物
proto/ # 协议定义
sdk/ts/ sdk/go/ sdk/python/
deploy/ # Dockerfile + docker-compose.yml
dev_docs/ # 本文档与需求/可行性文档
I1/I2 → Raft 故障注入套件
I3 → 结构一致性校验测试(任意操作后断言全分支结构恒等)
I4 → SDK 契约测试:草稿编辑期间 GetConfig 不变
I5 → 发布原子性测试:断点注入(模拟提案中崩溃),断言无半发布状态
I6 → 版本不可变测试:发布后试图修改旧版本被拒
I7 → 并发登录测试:第二个登录收到 ERR_SESSION_IN_USE
I8 → 加密测试:磁盘文件/快照/备份中无明文
I9 → 回滚审计测试