visionA/visionA-backend/.env.example
jim800121chen 86b7175649 feat(visionA-backend): Phase 0.8b 步驟 2 — visionA → converter / FAA 改 API key 認證
對齊 ADR-015:visionA backend 從 OAuth client_credentials 改 pre-shared API key 服務間認證。Phase 0.8 stage e2e 撞 4 個 blocker(MC scope 沒註冊 / converter image 舊版 / converter 缺 env / FAA 不確定)後,使用者拍板 1:1 internal trust 用 OAuth 過度設計,改 API key。

實作(5 個增量 task,T1-T5 全綠 + Reviewer 5 輪 + final cross-task review):

T1 config + env:
- ConversionConfig 新增 ConverterAPIKey / FAAAPIKey 欄位
- Enabled() 改判定 4 欄位齊全(含兩個 API key)
- .env*.example 移除 OIDC service client / OIDC tenant / FAA delegated TTL env、新增 API key env
- TenantID / DelegatedTTLSeconds T1 暫留、T5 整批清

T2 client 改造:
- converter_client / faa_client 移除 MCTokenClient 依賴
- 直接讀 cfg.Conversion.{Converter,FAA}APIKey、set Authorization: Bearer <key>
- NewConverterClient / NewFAAClient APIKey 為空時 panic(fail-fast,對齊 ADR-015 §3.5.3 #1)
- 新增 ErrConverterAuthFailed / ErrFAAAuthFailed sentinel
- 對外 mask 成 converter_unavailable / faa_unavailable(不洩漏 401 細節,對齊 ADR-015 §3.5.3 #3)

T3 砍 mc_token_client:
- mc_token_client.go (624 行) + mc_token_client_test.go (864 行) 整檔砍
- 砍 5 個僅 mc_token_client 用的 sentinel(ErrServiceClientUnauthorized / ErrMCTokenUnavailable / ErrIDPMisconfigured / ErrIDPUnavailable / ErrDownloadTokenFailed)
- helper(truncate / silentLogger)搬到 util.go / testing_helpers_test.go

T4 flow + handler stream proxy:
- Service interface DownloadRedirectURL → DownloadStream(ctx) (io.ReadCloser, *DownloadMetadata, error)
- flow.DownloadStream 用 faa.GetFile 直接 stream NEF binary(取代 MC delegated token + 302)
- handler conversionDownloadHandler 改 io.Copy + Content-Type/Disposition/Cache-Control header
- 新增 sanitizeDownloadFilename helper 防 HTTP header injection
- 跨 package handler test (conversion_test.go) 改測 ErrFAAUnavailable + 補 *_AuthFailed 對稱 test

T5 wire 切換 + cleanup:
- main.go 砍 mcTokenClient wire、改 APIKey 注入、startup log 用 *_api_key_set boolean(不印 key)
- ConverterClient/FAAClient struct Tokens 欄位移除
- mc_token_stub.go (T4 過渡期) 整檔砍
- ConversionConfig TenantID / DelegatedTTLSeconds 欄位移除
- e2e_test.go TestConversionE2E_Download302Redirect 改寫為 TestConversionE2E_DownloadStream
- 補 MaxDownloadStreamBytes = 1 GiB size cap(io.CopyN,T4 reviewer Minor M-1)
- escapeObjectKeyPath Phase 1+ 預留 godoc + //nolint:unused(T4 reviewer Minor M-2)
- conversion.md §4.1 line 502 filename 來源描述歧義修訂(T4 reviewer Minor M-3,由 architect 處理)
- conversion_e2e_test.go 檔頭 docstring 更新(final reviewer Minor #1)

驗證:
- go build ./... exit 0
- go test -race -count=3 ./... 17 packages 全綠
- ADR-015 §6 砍除清單 100% cover(reviewer 獨立 grep 確認 MC chain / TenantID / DelegatedTTLSeconds 全清)
- ADR-015 §3.5.3 部署檢查清單 visionA 範圍 4/4 達成(fail-fast / mask / 不印 token / placeholder env)

不動:
- OIDCConfig.ServiceClientID/Secret 欄位保留(使用者拍板 backward compat)
- user login OIDC 完全不動

下一步:
- 步驟 4 — converter scheduler middleware 改 API key(jimchen 跨 repo,ADR-015 §3.5.1 Go snippet)
- 步驟 5 — FAA middleware 改 API key(warrenchen 跨 repo,ADR-015 §3.5.2 C# snippet)
- 步驟 6 — stage redeploy + e2e 完整測試

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 09:45:45 +08:00

197 lines
8.5 KiB
Plaintext
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# visionA-backend 環境變數範本
#
# 使用方式:
# cp .env.example .env
# # 視情況修改 .env 內的值(尤其 VISIONA_STORAGE_SIGNING_SECRET 與 VISIONA_PAIRING_TOKEN
#
# ⚠️ 不要把 .env commit 進 git已在 .gitignore 中排除)
# 相關文件:
# - .autoflow/04-architecture/build-deploy.md §9變數對照表
# - internal/config/config.go每個欄位的定義
# ============================================================
# 共用
# ============================================================
# 日誌等級debug / info / warn / error
VISIONA_LOG_LEVEL=info
# ============================================================
# api-server
# ============================================================
# 對前端的 REST / WebSocket port對齊 local-tool 的 base URL 預設)
VISIONA_API_PORT=3721
# api-server 連 remote-proxy 的 internal HTTP base URL
# 本機 go run 時用 localhostdocker-compose 內部會被 compose 覆寫為 http://remote-proxy:3801
VISIONA_PROXY_INTERNAL_URL=http://localhost:3801
# Static user — Phase 0.7 security audit 後僅供 dev seedVISIONA_SEED_DEMO_DATA=true
# 與 unit test fixture 用;不再注入 api.Deps、stage/prod 留空無影響。
# 見 .autoflow/05-implementation/review/phase-0.7-security-audit.md C1。
VISIONA_STATIC_USER_ID=demo-user
# 啟動時 seed 示範資料device + model + pairing token方便前端 demo
VISIONA_SEED_DEMO_DATA=true
# CORS 白名單(逗號分隔)— 預設允許 frontend dev serverhttp://localhost:3000
VISIONA_CORS_ALLOWED_ORIGINS=http://localhost:3000
# ============================================================
# OIDC必填 — OB5 起 OIDC 是唯一認證路徑A1 起支援 public PKCE-only client
# ============================================================
# 必填欄位缺任何一項main.go 啟動時會 fatal log 退出。
#
# 對應 Innovedus Member Center 的 OIDC client 設定:
# - 在 Member Center 註冊一個 OAuth clientconfidential 或 public 皆可)
# - 取得 client_idpublic client 沒有 client_secret
# - 將 RedirectURL 加入 Member Center 的白名單
# Member Center 的 issuer不帶結尾斜線MC 的 issuer 末尾斜線必要時請保留)
# dev: http://localhost:5050
# stage: https://stage-9527.innovedus.com:7850/
# prod: https://members.innovedus.com
VISIONA_OIDC_ISSUER_URL=http://localhost:5050
# 在 Member Center 註冊的 OAuth client_id
VISIONA_OIDC_CLIENT_ID=visiona-cloud
# Client secretA1選填 — public PKCE-only client 留空)
# - 有值 → confidential client modeclient_secret + PKCE 雙保險)
# - 留空 → public PKCE-only client mode依靠 PKCE 防 code interception
# ⚠️ 不可 commitprod 用 Secrets Manager。Stage MC 配的 client `b8093fea...` 是 public留空。
VISIONA_OIDC_CLIENT_SECRET=
# Backend callback URL — 必須與 Member Center 註冊值完全一致
# dev: http://localhost:3721/api/auth/callback
# stage: https://stage-9527.innovedus.com:9527/api/auth/callback
# prod: https://api.visiona.cloud/api/auth/callback
VISIONA_OIDC_REDIRECT_URL=http://localhost:3721/api/auth/callback
# Frontend base URL — callback 完成後 302 redirect 的目的地
# dev: http://localhost:3000
# stage: https://stage-9527.innovedus.com:9527
# prod: https://app.visiona.cloud
VISIONA_FRONTEND_URL=http://localhost:3000
# Phase 0.8b 移除VISIONA_OIDC_SERVICE_CLIENT_ID / _SECRET
# 服務間認證從 OAuth client_credentials 改為 pre-shared API key見 ADR-015、conversion.md §3
# 兩個 service client env 不再讀取OIDCConfig.ServiceClientID/Secret struct 欄位
# 為了 backward compat 暫保留、但 conversion 模組不再依賴)。
# 取代設定見下方 Phase 0.8 / 0.8b 區塊的 VISIONA_CONVERTER_API_KEY / VISIONA_FAA_API_KEY。
# Cookie HMAC 簽章 secret 至少 32 byte 隨機字串prod 用 openssl rand -hex 32
VISIONA_SESSION_SECRET=CHANGE_ME_TO_RANDOM_64_BYTES_in_production
# Cookie 設定dev 預設 host-only / non-secureprod 改 .visiona.cloud + Secure=true
VISIONA_SESSION_COOKIE_NAME=visiona_session
VISIONA_SESSION_COOKIE_DOMAIN=
VISIONA_SESSION_COOKIE_SECURE=false
# Session TTL — 預設 7 天 absolute / 24h idle
VISIONA_SESSION_ABSOLUTE_TTL=168h
VISIONA_SESSION_IDLE_TTL=24h
# Relay 對外可達 URLagent tunnel 用)— POST /api/pairing/exchange 會回給 agent。
# 雛形為空時會 fallback 到 wss://relay.visionA.cloudplaceholder
# 實機請設為實際可達的 WSS URLwss://relay.visionA.cloud
VISIONA_RELAY_PUBLIC_URL=
# ============================================================
# remote-proxy
# ============================================================
# 對 local agent 的 WebSocket tunnel port
VISIONA_TUNNEL_PORT=3800
# 對 api-server 的 internal HTTP port不對外暴露
VISIONA_PROXY_INTERNAL_PORT=3801
# ============================================================
# Tunnel 心跳 / 掉線判定(對齊 tunnel.md §4.2
# ============================================================
VISIONA_TUNNEL_HEARTBEAT_INTERVAL=10s
VISIONA_TUNNEL_IDLE_TIMEOUT=30s
# ============================================================
# StorageLocalFS — Phase 0 雛形Phase 1 會改 S3
# ============================================================
# 儲存根目錄容器內docker-compose 已 mount 成 volume
VISIONA_STORAGE_BACKEND=localfs
VISIONA_STORAGE_LOCALFS_ROOT=./data/storage
# 瀏覽器 / 上傳 client 看到的 presigned URL base
# 本機開發http://localhost:3721/storage
# docker-compose demo同上透過 host port mapping
VISIONA_STORAGE_BASE_URL=http://localhost:3721/storage
VISIONA_STORAGE_LOCALFS_BASE_URL=http://localhost:3721/storage
# HMAC 簽章 secret — 用於 LocalFS presigned URL 與Phase 1pairing token hash
# ⚠️ 生產環境必改openssl rand -hex 32 產生 64 字元 hex
VISIONA_STORAGE_SIGNING_SECRET=CHANGE_ME_IN_PRODUCTION_use_openssl_rand_hex_32
# ============================================================
# Model 上傳限制
# ============================================================
# 單檔上限MB— Phase 0 規範 100 MBPRD §8.4
VISIONA_MODEL_MAX_SIZE_MB=100
# ============================================================
# Pairinglocal agent ↔ remote-proxy 配對)
# ============================================================
# 格式vAc_ + 32 hex見 security.md §1.3
# 建議用vAc_$(openssl rand -hex 16)
# 留空代表雛形 InMemoryPairingStore 會動態配發(前端呼叫 POST /api/pairing/token
VISIONA_PAIRING_TOKEN=
# ============================================================
# Phase 0.8 / 0.8b — 轉檔功能整合converter / FAA pre-shared API key
# ============================================================
# 對齊 .autoflow/04-architecture/conversion.md §3 + ADR-015。
#
# Phase 0.8b 變更:服務間認證從 OAuth client_credentials 改為 pre-shared API key。
#
# 啟用判定4 個欄位ConverterBaseURL / FAABaseURL / ConverterAPIKey / FAAAPIKey
# **全部非空**才視為啟用;任一缺 → 5 個 /api/conversion/* endpoint 不註冊 / 回 501。
#
# Phase 0.8b 移除(不再讀取):
# - VISIONA_OIDC_SERVICE_CLIENT_ID / _SECRETOAuth client_credentials 機制取消)
# - VISIONA_OIDC_TENANT_ID取消 tenant 概念converter 端的 user_id 仍由 visionA 灌入)
# - VISIONA_FAA_DELEGATED_TTL_SECONDSdelegated download token 機制取消,改 server-side stream proxy
# kneron_model_converter task-scheduler base URL
# dev/stagehttp://192.168.0.130:9501
# prodhttps://converter.visiona.cloud
VISIONA_CONVERTER_BASE_URL=
# File Access Agent base URL
# dev/stagehttp://192.168.0.130:5081
# prodhttps://faa.innovedus.com
VISIONA_FAA_BASE_URL=
# Pre-shared API key — visionA → converter 服務間認證Phase 0.8b 新增ADR-015 §3
# 產生openssl rand -hex 3264 字元 hex
# 與 converter 端 CONVERTER_API_KEY env 對齊(雙方獨立持有,嚴格分環境 dev / stage / prod
# ⚠️ 不可 commitprod 用 Secrets Manager / Vaultlog 永遠不印此值全文
VISIONA_CONVERTER_API_KEY=
# Pre-shared API key — visionA → FAA 服務間認證Phase 0.8b 新增ADR-015 §3
# 產生方式同上;與 FAA 端 FAA_API_KEY env 對齊warrenchen 配置)
# 與 ConverterAPIKey **不共用**(每條 trust boundary 各自獨立,避免一處洩漏連坐)
VISIONA_FAA_API_KEY=
# 上傳模型檔大小上限MB— 與 converter 端 limit 對齊
VISIONA_CONVERTER_MAX_MODEL_SIZE_MB=500