repo:
compliance-manager-be。依據design-agent-auth.mdv2。 BE 擁有共用契約 + DB migration(A0);FE / Agent 依本文件 §1 契約開發。 全程AGENT_AUTH_MODE=none包覆,不破壞現有 demo;全綠才切full。 已完成:Phase 1 —remote_agents加device_fingerprint(=device_uuid) +status(active/revoked)。 📋 進度回填:開工/完成/卡住都更新 TRACKER;動到 §1 契約必在 TRACKER 契約 Log 公告。
POST /api/1.0/agents/register(bootstrap:TLS + token,免 mTLS)
// req
{ "token":"<tenant 註冊token明文>", "device_uuid":"<sha256>", "base_url":"http://<agent>:8080",
"hardware_info":{"hostname":"..","os":"..","ip":".."}, "agent_version":"0.1.0",
"status":"registering", "csr":"<PEM CSR>" }
// resp
{ "agent_uid":"..", "certificate":"<PEM 簽好的 server cert>", "ca_certificate":"<PEM>",
"jwt_public_key":"<PEM>", "heartbeat_interval_sec":300 }
POST /api/1.0/agents/heartbeat(mTLS:agent 持註冊時拿到的 cert 當 client cert)
// req { "device_uuid":"..", "status":"online" } // resp { "status":"ok" }
Authorization: Bearer <JWT>;JWT(RS256) claims:iss=compliance-cloud, aud=<agent_uid>, exp≈now+60s, op, tenant_id, bound_fp=<device_uuid>。X-Agent-Fingerprint: <device_uuid>。GET /api/1.0/agents/enroll-token → {exists, label, created_at}(不回明文)POST /api/1.0/agents/enroll-token → 產生/重新產生 → {token:"<明文,僅此次>", endpoint}POST /api/1.0/agents/enroll-token/revoke → 停用POST /api/1.0/remote-agents/<uid>/revoke | /unrevoke → 改 statusPOST /api/1.0/remote-agents/<uid>/rebind → 更新 device_fingerprint(manager;硬體換新後續同一 agent_uid)scripts/sql/2026-06-19-fr039-agent-enroll.sql:
remote_agents 加 last_seen_at TIMESTAMP NULL、agent_version VARCHAR(50) NULL、hardware_info JSONB NULL。compliance.remote_agent_enroll_tokens:id, uid, tenant_id, token_hash VARCHAR(64), label VARCHAR(100), enabled BOOLEAN DEFAULT true, created_at/created_user/...;index(tenant_id);GRANT cm_app + sequence;RLS policy 比照 remote_agents(per-tenant);schema_migrations。remote_agents 補三欄;新建 RemoteAgentEnrollToken 全套(model/entity/query/mapper/repo,放 domain|infra/remote_agent/...)。common/util/agent_auth/ca.py:載入內部 CA(cert+key 路徑來自 config/secret);sign_csr(csr_pem, san) -> cert_pem。common/util/agent_auth/jwt_util.py:mint(agent_uid, op, tenant_id, bound_fp) -> jwt(RS256,雲端私鑰,exp 60s);(驗證在 agent 端)。AGENT_AUTH_MODE(none|full)、AGENT_CA_CERT/KEY、AGENT_JWT_PRIVATE_KEY、AGENT_CLOUD_CLIENT_CERT/KEY 路徑(走 env/secret,不入版控)。generate(tenant)(亂數 token→存 hash→回明文一次)、get_current(tenant)、verify(token)->tenant_id、revoke。/agents/register(enrollment)verify(token)→tenant → 算/收 device_uuid → upsert remote_agents by (tenant_id, device_fingerprint)(寫 base_url/hardware_info/agent_version/last_seen_at=now/status=active) → sign_csr(B1)簽 server cert → 回 §1 resp。/agents/heartbeat + online/offlinelast_seen_at + status。is_online(last_seen_at, interval) = now - last_seen < interval * 3(或固定 30 分);清單 / detail 回傳時附 online 布林。cert=(client_cert,key) + verify=ca + Authorization: Bearer mint(...) + 讀回應 X-Agent-Fingerprint 比對該 agent device_fingerprint(不符→稽核+擋) + 呼叫前 status != revoked 檢查。infra/upload_file/remote_agent_adapter.py。revoke/unrevoke(status)、rebind(更新 device_fingerprint,manager 權限)。common/code/error_code.py 既有 FILE_AGENT_* 沿用 + 新增 enroll/token 相關(<前綴>_<HTTP><序>)。test/。