Files
aios/docs/gitops/SILENT_RELEASE_CONTROLLER_DESIGN.md
T

8.0 KiB
Raw Blame History

AIOS Silent Release Controller Design Report

状态:隔离分支实现;生产 mutation 禁止 目标:自动准备 → 自动验证 → 单次 Owner 签名授权 → 分控制器自动执行与验收

1. 当前痛点

现有流程需要多轮人工传递 commit、tree、artifact、manifest、测试、canary、drop-in 和回滚证据。证据在不同消息里重复抄写,容易出现:

  • 构建后 artifact、manifest 或 policy 已变化,但旧批准仍被使用;
  • Git、Gitea、systemd 与回滚结果由人工拼接,缺少同一一致性快照;
  • runtime switch、tag、Baseline、Registry 的授权边界不清;
  • 重启后只剩工作树内临时 JSON,无法可靠恢复;
  • approval replay、并发 writer、key revoke 和 secret inheritance 缺少统一防线。

2. 新架构

Build / Test / Canary
        |
        v
ReleaseCandidate + DeploymentPlan
        |
        v
ReleaseAcceptanceEngine
        |
        v
READY_FOR_APPROVAL
        |
        v
SignedReleasePlanApproval (Ed25519)
        |
        +--> runtime_switch capability
        |       DeploymentController
        |
        +--> tag_publish capability
        |       TagController
        |
        +--> baseline_registry_finalize capability
                BaselineRegistryFinalizeController

Durable ReleaseSagaStore + CapabilityLedger + Event Journal
贯穿三个控制器;控制器互相不拥有对方的 mutation API。

一次 Owner 批准是同一个不可变 release plan 的签名,不是一个万能执行入口。三个 capability 分别消费、分别留痕、分别幂等恢复。force_push、tag_rewrite、direct_main_push 永远不是可签发 capability。

3. 状态机

PREPARING
  -> VERIFYING
  -> READY_FOR_APPROVAL
  -> APPROVED
  -> PRE_SWITCH_REVALIDATING
  -> SWITCH_PENDING
  -> SWITCHED
  -> RUNTIME_VERIFYING
  -> TAG_PENDING
  -> TAG_VERIFIED
  -> BASELINE_REGISTRY_PENDING
  -> COMPLETED

任一可恢复故障 -> RECONCILIATION_REQUIRED
对象冲突       -> MANUAL_OWNER_REVIEW_REQUIRED
切换后失败     -> ROLLBACK_PENDING -> ROLLED_BACK

状态快照使用 revision compare-and-set;事件同时写入 append-only journal。writer 崩溃后根据 状态确定唯一恢复动作。并发控制器中只有一个 CAS winner。

4. 数据模型

ReleaseCandidate

candidate_id
source_commit
tree_oid
artifact_sha256
manifest_sha256
tests
canary
rollback_ready
git_consistency
gitea_consistency
approval_state

DeploymentPlan

service
release_path / release_root
dropin_path / dropin_sha256
rollback_path / rollback_sha256
source_commit
artifact_sha256
manifest_sha256
policy_sha256
health_contract
sha256

SignedReleasePlanApproval

release_plan_id
candidate_id
source_commit
tree_oid
artifact_sha256
manifest_sha256
deployment_plan_sha256
policy_sha256
approval_plan_id
evidence_closure_oid
owner_approval_record_oid
capabilities
expires_at
nonce
key_id
signature
fingerprint

签名内容使用 canonical JSON,signature 不参与被签名字段。验证包括 Ed25519 签名、过期时间、 公钥状态、approval revoke、candidate/plan/policy 逐字段绑定和 capability allowlist。

5. API

POST /createReleaseCandidate
GET  /getReleaseCandidate?candidate_id=...
GET  /getApprovalEnvelope?candidate_id=...
POST /submitOwnerApproval
POST /startDeployment
GET  /getReleaseSaga?candidate_id=...

写接口要求 Bearer scope。AIOS_SILENT_RELEASE_EXECUTION_ENABLED=false 是默认值;此时可以 持久化 candidate、生成 approval envelope、保存经验证的 Owner approval 和读取 Saga, 但 /startDeployment 只返回 SHADOW_MODE 与空 side_effects。

6. PreSwitchValidator

切换前重新收集并比较:

  • source commit 与 tree;
  • artifact、manifest、policy 和 deployment plan SHA-256;
  • rollback readiness;
  • local、authoritative bare 与 Gitea refs;
  • repository identity;
  • task writer/production writer 边界;
  • health contract。

任一变化都抛出:

APPROVAL_STALE_REAPPROVAL_REQUIRED

旧批准不会被“最佳努力”继续使用。

7. systemd 防线

  • service 与 drop-in 均为显式 allowlist;
  • release 与 drop-in resolved path 必须位于 release root 内;
  • 拒绝普通文件被替换为 symlink,拒绝 symlink escape;
  • deployment lock 覆盖 switch、restart 和首次 health check;
  • restart 前后记录 MainPID 与 ActiveEnterTimestampMonotonic;
  • artifact、drop-in、rollback 和 manifest 均按 plan hash 复验;
  • 子进程只接收固定 allowlist 环境,不继承 token、password、credential、askpass、 auth socket、LD_PRELOAD、PYTHONPATH 或数据库连接串。

8. TagController 分离

TagController 必须独立完成 approval、execute、verify,并留下:

evidence_closure_oid
approval_plan_id
repository_identity
remote_tag_oid
remote_peeled_target_oid
gitea_api_tag_oid
gitea_listing_visible

同名 tag 的 tag OID 或 peeled target 不同,进入 MANUAL_OWNER_REVIEW_REQUIRED;自动流程不 push、不删除、不重写、不 force push。

9. Baseline / Registry 门禁

只有以下全部一致时,baseline_registry_finalize capability 才能完成:

Registry = Baseline = remote tag = Gitea API = expected target

UNKNOWN、API 不可达、repository identity 不符或 listing 不可见均 fail closed。

10. Policy as Code

automatic:
  build, test, artifact, canary, evidence

owner:
  signed_release_plan

capabilities:
  runtime_switch
  tag_publish
  baseline_registry_finalize

forbidden:
  force_push
  tag_rewrite
  direct_main_push
  baseline_mutation
  registry_mutation

最后两个 forbidden 项指“脱离受治理 finalize controller 的直接 mutation”。

11. Security model

  • Ed25519 公钥 ring 支持 rotation 和 revoke,私钥不进入 runtime;
  • approval plan 可撤销,过期、未知 key、错误签名均 fail closed;
  • nonce 与 capability operation 持久化,重复相同请求幂等,不同 fingerprint 重放拒绝;
  • Saga snapshot 原子写、journal append-only、CAS 防并发 writer;
  • credential 只通过受控 reference 使用,不进入 Git、API 响应或异常;
  • 子进程环境从空集合构建,不复制 os.environ;
  • Shadow 模式默认开启,execution adapter 缺失时不能降级执行;
  • runtime、tag、Baseline/Registry 分属三个能力最小化控制器。

12. Migration plan

  1. Shadow:生成 candidate、plan、acceptance、approval package,仅比较现有人工结果。
  2. Signed approval:接入 Owner 公钥 ring、撤销源和审计 record;仍不接生产 executor。
  3. Canary execution:使用非生产 service/release root 验证锁、PID、health、崩溃恢复和回滚。
  4. Controlled production switch:另行 Owner 授权启用 execution flag,只开放 runtime capability。
  5. Tag/finalize:分别完成 Gitea 与 Registry/Baseline 生产验收后再启用对应 capability。

每个阶段均可退回上一个阶段;不开启下一能力不会影响已经启用的控制器。

13. Rollback plan

  • switch 前保存旧 symlink target、PID、启动时间、artifact 与 plan fingerprint;
  • restart 或 health 任一失败进入 ROLLBACK_PENDING;
  • 原子恢复旧 target,只 restart 指定 allowlist service;
  • 重新验证旧 runtime commit、writer=false 与 repository identity;
  • 成功进入 ROLLED_BACK,失败进入 MANUAL_OWNER_REVIEW_REQUIRED;
  • rollback 不修改 Git main、production tag、Baseline 或 Registry;
  • 保留脱敏 journal、approval fingerprint 和失败证据。

14. 测试与外部依赖边界

自动测试覆盖签名伪造、字段篡改、过期/revoke/key rotation、nonce replay、CAS race、 崩溃恢复、artifact/policy/plan mutation、tag conflict、secret leakage、runtime without .git、rollback 和 shadow no-side-effects。

本隔离实现不创建生产 KMS/key、不安装 systemd unit、不部署 Action runtime、不创建 tag, 也不修改 Baseline/Registry。上述动作必须在单独生产批准中执行。