✅ ARC CLOSED — 2026-08-16 收尾:母案 CM-1188+34 卡全 Done;兩 P0(BUG-1 死透/BUG-2 指紋)真 image 實證修復;188 已用 compose 起含兩 P0 的正式容器。收尾彙整見同資料夾
2026-08-16-FR-064-SUMMARY.md。下方為收尾當下現況快照,保留供回顧。
這份是 living 檔:每棒就地 Edit,只描述「此刻」。歷史脈絡看同資料夾的
FR-064-LOG.md。
| 項目 | 值 |
|---|---|
| 最後更新 | 2026-08-16 晚(第 10 棒 CM-1242:BUG-2 修復+A-4 unlock 真循環+socketio 死透全綠,兩個 P0 出貨阻斷皆已解除,待決策者放行部署) |
| branch | BE feature/FR-064 / LC feature/FR-064(~/Projects/Billows/Audit-Manager/license_center) |
| 母案卡 | CM-1188 https://app.notion.com/p/FR-064-3bc346da4cd081b0afecdac6f11df640 |
| Notion 回寫規範 | docs/claude/notion-issue-tracker.md/CLI scripts/notion_case.py |
| 接手前必讀 | 本檔全文 + design.md §2 決策表 + LOG 最後 block |
三階段落地版戰線第二棒(FR-063 編譯 ✅ → FR-064 防竄改【本案,母案 CM-1188】 → FR-065 Installer)。
首腦=分析決策/協調;實作全走 runner(開 case 給新 session 為主,機械小修才本體 subagent);驗收一律親驗不信自報。
D1–D10 全拍板 2026-08-15,D11 拍板 2026-08-16,完整表在 design.md §2(索引:分層驗證/簽驗共用層/main.py 錨點/唯讀化/雙落點/離線 unlock/邊界承認/4h+jitter 抽查/D9 回報 LC/D10 二次確認/D11 lockdown 殼+forensic 存證+邊界明文)。
| repo | branch | 與 origin | commits(FR-064 範圍) |
|---|---|---|---|
| BE | feature/FR-064 |
ahead 3(未 push:7cc7989e/11c9502e/19a086af) |
頂部見下表 |
| LC | feature/FR-064 |
同步 | 頂部 5 支屬 FR-064:651db9c(T-5.1 unlock token 簽發)、7c2974d(T-6.3 tamper 事件接收 D9)、076c06b(CLAUDE.md migration 鐵則+部署四步)、57c6566(T-2.1 manifest 簽章)、6ce339e(T-1.1 sign/verify_payload 層) |
| FE | feature/FR-064 |
— | 最新 de23772 T-8.2 鎖定頁+interceptor |
BE 最新(新→舊):19a086af CM-1242 掛載宿主 /etc/machine-id 修指紋漂移(BUG-2,未 push)/11c9502e+7cc7989e CM-1241 BUG-1 終止改依承載送訊號(未 push)/0aaa0269 STATE 用語消歧/4e64ca07 CM-1231 殼內容協商改只看 Accept(UA 嗅探讓 XHR 拿到 HTML 的修正)/adf77882 CM-1236 scripts/README 修正/2d56edb6 CM-1238 落地版 docker-compose 定式(api+socketio 雙容器+pki volume)/4c06ddc8 CM-1236 scripts/ 盤點整理/f929abe7 CM-1237 build_all.sh 一鍵管線/be3868f4 STATE/LOG docs/aeb2bd44+24834d73 T-8.3 forensic。更早鏈(T-8.1 殼 520fc5e3、T-5.2 f8133e72、gate/manifest/唯讀化等)見 LOG。
| 卡 | 狀態 |
|---|---|
| CM-1188(母案) | Not started(母案收尾時等令收 Done) |
| CM-1215~1223/1226/1227/1230/1224/1225/1228/1232/1234 | 修正待驗證(1224/1225/1228/1232/1234 已過首腦親驗 2026-08-16 晚) |
| CM-1231/1233 | 修正待驗證(殼+forensic;殼另補內容協商修正 4e64ca07) |
| CM-1236/1237/1238 | 修正待驗證(scripts 盤點/build_all.sh 管線/compose 定式) |
| CM-1229 | 修正待驗證(第 8 棒實機驗收測試=人工實測非自動化測試碼;A-2 FAIL 如實在卡,卡不收 Done) |
| CM-1239 | 修正待驗證(解鎖 SOP 雙份 91a3bcd8:原廠側 unlock-sop.md+客戶側手冊,首腦已親驗,等 user 收 Done) |
| CM-1241 | 修正待驗證(BUG-1 修復,第 9 棒;真 image 端到端已由第 10 棒 V3-① 順帶證實) |
| CM-1242 | 修正待驗證(BUG-2 修復+A-4/socketio 複驗,第 10 棒;兩個 P0 皆解除) |
integrity_tamper_events 表存在確認 ✅(psql -h 192.168.50.188 -p 25432 -d guidant_ai_dev,to_regclass 回表名)。
/opt/guidant_ai:branch feature/FR-064、HEAD 4e64ca07(與本地同步)guidant-api+guidant-socketio 皆 guidant-ai-be:1.14.0、Up (healthy);guidant-api log 有 [INTEGRITY] 產物完整性驗證通過(core+resources 共 1304 檔,version=1.14.0) ✅ gate 真容器實跑通過/srv/guidant/pki/:空(tamper 標記已清)✅guidant-ai-be:1.14.0+:latest 皆 2026-08-16 15:38 新打;Harbor tag 仍是 08-15 舊版(新 image 未推)/opt/license_center:HEAD 651db9c(與本地同步);systemctl is-active license-center → active/var/www/html/audit-manager/assets/index-B6KcAQdr.js 含 INTEGRITY_503001(×2)✅ 鎖定碼已在線上 bundle-c safe.directory=/opt/license_center| 子需求 | 卡 | 狀態 |
|---|---|---|
| .1 簽驗共用層 | CM-1215/1216 | ✅ 驗收過(LC 6ce339e+BE 97bca82e) |
| .2 build manifest | CM-1217/1218 | ✅ 驗收過+三次返工(參數改名 6f37019e、.env fallback 4fe7638a,皆已驗) |
| .3 gate+落點 | CM-1219/1220/1221 | ✅ 驗收過(eba7f5a0/d4ef0285/607b4b3a) |
| .4 抽查+埋點 | CM-1222/1223 | ✅ 驗收過(b4a102b7) |
| .6 唯讀化 | CM-1226 ✅/CM-1227 ✅/CM-1228 修正待驗證 | T-6.1/6.2 過(7079ae24/3f338ffc);T-6.3(D9 回報 LC)已做(LC 7c2974d/BE d181fdc6),三情境實測皆過,待 user 複測 |
| .5 unlock 全鏈 | CM-1224/1225 | 兩張皆已做(本棒),修正待驗證——LC 651db9c(後台頁+CLI+migration 49629d9e5d99 已套 DEV)/BE f8133e72(核銷四關+回執回填 DB);六情境活體實測皆過(含以真簽章分別驗指紋關與效期關),待 user 複測 |
| .7 實機驗收 | CM-1229+修復棒 CM-1241/1242 | 修正待驗證——第 8 棒:A-1/A-1b/A-3/A-5 縮短版+B license 迴歸+E orchestrator PASS,A-2 FAIL 抓出 BUG-1;第 9 棒修 BUG-1(C-1 銷案);第 10 棒:A-2/C-2 死透、A-4 unlock 真循環(含 4 條負面案)、socketio 死透、BUG-2 指紋穩定 全數 PASS。餘 24h 長跑(A-5 完整版)與 4h 真窗 |
| .8 D11 lockdown 殼+forensic | CM-1231/1232/1233/1234 | 四張全數已做,皆修正待驗證——T-8.4 文件/T-8.2 FE(de23772,FE repo)/T-8.1 lockdown 殼(520fc5e3)+T-8.3 forensic 擴充(24834d73+aeb2bd44),本棒合做。BE↔︎FE 整鏈端到端(真殼回 503 → 真瀏覽器 → 跳鎖定頁)尚未跑 |
| 場外 | CM-1230 | .env 稽核回補✅(bfbe6cc9/4b2234ac/f681a583/dc5c1090/9508c7f8) |
188 真 dist 產 manifest(core 1291/resources 13/thirdparty 5492)→ LC 簽章(kid 04b1e65f=STG 鑰)→ BE verify_payload 驗章 PASS → 三層各抽一檔 sha256 與實檔全中。首次端到端全通。
第 7 棒已銷:gate 真 dist/正式 image 唯讀化/真容器 gate/殼真容器。
第 8 棒已銷:殼修正(4e64ca07)後整鏈(真 image BE 側四組協商全對:瀏覽器 UA+JSON Accept 回 INTEGRITY_503001 JSON;FE 側 user 已於第 7 棒實測)/真實已出貨 license 匯入迴歸(三環境三把 kid 真照全過+兩條負控+D2 缺鍵向後相容確認)/T-7.1 五條中的 A-1/A-3/A-5 縮短版/殼在 orchestrator 下的行為(unhealthy 但不進重啟迴圈)。
第 9 棒已銷:C-1 scheduler 觸發×兩承載(scheduler 路徑不同病,修前修後皆 exit=1 死透)。
第 10 棒已銷(CM-1242):C-2 真產物×gunicorn hook 觸發後服務死透(真 image 1.14.0 實測 Handling signal: quit→Shutting down: Master→容器 Exited、ping 000,BUG-1 修正端到端成立)/A-2 運行中改檔觸發(同上,hook 正常判定並終止)/A-4 unlock 真容器完整循環(鎖定→LC 簽 token→放檔→restart 解鎖成功+標記清+nonce 入帳+DB unlocked_at 回填,四條負面案他機/過期/錯事件編號/重放全擋)/socketio 承載死透(PID 1+eventlet+APScheduler 條件下 _termination_signals() 回 []、os._exit(1)、容器 Exited(1))/BUG-2 指紋漂移(掛載後跨 restart/換容器皆不變,附不掛載的負控復現)。
仍未實證:
docker compose up -d recreate 兩容器才生效,等決策者放行_terminate_process();「真 binary 被改→真抽查 job 比對出不符→終止」在 socketio 模式未實跑(8002 不載 REST 故無埋點端點、抽查 4h 且參數寫死不可調)。比對邏輯與 api 共用同一份 runtime_check,風險低但非已驗/etc/machine-id 時 docker 建成目錄的退化行為——屬 docker 已知語意(掛載來源不存在時依目標推斷建立),deployment-env.md 已寫前置檢查,但未在缺檔宿主上實測;同理「machine-id 重生成會讓既有綁機憑證失效」亦為推論(不宜在 188 重生成驗證)症狀:真產物×gunicorn 下 hook 觸發 tamper 後服務沒死透——落點都寫了、FATAL 也印了,但 master 存活並 respawn,服務照常回 200。 真因(第 9 棒推翻原假設):不是「master 收到 SIGTERM 走 graceful 沒死」,而是master 根本沒收到——容器內 pgid=1,killpg(1,sig)=kill(-1,sig) 是 POSIX 廣播特例、Linux 明文排除 PID 1;且 kill(1,SIGKILL) 對無 handler 的 PID 1 也被核心忽略。 修法:_terminate_process() 改依承載分流——gunicorn worker 指名對 master 送 SIGQUIT(有 handler 收得到、quick shutdown 不 respawn),單 process 承載/容器 PID 1 只 os._exit(1),bare metal 僅 pgid==pid 時 killpg。master pid 由 main.py post_fork 登記。 第 10 棒真 image 端到端證實:Handling signal: quit → Shutting down: Master → 容器 Exited、ping 000 ✅
症狀:容器機器指紋每次重啟就變 → 解鎖憑證重啟即失效(永遠解不了鎖)、license 綁機漂移。 根因:Debian base image 的 /etc/machine-id 是 0 bytes 空檔(容器不跑 systemd 永遠填不上)+/var/lib/dbus/machine-id 不存在 → 指紋退到第三級 uuid.getnode()(容器 MAC,每次 restart 就換)。 修法(選項 A,code 零改動):compose 掛 - /etc/machine-id:/etc/machine-id:ro,指紋穩定錨定宿主機。實測跨 restart/換容器皆不變,附「不掛載」負控復現 bug。 ⚠️ 掛的是檔不是目錄——宿主缺檔時 docker 會建成目錄、指紋靜默退回 MAC,症狀與沒掛一樣;deployment-env.md 已補前置檢查。 綁機回歸:DEV 16 列綁舊指紋但全為歷史照(is_current=false),現行照 0 列綁機;STG/POC 皆 null → 無回歸。
相關卡:
7cc7989e/11c9502e/19a086af)→ 188 pull → rebuild image(含 BUG-1 修正的 1.14.0 已在 188 本機,但 BUG-2 是 compose 組態不需 rebuild)docker compose up -d recreate 兩容器)——等決策者放行,非開發期動作91a3bcd8,修正待驗證);CM-1240 FE details 開關已收 Done(29fead0 待 push/部署)⚠️ 本節是建議清單,不是執行授權。接手方讀完盤點完即停,等 user 發令。
rm /srv/guidant/pki/.integrity-tamper+compose 重起)-v /tmp/pki-test:/opt/guidant/pki;要看 FE 就讓它佔 8000(先 docker rm -f guidant-api,測完 compose 起回來)第 8 棒補充(真產物實測校正):
-e DB_NAME=guidant_ai_dev 覆寫——拋棄式容器沿用 /srv/guidant/guidant.env 會指向 STG(該庫無 integrity_tamper_events 表)。啟動 gate 本就不寫 DB,所以只驗 boot 拒啟時不必覆寫;驗 runtime/hook 落點時必須。printf >> /app/guidant-ai 會被 Text file busy 擋下,正確作法 cp → 改副本 → mv 原檔改名 → mv 副本頂上。用前者會誤以為 binary 天然免疫。_HOTPATH_PRIORITY_PREFIXES 那三個 common/* 前綴在真產物中不存在為獨立檔(全編進 binary),manifest grep 零命中 → 選檔退化成只剩 binary 本體。要觸發 hook 偵測,改的必須是 binary。POST /api/1.0/license/upload(需 root tenant(/1/)admin token,一般租戶 admin 會被 GRC_403060 擋在埋點前)或 SSP 報告匯出。第 10 棒補充(CM-1242 指紋與 unlock 實戰):
-v /etc/machine-id:/etc/machine-id:ro,否則指紋每次 restart 就變、unlock token 必然對不上(BUG-2 本體)。加了之後 188 上的指紋恆為 ff793254d00c364107dda07846e5e90e4d4db79fbdbd2a4259fdf24e4a3da080。GET /api/1.0/license/activation/machine-code(需登入+DEPLOYMENT_MODE=host,故拋棄式容器要 -e DEPLOYMENT_MODE=host)② 鎖定時看拒啟 log 的 machine_fingerprint: 行,或 lockdown 殼 503 JSON ③ 標記檔 .integrity-tamper 的 machine_fingerprint 欄位。admin/密碼同 DEV 慣用(blsadmin 是 tenant 102,打埋點端點會被 GRC_403060 擋掉)。--valid-hours(有 1–336 下限防呆,擋在簽發端就中止了,核銷的效期關根本沒執行到)——要用 LC 私鑰 sign_payload 直接造「真簽章但 expires_at 在過去」的 token(load_private_key(Path("/opt/license_center/keys/private.pem"), os.environ["LICENSE_CENTER_KEY_PASSPHRASE"].encode()))。event_uid 寫回 .integrity-tamper 再放已用過的 token。main.py 設計),且抽查 4h 週期參數寫死不可調 → 要驗終止行為得自建 harness 重現承載條件(PID 1+monkey_patch(all=False, socket=True)+socketio.run+BackgroundScheduler)但 mount 真源碼執行(PYTHONPATH=/src)。PYTHONPATH=src → restart,見 memory feedback_lc_deploy_four_steps)