Why: U1 requires account ops to hit manifest data_root/DSN with OpenExisting and refuse missing flock or default ./data. Evidence: python3 -m unittest tests.test_svc_user_binding (T1–T5) + test_svc_identity Impact: svc <env> user … calls ENV_BIN with --open-existing; deploy/user fail closed without flock. Refs: docs/svc-deploy/u1-instance-account-binding.md; Backend OpenExisting (PR #171) Co-authored-by: Cursor <cursoragent@cursor.com>
4.4 KiB
4.4 KiB
U1 实例—账户绑定补齐(规格 v0.1)
状态:已启动实施(分支 feature/svc-deploy-u1-account-binding)。主记录:modules/documents/docs/proposals/2026-10-12-svc-unified-deploy-and-login-key.md §7 U1。
前置:恢复方案 recovery U1 已合入 main(Dev #415)——docs/recovery/u1-identity-manifest.md、scripts/svc_manifest.py、启动身份校验与 svc init。
本增量不重做 manifest 层;只补齐账户操作与安装入口对既有身份合同的消费缺口。
基线:任务卡 R0;实施中。真实 PG 绑定专项缺环境标 NOT_RUN。
1. 目标与非目标
目标: 同一实例的启停、升级、开户、签发/撤销登录 Key 始终命中同一存储与数据根;缺失、不符或锁不可用时拒绝;为后续 install / doctor 提供稳定的实例解析入口。
非目标:不实现二进制下载(U2)、不实现 doctor/env prepare 全量(U3)、不实现一次性 Key 语义(U4)、不改登录页(U5)。
2. 不变量(相对 recovery U1)
| ID | 不变量 | 相对 #415 |
|---|---|---|
| B1 | 账户 CLI 只接受「实例名 → manifest 解析出的 data_root / db / secrets 引用」 | 新增消费方 |
| B2 | 禁止回落默认 ./data 或未声明 DSN |
新增硬拒绝 |
| B3 | OpenExisting:目标不存在则失败;可选禁止自动 migrate | Backend 新增打开模式 |
| B4 | 无 flock / 锁获取失败 → 拒绝冲突操作(deploy/start/stop/user/*) |
收紧现状「直通」 |
| B5 | install / 兼容 --from 写入同一 manifest 身份字段,不另起配置根 |
入口骨架 |
3. 触点与文件
| 文件 | 变更 |
|---|---|
scripts/svc.sh |
user 子命令骨架:解析 ENV_NAME → 读 manifest/svc_manifest.py → 组装 --data/AGENT_SYSTEM_DSN/--open-existing 调 $ENV_BIN;锁检查收紧;install 可先做 dry-run 计划打印(完整 binary/source 属 U2) |
scripts/svc_manifest.py |
如需:导出「账户操作所需最小字段」查询(data_root、db mode/ref、auth);不改 schema=1 语义 |
modules/backend/cmd/agent-system/main.go |
user 子命令增加 --open-existing(或全局 store 打开标志);缺库/缺目录失败 |
modules/backend/internal/store/store.go |
OpenExistingSQLite / PG 等价:不 MkdirAll 创建新数据根;可选 MigrateMode=requireCompatible|forbid |
tests/test_svc_identity.py 或新 tests/test_svc_user_binding.py |
错误 data、缺 manifest、无 flock 拒绝;正确绑定命中 |
4. OpenExisting 合同
OpenExisting(path|dsn, opts):
if storage path/parent missing → error (do not create)
if SQLite file missing → error
open DB
if opts.migrate == forbid → read schema version only; mismatch → error
if opts.migrate == requireCompatible → existing migrate(); reject unknown newer schema (keep current migrate gate)
never create empty business DB as side effect of account recovery
CLI 默认:账户运维命令使用 OpenExisting + requireCompatible。
svc init / 首次服务启动仍走现有 Open(可创建)路径,与 recovery U1 一致。
5. 锁合同
- 复用
runtime/<name>/.start.lock。 flock不可用或超时:退出非 0,写 hist_event,不得执行 deploy/start/stop/user。- 账户操作与生命周期互斥(同一锁)。
6. 验收用例(U1)
| # | 场景 | 判据 |
|---|---|---|
| T1 | 实例有合法 manifest;svc <name> user list |
使用 manifest data_root/DSN;列表与该库一致 |
| T2 | 故意传错误 --data 或未绑定实例 |
拒绝;不创建目录;不写默认 ./data |
| T3 | 删除 data 根后再 user create | OpenExisting 失败;无新空库 |
| T4 | 隐藏 flock(PATH 去掉)后 deploy/user |
拒绝;旧服务若在跑则仍在 |
| T5 | recovery U1 既有 T1–T6 回归 | 全绿(不破坏 identity) |
真实 PG:绑定 dbref 与 marker 一致才允许 user 操作;缺环境标 NOT_RUN。
7. 完成门槛与依赖
- 完成条件见主提案 §7 U1 行。
- 依赖:main 已含 recovery U1(#415)。
- 被依赖:U4 CLI 签发、U5
svc user接线、U2install写身份。
8. 任务卡建议(待授权后 task-start)
- 分支名:
feature/svc-deploy-u1-account-binding - R0:与本提案合入后的
origin/main(或与 U4 兄弟共享批准时的同一 R0) - 交付模式:manual;实施需主提案阶段批准后再授权