ait-server:远程权威与恢复
了解 ait-server 的远程工作流权威、Snapshot 保存、灾难恢复、runner 边界与运维职责。
适用人群: Repository 所有者与运维
为什么要跑 ait-server#
本地的 AIT 仓库没有服务端照样能用。下面这几件事里只要有一件是必须 的,就该加上 ait-server:
- 远程工作流管理: 把 Repository、Plan、Task、Change、Patchset、 证据、策略、评审、Land 和 Worker Job 的状态,统一放在一个远程权威 之下。
- 异地保存与灾难恢复: 把已记录的 Snapshot 内容和 Line 头放到 另一台机器或另一个故障域,之后再用它们重建本地 AIT 权威、把源码 文件物化出来。
- 仓库自己的 CI: 接纳带类型的 job,由兼容的
ait-runner通过 仓库声明的ci/run.sh或ci/run.ps1执行。
这几件事共用同一台服务端,但彼此是分开的。服务端连得上,不等于本地 每个文件都能恢复;有一份异地副本,本身也不等于有了自动高可用或故障 切换。
服务端拥有的权威#
一个服务端数据根,拥有一份全局的、只追加的 Repository 注册表。每个 注册过的 Repository 都会拿到一个数字 repository_index;选中它的 权威靠的是这个索引,不是显示名。Repository 的名字是可以重复的。
每一份数字 Repository 权威,都拥有它自己的远程侧:
- Snapshot 元数据、内容包、祖先关系和 Line 头;
- Plan、Task、Change、Patchset、attestation、评审、策略和 Land 记录;
- 仓库范围内的 Worker Job 和 CI 结果状态;以及
- 校验并排序这些操作所需的协议状态。
那些远程记录,服务端说了算。它不拥有开发者当前的工作目录,不替仓库 挑测试框架,也变不出从来没被记录过的文件。
在仓库的恢复清单里,把确切的 repository_index 留好。它是路由元 数据,不算密钥,但一丢,重建客户端时就没有安全办法认出已有的那份 权威了。索引绝对不要靠猜,也别拿另一个 Repository 的顶上。
注册并核验一个 Repository#
在一个初始化过的仓库里,把服务端加成 remote:
ait remote add origin <server-url> --default
ait remote list --json
ait config show --json
ait repo show --remote origin --json还没配 Repository 索引时,remote add 会注册一份新的数字权威,并把 返回的索引存下来。已经配了确切索引时,这条命令会去那台服务端核验 那份权威,对不上就直接失败。
注册不会传任何 Snapshot 或工作区内容。它只是把后续工作流、push、 pull 和恢复操作所需的地址和权威定下来。
远程工作流管理#
对一个远程范围的 Task 来说,本地的 Task worktree 仍然是编写的边界。 创建完 Snapshot 之后,就绪流程会把选定的 Patchset,以及远程 Change 所要求的 Snapshot 证据发布出去:
ait workflow ready <change-id> --apply
ait task land <task-or-change-id>CI、attestation、评审、策略和 Land 的状态顺序,由服务端排。Task 的 最终 land 消费的是那个已就绪的选定 Patchset,并推进远程的目标 Line。 workflow ready 只是个纯文本的决策界面;某道关卡还挂着时,照它打 出来的确切下一步动作走。
运维可以在不改动的前提下查看已注册的权威和它的执行状态:
ait repo show --remote origin --json
ait repo jobs --remote origin --json
ait repo ci-capabilities --remote origin --json远程工作流的发布,同时也是它所记录 Snapshot 内容的一份异地副本。 一个发布了但还没 land 的 Patchset,只是工作流证据;它不代表远程的 目标 Line 已经往前走了。
明确地保住一条 Line#
如果目的是把一条已经记录下来的 Line 保住,而且跟受管的远程 Task 无关,那 ait push 就是那条直接的同步路径:
ait status --json
ait snapshot create --message "Record the recovery point"
ait push --remote origin --line mainpush 会上传选定的 Line 头和必需的 Snapshot 祖先,然后推进那条远程 Line。它不会捕获工作区里的脏文件,不会创建 Snapshot,不会发布 Patchset,不会合并分叉的历史,也不会执行 Task land。每条需要自己 恢复点的 Line 都得 push;注册本身不会启动什么自动同步计划。
一条 Line 最近一次成功 land 或 push 出去的远程 Line 头,就是它的可 恢复点。用你能接受的最大数据丢失窗口来定发布节奏,然后去核验服务端、 真的演练一次恢复,而不是把"注册成功了"当成备份证据。
保护边界#
服务端能保住的是:
- 由 push 或远程工作流发布上传的 Snapshot 内容和祖先关系;
- 成功推进过的远程 Line 头;
- 已发布的远程工作流记录,以及它们接纳的证据;以及
- 由那份权威存下来的、仓库范围内的 CI 和 Worker Job 状态。
服务端恢复不了的是:
- 脏的、未跟踪的、被忽略的,或者没进过 Snapshot 的工作区内容;
- 一直留在本地、从没 push 或发布出去的 Snapshot 和 Line 移动;
- 在记录下那个恢复点之前就被删掉的文件;
- 密钥、凭据、编辑器状态、外部服务,以及任何不属于已记录 Snapshot 的依赖;或者
- 服务端本身丢了之后的服务端数据根——除非运维另外在别处保护过这份 数据根。
正因为有这条边界,异地保存才必须是有意为之的动作:创建 Snapshot、 把它传出去、确认远程状态,并留好用来定位它的 Repository 索引。
本地权威完好时的还原#
.ait 和 remote 配置都完好时,把远程 Line 拉下来,再把它的头物化 到一个干净的工作区里:
ait status --json
ait diff --stat
ait pull --remote origin --line main --restorepull 会先导入远程的 Snapshot 祖先,再去判定本地头和远程头的关系。 远程更靠前就快进,本地历史更靠前就不动这条 Line,没让它处理的分叉 它会拒绝。--restore 会用选定的、拉下来的那个头替换掉工作区。工作 区是脏的就会被拒,除非明确带上 --force;做这个选择之前,先把只 存在于本地、还活着的文件保好。
本地权威丢失或损坏时的恢复#
原来的 .ait 权威没了或者信不过时,另开一个干净的恢复目录。损坏的 那个目录留着做诊断,别删。先把可信的、存好的根路由字段作为本地权威 的一部分恢复回来:确切的 repository_index、default_remote 和 remotes 映射。1.0.0 没有公开的 repository_index 设置命令,而且 ait remote add 是注册操作,不能拿来安全地顶替那份恢复清单。
# Run after the exact saved routing fields are restored.
ait config show --json
ait repo show --remote origin --json
ait remote recover-head --remote origin
ait remote recover-head --remote origin --apply
ait pull --remote origin --line main --restore第一条 recover-head 是预演。--apply 会下载服务端逻辑 main 头 经过校验的 Snapshot 祖先,并在一个校验过的本地 Binary 权威世代里把 它重建出来。1.0.0 的 recover-head 没有 --line,也不支持多条 Line。最后那条 pull 把 main 物化成源码文件。恢复不会把服务端的 远程工作流账本克隆成一份单独的本地工作流账本;远程的 Task、Change、 Patchset 和 job 记录,仍然从它们的服务端权威那里查。
只要出现这几种情况就停下来:权威的响应和存下来的索引对不上、预演报 出了预期之外的头、校验没过,或者工作区里还有必须保住的文件。如果确 切的路由字段恢复不出来,也停。别为了绕过一个对不上,就去注册一份 替代权威。完整的退役归档走的是另一套生命周期: ait repo restore --remote origin 会创建出一份带新 Repository 索引 的还原权威。
Runner 与 CI 的边界#
ait-runner 是执行器,不是持久的 Repository 权威。它认领一个兼容的 带类型 Worker Job,把确切的 Snapshot 内容物化到隔离的尝试存储里,跑 仓库自己写的入口,返回有界的结果。准入、租约、job 状态和结果接纳, 都归服务端。
runner 不会替你挑某种语言专属的构建系统,不会变成 Repository 权威的 第二份副本,也不会把它尝试工作区里的任意文件留下来。尝试目录要保持 隔离,投递失败的结果要盯着,终态 job 之后要把尝试目录自己的数据清掉。
1.0.0 的确切命令、job 生命周期、请求与结果契约、物化上限、租约行为 和部署检查,见 `ait-runner`:原生 CI 执行面。
保护服务端数据根#
用异地服务端来保护客户端,前提是服务端权威本身还活着。容器示例把它 放在挂到 /var/lib/ait 的持久卷里;安装版的用户模式和服务模式,用 的是各自配置的数据根。运维必须保护的,就是这一整个根。
ait-server 不会去排第二个副本,不会自己复制数据根,不会选主,也 不会自动故障切换。请在另一个故障域用独立的备份或存储快照系统。抓一 份应用一致的副本:要么停掉写入,要么用能提供同等一致性保证的存储; 别把随手拷贝的、正在被写的单个权威文件当成可恢复的东西。
服务端版本或不可变镜像摘要、数据根备份、确切的 Repository 索引清单、 远程 URL,以及单独管理的访问凭据——恢复演练要用的这些都得留着。还原 出来的副本,在对外提供服务之前先隔离验证:
ait-server probe --data <restored-data-root> --defer-ci-admissionprobe 失败就意味着这个还原出来的根不能启用。/healthz 返回成功只 能证明正在跑的这个进程是健康的;它证明不了备份够新、够全或者能恢复。
运维清单#
- 默认把服务绑到 loopback,或者绑到一条评审过的私有入口上。
- 在受信任的入口和主机边界上终结 TLS,并强制做调用方校验、网络管控、 监控和容量限制。
- 把兼容的 1.0.0 客户端、服务端和 runner 制品都钉死版本。
- 每个 Repository 的数字索引,都记在客户端数据根之外的地方。
- 为每条需要保护的 Line 定好并核验 Snapshot 的发布节奏。
- 把完整的服务端数据根备份到另一个故障域。
- 拿一份隔离的副本,把
probe、recover-head和pull --restore演练一遍。 - 盯着服务端健康度、失败的 job、runner 兼容性和存储增长。
1.0.0 的每条 HTTP 路由、请求契约、响应和错误边界,见完整的 `ait-server` REST API 参考。 远程基础设施 讲容器 边界,`ait-runner`:原生 CI 执行面 讲完整的 runner 运维契约, 排查 讲通用的证据保全 规则。