# 部署与运维模块 ## 1. 定位 本模块规定 cpa-ext 如何构建、安装、启动、观察、备份、升级和恢复。它把 [dev.md](dev.md) 的 native runtime 约束、[security.md](security.md) 的 fail-closed 前提和 [persistence.md](persistence.md) 的数据库规则转成可执行运维流程。 当前仓库仍是只声明 `usage_plugin` 的基础骨架,不具备本文所述完整生产能力。只有 [test-plan.md](test-plan.md) 的相应发布门禁通过后,才能把某个版本标记为 production-ready。 ## 2. 支持基线 设计验证基线: | 项目 | 目标 | | --- | --- | | CLIProxyAPI | `v7.2.130` / `f43aad7637ad813745bf7d341acb5663617570c5` | | Native ABI | `1` | | RPC schema | `3` | | cpa-ext plugin ID | `cpa-ext` | | 当前 module Go | `1.24` | | CPA module Go | `1.26.0` | | Home mode | 不支持,必须关闭 | | CPA build | 必须是 CGO/plugin-capable,不能使用 `no-plugin` 产物 | 生产兼容性按“精确 CPA tag + OS + arch + cpa-ext artifact”发布,不只写“ABI 1”。每次升级 CPA 都要重新跑 ABI、E2E、安全、性能和 soak 门禁。 目标发布矩阵: - Linux amd64/arm64 `.so`; - Windows amd64 `.dll`; - macOS amd64/arm64 `.dylib`(若实际维护); - WSL 构建的 `.so` 只供运行在 WSL/Linux 的 CPA,不能装入 Windows CPA。 Go c-shared 需要目标平台 C toolchain,不能只设置 `GOOS/GOARCH` 假装完成 CGO 交叉编译。 ## 3. 运行拓扑 ### 3.1 默认 MVP ```text reverse proxy / readiness gate │ ▼ CLIProxyAPI + cpa-ext.so │ ├─ synchronous short SQLite transactions ├─ callback-driven bounded maintenance └─ no long-lived plugin worker ``` 第一版默认 callback-driven:认证、准入、事实和账本在回调内完成必要短事务;聚合追赶、清理和 checkpoint 只在安全宿主调用中按时间/页数预算推进。 ### 3.2 Sidecar 拓扑 P0 runtime/24h soak 失败,或需要长期 quota refresh、备份、归档、大导出时: ```text CLIProxyAPI + thin cpa-ext plugin ── SQLite/IPC ── cpa-ext sidecar ``` sidecar 只承担长期任务/公共 API,不得形成第二套余额或价格权威。SQLite 单 writer、IPC 认证、文件锁和 crash ownership 必须另行验证。 ### 3.3 Worker 拓扑 只有目标 CPA/Go/toolchain/SQLite driver 的双 runtime 24h soak 通过,才允许动态库启用有界 worker。发布元数据必须标出 topology;不能在补丁版本中静默从 callback-driven 改为 worker。 ## 4. 文件与权限布局 建议 Linux 布局: ```text / ├─ cliproxy ├─ config.yaml ├─ plugins/ │ └─ linux/amd64/cpa-ext-v0.1.0.so └─ data/cpa-ext/ ├─ cpa-ext.db ├─ cpa-ext.db-wal ├─ cpa-ext.db-shm ├─ backups/ └─ diagnostics/ /etc/cpa-ext/ └─ hmac-keyring.json # 或 OS secret store,绝不放进 data/backups ``` 要求: - CPA 运行用户拥有 plugin/data,其他用户默认不可读; - keyring `0600`、目录 `0700`; - DB/backup `0600`、目录 `0700`; - plugin binary 只由发布/运维用户写,运行用户只读; - 数据目录不能位于临时目录、网络共享或会被自动清理的位置; - 不把 SQLite、WAL、keyring、Auth、日志放进发布归档; - Windows 使用等价 ACL,不能只依赖扩展名。 ## 5. 目标配置 ```yaml remote-management: allow-remote: false secret-key: "" # 仅保留一个运维离线 sentinel;绝不分发给用户。 api-keys: - "" plugins: enabled: true dir: plugins configs: cpa-ext: enabled: true priority: 100 store: version: "0.1.0" codex_only: true data_dir: data/cpa-ext database_file: cpa-ext.db settlement_currency: USD frontend_auth_exclusive: true unbound_key_policy: deny unpriced_usage_policy: deny_new_requests maintenance_mode: callback ``` 说明: - 上述是完整目标配置,当前基础插件只解析 `codex_only`; - CPA `Home` 是由 `-home-jwt` 注入的 runtime-only 状态,YAML 中的 `home:` 会被当前 parser 忽略;生产启动命令不得使用 `-home-jwt`,并由 readiness 检查实际 runtime 状态; - `enabled`、`priority` 是 CPA host-owned 字段,也会出现在 `config_yaml`; - `store.version` 用于选择版本化动态库; - `database_file` 相对 `data_dir` 解析,不能允许 `..` 逃逸; - `settlement_currency` 第一版只能是 USD; - HMAC keyring 通过受限 secret-file/OS store locator 注入,不把 key 值放进 YAML; - 生产不允许 observe-only、unbound allow 或 unknown price=0; - 所有可互相绑定的 Codex Auth 应使用相同 CPA priority。 热配置:UI 展示、查询限制、非安全统计刷新参数、明确允许的业务阈值。冷配置:data/database path、keyring source、currency、plugin identity、runtime topology。冷配置变化由 LKG reconfigure 拒绝并提示排空重启。 ## 6. 本地开发构建 ### 6.1 WSL/Linux ```bash cd /mnt/d/agent/cpa-plugin ./scripts/check-env.sh go test ./... go test -race ./... ./scripts/build.sh file bin/cpa-ext.so nm -D bin/cpa-ext.so | grep cliproxy_plugin_init ``` `build.sh` 会执行 gofmt、`go mod tidy`、单元测试和 c-shared 构建。正式 CI 应把“依赖文件是否被意外修改”作为检查,不能让 tidy 的变化无人审阅。 ### 6.2 PowerShell 调用 WSL ```powershell ./scripts/build.ps1 wsl file /mnt/d/agent/cpa-plugin/bin/cpa-ext.so wsl nm -D /mnt/d/agent/cpa-plugin/bin/cpa-ext.so ``` 当前产物是 Linux `.so`。Windows `.dll` 必须在 Windows CGO toolchain 下单独构建和验证 export。 ### 6.3 当前测试宿主 仓库已有: ```bash ./scripts/run-test-host.sh tail -f .runtime/cliproxy.log ./scripts/stop-test-host.sh ``` `.runtime/` 只用于本地测试,不是生产目录。`/healthz` 只证明 CPA 进程存活,不证明 cpa-ext active、价格可用或不存在认证旁路。 ## 7. 发布物 每个 artifact 必须包含/伴随: - `cpa-ext` 动态库; - version、git commit、build timestamp; - target OS/arch、Go version、C toolchain; - minimum/exact tested CPA version; - ABI=1、max RPC schema=3、capability 清单; - runtime topology; - LICENSE、NOTICE/THIRD_PARTY; - SHA-256 checksum,推荐额外签名/provenance/SBOM。 CPA plugin store zip 根目录中的动态库文件名使用: ```text cpa-ext.so # 或 cpa-ext-v0.1.0.so ``` 安装目标可位于 `plugins///`。发布包不能包含生成 `.h`,除非消费者明确需要。 ## 8. 安装前检查 1. 确认 CPA exact version、plugin-capable build 和平台架构; 2. 确认 CPA 未使用 `-home-jwt` 启动,实际 runtime `Home.Enabled=false`; 3. 备份 CPA config、SQLite 和匹配 keyring; 4. 校验 artifact checksum/signature; 5. 校验动态库 export `cliproxy_plugin_init`; 6. 检查 data/keyring 目录权限和磁盘余量; 7. 检查只有 sentinel 留在 CPA native `api-keys`,没有用户 Key; 8. 检查 management 仅 loopback/受信网关可达; 9. 检查所有可绑定 Codex Auth priority; 10. 准备 gateway drain 和 rollback artifact; 11. 首次生产部署必须先有已审阅 published price version; 12. 确认 P0 runtime/performance/soak gate 结论与所选 topology 一致。 ## 9. 首次安装 1. 在排空环境停止 CPA; 2. 原子复制版本化 dynamic library 到目标目录; 3. 创建 data/keyring 目录和权限; 4. 写入/审阅配置,先不开放用户入口; 5. 启动 CPA,观察 plugin load/register/schema/capabilities; 6. 等待 migration、integrity 基础检查、keyring、price 和 recovery 完成; 7. 用 Management `/plugins` 确认 `cpa-ext` active 和版本; 8. 调用 cpa-ext `/readiness` 与最小 `/ready`; 9. 执行无效用户 Key 负向探针,确认不能访问上游; 10. 在隔离测试账户完成“签发 → 准入 → strict route → Usage → 金额账本”; 11. 对比无插件 OAuth 基线的 TTFT/吞吐; 12. gateway 才开始逐步放量。 任何一步失败都保持用户入口关闭。不能因为 CPA `/healthz` 为 200 就继续。 ## 10. 启动顺序与 readiness 插件内部: 1. 解析 lifecycle config/schema; 2. 验证冷配置和路径; 3. 打开 SQLite、migration、quick integrity; 4. 加载 HMAC keyring; 5. 加载 active PriceVersion; 6. 恢复 pending/Usage/settlement/outbox; 7. 加载账户、Credential、route 和统计 Snapshot; 8. 装配 callback runner/sidecar; 9. 原子发布 Runtime; 10. 返回 registration。 Readiness component: | Component | 失败影响 | | --- | --- | | runtime/schema/capabilities | 不注册/不开放流量 | | SQLite/migration/ledger | fail closed | | HMAC keyring | fail closed | | active pricing | fail closed | | Credential/account directory | fail closed | | statistics projection | 可降级,显示 lag | | quota refresh | 可降级,显示 stale | | UI resources | 不影响计费,但管理页不可用 | | backup/archive runner | 告警,不允许掩盖核心 readiness | 外部 gate 组合: - CPA process health; - Management plugin active/version/capabilities; - cpa-ext self readiness; - resource `/ready` 存在; - Home 关闭; - 负向认证探针; - gateway 自己确认 sentinel 未被分发。 ## 11. 监控与告警 最低指标: | 类别 | 指标 | | --- | --- | | Runtime | active/fused/reconfigure rejected/schema/topology | | RPC | 每 method 调用数、错误、panic、耗时、payload bytes | | Auth | success/reject/unknown/dependency failure、rate limit | | Access | terminate code、strict mismatch、scheduler fallback | | Billing | settlement、amount、late revision、unpriced、unmeasured、negative balance | | Persistence | transaction latency/error/busy、WAL/DB size、integrity、backup age | | Projection | event_seq、checkpoint、lag、rebuild status | | Upstream | account active/missing/priority shadow、quota age/error | | Performance | TTFT、total latency、chunk gap、alloc/GC、stream RPC bytes | | Gateway | open/drained、last readiness/negative probe | 必须告警: - plugin route/active 状态消失或 fuse; - readiness 核心 component 失败; - persistence gap、账本不一致; - active price 缺失/未知 GPT-5.6; - unpriced/unmeasured 超阈值; - keyring mismatch; - DB/WAL/磁盘逼近上限; - backup 超过目标 RPO; - projection lag; - priority 变化导致 binding 不可用; - stream 性能相对基线退化。 指标端点默认只在 Management/受信 sidecar 暴露,不把账户数、金额或错误详情放进公开 `/ready`。 ## 12. 日常维护 ### 12.1 每日 - 检查 readiness、gateway 和关键告警; - 检查 unpriced/unmeasured/persistence gap; - 检查 backup 成功和磁盘; - 检查 quota stale、missing Auth 和 strict mismatch; - 检查异常 Credential 消费和负余额。 ### 12.2 每周 - 运行账本余额 reconciliation; - 检查 projection checkpoint 与 archive eligibility; - 审阅价格候选,但不自动发布; - 审阅 audit、Key rotation/expiry 和管理员操作; - 抽样恢复 backup 到隔离目录。 ### 12.3 每月/发布前 - 完整离线 restore drill; - 容量/查询计划检查; - 依赖、CPA 和价格来源复审; - sentinel/Management/HMAC/key rotation 演练; - 安全故障注入和 24h soak(涉及 runtime/toolchain/SQLite 变化时)。 ## 13. 备份与恢复 ### 13.1 备份 - 使用 SQLite online backup API,不直接复制 WAL 模式 `.db`; - 备份包含 schema/version/content hash/created_at; - keyring 单独加密备份,记录非秘密 fingerprint/key IDs; - 保留 7~30 天并至少一份异地副本; - 备份目录不由 public API 任意下载; - 每次破坏性 migration 和版本升级前强制备份; - backup 成功必须通过打开、integrity 和基本账本检查。 ### 13.2 离线恢复 1. gateway 排空并停止 CPA/sidecar; 2. 保存损坏现场副本; 3. 选择匹配时间点的 SQLite + keyring; 4. 在隔离目录验证 checksum、schema、integrity、key fingerprint; 5. 替换明确目标文件,不操作宽泛目录; 6. 启动时运行 migration/recovery; 7. 从权威事实重建 projection; 8. 运行账本、Credential 和 PriceVersion reconciliation; 9. 完成全套 readiness/负向/计费探针; 10. 逐步恢复流量并记录事件审计。 缺失/不匹配 keyring 时不得自动生成新 key 后继续;保持 fail closed。 ## 14. 升级与回滚 ### 14.1 禁止热替换生产动态库 当前 CPA binary hot reload 会把旧 native 实例放入 retired 集合,直到宿主整体 shutdown 才真正 shutdown。旧 runtime/worker/SQLite handle 可能继续存在,因此生产升级固定为:排空 → 停 CPA → 替换/切换 version pin → 启动。 ### 14.2 升级流程 1. 阅读 release notes、CPA compatibility、migration 和 topology 变化; 2. staging 完成 test-plan 全门禁; 3. 备份 SQLite + keyring + config; 4. gateway drain; 5. 停止 CPA 并确认进程退出; 6. 安装新版本化 artifact、更新 version pin; 7. 启动、migration/recovery/readiness; 8. 执行负向认证、金额 golden、route、cancel/stream 和性能 smoke; 9. 小流量观察; 10. 标记升级完成并保留旧 artifact/backup。 ### 14.3 回滚 如果 migration 保持旧版兼容,可以停机切回旧 artifact。若 schema 已不兼容: - 停机; - 恢复升级前 SQLite + 对应 keyring; - 恢复旧 config/artifact; - 重新完成 readiness/探针; - 对升级窗口产生的真实上游消费人工 reconciliation,不能静默丢账。 价格回滚不需要二进制回滚,走 [pricing.md](pricing.md) 的“创建新发布版本”。 ## 15. 配置变更 - 使用 CPA Management API/YAML 变更前保存当前 revision; - 安全敏感变更必须双人复核和 reason; - 写入后检查 LKG/`reconfigure_rejected`; - capability shape 不因热配置错误消失; - 冷配置只在排空重启中修改; - 不用手改 SQLite 代替管理 API; - 不在 config 中放 Management/downstream/HMAC/OAuth secret 的副本; - 变更 Codex Auth priority 前预览受影响 binding。 ## 16. 常见故障 Runbook ### 16.1 CPA 正常但 `/ready` 404 立即撤流量 → 检查 Home、plugin global/instance enabled、文件名/版本 pin、架构、export、load/register 日志 → 不要临时删除 sentinel 或放开 native Key。 ### 16.2 Readiness 显示 DB/keyring/price unhealthy 撤流量 → 保留现场 → 检查权限、磁盘、key ID、migration、active price → 修复后运行 integrity/reconciliation → 不切 observe-only。 ### 16.3 用户请求突然变慢 先 A/B 比较同 CPA/OAuth/model/tier/transport:无插件、空 hook、collection-only、完整插件 → 检查 schema=3、每 chunk RPC 字节、HistoryChunks、response-before 重复 body、DB/锁和 GC → 超过 [dev.md](dev.md) 阈值即回滚/撤下相关 hook。 ### 16.4 strict binding 不可用 检查 CPA Auth active/disabled/unavailable、模型和 priority → 查看 scheduler candidates/bindable reason → `priority_shadowed` 时统一 priority 或调整产品绑定,不强行返回隐藏 Auth ID。 ### 16.5 quota stale 不影响已由 CPA candidate 证明可用的基础 strict 请求,但 UI 标 stale → 手动小批 refresh → 检查 provider allowlist、Auth ref、限流和 sidecar → 不把 stale 写成 0/无限。 ### 16.6 persistence gap/unmeasured 激增 撤流量或收紧并发 → 检查 callback、DB、response correlation 和 CPA 版本 → 保存上游证据 → 修复后追加 settlement/adjustment;禁止猜 Token 或覆盖旧账本。 ## 17. 容量与保留 发布前用真实形状数据验证: - 50/500/更多 Credential 与上游账户; - 百万级 Request/Execution/Usage/Ledger; - 30/90 天查询; - streaming 长 Prompt/多 chunk; - 并发 1/8/32 及计划上限; - WAL、backup、rebuild 和 archive; - 磁盘增长、峰值内存、CPU、p95/p99。 容量结论记录最后成功点和最小失败点,生产持续负载取安全折扣。可参考 usage-keeper `internal/benchmark/capacity-v1` 的 canonical dataset、clone、cgroup memory peak 和 p99 方法,但需要增加 native plugin/CPA/stream 维度。 ## 18. 验收标准 - 文档明确当前骨架与 production-ready 版本的差别; - artifact、CPA、ABI/schema、OS/arch/toolchain 可追溯; - 安装前 checksum/export/权限/version pin 均验证; - 生产 gateway、sentinel、exclusive 和负向探针缺一不可; - `/healthz` 不被误用为插件 readiness; - SQLite/keyring 成对备份和隔离恢复演练通过; - 升级一定排空并重启 CPA,不热替换; - migration 失败、keyring mismatch、price 缺失时保持 fail closed; - 关键指标/告警/日常检查和故障 runbook 可执行; - WSL/Linux 与 Windows artifact 不混用; - 生产配置不使用 `000`、observe-only、用户 native CPA Key 或未确认价格; - 发布流程引用并满足 [test-plan.md](test-plan.md) 全部门禁。