visionA/docs/autoflow/04-architecture/adr/adr-019-local-direct-media-upload.md
jim800121chen 329023085e docs(arch): ADR-019 轉 Accepted(實作已 merge,據實標記 WP-4 未接線)
實作經完整審查流程(security 1C+3M → 契約修正 → confirm-only →
逐 WP reviewer → security code-level APPROVED → testing 270 測試全綠)
並 merge(PR #1 / 3eaf3dc)後轉 Accepted。

明確記錄「Accepted ≠ 端到端啟用」:僅底層能力已 merge,WP-4 影片分頁
接線未做,影片仍走 tunnel、90MB 過渡上限仍生效。含兩筆 backlog。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-30 12:53:16 +08:00

30 KiB
Raw Permalink Blame History

ADR-019: 影片 / 圖片 / 批次上傳走同機 localhost 直連 local-agent混合路徑

狀態

Accepted。

底層能力已於 2026-07-30 完成並 merge 進 maingithub PR #1、merge commit 3eaf3dc)。 走過完整審查流程security pre-implementation review1 Critical + 3 Major→ 契約修正C1 / M1 / M2 / M3 / 議題1→ security confirm-only 複審 → 逐 WP reviewer 通過 → security code-level 複審 APPROVED → testing 回歸270 測試全綠、既有 tunnel 路徑未打斷、無 regression。C1 定案採方案 Aroute 分離),見 §2.4。

⚠️ 「Accepted」≠「端到端功能已對使用者啟用」。 本次 merge 的是 backend/frontend 的底層能力local-agent CORS/PNA/token/新 route、雲端 ticket endpoint、前端 port 探測抽象、批次合計檢查)。前端影片分頁 UI 尚未切到新的 localhost 路徑WP-4 未做),故:

  • 影片上傳目前仍走既有雲端 tunnel 路徑端到端「localhost 直連」尚未對使用者啟用。
  • 90MB 過渡上限仍在生效(見 .autoflow/ 相關過渡處置)。
  • WP-4 接線完成前不可視為「ADR-019 功能已上線可用」。詳見 §7 合規性與 §8 WP 清單狀態標記。

日期

2026-07-24

作者

Architect Agent


1. 背景與範圍 (Context)

1.1 觸發問題

visionA 的 media 上傳(影片 / 圖片 / 批次)目前全部經雲端 tunnel forward(見 api/api-spec.md §6「Camera / Media — 與 local-tool 相同,全部透過 tunnel forward」。對影片這種大檔此路徑有三個實質問題

問題 說明 證據
頻寬雙倍佔用 500MB 影片經 tunnel = 上傳本機→雲端500MB + 結果下行雲端→本機500MB ≈ 1000MB 總傳輸,兩次都佔用使用者本地頻寬 第一輪資料流調查 §5.3
撞 nginx 上限 stage nginx 兩處 client_max_body_size 100M500MB 影片直接 413 docker/nginx.stage.conf:97, 417
api-server 300s timeout + stage 磁碟落地 即使調大 nginx10 Mbps 上行需 400s > 300s timeoutproxy_request_buffering 預設 on每次上傳在 stage 落地 500MB 暫存檔 visionA-backend/internal/api/proxy.go:32, 89-94camera.go:47

1.2 被忽略的邏輯漏洞(本 ADR 修補的核心)

design-doc.md §5.3(模型上傳)早已建立原則:

「模型檔可達百 MB走 tunnel = 占用使用者本地頻寬兩次(上傳→雲端→下載)」,故模型上傳不走 tunnel(改 presigned URL 瀏覽器直連)。

這個「大檔不走 tunnel」的原則從未套用到 media 上傳——影片同樣是大檔、同樣佔用雙倍頻寬,卻仍走 tunnel。本 ADR 把同一原則延伸到 media修補此文件內部邏輯漏洞。

1.3 有利前提(讀 code 後確認)

  • local-agent 已有完整的 loopback HTTP serverGin強制綁 127.0.0.1:<port>local-agent/server/main.go:298-305config.go:34-35,45)。
  • POST /api/media/upload/video(及 image / batchroute 已存在於 local-agent路徑與雲端完全一致local-agent/server/internal/api/router.go:90-94。handlercamera_handler.go:232-336)與 tunnel 完全解耦tunnel client 只做透明轉發,internal/tunnel/client.go:337-371)——瀏覽器直連走的是同一個 handler、同一 code pathhandler 零改動

1.4 使用者已拍板的前提

  • 跨機不存在:使用者確認「都是同機操作」,故非同機情境採「停用該分頁 + 明確提示」,不做 tunnel fallback。這是產品定位,不是能力缺口。
  • 瀏覽器範圍:僅需支援 Chrome / EdgeChromium 系。Safari 不在支援範圍。
  • 認證必做:本機 endpoint 經既有 tunnel 下發 one-time token。

2. 決策 (Decision)

我們將讓瀏覽器在同機情境下,直接 POST 影片 / 圖片 / 批次到 local-agent 的 localhost HTTP endpoint,繞過雲端 tunnel。控制面token 取得與結果面MJPEG 串流、推論 WS仍走雲端 tunnel——這是「上傳走本機、結果走雲端」的混合路徑,不是把整條資料流搬到本機。

具體包含五塊決策:

2.1 混合路徑(只搬「上行大檔」,控制面 / 結果面不動)

  • 改走 localhost:影片 / 圖片 / 批次的檔案上傳 POST。
  • 仍走 tunnel刻意保留token 取得控制面、MJPEG 結果串流(streamUrl 是相對路徑 /api/camera/stream,前端用 getApiBaseUrl() 組雲端絕對路徑,camera_handler.go:325visionA-frontend/src/lib/camera.ts:35-42)、推論結果 WS/ws/devices/:id/inference)。
  • 理由MJPEG 結果資料量遠小於原始影片15fps down-scaled JPEG走 tunnel 成本可接受;強行把結果也搬本機需在前端維護兩套連線來源,複雜度大增。保持「控制面 + 結果面走雲端、只有大檔上傳走 localhost」是風險最低的最小切口。

2.2 範圍:影片 + 圖片 + 批次全部改走 localhost

  • 三個分頁全部改走 localhost 直連(不只影片)。前端上傳抽象設計為 endpoint 無關uploadToLocalAgent(path, form, options)),三個 caller 換 path 即可,不需重構。
  • 批次額外補「合計大小檢查」:現況 validateBatchFilesvisionA-frontend/src/lib/media.ts:301-309)只逐張檢查 ≤20MB、不加總——50 張各 19MB合計 950MB會通過前端驗證後撞 413。這是已上膛的地雷須補合計上限建議 MAX_BATCH_TOTAL_BYTES = 80MB,留 nginx 100M 的 20% 餘裕)。

2.3 同機偵測 + port 探測(前端掃描候選 port + 身分驗證)

  • local-agent 的 port 是動態的(pickPort 從 3721 fallback 到 3740app.go:46-47, 1450-1468),前端不可寫死 3721
  • 前端探測策略:sessionStorage 快取372137223740 並發Promise.any 取第一個成功者),每次 timeout 500ms。
  • 探測動作本身就是同機偵測:任一 port 回應且身分相符 = 同機;無回應 = 非同機。
  • 新增專用探測 endpoint GET /api/local/hello(不用既有 /api/system/health——後者對非白名單 origin 的 GET 不回 ACA*,瀏覽器 JS 讀不到內容)。
  • 身分驗證必做/api/local/hello 回傳 agent 管理的裝置 serial 的 salted SHA-256 雜湊SHA-256("visiona-local-v1" || serial),已裁決見 §5 / api-spec §6.3),前端重算比對 selectedDevice.serialNumber 是否相符。防止「同機跑著另一台 agent、影片被送到錯的裝置」這種靜默錯誤資料流。沿用 ADR-018 的 serial 路由慣例,不新造識別體系。

2.4 認證:經既有 tunnel 下發 one-time token縱深防禦

開放雲端 origin 打 local-agent 後,威脅模型從「只有本機 loopback 網頁能打」變成「同機任何網頁只要通過 origin 檢查就可能控制 Kneron 裝置」。CORS 白名單擋不住 XSS合法 origin與 DNS rebinding繞過 DNS 解析層),故必須有認證作為第二道防線。

流程(契約):

[1] 前端進影片分頁 → 向雲端要 token經既有 OIDC 認證 + 既有 tunnel
      POST /api/devices/:serial/local-upload-ticket新增雲端 endpoint
      → api-server 驗 OIDC session + 裝置歸屬 → 經 tunnel 打 local-agent
        POST /api/local/issue-token → 產 token 回傳
[2] 前端掃描找到 localhost port§2.3
[3] 前端 POST 影片 → http://127.0.0.1:<port>/api/local/media/upload/video   ← 新的、一律要 token 的 route
      Header: X-Visiona-Local-Token: <token>
[4] local-agent 驗 token存在 / 未過期 / 未用過 / deviceId 相符)+ 驗 Host = loopback§2.5
      → 消耗 token → 內部轉呼叫既有 handlerhandler 零改動)

Token 設計方向(已經 security agent 審,核心設計通過,見 review 議題 2

  • 生成 crypto/rand 32 bytes → base64url不准 math/randTTL 120sone-time驗證後立即刪綁 deviceId記憶體 storemap + sync.Mutex,不持久化);上限同時 32 個未使用 token防 DoS比對用 crypto/subtle.ConstantTimeCompare(防 timing attack
  • 實作時注意項security review m2 / m3不改契約但工程師必守consume 與 issue 各自在單一 Lock() 內完成consume = 查存在 + ConstantTimeCompare + 刪除三步一次持鎖issue = 查 len<32 + 插入一次持鎖),避免併發雙重消費 / 上限突破token 驗證放在讀 multipart bodyFormFile之前(用 middleware 更佳),避免「未驗證就先收大檔」放大 DoS。

2.4.1 [C1 — 已定案:方案 A route 分離] 認證判準不得依賴攻擊者可控的 Origin

背景security review C1Critical blocker:原契約訂「來自 tunnel 的請求(無 Origin header不得要求 token」把「有無 Origin」當成「是不是可信 tunnel 來源」的唯一判準。但 Origin header 由請求發送方完全控制,同機任何程序curl / 惡意 App / 被入侵的其他本機服務)都能發出不帶 Origin 的 HTTP 請求到 127.0.0.1:<port>local-agent 綁 loopback、同機所有程序皆可連等於「不帶 Origin 就免 token」的後門整層 token 認證被繞過。

查證結論(讀 local-agent/visiona-agent/internal/tunnel/client.gotunnel client 的 handleStream 把收到的 HTTP request 直接 http.DefaultTransport.RoundTrip(req)req.URL.Host = c.localAddr= 127.0.0.1:<port>,普通 HTTP over loopback見 client.go:337-355、73。tunnel 未在轉發時注入任何可辨識標記,且 tunnel clientWails app shell module visiona-agent)與 HTTP server獨立子行程 module server)是兩個獨立行程。結論:同機程序可完全偽裝成「tunnel 來的請求」server 端無法用 request 本身區分。

定案:採方案 Aroute 分離),不採方案 Btunnel 注入 process-local secret。理由

  1. tunnel client 與 HTTP server 是兩個獨立行程 / 獨立 Go module見 client.go 檔頂註解),方案 B 的「process-local 共享 secret」實際上需要跨行程 IPC 傳遞 nonce脆弱且增加耦合方案 A 不需任何跨行程協調。
  2. 方案 A 把「要不要 token」交給 server 端固定的 route 決定,不由攻擊者可控的 Origin 決定。

契約鐵則(取代原「無 Origin 免 token」硬規則

  • 媒體上傳 endpoint 絕不能有「無任何憑證即放行」的 code path。 刪除原「來自 tunnel 的請求(無 Origin不得要求 token」的無條件放行語意。
  • 瀏覽器 localhost 直連新的、一律要求 token 的 routePOST /api/local/media/upload/{video|image|batch-images}。此 route 不看 Origin、一律驗 tokentoken 無效 / 缺失 → 401並經 §2.5 Host 驗證。內部轉呼叫既有 handlerhandler 零改動)。
  • 既有 tunnel 轉發路徑維持既有 POST /api/media/upload/{video|image|batch-images}tunnel client 內部轉發至此,路徑不變、行為不變)。此路徑不因本 ADR 新開放給任何雲端 originCORS 白名單擴充只作用於直連情境的 /api/local/* 行為)。

誠實揭露的殘留風險:因 loopback 對同機程序無隔離,同機惡意程序仍可直接打舊 tunnel-path route/api/media/upload/*)——但這是 ADR-019 之前就存在的既有 loopback 攻擊面(非本 ADR 新增),且舊 route 未被本 ADR 新開放給雲端 origin。緩解舊 tunnel-path route 也套用 §2.5 Host 驗證Host=loopback 才放行),把「同機直打舊 route」限制在同機、且不影響 tunnel 轉發tunnel 轉發的 Host 本就是 127.0.0.1:<port>)。完整消除同機程序攻擊面超出本 ADR 範圍(需 OS 層 socket 權限 / peer credential 驗證),列為 backlog。

2.5 CORS 白名單擴充 + Host 驗證 + PNA 緩解(全部必做)

  • Origin 白名單改為可設定env VISIONA_CLOUD_ORIGINS,逗號分隔完整 originstage 需同時允許雙入口
    • https://stage-9527.innovedus.com:9527(公網 HTTPSnginx.stage.conf:86-89
    • http://192.168.0.130:9527內網純 HTTP 入口,nginx.stage.conf:411-414
    • devhttp://localhost:3000
  • loopback 舊規則(middleware.go:17-22)保留不動。
  • [M3 — 契約必做] 雲端 origin 採「完整 origin 精確比對」scheme+host+port 全等逐字比對),不可沿用既有 isAllowedOrigin 的 hostname-only + 任意 port + 放寬 scheme 邏輯(middleware.go:41-45)。白名單裡存的是完整 origin 字串(如 https://stage-9527.innovedus.com:9527),比對時整串相等才通過。原因:既有 allowedHosts 只比 hostname、放任任意 port若直接把雲端網域塞進去會變成「該網域任意 port 都放行」,攻擊面比預期大;放寬 scheme 若寫成「不檢查 scheme」則 https://127.0.0.1http:// 混用可繞過。故雲端 origin 必須獨立於既有 loopback 邏輯,走完整字串精確比對。
  • [M3 — 契約必做] Access-Control-Allow-Credentials: false(對雲端 origin:本路徑用 headerX-Visiona-Local-Token)帶 token、不需 cookie。不可沿用既有 middleware 對雲端 origin 回 Allow-Credentials: truemiddleware.go:97)——那會無謂讓瀏覽器願意帶 credential、擴大 CSRF / 憑證面。雲端 origin 一律回 falseapi-spec §6.4 已列)。
  • [M2 — 契約必做,從「待審 / optional」升為「必做」] Host header 驗證 = loopback:對所有 /api/local/*(含新的 /api/local/media/upload/*)與舊 tunnel-path media route加 Host 驗證 middleware——Host header去掉 port 後)必須 ∈ {127.0.0.1, localhost, ::1},否則 400 Bad Request。這是獨立於 CORS 的第二道、擋 DNS rebindingrebinding 後 Host 為攻擊者網域 ≠ loopback → 400成本低、對本機服務屬 baseline 而非 optional。tunnel 轉發的 Host 本就是 127.0.0.1:<port>(見 client.go:347故不受影響。驗證邏輯契約不寫進 production code
    host := c.Request.Host                 // 含 port
    h, _, _ := net.SplitHostPort(host)     // 去 port無 port 時退回原值)
    if h ∉ {"127.0.0.1", "localhost", "::1"} → 400 Bad Request
    
  • PNA 緩解(必做,非 optional:對帶 Access-Control-Request-Private-Network: true 且通過白名單的 preflightAccess-Control-Allow-Private-Network: true。原因見 §4.3 R-1——即使只支援 Chrome/EdgeChrome 未來可能把 PNA 從 warning 升為 blocking而已安裝的舊版 local-agent 不會自動升級,此 header 現在補、成本 <30 分鐘,省未來一顆不定時炸彈。
  • preflight 帶 Access-Control-Max-Age: 600api-spec §6.4,緩解 port 掃描的大量 preflight

3. 考慮過的替代方案 (Alternatives Considered)

方案 優點 缺點 排除原因
維持 tunnel + 調大 nginx client_max_body_size 改動最小、不動前端架構 不解決頻寬雙倍批次分頁仍是地雷stage 每次落地 500MB 暫存檔;還需 IT 改公司邊界 nginxSTAGE-DEPLOY.md:397-401 待辦未完成api-server 300s timeout 仍會炸 治標不治本,且引入「改 nginx→改 timeout→關 request buffering→改公司邊界 nginx」的連鎖與「最小改動」背道而馳
傳檔案路徑給 local-agent不傳檔案內容 傳輸量 ~100 bytes 瀏覽器基於安全拿不到真實路徑File.path 已被所有現代瀏覽器移除);需在 local-agent 加業務 UI違反 server_control.go:1023-1033「使用者不感知 server 存在」的既有決策path traversal 風險 技術不可行(拿不到路徑)+ 違反既有架構方向
前端壓縮 / 抽 frame 後上傳(縮小檔案再走 tunnel 沿用現有 tunnel 路徑 失真影響推論準確度;瀏覽器端影片處理相容性差、耗時;不解決架構問題(只是把大檔變小檔) 犧牲推論品質,且沒有真正解決頻寬 / 落地問題
localhost 直連(採用) 傳輸量 1000MB→loopback0.12.5s,改善約 1001000 倍);不受 nginx / timeout 約束handler 零改動 需同機操作local-agent 新增對外攻擊面需認證PNA 未來風險 採用

4. 後果 (Consequences)

4.1 正面影響

  • 頻寬500MB 影片從「1000MB 經雲端 tunnel」降為「loopback 本機傳輸」(現代 SSD 筆電 0.10.5s、較舊機器 12.5s)。
  • 不受基礎設施約束:繞過 nginx 100M 上限、api-server 300s timeout、stage 磁碟落地。
  • handler 零改動local-agent 的 UploadVideo handler 一行不用改route 已存在、與 tunnel 解耦)。
  • 修補文件邏輯漏洞:把 design-doc.md §5.3 的「大檔不走 tunnel」原則正確延伸到 media。
  • 順帶修批次地雷:補合計大小檢查,消除 50×19MB 撞 413 的隱患。

4.2 負面影響與限制(接受的取捨)

  • 影片 / 圖片 / 批次上傳需同機操作:必須在執行 visionA Agent 的那台電腦上操作。非同機時該分頁停用 + 提示。Camera即時攝影機仍支援遠端檢視——只有「上行大檔」需同機限制。
    • 依使用者拍板「跨機不存在」,此限制不影響實際使用;且現況跨機使用者本來就傳不了 >100MB413此限制是「把隱性限制變成明確提示」非新增限制。
    • 不動 PRD:使用者已明確跨機不存在,此限制在 ADR 記錄即可,不需 PM 改 PRD。
  • local-agent 新增對外攻擊面:首次開放雲端 origin 直連 local-agent需維護 CORS 白名單 + token 認證 + PNA header 的安全性。
  • 僅支援 Chromium 系Chrome / EdgeSafari 不支援(使用者已確認不需要)。
  • 狀態分裂風險:上傳走 localhost、結果走雲端 tunnel兩條路徑可能不同步見 §4.3 R-3

4.3 風險

# 風險 等級 緩解
R-1 PNA 未來變硬阻擋,已安裝的舊版 local-agent 無法自動升級 → 影片功能無預警壞掉 第一版就加 Access-Control-Allow-Private-Network: true§2.5,成本 <30min
R-3 狀態分裂:上傳走 localhost 成功,但結果 tunnel 剛好斷線 → 上傳成功卻看不到結果 上傳前檢查既有 tunnel_online 欄位(visionA-backend/internal/api/devices.go:66-67),離線時 disable 上傳
R-4 同機多個 visionA agent多裝置場景掃描命中錯的 agent §2.3 serial 身分驗證;掃描收集所有回應再挑 serial 相符者
R-6 影片 / 圖片 / 批次上傳完全無 size 上限 + temp 檔洩漏(stopActivePipeline 切 pipeline 時把 videoPath="" os.Removebatch 成功路徑後也未刪),反覆上傳可磁碟 DoS配合 C1 未修時為 High 中→高 [M1 — 契約必做,非查證] 見 §4.3.1

4.3.1 [M1 — 契約必做] media 上傳 size 上限 + temp 檔清理

security review M1temp 檔洩漏已確認為實錘(非待查證)——camera_handler.gostopActivePipelineline 535h.videoPath = "" 但從未對前一支影片 temp 檔 os.Removebatch 路徑在 NewMultiImageSource 成功後也未追蹤刪除。結合無 size 上限 → 反覆上傳磁碟必爆。

契約必做項WP-2非「查證」

  • size 上限(各自值,必做)video / image / batch-images 各自的上傳 size 上限納入契約。
    • video 上限:與前端 90MB 對齊,硬上限不超過 500MBsecurity 明確要求「不要因為走 loopback 就給 1GB」——攻擊面不因 loopback 縮小)。建議實作值 = 前端 90MB正常上限server 端 http.MaxBytesReader 硬牆設 ≤ 500MB 作為 DoS 上界。
    • image / batch 單張沿用既有逐張限制;batch 合計上限 = MAX_BATCH_TOTAL_BYTES = 80MB§2.2,補 nginx 100M 的 20% 餘裕;此上限對 localhost 直連仍保留,作為資源上界)。
    • 驗證時機size 檢查與 token 驗證都放在讀 multipart bodyFormFile之前review m3
  • temp 檔清理(必做)stopActivePipeline 切換 / 停止 pipeline 時對前一支 videoPathos.Removebatch 的 MultiImageSource 在生命週期結束時刪除其 filePaths。反覆上傳不得殘留 temp 檔testing 寫回歸測試驗證)。 | R-5 | 企業防火牆 / EDR 攔截 localhost HTTP | 低 | 明確錯誤訊息讓使用者知道要檢查什麼 |

註:第一輪風險表中的 R-2Safari 相容性)因使用者確認僅支援 Chromium已移除。


5. security agent 裁決事項(已裁決)

依 Architect CLAUDE.md §14.3 邊界表token / secret 管理與隱私洩露屬 security agent 審查範圍。以下兩點已經 security agent 審查裁決(.autoflow/05-implementation/review/adr-019-security-review.md 議題 1 / 議題 2契約內容已依裁決更新

# 議題 裁決結果(已落實於本 ADR / api-spec
1 GET /api/local/hello 回傳完整 serial 還是雜湊 serial 已裁決回「salted SHA-256 雜湊」,不回完整 serial。 serialHashes[i] = SHA-256(公開固定 salt || fullSerial) 的 hexsalt 常數 = visiona-local-v1(前後端共用、非 server 私有,讓前端可獨立重算比對);agentVersion 粗化或移除(改回布林 supportsLocalUpload: true 已足夠hello 不回任何其他欄位(最小揭露)。契約見 api-spec §6.3。殘留風險(序號熵低 + salt 公開 → 有心攻擊者可枚舉回推security 判定「與威脅相稱、不需過度設計」(序號非 credential、控制裝置仍需 token+OIDC
2 one-time token 設計是否足夠 + DNS rebinding 完整緩解 已裁決token 核心設計通過。 附加要求已落實aC1 route 分離§2.4.1token 認證不得被無 Origin 繞過,是 token 設計的前置 blockerbHost 驗證必做§2.5rebinding 主防護,非 optionalcCORS 完整 origin 精確比對 + Allow-Credentials: false§2.5dtoken race 用 single-flight 持鎖(實作注意項 m2。upload handler 需在內部再綁 Origin 二次驗證CORS 已做、且避免與 C1 方案衝突)。

6. 本 ADR 修正 / supersede 的內容

文件 章節 原內容 修正為
api/api-spec.md §6 Camera / Media 「與 local-tool 相同,全部透過 tunnel forward 影片 / 圖片 / 批次上傳走 localhost 直連Camera 與推論結果串流仍走 tunnel見本 ADR
design-doc.md §5.3 之後 「大檔不走 tunnel」原則只涵蓋模型上傳 新增一節說明 media 上傳套用同原則localhost 直連),修補邏輯漏洞

註:本 ADR 不 supersede 任何既有 ADR僅修正上述兩份設計文件的敘述。ADR-018serial 路由)的識別慣例被本 ADR 沿用§2.3),無衝突。


7. 合規性

  • 與使用者確認方向(同機 localhost 直連 + 混合路徑 + 範圍含影片/圖片/批次 + 非同機停用 + 僅 Chromium + 認證必做)— 已裁決
  • security agent 審 token / 隱私設計§5 兩點)— 已審(初審 REQUEST CHANGES [1C+3M] → 契約已依 C1/M1/M2/M3/議題1 修正 → confirm-only 複審 → code-level 複審 APPROVED
  • backend agent 落地 local-agent CORS/PNA/token/endpoints + 雲端 ticket endpoint — 已 mergePR #1
  • frontend agent 落地 port 探測抽象WP-3+ 批次合計檢查WP-7 已 mergePR #1
  • frontend agent 影片分頁接線WP-4 未做(下一批)。WP-3 port 探測抽象與 WP-7 批次合計檢查已完成,但「影片分頁 UI 切到 /api/local/media/upload/video 新路徑」屬 WP-4尚未落地 → 端到端功能未對使用者啟用、90MB 過渡上限仍生效
  • testing agent 回歸:既有 tunnel 路徑(無 Origin不受 token 影響 — 270 測試全綠、既有 tunnel 未打斷、無 regression
  • 成本影響:無新雲端資源(沿用現有 DB / tunnel主要是三 module 開發工時(見 §8 WP 清單);反而降低 stage 磁碟與頻寬壓力 — 已實作,符合預期(無新增雲端資源)

Backlog本次 merge 未處理,記錄於此供後續追蹤):

  • 同機程序存取控制(優先級 Low既有 loopback 攻擊面(非本 ADR 新增,見 §2.4.1 誠實揭露的殘留風險)。同機惡意程序仍可直打舊 tunnel-path route根治需 OS 層 peer credential / socket 權限驗證,超出本 ADR 範圍。
  • WP-4 接線時的真實 Kneron 序號實機驗收:需做真實序號的前後端 hash 實機驗證——後端 SHA-256 輸入的序號格式(0x%08X 大寫)與前端 serialNumber 須同形,否則 hash 不符會誤判非同機。此為 fail-closed不符即停用分頁WP-4 接線時必驗。

8. WP 工作包清單(給 Orchestrator 排程)

完整依賴圖 / 關鍵路徑 / work-stream 同步點見 .autoflow/04-architecture/video-localhost-wp-breakdown.md(個人層)。此處為摘要。

本清單已依 security review 更新C1 route 分離、M1 size 上限 + temp 清理必做、M2 Host 驗證必做、M3 CORS 完整 origin 精確比對)。

實作狀態2026-07-30PR #1 merge commit 3eaf3dcWP-1 / WP-2 / WP-3 / WP-5 / WP-7 已完成並 mergeWP-0 / WP-4 / WP-6 為下一批(未做)底層能力已具備,但 WP-4影片分頁接線未做 → 前端 UI 尚未切到新 localhost 路徑、端到端功能未對使用者啟用、90MB 過渡上限仍生效。

WP 內容 負責 agent 依賴 人時 狀態
WP-0 SpikeChrome/Edge 實測 https/http 入口 → http://127.0.0.1:3721 + PNA console warning frontend 24 下一批(未做)
WP-1 local-agent CORS / Host / PNA雲端 origin 完整 origin 精確比對(獨立於既有 hostname-only 邏輯,不放寬既有 scheme 檢查+ Allow-Credentials: false(雲端 origin + Host header 驗證 = loopbackM2 必做,套用 /api/local/* 與舊 media route + PNA header + Access-Control-Max-Age: 600 backendGo 契約 §2.5 57 已完成並 merge
WP-2 local-agent新 route /api/local/media/upload/{video|image|batch-images}(一律要 token、不看 Origin、內部轉呼叫既有 handler + /api/local/hello(回 salted SHA-256 serialHashes + /api/local/issue-token + token storesingle-flight 持鎖)+ token 驗證中介(放 FormFile 前)+ media size 上限video ≤500MB 硬牆 / batch 合計 80MBM1 必做) + temp 檔清理(stopActivePipelineos.Remove + batch 生命週期刪檔M1 必做) backendGo WP-1 913 已完成並 merge
WP-3 前端:lib/local-agent.tsport 探測並發+快取+timeout、同機判定用 salted SHA-256 比對 serialHashes、uploadToLocalAgent() endpoint 無關通用函式;上傳目標改新 route /api/local/media/upload/* frontend 契約 §2.3/§6.3 610 已完成並 merge
WP-4 前端:影片分頁接線(取 token、改上傳目標為 /api/local/media/upload/video、三種錯誤訊息 i18n、tunnel 離線檢查 R-3 frontend WP-3 46 下一批(未做) — 端到端啟用的關鍵缺口;接線時須做真實 Kneron 序號前後端 hash 實機驗收(後端 0x%08X 大寫 vs 前端 serialNumber 須同形、fail-closed
WP-5 雲端:POST /api/devices/:serial/local-upload-ticket(經 tunnel 轉發 issue-token backendGo 契約 C-1 24 已完成並 merge
WP-6可選 圖片 + 批次接上 localhost 路徑 frontend WP-4 驗證通過 35 下一批(未做)
WP-7獨立建議先做 validateBatchFiles 加合計大小檢查 frontend 0.51 已完成並 merge

推薦範圍WP-0..WP-5 + WP-7總計26.543 小時 ≈ 3.35.4 人天。

關鍵路徑(串行、決定最短工期):契約定稿 → WP-0 → WP-1 → WP-2 → 整合 → E2E ≈ 1422 小時。 可平行WP-3前端對契約 mock與 WP-1/WP-2backend完全平行WP-5雲端 ticket與 WP-1/WP-2 平行WP-7 完全獨立。建議開 23 條 work-streamfrontend / local-agent backend / cloud backend不宜再切。


9. 一句話總結

影片 / 圖片 / 批次上傳從「經雲端 tunnel頻寬雙倍 + 撞 nginx 100M + 300s timeout + stage 落地)」改為同機瀏覽器直連 local-agent 的 localhost endpointhandler 零改動、route 已存在),修補 design-doc.md §5.3「大檔不走 tunnel」原則未套用到 media 的邏輯漏洞;採混合路徑(上傳走 localhost、控制面 + MJPEG 結果 + 推論 WS 仍走 tunnel動態 port 用前端掃描 37213740 + serial 身分驗證偵測同機;認證用經既有 tunnel 下發的 one-time token縱深防禦擋 XSS / DNS rebindingCORS 擴充雙入口白名單 + PNA header防未來 Chrome 升級 blocking非同機停用該分頁產品定位「跨機不存在」不做 fallback。token / 隱私設計待 security agent 審。