Files
cpa-plugin/docs/modules/operations.md
T

455 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 部署与运维模块
## 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
<cpa-root>/
├─ 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: "<strong-random-management-key>"
# 仅保留一个运维离线 sentinel;绝不分发给用户。
api-keys:
- "<strong-random-sentinel-key>"
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/<goos>/<goarch>/`。发布包不能包含生成 `.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) 全部门禁。