# 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](2026-06-19-agent-auth-v2-plan-backend.md) · [agent](2026-06-19-agent-auth-v2-plan-agent.md) · [frontend](2026-06-19-agent-auth-v2-plan-frontend.md)。設計：`../design-agent-auth.md` v2。

## 狀態圖例
⬜ 待辦　🟡 進行中　✅ 完成　🔴 卡住　⏸️ 暫緩

## 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 守門 |

## 整合（等三 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)|

## 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_get` 走 `build_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,只新增可填。✅

---

## §共用契約 / 決策變更 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 一鍵核可」。

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