8.0 KiB
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
- Shadow:生成 candidate、plan、acceptance、approval package,仅比较现有人工结果。
- Signed approval:接入 Owner 公钥 ring、撤销源和审计 record;仍不接生产 executor。
- Canary execution:使用非生产 service/release root 验证锁、PID、health、崩溃恢复和回滚。
- Controlled production switch:另行 Owner 授权启用 execution flag,只开放 runtime capability。
- 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。上述动作必须在单独生产批准中执行。