FR-065 落地版 Installer — STATE(living,只描述此刻)

這份是 living 檔:每棒就地 Edit,只描述「此刻」。歷史脈絡與決策軌跡見同資料夾 FR-065-LOG.md

項目
最後更新 2026-08-19 上午(首腦第四棒交棒:v1.15.0 進版+Harbor 出貨+188 升級驗證;工程全收,剩 PROD 鑰/T-4.2/收尾)
branch BE/FE 皆已 merge main、與 origin 同步、tag v1.15.0 已推;LC/test 無涉
母案卡 CM-1189(In progress)
子需求卡 CM-1243(.1)/CM-1244(.2)/CM-1245(.3)/CM-1246(.4)
Notion CLI python3 scripts/notion_case.py get/status/append/query(見 memory reference_notion_case_cli)
接手前必讀 §0 讀序

🧭 Arc 定位(WHY)

落地版三階段戰線第三棒:FR-063 Nuitka 編譯 ✅ → FR-064 防竄改 ✅ → FR-065 Installer【本案,母案 CM-1189】

前兩案產出了「防竄改的 BE image+compose 定式」,但那是給我們自己人用的。本案要把它們包成**「客戶拿到一包東西,自己裝得起來、開得了通、升得了版」**的落地安裝方案。四棒拆分:

  • .1(CM-1243)full-stack compose 地基
  • .2(CM-1244)建庫+install.sh+出貨 bundle
  • .3(CM-1245)開通精靈+升級
  • .4(CM-1246)手冊+真機驗收
  • .5(CM-1275,2026-08-18 追加)SeaweedFS 物件儲存替換(D14)

角色定位

本 arc 首腦 session=分析決策/協調:需求釐清、拍板、拆卡、派工單、親驗抽查(不信 subagent 自報)。實作全走 runner session——派工只給卡號一句話,內容都在卡裡。push 永遠 user 本人做

§0 接手讀序

0.1 🔒 先懂需求 gate(全讀,讀完才能碰任何東西)

  1. 本檔 🧭 WHY 節+「重大定案速記」節
  2. docs/features/FR-065-2608-onprem-installer/design.md §2(決策表)+§5(拆分表)全讀;要細節再開 §4
  3. notion-fetch CM-1189+對應子需求卡

0.2 現況與待辦

  1. 本檔 §1 當前狀態、§3 待辦地圖
  2. 本檔 §8 .1 棒實測發現(四條實證,其中兩條推翻既有文件記載——不讀會照著錯的前提做)
  3. FR-065-LOG.md 最後 2 個 block

0.3 ✅ 冷接自檢(答不出來回 0.1,不要碰任何東西)

  1. 本案要解決誰的什麼問題?(客戶自裝自開通自升版)
  2. 四棒各自產出什麼?
  3. D10 為何是「純 bash+Web 設定精靈」混合式?
  4. D13 root 的定位是什麼?殘項是什麼?

§1 當前狀態(2026-08-19 上午,首腦第四棒交棒)

v1.15.0 已進版並出貨到 Harbor;FR-065 工程面全數收口,只剩 PROD 簽章鑰+T-4.2 驗收+全案收尾。 所有工作卡(含實測回修)皆 Done——本棒一次收 15 卡+CM-1302,Notion 無「修正待驗證」殘留。母卡 CM-1189/子需求卡 CM-1246(.4)/CM-1275(.5)維持 In progress,待全案收尾時收。

面向 實況
版本 v1.15.0 已發版a5e32bb9):RN+pyproject+FE package.json+spec 快照 docs/specs/v1.15.0/ 四步齊;BE/FE 兩 repo 已 merge main、tag v1.15.0 已打並由 user 推上 GitLab
Harbor 已推harbor.jedicogy.com.tw:8081 專案 guidant-ai):be:1.15.0=latest(sha256:d558ada9…)/fe:1.15.0(cd9bb261…)/init:1.15.0(207a2f76…);pull 回比對 digest 複驗通過
188 實測機 已從 1.14.0 升級至 1.15.0(install.sh --upgrade 正規流程,非重裝);六服務 healthy、/api/1.0/versiona5e32bb9;資料全保留(admin/blsadmin/兩租戶/SMTP 列/8 capability 旗標)。升級前備份在 /srv/guidant-ai/backup/
PROD 簽章鑰 🔴 唯一未做的出貨前置——public_keys.py 現有 DEV/STG/POC 三把、無 PROD。build_bundle 會擋(內測包需 --skip-prod-key-check)。四步一組見 §3
手冊 T-4.1 收(0f056776,11 章);CM-1302 另補「同版號重套 --force」段
seed 鐵則 改基線庫必跑 gen_seed_sql.sh regenerate+commit(scripts/init/README.md)
jedi jedi-file-upload 0.0.22/jedi-project 0.0.8/jedi-oscal-v2 2.2.3;lock 內 pymupdf 歸零
待 push 無(BE/FE 皆與 origin 同步,tag 亦已推)
權限備忘 188 jedi 帳號無 sudo 免密——install.sh(安裝/升級/uninstall)必須由 user 本人執行;Claude 可做 build/打包/docker push/唯讀查驗。docker manifest inspect 亦因 certs.d 私鑰權限失敗,驗 Harbor 改用 pull 比 digest

§2 重大定案速記(不要重新討論;完整背景見 discussion.md 各 D 項)

  • D1 PG/Redis 全包進 compose(外接 DB=客戶自己改註解,不做 --external-db 參數)
  • D2 FE 出 image+nginx.onprem.confHTTPS 憑證由 install.sh 產 per-install 自簽(2026-08-17 補裁,T-2.5 實作;掛載路徑 T-1.2 已釘死,見 §8.4)
  • D3 出貨形式=tar bundle+digest
  • D4 建庫=分段腳本+one-shot init 容器
  • D5 升級四步
  • D6 指紋取得以精靈頁為主
  • D7 PROD 簽章鑰劃入 T-2.6(含 FR-064 SUMMARY §10 出貨遺留:進版、推 Harbor)
  • D8 Turnstile 預設停用
  • D10 純 bash+Web 設定精靈(setup token 防搶注;不做 TUI)
  • D11.1 框架只出 CMMC 2.0(user 備初始化資料);D11.9 debug view 連 DEV 都刪;D11.10 DB 名=guidant_ai(新制無後綴)
  • D13 root=原廠系統殼:root admin 原廠持有,精靈負責建客戶第一個業務租戶

§3 待辦地圖

🔴 下一個動作(三選一,等 user 發令)

  1. PROD 簽章鑰四步(出貨硬前置,缺了客戶匯照必報「未知的 kid」): ① LC 端(188 /opt/license_center)生成 PROD 鑰對——🔴 私鑰只留 LC 端,絕不進 BE repo/bundle/版控 ② kid+公鑰加進 common/license/public_keys.py(現有 DEV 2ce3bb59…/STG 04b1e65f…/POC f3b562a6… 三把) ③ 重 build BE image(公鑰是編譯進去的,不是設定檔——漏這步等於白貼) ④ 用含 PROD 鑰的 image 重跑 build_bundle.sh(此時不必再帶 --skip-prod-key-check) 完成後版號處置需裁示:同版重出(1.15.0 重打包,客戶端升級需 --force)或 bump 1.15.1。

  2. T-4.2 正式真機驗收(CM-1264,範圍已縮減,卡上有定案段):①全鏈一輪用 STG 簽的照即可(不必等 PROD 鑰)②PROD 鑰窄驗證(四步做完簽一張照匯入成功,數分鐘)③負面案只挑「竄改 image tar」+「setup token 重放」。移出項(升級回滾/machine-id 擋下/版號區間/air-gap 登入)引用前幾棒既有驗證,不重跑。

  3. 全案收尾(走 closing-and-handoff skill):SUMMARY 由本資料夾 LOG 各 block 濃縮、母卡 CM-1189+子需求卡 CM-1246/1275 收 Done、spec 與使用手冊盤點、memory 教訓入檔。

上版 checklist(1.15.0 若要上 STG/POC):本版 10 支 migration + 前版遺留三支(fr058-22/fr063-1g/fr064-3)需補套;🔴 依環境鐵律等決策者當次明示才可動 STG/POC。

全案後另排:CM-1274(log 轉拋 Fluent Bit)/CM-1279(儲存後端遷移工具,含 per-tenant key 前綴連動)/CM-1204(手冊下載端點死碼,待裁是否活化)/CM-1265(T-4.3 Windows 評估,user 已裁本次跳過)。

另一條戰線(user 已表示接下來優先做這個)FR-066 檢測 Agent 原生安裝包——母案 CM-1284+16 張子卡(CM-1285~1300)已開好在 Notion,設計文件 docs/features/FR-066-2608-agent-native-installer/(discussion+design 已定案、D8 含 Windows 可行性初判)。方向:agent 從 docker 三容器改成 Nuitka 單檔+tarball+systemd(agent 要貼宿主量測,容器化是反模式)。與 FR-065 無依賴,可平行推進。

⚠️ 本節是建議清單,不是執行授權。接手方讀完盤點完即停,等 user 發令。

§4 關鍵座標

素材 路徑
design(§2 決策表/§4 詳細設計/§5 拆分表) docs/features/FR-065-2608-onprem-installer/design.md
討論稿(D 項完整背景) 同資料夾 discussion.md
DB init 輸入(CM-1207 產出) 同資料夾 db-init-inventory.md
env 契約 docs/features/FR-063-2608-nuitka-packaging/deployment-env.md
FR-064 出貨遺留(進版、推 Harbor→本案 T-2.6) docs/features/FR-064-2608-tamper-detection/handoff/ SUMMARY §10
compose 定式(.1 已擴充為 full-stack) docker/production/docker-compose.yml
compose 變數範本 docker/production/.env.example(含 guidant.env 最小必填對照)
落地版 nginx 設定 FE repo nginx.onprem.conf
落地版 FE build mode FE repo .env.onprem(+npm run build:ONPREM
FE image build 腳本 scripts/build/build_fe_image.sh
HTML 開啟方式 資料夾內 python3 -m http.server 後瀏覽對應 .html
Notion CLI scripts/notion_case.py

§5 Pre-flight(必跑)

cd ~/Projects/Billows/Audit-Manager/compliance-manager-be
git status -sb            # 應在 main、與 origin 同步、working tree 乾淨
git log --oneline -5      # 頂端應為 a5e32bb9(v1.15.0 進版)
git tag -l "v1.15.0"      # BE/FE 兩 repo 都應有此 tag
grep -m1 '^version' pyproject.toml            # 1.15.0
python3 scripts/notion_case.py get CM-1189    # 母案 In progress
python3 scripts/notion_case.py query --status "修正待驗證"   # 應為空(本棒已全收)
ssh jedi@192.168.50.188 'curl -sk https://localhost/api/1.0/version'   # 1.15.0 / a5e32bb9

§6 環境紀律

  • 🔴 開發只動 DEV;STG/POC 寫入類異動一律等決策者當次明示;唯讀操作不必請示。
  • 🔴 部署機(188)程式碼一律走 git pull,禁 rsync/scp/直接編輯。
  • 不切 branch、不自動 push(push 永遠 user 本人做)。

§7 本棒新立規則(接手要遵守)

  • 設計文件先概述後詳細(已入 docs/claude/docs-conventions.md+big-feature-workflow skill)
  • 開卡必須夠白話:卡名寫做什麼、內文首段白話(已入 big-feature-workflow skill)

§8 .1 棒實測發現(仍有效的實證輸入,勿當背景雜訊略過)

後續棒次新增的操作性事實(sudo 權限、Harbor 驗證方式、版號單一來源)記在 §1 表格與 §3,不重複於此。

四條都是實跑撞到、與既有文件記載不符或未記載的事實。

8.1 🔴 ENABLE_MULTI_TENANT 漏設的症狀與文件寫的不同

  • 既有文件(e2e-env 註解/deployment-env.md)說:服務全綠但「查無資料且無錯誤訊息」
  • 實測(拿掉該項重起 api):服務仍 healthy、登入仍成功拿得到 JWT,但所有走租戶脈絡的端點回 LICENSE_403001 此模組未在授權範圍內——授權判定要讀租戶資訊,讀不到就一律當無授權
  • 為什麼重要:這個訊息指向錯誤的方向,看起來像 license 沒裝好或過期。裝機/客服排錯時第一個要查的是這個環境變數。已補回即恢復,已驗可逆
  • 影響面:客戶手冊(T-4.1)的排錯章節應收錄此對照

8.2 DB_PORT 不填不會連錯埠(既有記載已過時)

config/config.py:37 現行是 os.getenv("DB_PORT", "5432"),預設就是 5432,實測拿掉照樣連得上。e2e-env 註解記載的「BaseConfig 硬編 25432」已不成立。仍建議顯式寫出以防日後 config 改動,但不要再把它當成「不填就會壞」的事實

8.3 🔴 空庫/新庫必須先 grant cm_app,否則 BE crash loop(exit 139)

restore 資料後 BE 初次啟動反覆 exit 139,根因是業務帳號 cm_app 完全沒有 schema/table/sequence 權限。症狀極具誤導性——exit 139 是 SIGSEGV,第一直覺會懷疑 Nuitka 產物或跨架構模擬,實際是權限。

給 T-2.1(CM-1252):這正是該卡「cm_app 權限掃描零缺口」驗收條件的實證。init 腳本除了建表建 RLS,必須逐 schema 掃 GRANT(USAGE on schema/SELECT,INSERT,UPDATE,DELETE on tables/USAGE,SELECT on sequences),且要含 ALTER DEFAULT PRIVILEGES(否則之後新建的表又漏)。

8.4 給 T-2.5 的憑證產生規格已由 T-1.2 約定

T-1.2 已把掛載路徑釘死為容器內 /etc/nginx/certs/server.crtserver.key(唯讀 bind mount,私鑰不進 image),並實測憑證缺失時 nginx 直接 emerg 啟動失敗、不會退回 http 供站(刻意設計:退回 http 會變成「站台活著但登不進去」的靜默故障)。T-2.5 產憑證時照這兩個路徑落檔即可,不需再協商。


§9 給 fresh session 的短 prompt

接手 FR-065 落地版 installer(母案 CM-1189)。先讀
docs/features/FR-065-2608-onprem-installer/handoff/FR-065-STATE.md(含 §0 讀序全走)
+ FR-065-LOG.md 最後 2 block,答完自檢 4 問、跑完 §5 pre-flight 後回報現況。
讀取與盤點完成後不做任何事——不派工、不跑測試、不改檔,等我下指令才開始作業。