# FR-175 機制問答首腦：交接現況（STATE）

> **這份是 living 檔**：每棒就地改，只描述「此刻」。每一棒做了什麼看同資料夾的 `fr175-LOG.md`。

| 項目 | 值 |
|------|-----|
| 最後更新 | 2026-11-03（FR-172 結案時改寫「執行流程」頁為主系統與 Agent 三張循序圖；下一棒從「密碼」討論開始） |
| 問答頁 | `qa/mechanism-qa.html`；線上 https://claude.ai/artifact/AmUhtjZVptzoqExGEUhjhx （第 29 版：執行流程改寫，2026-11-03） |
| branch | BE `feature/FR-172`（session 中途工作樹被別的 session 切過來，本區 commit 都在這）；全部未 push |
| Notion | 無卡。本區是問答與整理，不是實作案 |

## 🧭 這個角色是什麼

決策者與 PM 會連續問「某件事是怎麼運作的」。這個 session 負責**回答並整理成問答頁**，每個說法要對回程式碼那一行或三台主機的實查結果。範圍不限線上更新：安裝、發版、主機與容器、密碼、設定檔都問過，之後問到別的機制也放這裡。

**修程式、出包、發布、改各案 STATE／LOG 都不是這裡的事。** 發現的問題寫進問答頁「發現的問題」並告訴決策者，由各案首腦開卡。

## 權限與界線

- **可以做**：讀任何 repo、grep、讀本機 DB（`SELECT`）；`ssh` 唯讀查 188／189／190 與 Agent（`docker ps`／`inspect`／`logs`／`exec … cat`、`ss`、`systemctl status`、`journalctl`）；查更新站 D1（`wrangler d1 execute --remote` 只下 `SELECT`）；改並 commit 本資料夾的檔與問答頁；發布問答頁到線上網址。
- **不要做**：改程式、出包、發布更新站、改其他案的 STATE／LOG 或 README／FINAL-SPEC、寫入任何主機或資料庫、碰 188 STG／189 POC 的狀態、push。
- **讀設定檔**：主機上的 `.env`、`guidant.env` 只准列**鍵名**（`sed -n 's/^\([A-Z_][A-Z0-9_]*\)=.*/\1/p'`），不讀值；比對兩台密碼是否相同只比雜湊前幾碼。
- **回答紀律**：一題一答，先結論再事實；查不到就說查不到；有問題用「問題、建議、理由」三段；白話、繁體中文。決策者會邊問邊插話：**說「先別寫」就只回答不改文件**，問完一輪說「更新文件」才一次寫。
- **不要先動手**：交接讀完回報「已讀完，可以開始問」就停，不先去查別的。

## 問答頁現況

維護做法（新增一題、畫圖、發布、權限、驗證）全在 [維護手冊](../GUIDE-qa-maintenance.md)，這裡只列現況。

| 題 | 內容 |
|---|---|
| Q6 ＋2 補問 | 安裝八步與 root；公鑰為何在 `/etc/guidant/`；安裝目錄樹狀圖與 189、190 對照 |
| Q7 ＋3 補問 | 密碼與金鑰；`.env` 密碼全表、業務帳號＝`cm_app`；加密 `.env` 擋不住什麼與三層建議；docker 身分、rootless 與容器內非 root、入侵處置 |
| Q21 | docker compose 十個服務、`.env` 27 鍵、`guidant.env` 39 鍵、掛載、優先序與陷阱 |
| Q8 | 發版流程：現行三階段、CI 五 stage、實況（1.27.x 是 188 手跑出包）；改版後 FR-172 Agent 分開出版、FR-173 三條 CI 與 Vault 簽章；四張圖 |
| Q9、Q11 ＋2、Q12 ＋1 | 打一次密碼；預簽網址與長相；主版號兩道門與 2.0 授權模式 |
| 執行流程 ＋1 補問 | 升級循序圖、時間軸、逐步說明；升級觸發機制（更新單、systemd 監看、升級腳本組成、為何不用升級容器） |
| Q13～Q19 | 複製資料庫、快照、停什麼（Q15 ＋1 補問：資料庫停兩次）、清理、log、進度檔、健康檢查（Q19 已更正：不登入、驗 401） |
| Q5·Q20 | 備份現況 |
| 待討論 | 9 條 |
| 發現的問題 | 13 條；**最優先**：升級失敗時錯誤原文進不了系統（另開卡，出貨前修） |

**決策者對問答頁的要求**（不要改回去）：頁面固定亮色；時序圖用 `sequenceDiagram`＋`autonumber`＋階段橫條；目錄結構用樹狀文字圖；主欄 1180px；標題要專業不要口語；補問不分頁；Q8「現況打折」段不放；Q18 維持原文；本機要能直接開、圖可點擊放大。

## 已查實事實（回答時可直接用，不必重查）

1. **「有沒有新版」只看版號**：更新站、主系統、主機預檢都是 semver 逐段比數字；同版號重編不算新版；`guidantai upgrade --force` 可同版重跑。
2. **升級中畫面**：前門 `guidant-fe` 全程不停；探不到 `/api/1.0/version` 就送 `maintenance.html`；維護頁每 3 秒讀 `/upgrade-progress.json`，nginx 直接 alias 到主機進度檔。
3. **單支 API 502 不跳維護頁**：只有更新中心在升級中、輪詢拿到閘道錯誤才整頁 reload。
4. **主機接單**：`guidant-upgrade-order.path` 監看 `orders/`，觸發 `.service` 以 root 跑 `guidantai upgrade --from-order`。
5. **升級 log**：逐行輸出在 journal；migrate 另落 `log/upgrade-migrate-<單號>.log`。
6. **system_logs**：換接後每分鐘排程補寫 6239～6247＋6248，只寫一次。
7. **診斷包**：收 24 小時 system_logs、最近三支 upgrade／install log、`last-switch.json`；只收 `guidant.env`（遮罩），不收 `.env`。
8. **Agent 升級結果回報更新站**：1.27.9 起代送，真環境未驗。
9. **Agent 檢測 Profile**：心跳帶清單，1.27.10 起多帶 `tool`、`name`。
10. **draining 是兩件事**：worker 不撿新單（`app/background_job/worker_loop.py:78`）＋Agent 不派新任務（`core/plugins/_agent_commands.py:34`）；後端順手查檔不監看；不擋 API。
11. **更新單**：`orders/<單號>.json`，主機開跑前就刪；單上無路徑無指令，主機只用版號在 `incoming/` 拼路徑重驗。
12. **進度檔重置時機**：每次開始升級，以及每次新包下載完或離線上傳完的自動預檢（`scripts/installer/lib/upgrade_order.sh:251`）。歷史在資料庫 `product_update_jobs`，更新中心有「更新紀錄」表。
13. **資料庫停兩次**：第 3 步停、`cp -a` 冷複製；第 4 步 `env GUIDANT_PGDATA_DIR=…pgdata-next` 臨時覆寫 compose 掛載、反查後跑 migration；第 6 步再停、先改 `.env` 再改名回 `pgdata`。
14. **健康檢查**：不登入；`/version` 版號、隨機帳號登入驗 401 `LOGIN_401001`、三支 menu／status 不帶 token 驗 401（`upgrade_health.sh:33`）。不驗業務資料。
15. **失敗原因去向**：system_logs 只有預寫一句＋退出碼；受檢容器 docker logs 隨容器刪除消失；`upgrade_apply_on_copy.sh:107`、`upgrade_rollback.sh:75／:84` 丟 `/dev/null`。
16. **第三方映像**：隨包出貨、build 機釘 digest、出包有弱點放行門；已升過兩次（`6a25bfe5b`、`30be617e8`）；PostgreSQL 16 支援到 2028-11。
17. **發版實況**：1.27.x 是首腦在 188 手跑 `/home/jedi/fr167-build-<版>.sh`（`build_bundle.sh --skip-prod-key-check`）＋本機 `publish-<版>.sh` 用測試章簽；CI `package` 段未跑過正式出版。
18. **FR-172／173 進度**（11-02）：FR-172 B-0／U-1／U-2／A-0／H-1／H-2 已做，在 `ws-CM-3401` 未合回；Vault 192.168.50.172 已架未接 GitLab；FR-173 未開卡。
19. **`api_logs_old`／`system_logs_old`**：188、189 有（189 共 206 MB），190 無；決策者裁刪，建議寫 migration；本區未執行任何寫入。
20. **`guidantai` 一律要 sudo**：讀 600 設定檔＋操作 docker；決策者同意維持。
21. **出貨包內容**（現行）：`install.sh` 與輔助腳本、`docker-compose.yml`、`images/`（自家 be／fe／init、分類服務、postgres、redis、seaweedfs、bugsink 全部 `docker save`）、`vulndb/`、`agent/`、`docs/`、`sbom/`（`scripts/build/build_bundle.sh` 檔頭）。FR-172 後主系統包拿掉 `agent/`。

## 🔴 下一棒：密碼討論的起點

決策者 11-02 說「想來討論一下密碼的問題」，還沒開始問。**問到什麼答什麼，不要先寫文件。**

**客戶主機上的密碼**

| 密碼／金鑰 | 怎麼產生 | 放哪 | 每台不同？ |
|---|---|---|---|
| 資料庫 `cmmgr`、`cm_app`，Redis 四組，物件儲存，分類服務 token，錯誤追蹤站 Django 金鑰與庫密碼 | 安裝時 `gen_pw`：`openssl rand -base64 24` 去掉 `/+=` 取 24 字（`install.sh:204`）；客戶可用設定檔自帶 | `.env` 明文，600 root | 是 |
| JWT、三把資料欄位加密金鑰 | `gen_key`：`openssl rand -base64 32`（`install.sh:210`） | `guidant.env` 明文，600 root | 是 |
| 系統管理員 `admin` 登入密碼 | 有填 `GUIDANT_ROOT_ADMIN_PASSWORD` 就轉 bcrypt；**沒填用 `scripts/init/06-admin.sql` 內建預設雜湊** | 資料庫使用者表 | **沒填時每台相同** |
| 錯誤追蹤站管理員 | **出包時烙進包**：CI 變數 `GUIDANT_BUGSINK_ADMIN_PASSWORD` → `gen_bugsink_admin_bake.sh` → `bugsink-admin.secret` | 主機 `/srv/guidant-ai/bugsink-admin.secret`，600 | **同一版包的所有客戶相同** |

**開始前先跟決策者對齊**：FR-173 design §1 D8 寫「客戶主機預設密碼（DB、Redis、Bugsink 每台同組）」，與現況不符。實查 DB、Redis 每台隨機；真正每台相同的是**預設 admin 雜湊**與**錯誤追蹤站管理員**。

**已知結論與缺口**

- 加密 `.env` 擋不住 root 與磁碟；建議三層：手冊要求全碟加密、密碼改掛檔案、有 TPM2 才用 systemd-creds（問答頁 Q7 補問 2）。
- `sudo guidantai rotate-credentials` 換資料庫兩帳號與 Redis；**不換** JWT、三把加密金鑰、錯誤追蹤站、物件儲存。
- 首次登入強制改密碼：jedi-iam 有 `need_change_password`（`jedi_iam/app/service/login_service.py:66`），**還沒查**預設 admin 是否被標成必改。
- 遮罩：診斷包、進度檔、log 遮 `PASSWORD=`、`SECRET=`、`TOKEN=`；錯誤追蹤 SDK 堆疊區域變數是已知盲區。
- 不給客戶印明文密碼的 CLI：`guidant credentials` 已移除。

**還沒查、可能會被問到**：預設 admin 有沒有強制首次改密碼；`rotate-credentials` 改兩個檔是否原子；物件儲存 `s3.json` 與 `.env` 兩份怎麼保持一致；Agent 本機管理員密碼怎麼產生（189 兩台 Agent 都提示尚未設定）；密碼複雜度與過期政策在哪設。

## 讀序

1. 本檔，特別是「下一棒：密碼討論的起點」。
2. [README](../README.md) 的結論與需求討論紀錄。
3. 問答頁 Q7（含三則補問）與 Q21：本機開 `qa/mechanism-qa.html#q7`，或線上網址加 `#q7`。
4. 要改問答頁前讀 [維護手冊](../GUIDE-qa-maintenance.md)。
5. 有需要再讀 `docs/features/FR-173-2611-release-pipeline/design.md` §1 D8、`scripts/installer/install.sh:200～215`、`scripts/init/06-admin.sql`、`scripts/build/gen_bugsink_admin_bake.sh`。
