依 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)。
6.1 KiB
6.1 KiB
ADR-009:visionA Agent 的 Pairing / Session Token 儲存策略
狀態
Accepted — 2026-04-22
背景 (Context)
visionA Agent 需要持久化存放兩種 token:
- Pairing Token(
vAc_+ 32 hex):使用者貼上的配對憑證,15 分鐘內有效、一次性,僅短暫存在 - Session Token(
vAs_+ 64 hex):長期 90 天,agent 日後重連都用它,核心安全資產
儲存要求:
- 機密性:token 等同連線權限,若明文保存被任何能讀檔的 malware 拿走 = 裝置被劫持
- 跨平台:macOS / Windows / Linux 都要能用
- 不依賴 root / sudo:安裝必須是使用者權限
- 不依賴雲端:離線時也要能讀 token 來嘗試重連
- 易於清除:「重置所有設定」要能乾淨清除
OS 原生方案
| 平台 | 原生方案 | 安全強度 |
|---|---|---|
| macOS | Keychain (Security Framework) | 高;解鎖鑰匙圈後僅當前使用者可讀 |
| Windows | Credential Manager (Wincred) / DPAPI | 高;DPAPI 綁定使用者帳號 |
| Linux | Secret Service / libsecret(GNOME Keyring / KWallet) | 中;需要使用者 session 解鎖;SSH 登入可能沒有 |
Go 生態 keyring library
github.com/99designs/keyring— 抽象 3 平台,API 清爽,但有 CGO 依賴github.com/zalando/go-keyring— 純 Go,但 Linux 只支援 Secret Service(無 KWallet)github.com/keybase/go-keychain— 只 macOS,不跨平台
決策 (Decision)
雛形階段(Phase 0)
採 AES-GCM encrypted file + OS machine ID 衍生 passphrase。不接任何 OS keychain。
設計:
// internal/tokenstore/encrypted_file.go
type EncryptedFileStore struct {
path string // ~/Library/Application Support/visionA Agent/tokens.enc 等
passphrase []byte // sha256(machineID || app_salt)
}
// 用 AES-256-GCM:nonce 隨機,認證標籤保護完整性
// 明文結構:
type tokenBlob struct {
SchemaVersion int `json:"v"`
SessionToken string `json:"session_token,omitempty"`
PairingToken string `json:"pairing_token,omitempty"`
AccountEmail string `json:"account_email,omitempty"`
LastPairedAt time.Time `json:"last_paired_at,omitempty"`
}
machineID 來源:
| 平台 | 來源 |
|---|---|
| macOS | ioreg -rd1 -c IOPlatformExpertDevice 的 IOPlatformUUID |
| Windows | Registry HKLM\SOFTWARE\Microsoft\Cryptography 的 MachineGuid |
| Linux | /etc/machine-id 或 /var/lib/dbus/machine-id |
app_salt 是 build 時寫進的常數(非秘密,只是讓 passphrase 不只依賴 machineID)。
Phase 1
切換到 OS 原生 keychain:github.com/99designs/keyring(CGO 可接受,因為 visionA Agent 本來就要編 Wails = CGO 大戶)。
TokenStore interface 設計讓 Phase 1 只需換實作:
type Store interface {
GetSession() (string, error)
SetSession(token string) error
GetPairing() (string, error)
SetPairing(token string) error
Delete(kind string) error // "session" | "pairing" | "all"
SetMetadata(m Metadata) error
GetMetadata() (Metadata, error)
}
考慮過的替代方案
| 方案 | 優點 | 缺點 | 排除原因 |
|---|---|---|---|
| 明文 JSON file | 零成本 | malware / 同機其他使用者可讀 | 不可接受 |
| 一次做完 OS keychain 3 平台(雛形) | 最終狀態 | 跨平台測試成本高;Linux Secret Service 在 headless session 可能沒 daemon;CGO 編譯複雜度 | 雛形先簡化,分階段 |
| 只做 macOS keychain(因為雛形開發環境多半是 macOS) | 開發順 | Windows / Linux 仍需 fallback,等於還是要 encrypted file | 不如雛形就統一 encrypted file |
| 讓使用者輸入 passphrase | 真正的 Zero-Knowledge | UX 慘 — 每次重啟 agent 都要輸入 | 違反「開了就忘掉它」定位 |
| 雲端存 token(只讓 agent 本機用 refresh token 換) | 洩漏立刻可撤銷 | 需要 agent 本地仍存 refresh token — 問題沒消失;離線情境壞掉 | 沒解決根本問題 |
後果 (Consequences)
正面影響
- 雛形開發快:encrypted file 純 Go,沒有 CGO / OS API 依賴,所有平台一視同仁
- 抽象清楚:
TokenStoreinterface 讓雛形 / Phase 1 切換零程式碼影響 - 完整性保護:AES-GCM 自帶 auth tag,檔案被竄改會 decrypt 失敗
- 合理的威脅模型:malware 拿到檔案還得拿到 machineID,且不同機器的檔案不互通(跨機器複製檔案會解不開)
負面影響(接受的取捨)
- machineID 可被同機 malware 讀取:已經 escalate 到能讀 user home 的 malware 幾乎等同於整台淪陷,此時 token 其實是次要損失
- 備份難題:使用者備份了
~/Library/Application Support/visionA Agent/到新機器 → token 無法 decrypt,必須重新配對(這其實是安全特性,不是 bug) - Phase 1 遷移需要一次性資料遷移:升級後第一次啟動時,讀舊 encrypted file + decrypt + 寫入 keychain + 刪舊檔
風險
- machineID 取得失敗:極端情況(沙盒 / 非標準 OS)→ fallback 到「隨機產生一次、寫在明文 checksum file」,此時檔案被竊 + passphrase 檔被竊才破(仍比單純明文好)
- Linux 多使用者共用機器:machineID 相同但 file 在各自
~/.config/,OK - 跨 Linux distro(WSL2 / Flatpak)machineID 取得差異:TODO 在 Phase 0 測試三大 distro(Ubuntu / Fedora / Arch)
雛形的明確限制(寫給未來)
- 雛形不做 token rotation(Session Token 90 天內一直用同一個;過期才換)
- 雛形不做「kill switch」(強制撤銷所有 agent)— 需 Phase 1 backend 支援
- 雛形不做使用者提示「你的 token 在這個檔案裡」— 太技術、嚇人;Phase 1 再評估
合規性
- 雛形不弱於明文檔案
- Security review:Phase 1 上線前必做
- Phase 1 遷移工具:TODO
相關文件
.autoflow/04-architecture/security.md§9(Secret 管理).autoflow/04-architecture/visiona-agent-tdd.md§9(Token 儲存策略)- ADR-003(Pairing Token 兩階段)