herdr-mcp源码版本 v1.1.0-rc.2

Runtime 升级

通过 A/B 升级让本机 runtime 更新不打断远程开发。

herdr-mcp 把公网入口和本机 runtime 拆成两个独立的发布平面。

对 ChatGPT 来说,公网 Edge、OAuth issuer 和 MCP URL 应该长期稳定;对工作站来说,herdr-mcp runtime 可以升级、验证、切换和回滚,而不必让 ChatGPT Connector 跟着重连。

ChatGPT
  │ stable MCP/OAuth origin
  ▼
Cloudflare Edge
  │ persistent workstation WSS
  ▼
herdr-link
  │ active generation pointer
  ├───────────────┐
  ▼               ▼
runtime A       runtime B
127.0.0.1:8772 127.0.0.1:8773

这就是 Runtime A/B。

它解决什么问题#

如果公网入口直接指向某一个本机 runtime 进程,那么一次普通升级可能同时影响:

  • HTTP/MCP 连接;
  • OAuth identity;
  • 正在进行的工具调用;
  • ChatGPT Connector;
  • 回滚路径。

A/B 把这些问题分开。

一个 candidate runtime 先在新的 loopback endpoint 启动并验证,通过 gate 后才成为 active。旧 generation 在需要时继续 drain 已经进入它的请求,并保留为 rollback 目标。

Runtime generation 是什么#

一个 generation 是本机某一份可独立访问的 herdr-mcp runtime 实例,例如:

generation A
  endpoint: http://127.0.0.1:8772/mcp
  version: current stable

generation B
  endpoint: http://127.0.0.1:8773/mcp
  version: candidate

herdr-link 持有 active-generation pointer。Edge 并不需要知道端口切换细节;它只和同一个 workstation link 通信。

因此:

切换 runtime ≠ 切换 Connector

这是整个机制最重要的边界。

DEV / PROD 是 generation 之上的 provenance 平面#

当前 runtime 在同一套 generation 机制之上提供明确的源码开发平面,但不会因此多出第三套长期 runtime 环境:

PROD
  published / verified installed binary
  固定 recovery source

DEV
  带 source provenance 的 repo/worktree build
  作为 managed generation 激活

日常源码 dogfood 使用:

herdr-mcp dev status
herdr-mcp dev sync
herdr-mcp dev rollback

dev sync 在激活 repo-built DEV generation 前先固定当前 PROD binary/checksum,而且只有 server/Link generation reconcile 完成后才接受切换。dev rollback 回到这个固定 PROD source,不重新编译源码,也不猜“哪个 previous generation 才是稳定版”。底层 bin/herdr-runtime-generation ... 仍可用于实现/UAT,但不再是维护者日常源码 dogfood 的主入口。

Runtime DEV/PROD 与浏览器扩展的 DEV/STANDALONE/STORE 身份模型无关。

A/B 不是进程管理器#

Generation manager 不负责“随便启动任何命令”。

推荐流程是:

  1. 构建 candidate;
  2. 独立启动 candidate runtime;
  3. 注册 generation;
  4. 运行 health / contract gate;
  5. activate;
  6. 观察;
  7. 成功后再回收旧 generation。

把 process creation 和 traffic activation 分开,可以让“新程序能不能启动”和“是否应该接生产请求”成为两个不同问题。

CLI#

bin/herdr-runtime-generation status

bin/herdr-runtime-generation register \
  --generation candidate-<id> \
  --endpoint http://127.0.0.1:8773/mcp \
  --runtime-version <version>

bin/herdr-runtime-generation activate \
  --generation candidate-<id>

bin/herdr-runtime-generation rollback

bin/herdr-runtime-generation remove \
  --generation candidate-<id>

生命周期 mutation 禁止使用 launchctl submit。launchd 的 inferred job 可能在命令退出后继续 replay,导致 rollback/update 等破坏性操作重复执行。需要独立进程时使用受管 lifecycle 命令;确需 launchd one-shot job 时,必须使用显式 plist,并设置 RunAtLoad=true、KeepAlive=false。

status 是第一入口。任何升级/回滚前都应该先知道:

  • desired active;
  • actual active;
  • previous generation;
  • last known good;
  • candidate health。

不要根据上一次发布日志猜当前 active generation。

Activation gate 检查什么#

Candidate 只有在真实 loopback endpoint 上通过验证后才允许切流。

核心 gate 包括:

  1. endpoint 可达;
  2. runtime health / discovery 可用;
  3. 真实 tools/list 可用;
  4. public tool contract 与当前 contract epoch 匹配;
  5. 如果声明 expected runtime version,则版本一致;
  6. 必要时连续多个 observation window 保持健康。

当前 first-party DEV/PROD 的公共 Edge contract 是 epoch 8 / 19 actions,workstation Runtime Execution Contract 是 epoch 5 / 18 tools。紧邻上一代冻结的 epoch 4 与冻结的 epoch 2 catalog 仅作为有界的 rollback/compatibility 基线保留,epoch 3 仅属历史,公共 Edge epoch 3 身份仍作为非 DEV/PROD 的有界基线保留。具体 canonical hash 属于构建/发布事实,不应该作为一篇长期教程里的常量;实际激活以当前运行配置和冻结 contract 定义为准。

为什么 contract epoch 和 Runtime A/B 必须分开#

A/B 适用于:

同一公开契约下,更换实现。

例如:

  • 修一个 fs bug;
  • 改 snapshot fallback;
  • 提升 exec 稳定性;
  • 修 OAuth relay 内部实现但 public tool surface 不变。

如果 tools/list 本身发生不兼容变化,就是 contract migration:

runtime implementation upgrade
    ≠
public MCP contract migration

后者会影响 ChatGPT tool snapshot、Edge identity、Link contract 以及新会话验收,需要单独的 epoch 迁移流程。

因此 herdr-self-update 不应该被用来偷偷跨 contract epoch。

跨一个 epoch(N → N+1)#

选择一个发布包含两个独立步骤:发现(discovery)与接纳(admission)。

发现只回答“这台工作站应该看哪个发布”。它读取 GitHub releases 索引,选出唯一一个同时满足以下条件的最高版本:非 draft 的语义化版本、该 tag 发布了 release manifest、并且当前 update 通道接纳该版本。发现流程不读取 contract identity,所以它既不会退而选择更旧的发布,也不会把选择改写到某个中间 bridge;通道内最高的合格发布就是被选中的发布。

接纳随后在下载任何东西之前校验被选中的 manifest。所有已发布的 updater 都只实现“完全相等”这一种接纳:manifest 的 contract identity {epoch, hash, tool_count} 必须等于本机 Runtime 的 contract identity,否则以 release manifest contract identity mismatch fail closed。因此任何已发布的 v1.0.x(epoch 4)都无法在任何通道上直接 update 到 epoch 5 的发布,而且结果是拒绝,不是回退到另一个发布。

向前跨恰好一个 epoch 是计划能力,任何已发布的 updater 都尚未实现。它将来会写在 runtime-manifest.json 里:

"contract": { "epoch": 5, "hash": "sha256:<epoch-5>", "tool_count": 18 },
"contract_migration": {
  "from": { "epoch": 4, "hash": "sha256:<epoch-4>", "tool_count": 18 },
  "state_schema_from": 15
}

这段声明属于 manifest 字节本身,因此会(在实现后)被现有的 manifest digest GitHub artifact attestation 覆盖。

计划中的接纳规则(L 为本机 contract identity,M 为清单中的 contract):

  1. M == L:普通同 contract 更新,行为不变。
  2. M != L:仅当以下全部成立时接纳:M.epoch == L.epoch + 1;contract_migration 存在且格式合法;contract_migration.from 与 L 完全相等(epoch、hash、tool_count);contract_migration.state_schema_from 等于本机 state schema;manifest.state_schema 等于本机 state schema。
  3. 其他情况在安装任何东西之前就拒绝,并报出本机与目标 epoch:跨多个 epoch、epoch 更低、from hash/tool_count 不匹配、声明缺失或格式错误、contract_migration 内出现未知字段。

其余关卡全部保留并照常执行:manifest attestation、tag/source_ref identity、asset sha256、candidate probe、健康门控激活、update fence 与通道规则。在跨 epoch 更新中,下载后的 candidate 会按目标 contract M(而不是 L)验证。

update auto 与交互命令使用同一套发现和同一套接纳检查,因此它可以在完全相同的规则下跨一个(计划中的)epoch,且仍仅限 PROD + Stable。本协议不允许 state schema 迁移;schema 变更仍走显式的 major migration 路径。

v1.0.4 bridge 是计划中、尚未发布的版本#

常规的 v1.0.x → v1.1 路径是两步:先 herdr-mcp update 到 v1.0.4 epoch-4 bridge(contract identity 相同,属于普通同 contract 更新),再 herdr-mcp update 到第一个在 manifest 中声明 contract_migration.from = epoch 4 的 v1.1 发布(计划 v1.1.0-rc.2)。

v1.0.4 是计划中、尚未发布的 bridge。在它发布之前没有可安装的 bridge,v1.1.0-rc.1 也无法从 v1.0.x 到达。

v1.0.4 一旦发布,已发布的 v1.0.0–v1.0.3 可以正常更新到它。它的 version 输出还会打印一行包含 contract epoch 2 / 18 tools 的旧版兼容文本,因此这些已发布 updater 已有的 candidate probe 会接纳该 bridge。前台 herdr-mcp update 和 macOS 后台 stable scheduler(update auto)都会安装它。不需要手工安装。

第二步仍取决于计划中的 contract_migration 接纳规则,任何已发布的 updater 都尚未实现。v1.1.0-rc.1 没有声明 contract_migration,所以只有第一个作出该声明的 v1.1 发布(计划 v1.1.0-rc.2)才能从 v1.0.4 bridge 被接纳。

跨 epoch 之后的回滚#

herdr-mcp rollback 重新激活上一个已安装的 generation(例如 1.1 → v1.0.4)。它属于 service 回滚,因此平台边界很重要:

  • 在 macOS 上,一次成功的 install/activation 会记录一份 ready service rollback(上一份 service 定义和上一个 generation),herdr-mcp rollback 会重新校验并重新激活它;
  • Linux 不写入成功之后的 service rollback 记录。herdr-mcp rollback 仅限 macOS。

两侧都是 state schema 15,所以不需要数据库恢复,但更新之后写入的持久状态不会被回退。在依赖它之前,先检查真实 service/state。

  • browser_dispatches.message_canonical_digest 是一个可空的前向新增列,不是编号迁移。schema 15 的 epoch 4 binary 会打开同一个数据库并忽略该列;更早写入的行保持 NULL 并只走 raw 比对,属 fail-closed,而不是伪造 canonical 匹配。
  • PR #605 合并后,包含该修复的发布版本将在 1.1 重新注册时保留显式的 webchat-control 拒绝,因此回滚到 epoch 4 将不会放宽权限。

激活发生时会怎样#

可以把 active pointer 理解成一个本机路由开关:

切换前
new request → A

activate B

切换后
new request → B
existing request on A → A 上完成/drain

理想情况下:

  • persistent WSS 不断;
  • Edge URL 不变;
  • OAuth issuer 不变;
  • ChatGPT Connector 不变;
  • 已经投递到旧 generation 的请求不会被重复投递到新 generation。

这也是为什么 activation 和 drain 要有明确语义。

Rollback#

如果 B 激活后暴露问题,不需要重新部署公网入口。

bin/herdr-runtime-generation status
bin/herdr-runtime-generation rollback

Rollback 让新请求回到 previous / last-good generation。

回滚前同样要观察真实状态:如果某个 mutation 已经在 B 上发生,回滚 runtime 不会自动回滚 Git、文件、远端服务或 Agent 行为。

Runtime rollback 是执行环境回滚,不是业务副作用回滚。

本机控制状态#

Generation manager 使用本机 control/status state 保存:

  • generation specs;
  • desired active;
  • revision;
  • observed active;
  • previous / last-good;
  • activation observations。

这些文件属于 workstation 控制状态,不是仓库源码,也不应该保存 bearer secret。

Candidate endpoint 限制在 loopback,是为了保持“generation 是本机 runtime”的安全边界,不把 A/B 变成任意远端转发器。

Heartbeat 如何让 Edge 知道版本变化#

workstation link 在 heartbeat 中报告当前 active runtime identity,例如 generation / version 等运行信息。

因此 Edge 可以观察:

workstation online
active generation changed
runtime version changed

不需要因为一次本机 A/B 切换重建 WSS。

公网状态可能在下一次 heartbeat 才收敛,所以刚 activate 后短时间内看到旧 version 并不等于切换失败;以本机 generation status + 后续 heartbeat 为准。

推荐升级流程#

Inspect current state
  ↓
Build candidate
  ↓
Start candidate on second loopback endpoint
  ↓
Register generation
  ↓
Validate health + tools contract
  ↓
Activate
  ↓
Observe real tool calls / heartbeat
  ↓
Keep old generation during observation
  ↓
Remove old generation only after confidence

如果任一步骤失败:

  • candidate 尚未 activate:修 candidate,不影响 production;
  • candidate 已 activate:先判断是否 rollback;
  • mutation delivery 不确定:先重新观察,不重复执行发布动作。

从现有 v0.4.8 一键升级到 1.0#

普通已 enrollment 用户只需要执行一条命令:

herdr-mcp update

用户不需要手动下载 1.0 binary、不需要自己运行 update major-apply、不需要删除或重建 Worker、不需要重新 pair 设备,也不需要重新添加 ChatGPT Connector。已经发布且不可修改的 v0.4.8 updater 会先发现仍保持 schema 5 / Runtime epoch 2 身份的 v0.4.9 migration bridge。bridge 只作为原 update job 的 detached worker 运行,成功路径不会把 v0.4.9 安装成 production service。它会下载并验签兼容的 schema 15 / Runtime Contract epoch 4 最终 Runtime,在精确 v0.4.8 service 仍然是 source 时调用已经验收过的 major migration,然后由新的 Runtime 对已有 Cloudflare Worker 做原地 reconcile。

Worker name 与公网 origin 保持不变;Durable Objects、secrets、已知可选 binding、已 enrollment 设备、OAuth issuer 和 Connector records 都保留。同一次 update job 中可能自动打开 Cloudflare 授权页面。若多个可访问 Cloudflare Account 都存在同名 Worker,则 fail-closed,用户可用非秘密的 CLOUDFLARE_ACCOUNT_ID 消歧。如果 v0.4.8 前台 updater 的有界等待先结束,而浏览器授权仍在进行,detached migration 会继续执行;不要求用户再输入第二条 update 命令。

迁移前会保存精确 v0.4.8 binary 与 schema-5 数据库 snapshot 作为 N-1 rollback material。如必须回退,显式使用 herdr-mcp update major-rollback。update major-apply 继续作为 bridge 内部及维护者恢复/UAT 的底层 primitive,不再是普通 v0.4.8 用户的升级步骤。

这条单命令路径只覆盖 schema-5 的 v0.4.8 → 1.0 边界。计划中的 v1.0.4 epoch-4 bridge 是另一条边界:该尚未发布的版本一旦存在,已发布的 v1.0.0–v1.0.3 会用前台 herdr-mcp update 和 macOS 后台 stable scheduler(update auto)安装它。见上一节。

herdr-self-update 适合做什么#

bin/herdr-self-update 是受监督升级入口,它复用 generation 机制,而不是原地覆盖当前 active runtime。

适合:

  • 同 contract epoch 的代码更新;
  • 可以构建 candidate、跑 gate、切换和回滚的升级。

不适合:

  • public tools contract 变化;
  • Edge/OAuth identity 迁移;
  • Custom Domain/DNS cutover;
  • Herdr daemon 本体的任意跨边界升级。

这些是不同 release plane,应单独操作。

与 Edge 发布的关系#

系统有两个主要 release plane:

Public Edge plane
Worker / Durable Object / OAuth / public MCP relay

Local runtime plane
herdr-link / active runtime generation

它们应该尽量独立发布。

例如本机 herdr_fs_* bugfix 通常无需改 Worker;OAuth relay bugfix 通常也无需切本机 runtime generation。

分离 release plane 可以减少一次发布的爆炸半径。

安全规则#

  • candidate 必须使用 loopback endpoint;
  • 当前 contract epoch 内,contract 不匹配的 candidate 不激活;
  • 已投递请求不因切代而重复执行;
  • 旧 generation drain 前不强杀;
  • credential 不写进 generation control/status;
  • 回滚 runtime 前后都重新观察 Git / Agent / 服务真实状态;
  • contract migration、Edge deployment、Domain/DNS mutation 不混成一次普通 self-update。

验收标准#

一次成功 A/B 升级至少证明:

  • candidate 独立健康;
  • 当前 public contract gate 通过;
  • active generation 已改变;
  • Edge heartbeat 收敛到新 runtime identity;
  • 新请求进入新 generation;
  • Connector/OAuth URL 没有变化;
  • 至少一次真实 MCP tool call 通过;
  • rollback target 仍然存在于观察窗口内。

这时才算“本机 runtime 升级完成”,而不是仅仅“新进程启动成功”。

相关内容:

文档

搜索 herdr-mcp

输入以搜索文档。