FR-039 Agent 認證 v2 — BE track 進度表 + 共用契約 Log(SSOT)

三份 tracker 分開放各自 repo,避免平行干擾

  • 🟦 BE(本檔,compliance-manager-be):A0 / B1~B6 / 整合 I1·I2 + 共用契約/決策 Log
  • 🟩 Agent(evidence-agent repo):docs/FR-039-agent-auth-v2-tracker.md(G1~G3)
  • 🟪 FE(compliance-manager-fe repo):docs/FR-039-agent-auth-v2-tracker.md(F1~F3)

每個 session 只更新自己 repo 的 tracker;唯一共用 = 本檔下方「契約/決策變更 Log」(BE 動契約時寫,Agent/FE 只讀)。 計劃:backend · agent · frontend。設計:../design-agent-auth.md v2。

§1

狀態圖例

⬜ 待辦 🟡 進行中 ✅ 完成 🔴 卡住 ⏸️ 暫緩

§2

BE 任務

ID 任務 依賴 狀態 commit 備註
Phase 1:device_fingerprint + status 1221c25f
A0 migration(補欄+enroll_tokens 表) + 契約凍結 094afba8/a27d6ee1 三 track 已解鎖
B1 憑證/JWT util(CA 簽 CSR + RS256) + AGENT_AUTH_MODE A0 ace55e01 10 單測綠
B2 註冊 token 管理(service+endpoint,先一組 active) A0 45406104 per-tenant 一組 active
B3 POST /agents/register(驗 token→upsert→簽 cert) A0,B1,B2 45406104 super-admin scope 顯式帶 tenant_id
B4 POST /agents/heartbeat + online/offline A0 45406104 online=last_seen<interval*3
B5 RemoteAgentAdapter 資料面認證(mode gated) A0,B1 7228c419 mTLS+JWT+指紋+revoked,none 不破
B6 撤銷/重新綁定 endpoint(manager) A0 45406104 admin 守門
§3

整合(等三 track,BE 主導)

ID 任務 依賴 狀態 commit 備註
I1 dev PKI 產生腳本(CA/cloud client cert/JWT keypair) B1 4f5c9525 scripts/gen_agent_dev_pki.py,產物 pki/ 不入版控
I2 端到端驗證(切 full,六情境) 全部 fa0ef1cd/c0fbde9e/ff1fd6b5 對真實 agent(mode=full+nginx mTLS)全通;過程抓出 3 個 bug(見下方 I2 Log)
§4

BE §問題 / 阻塞 Log

[日期][task] 問題 → 狀態/解法

  • [2026-06-19][I2] 上傳 mTLS 400 → httpx 0.28 request(cert=) TypeError、Client(cert=) 靜默不送 client 憑證 → 改自建 ssl.SSLContext+load_cert_chain+verify=ctx(c0fbde9e)。✅
  • [2026-06-19][I2] 下載/預覽 打到別 tenant 的 agent(.122) → 下載端點無 @jwt_required → 無 tenant → storage-config(super-admin scope)抓錯 agent → 改走「檔案自己記的 agent」(entity.path)路由(ff1fd6b5)。✅
  • [2026-06-19][follow-up] _to_https 無腦改 scheme 不換 port → 指到純 http agent 變 SSL WRONG_VERSION_NUMBER,應明確報設定錯。✅ 已修(_https_base:非 https 直接報 FILE_AGENT_500005,不再硬轉)
  • [2026-06-19][follow-up] MinIO 持久化 → 測試中 bucket 舊物件消失(agent↔︎MinIO 環境,非本 code)。⬜ 非阻塞,agent ops
  • [2026-06-19] 健康檢查 full 模式紅燈 → check_health 用裸 httpx.get 過不了 nginx mTLS(CERTIFICATE_VERIFY_FAILED)。✅ 已修:full 模式走 build_cloud_mtls_context(client 憑證+內部 CA);抽 common/util/agent_auth/tls.py 與 adapter 共用。
  • [2026-06-19][follow-up] reconcile 對帳 full 模式 → 同款裸 httpx 問題,且 /blob/*/sha256 還需 JWT。✅ 已接(3814c57c):抽 _agent_sha256_getbuild_cloud_mtls_context+jwt_util.mint(對齊 adapter);error 桶補 log。FE(26a1040)補 toast「異常」顯示。⚠️ 待 BE 重啟後對帳重測驗證
  • [2026-06-21][設計] 編輯 agent base_url 唯讀(FE ac47eb1) → full 模式 agent 自報 base_url + 憑證 SAN 綁定,雲端編輯無效還會斷 mTLS → 編輯時 disabled,只新增可填。✅

§5

§共用契約 / 決策變更 Log(唯一跨 track 共用區,BE 寫、Agent/FE 讀)

動到 plan-backend §1 契約或設計決策時,BE 在此記一筆並標影響哪些 task,Agent/FE 開工前先看這。 [日期][誰] 變更 + 影響

  • [2026-06-19] A0 契約凍結:register/heartbeat/cert(CSR)/JWT claims/X-Agent-Fingerprint/token 管理/revoke/rebind 介面定於 plan-backend §1。

  • [2026-06-19] 設計定案:token 先一組 active(表保留 label+enabled)、新設備自動 enroll 不審核、退場(手動新增/測試連線/對帳)保留並存、連線方向不動(tunnel 另案)、即時 UI/封裝延後。

  • [2026-06-19] BE B1~B6 實作補充(契約細化,不破壞 §1,影響 Agent/FE)

    • 權限=RBAC 控管(選單/路由層,非每個人都有),BE service 層不硬卡角色(對齊既有 agent CRUD,僅 @jwt_required)。影響 FE(F1~F3):token 管理 / 撤銷 / 重新綁定鈕靠 RBAC 選單權限顯示即可,不需前端自己判 is_admin、也不會收 manager 403。register/heartbeat 不走 user JWT(token/mTLS)。
      • ⚠️ 更正(69c71abc):原 B2/B6 一度加 is_admin 硬檢查 + FILE_AGENT_403001 MANAGER_REQUIRED,已移除;REVOKED/FINGERPRINT_MISMATCH 重編號為 403001/403002
    • register resp 在 mode=none 或無 CSR 時,certificate/ca_certificate/jwt_public_key空字串(非省略)。影響 Agent(G):空=沒拿到憑證,維持明文(demo);有值才落地跑 mTLS。
    • register name 由 BE 從 hardware_info.hostname(fallback agent-<fp前12>);Agent 不需送 name,但建議 hardware_info 帶 hostname。
    • heartbeat req 的 status 欄目前被接受但忽略(online/offline 一律由 last_seen_at 推算,門檻=heartbeat_interval*3=預設 900s)。Agent 照送無妨。
    • 撤銷狀態語意:remote_agents.status = active/revoked;heartbeat 對 revoked 回 403 FILE_AGENT_403002;資料面對 revoked / 指紋不符回 403(..._403002/..._403003)。
    • 新 error code:FILE_AGENT_400003~400006(CSR/device_uuid/token/fingerprint 必填)、401001(token 無效)、403001(revoked)、403002(指紋不符)、500004(認證未設定)。無 manager 403(權限走 RBAC)。
    • 清單/detail resp 新增欄位:last_seen_at / agent_version / hardware_info / online(布林,推算)。FE 狀態欄可直接用 online + status
  • [2026-06-19] 跨 repo 契約對齊(I2 前置 1~3)

    • FE(16c662b):撤銷/重綁註解改 RBAC(不再寫 manager 後端檢查);error-code.json(en/zh-tw)補 FILE_AGENT_* 全碼。FE 無前端 is_admin 硬 gate(靠 RBAC 選單),不需改。
    • Agent(evidence-agent d71af4e):register 回空憑證(雲端 mode=none/未設 CA)時不寫空 cert 檔,改丟錯重試 —— 避免 is_enrolled 誤判 + 資料面拿空金鑰炸。
    • 🔴 未解契約落差 — rebind body:FE POST /remote-agents/<uid>/rebind 沒送 body,但 BE B6 要求 device_fingerprint(缺→400 FILE_AGENT_400006)。rebind UX 待定:新指紋從哪來?(admin 從 /agent-info 貼 / 從新自動註冊的列挑 / auto-merge)。待 user 決策後補 FE Dialog 輸入 + BE 視情況調整。I2 rebind 情境在此之前會失敗
  • [2026-06-19] 🔶 身分模型 v3(決策 B) — rebind 退場,身分改 agent_uid(影響 Agent+FE,契約變更):

    • 身分主鍵 = 伺服器發、agent 持久化的 agent_uid(非 device_fingerprint)。指紋降為可更新屬性。
    • register 契約:req 加 optional agent_uid(re-auth 帶回;首次空,雲端發)。雲端 by uid 找列(驗 tenant)→ 退指紋去重 → 都無才新建;指紋變 → 更新同列 + 稽核。
    • heartbeat 契約:req 加 optional agent_uid;雲端 by uid 找列;找不到回 404 → agent 須清本地狀態自動重新 enroll;指紋變自動更新。
    • rebind 退場:FE 鈕移除;BE /remote-agents/<uid>/rebind 留 dormant(特殊維運手動覆寫)。換硬體不再需要人工動作。
    • 稽核:指紋變動記 FILE_AGENT_FINGERPRINT_CHANGED(event_code 6082)。
    • Agent 已配合(evidence-agent 0207734):心跳帶 agent_uid + 404 自動重 enroll。FE 已配合(137f203):移除 rebind 鈕。
    • commit:BE 3b6ffde3、Agent 0207734、FE 137f203;決策紀錄 docs/analysis/2026-06-19-fr039-agent-identity-v3-uid-vs-fingerprint.md
    • 安全取捨:指紋變動自動接受 + 留痕(dev/demo);未來可加「標 pending → admin 一鍵核可」。
§6

§更新規範

  1. 開工把 task 改 🟡;完成改 ✅ 填 commit;卡住改 🔴 + 寫問題 Log。
  2. BE 動到 §1 契約 → 先在上面「契約/決策變更 Log」公告,再改 code(Agent/FE 才跟得上)。
  3. 各 session 只 commit 自己 repo 的 tracker。