全栈知识治理平台首次入库(P1-P10 + 发布链路模拟验证闭环): - 治理核心:源目录/变更集/人工审核/发布批次/发布执行(fail-closed 闸口) - 测试通道:WeKnora 测试 KB 上传/轮询/验收;poll 完成时写入 parser_generation/index_generation,修复 manual/seed 路径发布闸口永久拦截 - 发布适配器 v071(本地 source=测试角色 / 云端 target=生产角色 模拟验证通过): * ES 导出/导入 mapping 自适应(.keyword 子字段、可选 tenant_id) * OSS locator 支持嵌套 scheme;chunks.seq_id 由目标序列分配 * bundle 归档同身份重试原子替换;verifier 按 bundle chunk_type 计数 * 回滚 job payload 支持 prior_execution_id(strict 解码) - 三通道发布+回滚演练实测:publish 4/4 verified,检索命中, rollback 4/4 rolled_back,目标端 PG is_enabled=false、ES is_enabled=false - 前端 A·Data-console 风格全站统一,120 用例通过 - 门禁:go vet / go test 全包通过,gofmt 干净
13 KiB
P6 生产发布 Worker 与回滚验收
结论
P6 的发布控制面、v0.7.1 三通道 Sidecar、Release Worker、归档、补偿和回滚代码已经完成,并通过本地真实依赖 E2E。
生产发布仍未授权、未配置、未启动、未执行:
PRODUCTION_RELEASE_REQUESTS_ENABLED=false
RELEASE_WORKER_ENABLED=false
persistent release_executions=0
persistent release_item_executions=0
production release jobs=0
因此本文的“通过”表示实现和隔离验收通过,不表示生产已发布。
范围
P5 sealed batch
→ 受保护的生产授权请求
→ ReleaseExecution + release.publish Job 原子入账
→ Release Worker 按 KB 路由
→ 从测试 WeKnora 导出经过验收的同一 Knowledge
→ PostgreSQL / Elasticsearch / 对象存储三通道发布
→ 生产 WeKnora 官方 API 验收
→ 成功记录 production revision
回滚:
已完成的 publish execution
→ 受保护的 rollback 授权
→ release.rollback Job
→ 载入旧 0600 release bundle
→ 三通道恢复
→ 官方 API 验收
首次发布没有旧 revision 时,失败补偿和回滚采用禁用而不是删除:
PostgreSQL Knowledge enable_status=disabled
PostgreSQL Chunks is_enabled=false
Elasticsearch 文档保留但 is_enabled=false
对象保留
官方 knowledge-scoped Search 必须零命中
人工授权与双门禁
发布请求门禁:
PRODUCTION_RELEASE_REQUESTS_ENABLED=false
执行门禁:
RELEASE_WORKER_ENABLED=false
两个门禁默认都关闭且职责不同:
- 请求门禁关闭时,授权 HTTP API 返回 403,Store 不会被调用。
- 执行门禁关闭时,Release Worker 不加载 release config、不读取生产 secret、不连接生产数据面。
- P5 的
sealed仍不创建 Job。 - 只有受保护的生产授权请求才会原子创建 ReleaseExecution 和 Job。
授权 HTTP API 不接受调用方提交的审核人身份。审核人来自服务端固定配置:
PRODUCTION_RELEASE_AUTHORIZER_REF
门禁开启时还必须提供普通、非软链接、权限 0600 的 Bearer token 文件:
PRODUCTION_RELEASE_AUTH_TOKEN_FILE
缺少固定 actor 或 token 文件时 API 拒绝启动。UI 不录入、不读取、不显示 token,并且没有“一键生产发布”按钮。
固定确认语句:
publish-to-production
rollback-production-release
retry-production-release
API
POST /api/v1/release-batches/{batch_id}/authorize-production
POST /api/v1/release-executions/{execution_id}/authorize-rollback
POST /api/v1/release-executions/{execution_id}/retry
GET /api/v1/release-capabilities
GET /api/v1/release-executions
GET /api/v1/release-executions/{execution_id}
授权响应和只读审计响应不返回:
- authorization token
- authorization_code
- bundle SHA
- WeKnora API key
- Elasticsearch / PostgreSQL / 对象存储凭据或 URL
- 测试 KB 内部连接信息
审计响应显示:
- publish / rollback
- pending / running / completed / failed
- 固定授权人
- 总项数、成功数、失败数
- 每项固定错误码
- production revision / previous production revision
- 开始和完成时间
Migration 7
新增:
release_executions
release_item_executions
约束:
- operation 只允许
publish/rollback。 - target_environment 固定为
production。 - state 使用固定枚举。
- publish execution 每个 sealed batch 唯一。
- rollback execution 每个 prior publish execution 唯一。
- Job 外键、逐项 Publication 外键和计数约束保留审计关联。
- Migration 7 不回填历史 release execution,也不自动创建 Job。
往返验收:
up 7 → down 6 → up 7
Migration 7 应用前备份:
tmp/backups/governance-pre-migration7-20260906T133742+0800.dump
bytes=263001
mode=0600
pg_restore catalog=valid
Release Worker 状态机
批次 Job 在 Worker 中按每批最多50项展开,并持久记录当前批次/总批次;批内逐项执行:
pending execution
→ running
→ 为 batch items 幂等建立 release_item_executions
→ 每个 item 执行 publish 或 rollback
→ verified / rolled_back / failed
→ execution completed 或 failed
行为:
- 一项永久失败会立即停止本项之后的同批项目与全部后续批次;未执行项记录固定
release_stopped_after_failure,随后仅对已验证成功项安排自动回滚。 - 可重试故障在重试次数耗尽前保留重试语义,不把尚未终态的项目误标为永久失败。
- 可重试失败在 Job 还有 attempt 时返回固定
release_retryable。 - 最后一次 attempt 仍失败时,逐项写固定错误码并收口,不留下永远 running 的 Execution。
- 外部响应正文、对象 key、路径、凭据和错误正文不写入治理库。
- Worker 重启后跳过已经 verified / rolled_back 的项。
- 终态failed execution可由同一Bearer门禁和固定
retry-production-release确认重排;只重置failed items,verified / rolled_back项保持完成,Job从completed/failed回pending并从attempt 0重新开始。 - publish重排前重新验证P5 sealed snapshot;过期审核或Publication变化时返回冲突。
- 未知 KB 不会落入默认适配器,而是
release_contract_mismatch。
v0.7.1 release bundle 契约
固定:
format weknora-release-bundle/v1
WeKnora v0.7.1
migration 74
retriever elasticsearch_v8
embedding dimension 1024
每个 bundle 绑定:
- ReleaseExecution ID
- Publication ID
- SourceItemVersion ID
- source revision
- parse revision(parser generation + index generation)
- release revision(封批时锁定的 review ID)
- tenant / KB / Knowledge ID
- embedding model identity
- Elasticsearch mapping SHA-256
- 各 PostgreSQL 表行数和 canonical SHA-256
- ES 文档数和 canonical SHA-256
- 资产数量、总字节和 canonical SHA-256
- 整包 SHA-256
任何 revision、模型、维度、KB、tenant、mapping 或 bundle digest 不一致都在目标写入前 fail-closed。
PostgreSQL 通道
只允许以下 v0.7.1 内容表:
knowledges
chunks
knowledge_tags
knowledge_tag_relations
resources
resource_bindings
没有读取或复制:
- users / sessions / messages
- tenants / memberships / permissions
- API keys / model provider credentials
- vector store connection credentials
- Jobs / audit logs / chat history
导出范围同时绑定:
tenant_id + knowledge_base_id + knowledge_id
目标导入:
- 必须是 migration 74 且 dirty=false。
- 使用 per-Knowledge PostgreSQL advisory transaction lock。
- 单 Knowledge 单事务替换。
- ID 在其他 tenant / KB 已存在时拒绝。
- Tag 定义和 relation 一起复制;共享 Tag 不删除。
- Resource stable handle 保留。
- production storage_backend_id、provider、storage URI 和 location_hash 按目标配置重写。
- Chunk seq_id 冲突时整个事务失败,不静默改 ID。
- 不调用 WeKnora 解析和 Embedding。
Elasticsearch 通道
源导出:
- tenant / KB / Knowledge 三重 term filter。
- 原样保存
_id、chunk文本、向量和其他_source字段。 - mapping 只计算
mappingscanonical SHA,不把索引名混入指纹。 - 导出后按
_idcanonical排序。 - 单 Knowledge 超过 10000 个 ES 文档时 fail-closed,不截断。
目标替换:
- 先 bulk upsert 新文档,
refresh=true。 - 再只删除同 tenant / KB / Knowledge 下、不属于新 release 的旧
_id。 - 读回数量和 canonical SHA。
- 失败时自动恢复 prior SearchSnapshot。
检索禁用使用 knowledge-scoped _update_by_query 将 is_enabled=false,不删除 ES 文档,并读回确认文档仍在且全部禁用。
对象存储通道
支持 S3-compatible MinIO / S3 / OSS:
- secret 仅从 0600 文件读取。
- 不在 URL 中携带 token。
- 支持 v0.7.1
storage://<backend-id>/<provider-path>。 - 拒绝 traversal、query、fragment、控制字符和越界 locator。
- 导出时核对 resource size / content SHA。
- staging:
.governance-release-stage/<release_revision>/<relative-key>。 - staging 写入后做完整流式 SHA-256。
- Copy 到 final 相同 relative key 后再次完整流式 SHA-256。
- ObjectStore 接口没有 Delete 方法。
- 回滚重传旧 bundle 中的资产;不删除新资产。
Release bundle 归档
每个 KB 使用独立目录:
<archive_root>/<knowledge_base_id>/
要求:
- archive root 和 KB 子目录是真实 0700 目录。
- 不允许目录或文件软链接。
- bundle 文件为普通 0600 文件。
- 原子写、文件 fsync、目录 fsync。
- 相同 release revision + 相同 SHA 可幂等重写。
- 相同 revision + 不同 SHA 为碰撞并拒绝。
- 加载时重验 manifest、PG、ES、资产和整包 SHA。
- 默认每 bundle 最大 1 GiB。
Bundle payload 可能包含对象 locator 和资产内容,因此不能进入 Web/API/普通日志;只有摘要进入治理库。
Release Worker 配置
模板:
config/release.v071.example.json
Runtime 配置文件必须:
- 使用绝对路径;
- 是普通文件;
- 不是软链接;
- 权限为 0600;
- JSON 不允许未知字段或尾随数据;
- 每个 KB 只允许一个 route;
- source 与 target 的数据库值、ES endpoint、对象 endpoint+bucket 必须不同;
- 所有数据库URL、ES token、对象AK/SK、WeKnora API key 只通过各自 0600 文件引用。
模板不能直接启用生产。启用前必须根据真实数据面生成独立受限配置和 Compose override,并由人工授权。
本地真实 E2E
精确 schema
使用:
ParadeDB PostgreSQL 17
WeKnora v0.7.1 migrations 000000..000074
source DB + target DB
结果:
source migration=74
目标 migration=74
合成 source:
knowledges=1
chunks=1
knowledge_tags=1
knowledge_tag_relations=1
resources=2
resource_bindings=1
通过:
TestPostgresExporterV071Integration
TestPostgresImporterV071ReplacesAndRestoresDocument
覆盖:
old → new → restore old
disable keeps 1 Knowledge + 1 Chunk
Knowledge=disabled
chunk enabled count=0
对象存储
使用本机已有的 disposable MinIO,无卷、loopback随机端口、合成凭据和合成bucket。
通过:
TestS3ObjectStoreIntegration
覆盖:
Open storage:// source
Put staging
streaming SHA Stat
Copy final
streaming SHA Stat
三通道发布与回滚
通过:
TestThreeLaneReleaseAndRollbackV071Integration
链路:
publish review-release-1
→ publish review-release-2
→ rollback review-release-1
→ target PG chunks=1
→ target ES has es-release
→ target two assets non-empty
说明:
- PostgreSQL 和 MinIO 为真实 disposable 服务。
- Elasticsearch 为状态化 HTTP contract server,真实执行 mapping/search/bulk/delete-by-query/update-by-query 协议和 digest 回读。
- WeKnora verifier 使用现有官方 client 接口 contract stub,真实执行 GetKnowledge/ListChunks/SearchKnowledge 的判断逻辑;没有连接生产 WeKnora。
- 所有 disposable 容器最终均已停止并由
--rm删除。
Worker装配
通过:
TestBuildReleaseRuntimeWithRestrictedFilesIntegration
验证:
两个真实 v0.7.1 DB连接
source/target secret文件0600
独立ES endpoint
独立对象bucket
独立官方WeKnora endpoint
KB archive 0700
RoutingPublisher构建
关闭时两个DB pool均释放
Web验收
Firefox Selenium只读验收:
review_candidates=5
release_batches=0
release_executions=0
production_request_gate=false
unauthorized_release_controls=0
截图:
tmp/P6_RELEASE_AUDIT_FIREFOX.png
可见:
- 左侧发布管理导航和顶部标签。
- P5审核候选与封批记录。
- P6生产执行审计。
- 生产请求门禁关闭。
- Release Worker保持关闭。
- 当前无生产执行记录。
- 页面没有生产授权按钮。
当前持久治理库
schema migrations=1,2,3,4,5,6,7
publication_reviews=0
release_batches=0
release_batch_items=0
release_executions=0
release_item_executions=0
release publish/rollback jobs=0
现有 5 条候选仍为知识工程师业务待审,未由测试代替人工批准。
生产前仍需人工完成
以下不属于本地自动验收,也未执行:
- 明确批准生产发布窗口。
- 对测试和生产各选一份同ID真实Knowledge,做只读PG/ES/OSS依赖盘点。
- 确认两端均是WeKnora v0.7.1 migration 74、1024维、同模型和同mapping。
- 为source创建只读数据面凭据。
- 为target创建仅允许约定KB内容表、ES索引和对象bucket的最小写权限凭据。
- 创建0600 release config和secret文件,并让Release Worker独占挂载。
- 先做1份无敏感canary。
- 官方API读取、Chunks、检索、资产和审计全部通过后再扩批。
- 保留Release Worker关闭状态,直到上述材料由人工核对。
不得因为本文完成而自动执行生产发布、创建生产Job、连接生产数据面、删除任何WeKnora Knowledge或对象。