feat(relay): Phase1 foundation — P3 Postgres store + async capability verifier + compose
RELAY-PHASE1 Wave A (2/3) + E1 infra: - A1: createPgStores() Postgres adapter for all 9 P3 store ports + runMigrations(); writable-CTE atomic INV8 status swaps; 0002_routes.sql. 23/23 pg tests, 96.72% cov. - A3: real capability verifier delegating to relay-auth verifyCapabilityToken; sync->async seam across authz/provision/main. Full CP suite 15 files/101 pass, tsc clean. - E1: deploy/docker-compose.yml (Postgres16+Redis7, loopback-only) + .env.example. - docs: PLAN_RELAY_PHASE1.md file-level execution spec; PROGRESS_LOG RELAY-PHASE1 section.
This commit is contained in:
@@ -24,6 +24,25 @@
|
||||
|
||||
> 新会话读到的第一块。保持准确,只描述"此刻"。
|
||||
|
||||
### 🚧 RELAY-PHASE1 — 把原生 rendezvous-relay 部署到单台 VPS(8.138.1.192,阿里云;2026-07-06)
|
||||
- **目标**: DEPLOY_RELAY §4 的 **Phase 1「1b 全量持久化 staging」**。VPS 上 Docker 自建 Postgres+Redis;已备案域名走 443 + Let's Encrypt;agent 跑在操作者本机向外拨号。完整文件级方案见 [PLAN_RELAY_PHASE1.md](./PLAN_RELAY_PHASE1.md)。
|
||||
- **进度**: 地基 Wave A 已 2/3。**A1 (PG store 适配器+迁移) DONE**、**A3 (F2 异步能力验证器) DONE**、E1 (docker-compose+env 模板) DONE。全套 `npx vitest run`(control-plane)= **15 files / 101 tests pass**(A1 的 pg 测试 + A3 的 verifier 测试并存无冲突)。
|
||||
- **下一步**: **A2** — P3 server 入口(`loadEnv→createPgStores→runMigrations→ioredis→createRedisRevocationBus→buildControlPlane→listen`)+ Redis 撤销总线接线(`main.ts:93` 现为 inert)。然后 Wave B(relay 数据面读同一持久 store)。
|
||||
- **未做/推迟到 Phase 2**: 真 KMS(dev signer 暂用)、F6 replay(`loadReplay` fail-closed)、WebAuthn step-up(staging 用 `NO_STEPUP_POLICY`)、通配多租户。
|
||||
|
||||
#### [x] A1 — Postgres store 适配器(P3)+ 迁移运行器(2026-07-06)
|
||||
- **交付**: `createPgStores(query): Stores` 把 9 个 store 端口映射为参数化 SQL(零字符串拼接);`runMigrations(query)` 按文件名顺序跑 `db/migrations/*.sql`(幂等)。
|
||||
- **文件(全在 Owns 内)**: `control-plane/src/store/pg.ts`、`src/db/migrate.ts`、`db/migrations/0002_routes.sql`(新增 — 0001 无 routes 表)、`test/store/pg.test.ts`。
|
||||
- **语义对齐 memory.ts**: INV8 accounts/hosts `swapStatus` 用单条 **writable CTE**(UPDATE 指针 + INSERT 版本行)原子完成,无显式事务;重复插入捕获 pg `23505`→`throw duplicate`;`casRedeem` 单赢家 `UPDATE...WHERE redeemed_at IS NULL RETURNING`;RouteStore 存 `expires_at`,`expires_at<=now()` 视为不存在(fail-closed INV7)+ 惰性 DELETE。
|
||||
- **验证(实测)**: `npx vitest run test/store/pg.test.ts` → **23/23 通过**(自起 `postgres:16` 一次性容器 :5433,afterAll 自动 `docker rm -f`);`pg.ts` 覆盖 **96.72% stmts / 100% funcs**。
|
||||
- **偏差**: ① `0002_routes.sql` 新增;② SubdomainStore 无独立预留表,按 `hosts.subdomain UNIQUE` 归属裁决;③ migrate 按 `;` 切分逐条执行(参数化路径拒绝多语句)。**阻塞**: 无。
|
||||
|
||||
#### [x] A3 — 真能力验证器(relay-auth verifyCapabilityToken)+ 同步→异步 seam(2026-07-06)
|
||||
- **交付**: 抛异常的 `refuseAllVerifier` stub 换成委托 P5 异步 `verifyCapabilityToken` 的真验证器;`await` 贯通整个 authorizer 路径。默认仍 fail-closed(异步化后 reject→401)。
|
||||
- **文件**: `src/api/authz.ts`(`CapabilityVerifier.verify`→`Promise`)、`src/api/provision.ts`(5 个路由 call site 改 `await principal(req)`)、`src/boot/verifier.ts`(新;`configureCapabilityVerifyKey` 把 `env.capabilitySignPubkey` 32 字节导入 WebCrypto verify key 后调 relay-auth `configureVerifyKey`,桥接 `CAPABILITY_SIGN_PUBKEY_B64` vs `RELAY_AUTH_VERIFY_PUBKEY` 命名差)、`src/main.ts`、`package.json`(加 `relay-auth: file:../relay-auth`)、`test/verifier.test.ts`(新)+ 更新 api 测试 stub 为异步。
|
||||
- **验证(实测)**: `npx tsc --noEmit` 全项目干净;`npx vitest run` **15 files / 101 tests pass**。
|
||||
- **偏差**: 手建 `control-plane/node_modules/relay-auth` 符号链接以配合新 file: 依赖(`npm install` 会重建)。**阻塞**: 无。
|
||||
|
||||
### ✅ 修复:项目面板把父文件夹当项目 & 会话点亮所有祖先项目(2026-07-06,分支 `feat/ios-client`)
|
||||
- **现象(用户截图)**: 只在 `web-terminal` 跑了一个会话,但 "Active now" 同时显示 `web-terminal`/`Documents`/`yiukai` 三张卡,且父文件夹本身被列为项目。
|
||||
- **根因(`src/http/projects.ts`)**: ① `belongsTo` 纯前缀匹配 → 会话按 cwd 归属到**每一个**祖先项目;② 历史合并(`mergeHistory`)把曾经跑过会话的 cwd(如 `~`、`~/Documents`)原样列为项目。
|
||||
|
||||
Reference in New Issue
Block a user