Merge pull request 'Deliver U7 account transactions on the current combination' (#486) from feature/u7-account-tx-audit into main
ci / svc-realcombo (push) Successful in 2m13s
ci / test (push) Successful in 5m3s
ci / test-pg (push) Successful in 7m52s

This commit was merged in pull request #486.
This commit is contained in:
2026-10-14 04:36:00 +00:00
4 changed files with 396 additions and 1 deletions
File diff suppressed because one or more lines are too long
+63
View File
@@ -0,0 +1,63 @@
# U7 账户事务、审计与只读打开
本规格落实账户方案 r2 的 U7(R13–R15、KTD11–14)。HTTP 响应形状不变。`scripts/svc.sh` 与登录页不在本增量。
## 1. 事务
存储层提供一个事务助手。业务函数在该事务内完成写入,成功则同事务追加审计,失败则整笔回滚。
严格建号使用 INSERT。owner 已存在时返回 `ErrConflict`,不读取后再 UPDATE,因此并发不能覆盖先提交的账户。
以下每条是一次提交:
- 开户:用户行、激活 Key(无初始密码时)、成功审计。
- `applyPasswordChange`:写密码哈希、更新 must_change、作废该 owner 未使用的登录 Key、按路径处理会话、成功审计。覆盖四条调用:主动改密、强制设密、管理员或 CLI 重置、恢复设密。
- `--revoke-all-credentials`:吊销长期 API Key、删除会话、作废未使用登录 Key、签发新 Key、成功审计,一次提交。
失败路径在回滚之后另记一条 `outcome=rejected` 的审计。该条不包含密码、哈希或明文 Key。
## 2. bootstrap
`bootstrap-admin` 在事务内复核,不能只看用户表在事务外为空。
- SQLite:`BEGIN IMMEDIATE`。
- PostgreSQL:`SELECT ... FROM agent_system_identity FOR UPDATE`。没有身份行则拒绝,本命令不补建身份。
复核条件:`users` 为空,且下列业务表都为空:`projects`、`conversations`、`messages`、`runs`、`api_keys`、`sessions`、`login_keys`。任一表有行则拒绝。通过后,管理员、激活 Key 与成功审计同事务提交。
## 3. 审计
迁移在 `audit_events` 增加列:`entry`、`instance_id`、`operation_id`、`outcome`。文本,默认空串。既有行保持可读。版本号取本分支当前最大版本的下一个整数。若 U8 已占用该号,task-sync 时顺延,不改已发布的迁移正文。
成功记录与变更同事务。`actor`:HTTP 取会话 owner;CLI 取操作系统 uid 的十进制字符串。`login_keys.instance_id` 在签发时写入调用方给出的实例标识,缺省空串。
## 4. 账户运维打开
`user`、`apikey`、`identity verify` 只打开既有库:文件或库不存在则失败,不创建父目录,不执行迁移。`user`、`apikey` 的 schema 版本与本二进制不一致时拒绝,错误文本提示使用 `migrate` 或先启动一次服务。`identity verify` 只校验既有实例身份并报告 schema 版本,不以版本不一致阻断启动前身份核验。
设置了 `AGENT_SYSTEM_INSTANCE_ID` 或 `--instance-id` 时,与库内身份行严格比对,不一致则拒绝。未设置时不补建身份。
## 5. 凭据输入输出
- DSN:`AGENT_SYSTEM_DSN`,或模式 0600 的 `--dsn-file`。
- 密码:`--password-stdin`。
- 明文 Key 或生成的密码写入 `--out-file`。创建使用独占创建(已存在则失败),权限 0600。
- stderr 不输出凭据明文;可打印 operation id、凭据 id,以及必要的输出路径、弃用警告和错误提示。
`--dsn` 与 `--password` 仍可用,并在 stderr 打印弃用警告,以兼容现有 `scripts/svc.sh` 与 `scripts/e2e.sh`。移除留给账户 U5。
`agent-system capabilities --json` 打印本二进制能力,不打开数据库,不创建目录。
## 6. HTTP
`internal/server/accounts.go` 的建号、改密、重置走同一事务助手和审计。响应 JSON 字段保持现状。
## 7. 测试
`internal/store/account_lifecycle_test.go` 固定在 SQLite 上运行;`account_security_integration_test.go` 使用 `openTestStore` 在 SQLite 和设有 `AGENT_PG_TEST_DSN` 的 PostgreSQL 上分别运行。`account_serialization_pg_test.go` 与 `account_two_process_pg_test.go` 覆盖真实 PostgreSQL 交叠和跨进程入场;未配置 DSN 时 PostgreSQL 专项 SKIP,不能记为已验证。Dev CI 的 `make test-backend-pg` 是父仓 PostgreSQL 组合的 GATE。
- T7-1 / T7-4:两个进程同时用同一 owner 开户,只有一个成功。并发 bootstrap,以及 bootstrap 与普通开户竞争,同样只有一个成功。
- T7-2:在提交中途注入失败,读到的只能是完整旧态或完整新态。
- T7-3:改密、重置、恢复设密之后,旧的未使用登录 Key 不能再兑换。长期 API Key 仅在 `--revoke-all-credentials` 时撤销。
- T7-5:关闭再打开后原密码仍能校验。真实进程收到 SIGTERM 或 SIGKILL 后同样能校验。两个版本的真实升级记 NOT_RUN。
- 账户 CLI 对既有库不执行迁移;缺文件时不建目录。既有登录 Key 与认证测试保持通过。
+5
View File
@@ -550,6 +550,11 @@ scripts/svc.sh help <主题> # 主题详解:model deploy lifecycle accounts db
# accounts:实例绑定账户 CLI(create/list/reset-password/issue-login-key/revoke-login-key/bootstrap-admin)
```
`agent-system` 的账户命令仍接受 `--dsn` 和 `--password`,并在 stderr 打印弃用警告。
新调用使用 `AGENT_SYSTEM_DSN` 或模式 0600 的 `--dsn-file`,密码使用 `--password-stdin`,
明文写入独占创建的 `--out-file`。`agent-system capabilities --json` 不打开数据库。
`scripts/svc.sh` 仍传 `--dsn` / `--password`,切换留给账户 U5。
## 7. 命令参考:数据库
### 7.1 db create(创建实例)