依 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)。
5.5 KiB
5.5 KiB
ADR-002:沿用 POC 的 Tunnel 協定(WebSocket + yamux)
狀態
Accepted — 2026-04-21
背景 (Context)
visionA Cloud 最核心的技術要素是:瀏覽器透過雲端反向代理,操作使用者本地電腦上的 Kneron 裝置。
這要求在「雲端 Proxy」與「使用者電腦的 local agent」之間建立一條:
- 由 local agent 主動出站建立(穿越 NAT / 企業防火牆)
- 雙向多工:同時傳遞 HTTP 控制指令、長輪詢 WebSocket、MJPEG 串流、推論結果串流
- 低延遲:串流資料需保留 real-time 特性
- 可重連:網路短暫斷線後自動恢復
POC(edge-ai-platform)已用 WebSocket + hashicorp/yamux 實作並驗證可行:
- 外層
gorilla/websocket負責「由 client 發起、能穿越企業代理」 - 內層
yamux把單條 WebSocket 升級為多工 stream,每個 HTTP 請求開一條 yamux stream - WebSocket binary frame 之上用
wsconnadapter 把 WebSocket 包成net.Conn
POC 源碼重點:
relay/server.go:收/tunnel/connect、建立yamux.Server(netConn)、按 token 收在map[token]*yamux.Sessiontunnel/client.go:Dial WebSocket →yamux.Client()→ 接收 stream → 轉給本機127.0.0.1:3721pkg/wsconn/wsconn.go:websocket.Conn→net.Connadapter(binary frame)
決策 (Decision)
雛形沿用 POC 的 tunnel 協定不做重大改變:
- 外層:
github.com/gorilla/websocket(version ≥ 1.5.3) - 多工:
github.com/hashicorp/yamux(default config) - Adapter:把 POC 的
wsconn搬到visionA-backend/internal/wsconn(或pkg/wsconn,視是否對外暴露) - 端點:
WS /tunnel/connect?token=xxx(remote-proxy 側) - 訊息語義:沿用 POC 的「yamux stream 裡傳完整 HTTP request/response」
沿用的好處:代碼已在 POC 驗證過能穿越 NAT、能處理 WebSocket upgrade(proxyWebSocket 的 Hijack 邏輯)、能處理 MJPEG streaming。
雛形會升級的部分(與 ADR-003 相關):
- Token 生成機制(SHA256(MAC) → Pairing Token,詳見 ADR-003)
handleTunnel加上 token 驗證 hook(雛形驗 env 寫死 token;未來驗 DB)- Session 管理抽 interface(雛形 in-memory,未來 Redis,詳見 ADR-001)
未來可能會重新評估的項目(非雛形範疇):
- 若 stream 吞吐或 head-of-line blocking 成瓶頸 → 評估 HTTP/3 (QUIC) 或 gRPC bidi stream
- 若需要端對端加密(繞過雲端看到明文)→ 在 yamux 之上再包一層 TLS / Noise
- 若需要跨雲的地理就近路由 → 加入 proxy routing 層,由 local agent 選擇 entrypoint
考慮過的替代方案
| 方案 | 優點 | 缺點 | 排除原因 |
|---|---|---|---|
| 重頭設計 gRPC bidi stream | 現代 RPC,有 code-gen、型別安全 | 要重新定義所有訊息 schema;POC 已用 HTTP semantics 讓搬遷成本低 | 雛形期不值得,未來可升級 |
| HTTP/3 (QUIC) 原生多工 | 未來標準、天生多工、零 HOL blocking | Go 生態的 QUIC 客戶端 / 伺服器還在 beta,反向代理成熟度低 | 還太新 |
| 單一 WebSocket 無多工(每請求新開 WS) | 最簡單 | 每請求一次 WS handshake(~50-200ms),串流無法疊加 | 效能差 |
| SSH tunnel / WireGuard 等 VPN 方案 | 標準 | 需要使用者端安裝額外軟體、開特定埠、難穿越企業防火牆 | 使用者體驗差 |
| 純 TCP tunnel over TLS | 簡單 | 通常被企業防火牆封鎖,WebSocket 因為外觀像 HTTP 能穿透 | 穿透力差 |
後果 (Consequences)
正面影響
- 零移植成本:POC relay / tunnel 程式碼可直接搬到
visionA-backend/internal/relay和visionA-backend/internal/tunnel - 已驗證 NAT 穿透:POC 測試過能穿越公司 NAT、家用路由器 NAT
- 已驗證 WebSocket-in-WebSocket:瀏覽器 → Proxy → tunnel stream → local agent 的 WebSocket upgrade 流程,POC 的
proxyWebSocket已處理 Hijack 並雙向 pipe,這個最難的部分已經能跑 - 已驗證 MJPEG streaming:POC 的
handleProxy用http.Flusher處理串流回應,visionA 的 camera stream 可直接用 - 擴展空間大:yamux 天生多工,一個 tunnel 可以同時跑 10+ 個 HTTP 請求不互相阻塞
負面影響(接受的取捨)
- 訊息格式是 HTTP 明文:yamux stream 內跑的是 raw HTTP request/response,需靠 TLS(WebSocket 外層)保密;Proxy 節點可以看到明文請求內容
- yamux 依賴:hashicorp/yamux 已久無大版本更新,但 v0.x 穩定
- 單 WebSocket = 單 tunnel:一個 local agent 一條 WebSocket;若要跨地理區域 active-active,需應用層路由
- HOL blocking 風險低但存在:yamux 是多工,但底層單一 TCP connection,若網路抖動整條都延遲
風險
- WebSocket 升級失敗率:企業防火牆 / 中間人代理可能阻擋 WebSocket upgrade。雛形先不處理 fallback(SSE / long-poll),TODO
- 單條 WebSocket 頻寬極限:若使用者端上傳 MJPEG 4K 60fps 會打滿一條 TCP。實測後若有問題再引入「多條 WebSocket + 分流」
- yamux 的 keepalive:POC 用 default config,是否足夠穿越企業 idle timeout?TODO 壓測驗證
合規性
- Architect 確認
- POC 實測驗證(
edge-ai-platform已部署到 EC2 + 本地測試) - 未來壓測:TODO
相關文件
- Design Doc §3(資料流)
- TDD §6(Tunnel 協定)
- TDD §7(Session 管理)
- POC 源碼:
edge-ai-platform/server/internal/{relay,tunnel}、edge-ai-platform/server/pkg/wsconn