jim800121chen fb7da5d180 chore(autoflow): migrate .autoflow/ 共享層文件至 docs/autoflow/
依 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)。
2026-05-04 16:55:55 +08:00

25 KiB
Raw Permalink Blame History

Design Doc — visionA Cloud

Metadata

  • 作者Architect Agent
  • 狀態Draft待三方交叉審閱
  • 最後更新2026-04-21
  • 範圍visionA-frontend + visionA-backend 的雲端版架構定位為「POC 升格為產品」的雛形骨架
  • 相關文件
    • 健檢:.autoflow/00-onboarding/health-check.md
    • PRD.autoflow/02-prd/PRD.mdPM 主導,平行產出中)
    • 設計規格:.autoflow/03-design/Design 主導,平行產出中)
    • TDD.autoflow/04-architecture/TDD.md(本文檔的姊妹檔,實作細節)
    • ADRs.autoflow/04-architecture/adr/adr-*.md

1. 系統總覽High-Level Overview

visionA Cloud 把「使用者的 Kneron 裝置」透過雲端反向代理提供給瀏覽器操作。核心流向:

┌────────────────────────────────────────────────────────────────────────┐
│                                使用者瀏覽器                              │
│                           visionA-frontend (Next.js)                    │
└─────────────────┬────────────────────────────┬────────────────────────┘
                  │ HTTPS REST                  │ WSS (訂閱)
                  │                             │
┌─────────────────▼────────────────┐  ┌─────────▼──────────────────────┐
│  visionA-backend / api-server    │  │  visionA-backend / api-server  │
│  (無狀態, 水平擴展)               │  │  (WebSocket 訂閱, 同 binary)    │
└─────────────────┬────────────────┘  └─────────┬──────────────────────┘
                  │ Session 查詢in-mem 同進程 / Redis 跨進程)          │
                  ▼                             ▼
┌────────────────────────────────────────────────────────────────────────┐
│                     共享 Session Storerouting table                 │
│           雛形in-memory單進程    Phase 1Redis                 │
└─────────────────────────────┬──────────────────────────────────────────┘
                              │ 查到該 token 的 tunnel 在哪個 proxy 節點
                              ▼
┌────────────────────────────────────────────────────────────────────────┐
│              visionA-backend / remote-proxy  (有狀態)                   │
│       持有 yamux.Session map[token]→Session                             │
└────────────────────────────┬───────────────────────────────────────────┘
                             │ WebSocket + yamux長連線
                             ▼
┌────────────────────────────────────────────────────────────────────────┐
│                        使用者電腦 local agent                            │
│     (local-tool 或 tunnel-only client連 remote-proxy 出站)            │
│                        本機 HTTP server :3721                           │
└────────────────────────────┬───────────────────────────────────────────┘
                             │ USB
                             ▼
                     ┌──────────────────┐
                     │  Kneron 裝置     │
                     │  KL520 / KL720   │
                     └──────────────────┘

外部依賴(均透過介面,雛形 stub
  ├─ kneron_model_converter (`/api/converter/*` stub, TODO)
  ├─ S3-compatible 物件儲存 (LocalFSStore for prototype)
  └─ PostgreSQL + Auth provider (in-memory for prototype, ADR-005)

關鍵概念

  • Session key = pairing token,一個 token 對應一個 yamux tunnel。
  • API Server 把收到的請求,查 session store 找到對應 token 的 tunnel轉發到該 tunnel 上的 remote-proxy 節點,由 proxy 打 yamux stream 送進 local agent拿到 response 再回傳瀏覽器。
  • WebSocket 訂閱同理api-server 升級 WS 後,在 session store 裡找到 tunnel把 WS 幀透過 yamux stream 送給 local agent 的 /ws/*

1.9 Non-Goals雛形明確不做的事2026-04-22 M-8

以下項目在 Phase 0 雛形階段刻意不支援,列出以避免誤解:

# Non-Goal 原因 對應 Phase
N1 多 instance 部署 — 雛形僅支援單一 api-server instance + 單一 remote-proxy instance 無共享 state 機制(無 Redis、無 DB session metadata開兩個 api-server 會因為 session lookup 散到不同節點而壞掉;開兩個 remote-proxy 會讓 tunnel 註冊到 A 節點、但 api-server 的 ProxyClientStore 配置只連 B查無 session Phase 1
N2 真實使用者註冊 / 登入StaticAuthProvider 無論帳密都回 demo-user Auth 不是 POC → 產品驗證的核心風險;整合 Clerk / 自建 OIDC 需 2-4 週,放 Phase 1 Phase 1
N3 真實 DB 持久化 — 所有 repository 走 in-memory map ADR-005 明定Phase 1 實作 Postgres*Repository Phase 1
N4 真實 Converter 整合/api/converter/* 走 Stub Converter 團隊 API 規格未定;見 api/api-converter-contract.md Phase 1
N5 Pairing Token 兩階段升級 — 雛形 Pairing 與 Session 合併為單一 token 單 token 比對流程足以驗證 tunnel兩階段 DB transaction 需 Postgres Phase 1必須在公開 release 前完成)
N6 HTTPS / WSS — 雛形走明文 HTTP / WS TLS termination 由 Phase 1 LB / reverse proxy 處理 Phase 1
N7 Rate limiting / Audit log — 雛形無 雛形只跑 dev / 內測 Phase 1
N8 Observabilitymetrics / trace / alert — 雛形只有 stdout log 雛形不追求 SLO Phase 1
N9 Local agent 自己的 binaryvisionA/local-agent/ — 雛形用 POC edge-ai-server 當 tunnel client 暫代 讓雛形專注在 backend 協定正確性Phase 1 從 local-tool 複製起步 Phase 1
N10 cmd/dev-all-in-one 單進程合併模式 會讓部署拓撲與 Production 不一致,反而掩蓋 bug 永不做

重要後果

  • 開第二個 api-server(例如為了測試無狀態性)會因 ProxyClientStore 只配一個 VISIONA_PROXY_INTERNAL_URL,導致兩個 api-server 都指向同一個 remote-proxy可以運作(真實多 instance 場景)。但開兩個 remote-proxy 會壞 — tunnel 連上的是 A但 api-server 可能查到 B視 LB 分配B 沒有 session 就回 404。
  • 雛形的部署指引明確:恰好 1 個 api-server + 恰好 1 個 remote-proxy
  • Phase 1 解多 remote-proxy 節點時會補 session metadata 共享機制(見 tunnel.md §5.4 + 待產出的新 ADR

2. 系統邊界與組件

2.1 visionA-frontend客戶端

屬性
技術 Next.js 16 App Router + React 19 + TypeScript + Tailwind 4 + Radix + Zustand 5 + Lucide + Recharts + driver.js
部署 靜態打包 + CDN雛形用 Next.js 本地 dev serverPhase 1 上 Vercel / Cloudflare Pages / S3+CloudFront
狀態 無(純前端,所有資料 via API
Auth JWT / session cookie雛形 stubPhase 1 真實)
連線對象 只連 visionA-backend/api-server不直連 remote-proxy 或 local agent

從 local-tool 搬來的內容所有頁面、components、stores、i18n、UI styling改 API base URL移除 localhost hardcode。

新增

  • /login/register雛形骨架Auth stub
  • /account(雛形骨架)
  • /devices/pair(輸入 / 產生 pairing token
  • /clusters(從 POC 搬)

2.2 visionA-backend / api-servercmd/api-server

屬性
Binary cmd/api-server/main.go
狀態 無狀態(所有狀態在 DB / session store / 物件儲存)
水平擴展 多實例 + 前置 L7 load balancer
對外 HTTP :3001雛形預設正式上 443
職責 REST API、WebSocket 升級、Auth、權限、把請求 proxy 到 remote-proxy、Storage presigned URL、Converter 呼叫

2.3 visionA-backend / remote-proxycmd/remote-proxy

屬性
Binary cmd/remote-proxy/main.go
狀態 有狀態(持有 yamux.Session
水平擴展 可多實例session 路由交給 session store
對外local agent WS :3800POC 預設)
對內api-server HTTP :3801內部 API不對外
職責 接受 local agent 的 tunnel 連線、維護 yamux session、轉發 api-server 送來的請求

2.4 共享狀態Session Store2026-04-22 Q1 裁決 C + ADR-006雛形即雙 binary無 Redis

雛形實作 Phase 1 實作
Session state 完全由 remote-proxy 持有in-memoryapi-server 無狀態,透過 internal HTTP 向 remote-proxy 查詢 多 remote-proxy 節點間的 metadata 共享機制待 Phase 1 評估Redis / gossip / sticky LB 等 — 不預設採用 Redis

雛形設計

  • cmd/remote-proxy唯一持有 *yamux.Session 的 processInMemoryStore 儲存
  • cmd/api-server:無狀態;internal/session 載入 ProxyClientStoreinternal HTTP client
  • 兩 binary 透過 internal/forward/*internal/session/* endpoints 溝通(詳見 api/api-internal.md

Non-Goal刻意不做

  • 不做 cmd/dev-all-in-one 單進程合併模式 — 會讓部署拓撲與 Production 不一致,反而讓 bug 在雛形階段無法暴露
  • 不引入 Redis — POC 從未用過(見 ADR-006

詳細見 TDD §2 與 tunnel.md §5。

2.5 local agent使用者電腦

三種形態:

  1. local-tool(桌面版 Wails 應用):原本離線用,加一個 config 選項「啟用雲端模式」後,額外啟動 tunnel client 連 remote-proxy。local-tool 本身不動;雲端模式是新增 opt-in 模組,不影響離線使用。
  2. tunnel-only client未來專為「headless / server 使用者」提供的輕量 Go binary只跑 tunnel client + 最小 HTTP server不含 Wails / UI。雛形不做。
  3. 既有 edge-ai-platformPOC 的 tunnel client 可直接繼續用來做技術驗證。

2.6 外部依賴

依賴 雛形 Phase 1
kneron_model_converter Stub/api/converter/* 回假資料 真實打 converter API
物件儲存 LocalFSStore(本地檔案) S3StoreAWS S3 / Cloudflare R2 / MinIO
資料庫 in-memory repositories PostgreSQL
Auth StaticAuthService(永遠 demo-user 真實 Auth
Observability stdout log Prometheus + Loki + OpenTelemetry

3. 12-Factor 合規策略

# Factor 雛形做法 Phase 1 做法
1 Codebase Monorepo (visionA/visionA-frontend + visionA/visionA-backend)
2 Dependencies go.mod + package.json 明確宣告;無系統隱式依賴
3 Config 全走 env varsVISIONA_* prefixinternal/config 讀取 同 + 密鑰用 Secrets Manager
4 Backing services Storage / DB / Auth 全走 interface 同(實作 swap
5 Build / Release / Run ⚠️ 雛形只有 make build + docker-compose BuildCI 產出 imageReleasetag + manifestRunK8s / ECS deploy
6 Processes api-server 無狀態state 全走 Store interface
7 Port binding api-server :3001remote-proxy 對 agent :3800,對 api :3801 port 由 env 指定)
8 Concurrency 單進程多 goroutine跨進程靠 SessionStore 多實例 + auto-scale
9 Disposability ⚠️ 雛形 SIGTERM graceful shutdowntunnel 斷線有重連 同 + drain period
10 Dev/Prod Parity Docker 一致化
11 Logs ⚠️ 雛形 stdout已有 broadcaster WebSocket 看 log 結構化 JSON log + 集中收集
12 Admin processes ⚠️ 雛形手動 後台 CLI / admin API

雛形妥協log 未結構化、缺 metrics、缺 trace。記錄為 TODO不影響功能驗證。


4. 水平擴展策略

4.1 api-server無狀態

  • 多實例,前置 L7 load balancerALB / nginx / Cloud Run 自動)
  • Sticky session 不需要(無狀態)
  • 擴展訊號CPU > 60% 或 p95 延遲 > 300ms

4.2 remote-proxy有狀態

remote-proxy 的狀態是 map[token]*yamux.Session。擴展的挑戰:

  • Local agent 連 proxy 時,會連到某一個具體節點(由 LB 分派ALB + IP hash 或 DNS round-robin
  • 後續 api-server 要送請求到該 token 的 tunnel 時,需要知道這條 tunnel 在哪個 proxy 節點

解決方案Session Store 記錄 routing table

Session Store 儲存Redis Hash:
  session:{token} = {
    proxy_node_id: "proxy-2",
    proxy_internal_url: "http://proxy-2.internal:3801",
    connected_at: "...",
    last_seen: "..."
  }

api-server 送請求的流程:

1. 收到 API 請求GET /api/devices + X-Pairing-Token: xxx
2. 查 Session Storetoken → proxy_internal_url
3. 呼叫該 proxy 的 internal endpointPOST http://proxy-2.internal:3801/forward
   - body = 原請求method, path, headers, body
4. proxy-2 收到後,從自己 in-memory map 取 yamux.Session
5. 開 stream傳 HTTP request
6. 等 response → 回給 api-server → 回給瀏覽器

4.3 雛形簡化(單節點)

雛形期 api-server 與 remote-proxy 同進程SessionStore 就是本機 map跳過跨節點轉發

1. 查 SessionStorelocal map取 yamux.Session
2. 直接開 stream 送請求

Phase 1 時把 SessionStore 換 Redis、引入「proxy internal HTTP API」不改 API handler 程式碼。

4.4 擴展邊界

資源 估算
remote-proxy 單節點 tunnel 數上限 ~10K受檔案描述符 / 記憶體限制,每個 tunnel ~100KB
api-server 單節點 QPS ~5KGin + Go 典型值)
Session StoreRedis單節點 QPS ~100K

雛形單節點即可撐 demo / 早期內測。Phase 1 再引入多節點。


5. 資料流

5.1 前端呼叫 REST API最常見情境

[瀏覽器] GET /api/devices
    ↓ HTTPS + Cookie/JWT
[api-server] authMiddleware 驗 user
    ↓ 從 user 找對應 pairing token雛形 env 寫死Phase 1 查 DB
[api-server] 查 SessionStore(token) 取 proxy location
    ↓
   (單節點雛形:直接拿到 yamux.Session
   (多節點:轉發 http://proxy-X.internal:3801/forward
    ↓
[remote-proxy] 從 session open yamux stream
    ↓ 把 HTTP request 寫進 stream沿用 POC 方法)
[local agent] tunnel client accept stream
    ↓ 轉發到本機 127.0.0.1:3721
[local HTTP server] 處理 /api/devices → 回 response
    ↓ 經 yamux stream 回到 remote-proxy
    ↓ remote-proxy 回 api-server
    ↓ api-server 回瀏覽器
[瀏覽器] 得到裝置列表

5.2 前端開 WebSocket 訂閱(例:裝置事件串流)

[瀏覽器] WS /ws/devices/events (connect)
    ↓
[api-server] authMiddleware → 升級 WS
    ↓ 查 SessionStore → 取 yamux session
    ↓ 開 stream把 HTTP upgrade request 寫進 stream沿用 POC proxyWebSocket 邏輯)
[local agent] handleStream 偵測 upgrade → 連本機 raw TCP → 雙向 copy
[local HTTP server] /ws/devices/events 升級 → 開始推事件
    ↓ 事件 frame → yamux stream → api-server → 瀏覽器

POC 已完整實作此流程visionA 直接沿用。

5.3 模型上傳(不走 tunnel

[瀏覽器] 點「上傳模型」
    ↓
[api-server] POST /api/models/upload-url
    ↓ 呼叫 Storage.PresignedPutURL → 回給瀏覽器
[瀏覽器] 直接 PUT 檔案到 S3 / LocalFS不走 tunnel省 tunnel 頻寬)
    ↓ 上傳完成後
[瀏覽器] POST /api/models/register (告知 api-server 上傳完成 + metadata
    ↓
[api-server] 寫入 model repository

為何不走 tunnel

  • 模型檔可達百 MB走 tunnel = 占用使用者本地頻寬兩次(上傳 → 雲端 → 下載)
  • S3 presigned URL 讓瀏覽器直連scalable
  • 需要時 api-server 再叫 local agent「下載這個 model 的 URL」由 local agent 用自己網路下載

5.4 轉檔呼叫Phase 1

[瀏覽器] POST /api/converter/convert {source_url, target_chip: "kl520"}
    ↓
[api-server] 驗 user → 呼叫 kneron_model_converter API
    ↓ (非同步)取得 job_id
[api-server] 回 job_id
    ↓ 輪詢 /api/converter/jobs/{id} 或 WS 訂閱進度

雛形 api-server 提供端點,但回 stubjob_id 假的,狀態永遠 queued)。


6. 安全架構

6.1 通訊加密

  • 瀏覽器 ↔ api-serverHTTPSPhase 1雛形開發用 HTTP
  • api-server ↔ remote-proxy 內部:雛形同進程無需加密;多節點 Phase 1 考慮 mTLS 或 VPC-only
  • local agent ↔ remote-proxyWSSPhase 1雛形 WS。TLS 由前端反代ALB / nginx終止

6.2 認證 / 授權

物件 雛形 Phase 1
使用者登入 StaticAuthService 永遠過 OIDC / SAML / 自建
前端 call API 無 token 也接受stub JWT / session cookie
local agent 連 proxy VISIONA_PAIRING_TOKEN env 寫死 DB-backed Pairing Token見 ADR-003
API handler 授權 一律通過 RBACuser 只能看自己的 device

6.3 CORS / CSRF

  • CORSapi-server 雛形設 Access-Control-Allow-Origin: *devPhase 1 白名單
  • CSRF:用 JWT in header非 cookie可天然免疫若用 session cookie 則需 CSRF token
  • WebSocket Origin:本 repo 的 local-tool/server/internal/api/ws/origin.go 已有成熟 allowlist搬過來用

6.4 Pairing Token 安全

詳見 ADR-003。重點

  • 雛形env 寫死
  • Phase 1DB 存 sha256(token)不存明文15min expiry可 revoke綁 user_id + device_id

6.5 輸入驗證

  • 模型檔案格式驗證(雛形僅 extensionPhase 1 加 magic bytes + file size limit
  • API body schema 驗證(用 struct tags binding:"required"go-playground/validator

6.6 STRIDE 分析(重點項目)

威脅 防護 雛形 / Phase 1
偽冒Spoofing他人冒用 token sha256(token) 存 DB + rate limit雛形 env 隔離 Phase 1 必做
竄改Tampering中間人改 request TLS Phase 1
否認Repudiation Audit log Phase 1
資訊洩露Information Disclosure TLS + least privilege log Phase 1
DoS rate limit on /tunnel/connect + API Phase 1
提權EoP RBAC Phase 1

雛形期驗證技術,不預期公開部署,以上多數 Phase 1 才補全。


7. 部署架構

7.1 雛形(開發 / 內測)

方案 A本機一鍵啟動

docker-compose up

docker-compose.yml

services:
  api-server:
    build: { context: ., dockerfile: docker/Dockerfile.api-server }
    ports: ["3001:3001"]
    environment:
      VISIONA_SESSION_BACKEND: inmemory
      VISIONA_STORAGE_BACKEND: localfs
      VISIONA_AUTH_MODE: static
      VISIONA_PAIRING_TOKEN: dev-token-abc123
    volumes: ["./data:/data"]

  remote-proxy:
    build: { context: ., dockerfile: docker/Dockerfile.remote-proxy }
    ports: ["3800:3800", "3801:3801"]
    environment:
      VISIONA_SESSION_BACKEND: inmemory
      VISIONA_PAIRING_TOKEN: vAc_<32 hex>   # 格式見 security.md §1.3

  frontend:
    build: { context: visionA-frontend }
    ports: ["3000:3000"]
    environment:
      NEXT_PUBLIC_API_BASE: http://localhost:3001

雛形交付物Phase 0:上述雙 binary + docker-compose 即完整雛形。不提供 cmd/dev-all-in-one(見 §2.4 Non-Goal

7.2 Phase 1正式部署草圖

                ┌──────────┐
                │   CDN    │ (Cloudflare / CloudFront)
                └────┬─────┘
                     │ static frontend
     ┌───────────────┴────────────────┐
     │                                 │
┌────▼─────┐                    ┌──────▼────┐
│   ALB    │                    │   NLB     │ (for WebSocket, TCP pass-through)
└────┬─────┘                    └──────┬────┘
     │ HTTPS /api/*, /ws/*             │ WSS /tunnel/connect
     │                                 │
┌────▼───────────────┐            ┌────▼──────────────────┐
│ api-server cluster │◄──────────►│ remote-proxy cluster  │
│ (K8s / ECS)        │   Redis    │ (K8s / ECS, StatefulSet│
│ stateless, N pods  │  Session   │ or DaemonSet)          │
└────┬───────────────┘   Store    └────┬──────────────────┘
     │                                 │
     ├─► PostgreSQL (RDS)              │
     ├─► S3 / R2 (object store)        │
     └─► Converter API                 │
                                        │ WSS + yamux
                                        ▼
                            使用者電腦 local agent

Cloud-agnostic所有組件Redis、PG、S3都有對應的 interface可用 AWS / GCP / 自建。

7.3 CI/CD雛形目標

  • make build → 兩個 binary
  • make docker-build → 兩個 image
  • make docker-compose-up → 本機跑起來
  • GitHub Actions / GitLab CIPhase 1→ 自動 build + push registry + deploy

8. 觀測Observability

8.1 雛形

項目 做法
Log stdout沿用 local-tool 的 log broadcasterWS 看 log
Metrics 無;/api/system/health 回 ok 即可
Trace
Alert

8.2 Phase 1TODO

項目 做法
Log 結構化 JSON → Loki / CloudWatch Logs
Metrics Prometheus exportqps, p95_latency, tunnel_count, session_active_count
Trace OpenTelemetry SDK → Tempo / X-Ray
SLO API 可用性 99.9%p95 < 500msTunnel 連線穩定度 99%7 天滾動)
Alert Grafana / PagerDuty

9. ADR 索引

編號 標題 狀態
ADR-001 單一 Go 專案 / 雙 binary 結構 Accepted
ADR-002 沿用 POC 的 Tunnel 協定WebSocket + yamux Accepted
ADR-003 以 Pairing Token 取代 SHA256(MAC) Token Accepted
ADR-004 模型儲存採用 S3-Compatible 介面 Accepted
ADR-005 雛形階段不接真實 DB 與 Auth Accepted

10. 三方交叉審閱檢核清單

給 PM 審閱

  • 所有 PRD 中列出的 P0 功能,是否在本 Design Doc 都有對應的技術路徑?
  • 「雲端能操作使用者本地裝置」的核心價值主張是否由架構支撐?
  • 雛形 vs Phase 1 的差異PM 能否向使用者解釋「現在能做什麼、未來會做什麼」?

給 Design 審閱

  • 「pairing token 輸入」頁面是否技術可行API 已定義)?
  • 模型上傳流程的 presigned URL 對 UX 是否清楚?
  • tunnel 斷線時瀏覽器要怎麼顯示?(已在 §5.2 指出走 POC 同機制;需 Design 規劃 UI

給 Architect 自我檢查

  • 每個 ADR 都有 Context / Decision / Consequences
  • 所有雛形實作都有明確的 Phase 1 替換計畫
  • 擴展策略說明完整§4
  • 安全架構覆蓋 STRIDE§6.6

下一步:閱讀 TDD.md(實作細節),以及各 ADR。