依 autoflow-agent workspace v2 設計把 PRD / 設計 / 架構 / 交付類 共享文件從個人層 .autoflow/(ignored)搬到 docs/autoflow/(進 git), 讓團隊可共享產品與架構文件,個人層只留 progress / review / testing 等 per-branch 筆記。 - 02-prd/ 21 個檔(PRD、features、market-analysis 等) - 03-design/ 18 個檔(design-spec、wireframes、flows 等) - 04-architecture/ 31 個檔(TDD、design-doc、ADR×14、API 規格等) - 07-delivery/ 3 個檔(project-summary、phase-0.6-handover、stage-deployment-setup) 合計 73 檔。原檔已從 .autoflow/ 移除(migration 工具執行 git mv, 但因 .autoflow/ 在 .gitignore 中、git 將此操作視為新增、無 rename history)。
182 lines
7.0 KiB
Markdown
182 lines
7.0 KiB
Markdown
# Feature:Pairing 流程(P0,雛形必做)
|
||
|
||
> 父文件:[PRD.md](../PRD.md) | 對應 User Stories:US-04、US-05、US-06、US-18、US-19
|
||
|
||
---
|
||
|
||
## 概述
|
||
|
||
Pairing 是 visionA Cloud 獨有的流程(local-tool 沒有):把使用者筆電上的 local agent 連到 visionA Cloud 的 remote-proxy,讓雲端能代管該裝置。
|
||
|
||
這個流程替代了 POC 中「Token = SHA256(MAC)[:16]」的硬編碼方式,改為**雲端發 token、綁 user + device、有 expiry、可撤銷**的產品化版本。
|
||
|
||
---
|
||
|
||
## 使用者行為
|
||
|
||
### 正向流程(Happy Path)
|
||
|
||
1. 使用者在瀏覽器登入 visionA Cloud
|
||
2. 在「裝置」頁面點「配對新裝置」
|
||
3. 彈出對話框,顯示:
|
||
- 一組 Pairing Token(例如 `vAc_7f3c8e2a9b1d0f5e...`)
|
||
- QR code(方便手機掃或直接貼)
|
||
- 說明文字:「在你的 local agent 貼上這個 token」
|
||
- 倒數計時:「15 分鐘內要完成配對」
|
||
4. 使用者打開筆電上的 local-tool
|
||
5. (Phase 0 妥協)手動編輯 local-tool 的設定檔,填入:
|
||
- `VISIONA_RELAY_URL=wss://relay.visiona.cloud`
|
||
- `VISIONA_PAIRING_TOKEN=vAc_7f3c8e2a9b1d0f5e...`
|
||
- 重啟 local-tool
|
||
6. local-tool 的 tunnel client 啟動,連到 remote-proxy
|
||
7. remote-proxy 驗證 token → 找到對應 user → 記錄 device 已配對
|
||
8. 雲端前端透過 WebSocket 收到「裝置上線」事件 → 即時更新裝置列表
|
||
9. 使用者看到新裝置出現,完成配對
|
||
|
||
### 異常流程
|
||
|
||
**A. Token 過期**
|
||
- 超過 15 分鐘未完成配對 → local-tool 連線時 remote-proxy 回 403
|
||
- local-tool 顯示「Pairing Token 已過期,請重新取得」
|
||
- 使用者回雲端重新產生
|
||
|
||
**B. Token 已被使用**
|
||
- Token 預設為一次性。配對成功後不能再用
|
||
- 再次用同一 token → 回 409 Conflict
|
||
- 雲端 UI 提示「此 token 已使用」
|
||
|
||
**C. Token 無效**
|
||
- 亂打 token → 401 Unauthorized
|
||
|
||
**D. 網路中斷後重連**
|
||
- local-tool 已配對的裝置,tunnel 斷線後自動重連
|
||
- 重連時用的是 **Session Token(長期)**,不是 Pairing Token(一次性)
|
||
- Session Token 在首次配對成功後由 remote-proxy 發給 local-tool,local-tool 存到本機 config
|
||
|
||
---
|
||
|
||
## Token 設計
|
||
|
||
### Pairing Token(一次性、短期)
|
||
|
||
| 屬性 | 值 |
|
||
|------|---|
|
||
| 格式 | `vAc_` + 32 字元 hex(範例:`vAc_7f3c8e2a9b1d0f5e...`)|
|
||
| 產生者 | api-server |
|
||
| 儲存 | Phase 0:in-memory map;Phase 1:DB |
|
||
| TTL | 預設 15 分鐘(可調整)|
|
||
| 使用次數 | 1 次 |
|
||
| 綁定 | user_id(產生者)|
|
||
|
||
### Session Token(長期、可撤銷)
|
||
|
||
| 屬性 | 值 |
|
||
|------|---|
|
||
| 格式 | `vAs_` + 64 字元 hex |
|
||
| 產生者 | remote-proxy(首次 pairing 成功後發)|
|
||
| 儲存 | Phase 0:in-memory map;Phase 1:DB<br>local-tool 端:寫入 config 檔 |
|
||
| TTL | **90 天**(到期或使用者主動撤銷時失效)|
|
||
| 使用次數 | 無限(TTL 內每次重連共用)|
|
||
| 綁定 | user_id + device_id |
|
||
|
||
> **兩階段 TTL 設計**:
|
||
> - **Pairing Token 階段**:使用者在雲端產 token、貼到 local agent,15 分鐘內要完成首次連線,一次性使用
|
||
> - **Session Token 階段**:local agent 首次連上 remote-proxy 後,由 remote-proxy 發 Session Token 寫入 local config;之後 tunnel 斷線重連、跨 session 維持長連線都用這把 token,90 天內有效
|
||
>
|
||
> Phase 0 先做固定 90 天 TTL;Phase 1 加 rotation(到期前一段時間自動換新 token,避免使用者介入)。
|
||
|
||
### 為什麼分兩種 Token
|
||
|
||
- Pairing Token 一次性、短期 → 防止 token 洩漏的危害被放大(最壞情況只影響 15 分鐘)
|
||
- Session Token 長期 → 避免每次重連都要使用者介入,但 90 天 TTL 限制洩漏風險
|
||
- 兩階段模式類似 OAuth 的 authorization code + refresh token
|
||
|
||
---
|
||
|
||
## 介面契約(API 層面)
|
||
|
||
完整 API spec 由 Architect Agent 寫入 TDD,本文件只列出關鍵端點:
|
||
|
||
### POST `/api/pairing/generate`
|
||
|
||
**Request** (需登入):
|
||
```json
|
||
{}
|
||
```
|
||
|
||
**Response 200**:
|
||
```json
|
||
{
|
||
"pairing_token": "vAc_7f3c8e2a9b1d0f5e...",
|
||
"expires_at": "2026-04-21T10:30:00Z",
|
||
"relay_url": "wss://relay.visiona.cloud/tunnel/connect"
|
||
}
|
||
```
|
||
|
||
### WebSocket `wss://relay.visiona.cloud/tunnel/connect?token={token}`
|
||
|
||
**流程**:
|
||
1. local-tool 發起 WebSocket 連線,帶 token(可能是 Pairing Token 或 Session Token)
|
||
2. remote-proxy 驗證:
|
||
- 若是 Pairing Token:檢查未過期、未使用 → 發 Session Token(透過 WebSocket 首個 message 傳給 local-tool)→ 標記 Pairing Token 已使用
|
||
- 若是 Session Token:檢查未撤銷 → 接受連線
|
||
3. 建立 yamux session(沿用 POC 的設計)
|
||
4. 後續所有 HTTP/WebSocket 請求透過 yamux stream 轉發到 local agent
|
||
|
||
### POST `/api/devices/{device_id}/revoke` (Phase 1)
|
||
|
||
**功能**:撤銷某 Session Token,強制該裝置斷線。
|
||
|
||
---
|
||
|
||
## 驗收條件(Phase 0)
|
||
|
||
- [ ] 登入後,裝置頁面的「配對新裝置」按鈕可點
|
||
- [ ] 點擊後彈出對話框,顯示 Pairing Token(明文 + QR code)
|
||
- [ ] Pairing Token 格式符合規範(`vAc_` + 32 hex)
|
||
- [ ] Pairing Token TTL = 15 分鐘,過期後 local-tool 連不上
|
||
- [ ] local-tool 用正確 token 連線 → 成功建立 tunnel
|
||
- [ ] local-tool 用錯誤 / 過期 / 已使用的 token 連線 → 收到明確錯誤
|
||
- [ ] 配對成功後,雲端裝置列表 5 秒內顯示新裝置
|
||
- [ ] local-tool 收到 Session Token 並寫入本機 config
|
||
- [ ] Session Token 格式符合規範(`vAs_` + 64 hex)
|
||
- [ ] Session Token TTL = 90 天,到期後 local-tool 連線被拒,需重新 pairing
|
||
- [ ] tunnel 斷線後,local-tool 用 Session Token 自動重連(TTL 內)
|
||
- [ ] 全流程繁體中文 UI
|
||
- [ ] TODO:Phase 0 不實作「撤銷」功能,但要在 UI 留按鈕(disabled)
|
||
|
||
---
|
||
|
||
## Phase 0 的 TODO
|
||
|
||
- **TODO 1**:local-tool 端的整合 — Phase 0 先讓使用者手動編輯 config 檔;Phase 1 要在 local-tool 內建 UI「配對到 visionA Cloud」
|
||
- **TODO 2**:QR code 產生 — 可用前端套件(例如 `qrcode.react`),但優先級低,可先只顯示明文 token
|
||
- **TODO 3**:Token 撤銷 — 介面留著,實作留到 Phase 1
|
||
- **TODO 4**:多 local agent 同時配對 — Phase 0 每個 user 最多 1 台 agent 同時連線,Phase 1 支援多台
|
||
- **TODO 5**:Session Token rotation — Phase 0 採固定 90 天 TTL(到期需重新 pairing);Phase 1 做到期前自動 rotation,無需使用者介入
|
||
- **TODO 6**:Pairing 流程的使用者引導(tutorial) — Phase 0 不做,Phase 1 加 onboarding
|
||
|
||
---
|
||
|
||
## 安全考量
|
||
|
||
**Phase 0 最小要求**:
|
||
|
||
- Token 必須透過 TLS(`wss://`)傳輸,不能走明文
|
||
- Token 儲存在記憶體不要 log 出來
|
||
- Pairing Token 產生後不要再可讀取(只有產生當下顯示給使用者)
|
||
|
||
**Phase 1 要加的**:
|
||
|
||
- Token 在 DB 以 hash 儲存(不存明文)
|
||
- Rate limiting(防暴力破解)
|
||
- IP 地址記錄(配對時、連線時)
|
||
- 異常偵測(同一 token 從不同 IP 嘗試連線)
|
||
|
||
---
|
||
|
||
## 連結
|
||
|
||
- 回:[PRD 索引](../PRD.md)
|
||
- 相關:[介面契約](../interface-contracts.md)、[非功能性需求](../nonfunctional.md)
|