FR-062 License 控管機制 — STATE(living,只描述此刻)

最後更新:2026-08-10(.8/.9/.10 三輪追加 + STG/POC 雙環境部署完成,全案收尾中) 本檔為 STATE/LOG 雙檔制的 living 檔;脈絡與決策軌跡見同資料夾 fr062-LOG.md

§1

🧭 原始需求(WHY)

產品(Guidant AI)準備上線,將以「雲端 SaaS」與「落地 Host」雙模式銷售。需要 License 控管:root tenant(系統維護特權租戶,永不開放客戶)以外的所有租戶,功能受 license 管控。要能簽發 license 給客戶開通、支援有效期與訂閱到期、按模組販售。簽發端(私鑰)與產品端(只驗不簽)物理分離是整個信任模型的根基。

§2

角色定位

本 arc 首腦 session = 分析決策 / 協調者:只做需求釐清、拍板、拆工派工、驗收抽查;一切實作與文件產出外包(subagent 或開 case 給新 session)。接手本檔者若也是首腦,同樣適用。

§3

當前狀態(2026-08-10)

階段開發全部完成(含 .8/.9/.10 三輪追加)+ STG/POC 雙環境部署完成,全案收尾中。剩餘=收尾文件批次 + STG 兩租戶正式發照(決策者手動)+ 雲端公網補驗。

項目 狀態
.1 簽發端地基 完成(LC repo:引擎+CLI+DB;DEV kid 2ce3bb59a6f5a2ce
.2 產品端驗照 完成(tenant_licenses+root 後台+FE 兩頁;「上傳授權照」鈕已在 T-5.1 移除)
.3 狀態機+橫幅 完成(每日排程 cron+conditional UPDATE 冪等+FE 橫幅;root 特判)
.4 執法進場 完成(authz 第六軸+唯讀 gate+動態過濾;兩環境開關 LICENSE_ENFORCEMENT_ENABLED/LICENSE_READONLY_GATE_ENABLED 預設 true;POST 讀白名單逐支列舉制)
.5 開通 完成(T-5.1 離線+T-5.2 線上皆落地;四案驗收:happy path/錯誤序號/已用序號/LC 斷網 fallback)
.6 簽發 Web 後台 完成(Tabler UI+下載+詳情頁+列操作 Menu;passphrase 已 .env 化表單欄位移除)
.7 通知+migration 完成(T-7.1 notify 信+T-7.3 grace/readonly 信=格式 v2.1+T-7.2 DEV 發照演練;SOP 在 existing-tenant-license-migration-sop.md,script 護欄只認 DEV——STG/POC 實際未用此腳本,改走 .10 的「向 LC 請照」
.8 角色矩陣 license 維度 完成(T-8.1~T-8.5:ui-routesis_licensed/FE 未授權反灰空框(推翻初版「已授予但未生效」打勾)/POST·PUT /roles 寫入端守門只擋新增未授權點/provisioning 第三道扣除/選單結構調整 is_platform 正解+group-license 群組+關閉舊儀表板入口)
.9 手動停權獨立欄位 完成(CM-1172~1174:停權升為獨立欄位、cron 不再覆寫人為決策、API 回傳旗標、FE 文案+狀態快取失效;migration 回填刻意不寫 suspend_reason——該欄直接對客戶顯示)
.10 產品後台向 LC 請照 完成(CM-1175~1182:LC API token 白名單+管理頁+三支內部簽發 API/BE lc-issue·lc-extend·lc/plans 走既有落地漏斗第五入口/FE 請照 dialog(新簽·展延·Trial 選項)/LC 總覽三態化+同客戶分組+排序+搜尋篩選/design.md §4.7 圖 4 修正)
照檔格式(最終形) v2 信封(zlib+base64 payload 打包,非加密)+ v3 外皮(.license 副檔名 PEM 風格 armor)+ v2.1 三段 send_email 旗標;兩 repo 引擎零依賴各自實作、驗章對 unwrap 原文、v1/裸 JSON 向下相容
簽發鑰(三環境獨立) DEV 2ce3bb59a6f5a2ce/STG 04b1e65f100c6c8f/POC f3b562a6d9755527,三把公鑰共存於 BE common/license/public_keys.py;私鑰各留該機不外流。STG 首把 ef99f508c74e43fd 因 passphrase 對不上作廢重產
Notion 卡 開到 CM-1183;FR-062 全案子卡與母案 CM-1112 今日已全數收 Done(查詢「修正待驗證」為零)。唯一非 Done=CM-1143 遺留模組整鏈退役(Not started,全案後另議)
三 repo 全部已 push,HEAD == origin/main:BE 8c82e8e6/FE d00ba1b/LC d001b76。BE 另有 release/v1.14.0 branch(落後 main 2 commit,v1.14.0 release note 尚未撰寫、pyproject 仍 1.13.0
DEV DB 現況 config.tenant_licenses 24 列/events 27 列;現行照三租戶=102 Billows Tech/131 歐洲航空/158 JEDI。root(tenant 1)已無任何照(先前的 T-7.1 殘留照已清)
STG DB 現況 現行照 2 張:102 Billows Tech(2027-08-10 到期)/131 AirAsia(2026-11-08,另有一張已被展延取代的 2026-10-09)。LC 側 license_issuance 3 筆(issuer=Staging×2、Raymond×1)
POC DB 現況 現行照 1 張:102 Billows Tech(host 模式,2027-08-10)。LC 側 1 筆(issuer=GuidantAI-POC)——首發那張 saas 記載錯誤的照已依決策者指示連同事件清空重發
服務 本機 BE :8000/FE dev :5180/LC :5062;STG 188 與 POC 189 皆 BE :8000+LC :5062+nginx :80(座標見下方環境座標節)
§4

Notion 卡對照表

母案 CM-1112。子需求:.1=CM-1113 / .2=CM-1116 / .3=CM-1120 / .4=CM-1123 / .5=CM-1128 / .6=CM-1131 / .7=CM-1134。 初版子任務:T-1.1=1114、T-1.2=1115、T-2.1=1117、T-2.2=1118、T-2.3=1119、T-3.1=1121、T-3.2=1122、T-4.1=1124、T-4.2=1125、T-4.3=1126、T-4.4=1127、T-5.1=1129、T-5.2=1130、T-6.1=1132、T-6.2=1133、T-7.1=1135、T-7.2=1136。 驗收回饋期追加:T-1.3=1137(v2 打包)、T-1.4=1138(passphrase 環境變數化+金鑰重產)、T-6.3=1139(下載+明文顯示)、T-1.5=1140(.license armor)、T-0.1=1141(design.md 回寫批次)、T-2.4=1142(root 狀態頁顯示)、退役案=1143(cruise/resource/report,全案後)、T-4.5=1144(反灰鋪面)、T-6.4=1145(詳情頁+Menu)、T-7.3=1146(grace/readonly send_email)、總驗收清單=1147、T-0.2 三文件同步=1148。

FR-062.8 角色矩陣 license 對齊:T-8.1=1167(ui-routesis_licensed)、T-8.2=1168(FE 反灰)、T-8.3=1169(roles 寫入端守門)、T-8.4=1170(provisioning 扣除)、T-8.5=1171(選單結構調整)。 FR-062.9 手動停權:母案=1172、T-9.1 BE=1173、T-9.2 FE=1174;回歸 bug(登出後誤打 license status)=1183。 FR-062.10 向 LC 請照:母案=1175、T-1 LC=1176、T-2 BE=1177、T-3 FE=1178、T-4 docs=1179;驗收當下追加=1180(開通狀態三態化)、1181(同客戶分組+排序)、1182(搜尋+下拉篩選)。

未開卡即修的兩筆(決策者裁示併收尾、不補開卡):BE 8c82e8e6(請照未指定 deployment_mode 時帶本站台設定)、FE d00ba1b(請照 dialog 補照型態選項,admin 可發 Trial)。

§5

剩餘工作隊列(2026-08-10 收尾時點)

開發與部署已無在途工作。剩下六項:

等 user  (a) 三 repo push ── 本次已 push 完(HEAD==origin/main),僅未來新 commit 適用;
             另 v1.14.0 若要正式發版,release note + pyproject bump 尚未做(走 version-bump skill)
決策者   (b) STG 兩租戶正式發照 ── BE/LC 已就緒,發照動作由決策者在 root 後台手動執行
             (現有 2 張為部署驗證照,是否沿用或重發由決策者定)
待部署   (c) POC FE 部署 ── 已於 2026-08-10 20:42 完成(/var/www/html/audit-manager,
             已驗證含 type_trial / lc_extend 字串);此項可視為完成,若有後續改動需重 build
等雲端   (d) 雲端 LC 公網補驗 ── TLS/網域/防火牆,清單在 CM-1130 卡內;目前 LC 皆為
             內網 :5062,BIND_HOST 已環境變數化
上正式前 (e) 正式環境簽發鑰 ── 產出後必須先進 common/license/public_keys.py 再 build 出貨
             image,順序顛倒=產品收不到正式照(會報「未知的 kid」)
全案後   (f) CM-1143 遺留模組整鏈退役(cruise-project/resource/report+jedi-resource-store)

未套的 migration(決策者裁示這次不動,非遺漏)2026-07-20-ssp-shared-metadata-DEV-cleanup(DEV 專用清理)、2026-08-01-fr058-22-openscap-content-path-advanced2026-08-03-fr060-1-detection-profile-split——三支 DEV 有、STG/POC 無。

§6

已定裁示(不重開)

  • D1–D15 全定案(見 design.md §2),含五項晚於 discussion v2 的最終修正:過渡照刪除(type 僅 formal/trial/extension)、簽發端自建(不採 Keygen)、Plan=簽發端 DB 資料、升級換發立即生效、數字定案(子租戶上限 5;expiry_policy 出廠預設 notify30/grace14/readonly 終態/lockout 關)
  • 照檔格式(實作期三次演進,全部落地+design.md §4.1 同步):v2 信封=payload zlib+base64 打包,非加密(防翻閱嚇阻,文件不可宣稱加密);v3 外皮.license 副檔名+PEM 風格 armor(BEGIN/END GUIDANT LICENSE);v2.1=grace/readonly 段補 send_email(from_dict 缺鍵容忍=硬需求)。簽章永遠對 unwrap 原文;v1/裸 JSON 向下相容(未出貨拒收無收益)
  • .6 Web 後台 UI=Tabler(MIT,pin 版入 static/vendor/,不依賴 CDN);顯示用明文、交付用信封;下載只限現行照(get_if_current 守門)
  • 簽發 passphrase=.env LICENSE_CENTER_KEY_PASSPHRASE 自動解鎖,表單欄位僅未設時顯示;正式鑰仍須密碼管理器+兩處站外備份(金鑰遺失=無法簽續約,上版前要再提醒 user 安排)
  • 照內 tenant_id=簽發端客戶識別代稱,產品端不強比對;實質綁定=SaaS root 指派動作/Host 機器指紋
  • 通知收件人=三者聯集(is_super_admin ∪ is_admin 角色成員 ∪ tenant.update capability 持有者,走 v_user_capabilities);空租戶 fallback 寄 root 管理員+log 警告(DEV 實據:is_admin/is_super_admin 覆蓋殘缺,131 只有 capability 收得到)
  • TENANT_ADMIN_EXCLUDED_RESOURCE_TYPES 不退役(實作者抗命經覆核接受:provisioning-time 預設授予 vs request-time 商務授權兩維度,license 取代不了;詳見 CM-1126 裁決段);cruise/resource/report 三類=全案後整鏈退役(CM-1143)、dashboard 留、workflow 頁面不動嚴禁誤傷 flow engine
  • T-5.2 裁示(2026-08-09):不等雲端,local 模擬(HTTP 流程 localhost 即等價);公網/TLS/網域留部署後補驗——CM-1130 卡有完整更新段
  • LC 架構裁示:不導 DDD(2,268 行分層架構+Service Layer 已足;領域核心純函式+依賴單向是真正要守的);演進觸發條件=第二產品線接入/訂單結算領域進場/多人維護——屆時再升級。LC repo 補 CLAUDE.md(分層守則)可併收尾批次
  • DB 命名新標準:license_center_{dev|stg}prod 不帶後綴;DB user 暫沿用 cmmgr(上 PRD 前換)
  • 到期終態=永久唯讀可下載匯出,不鎖登入(lockout 保留預設關);能通知就通知

追加裁示(2026-08-10)

  • 三環境各自獨立簽發鑰(決策者要求 POC/STG 不得與 DEV 同鑰):每環境自行 keygen,私鑰永不離開該機,公鑰以新 kid 加進 BE PUBLIC_KEYS 多把共存(不取代舊鑰,否則舊環境的照全部驗不過)。連帶紀律:每次 keygen ⇒ 一個 BE commit ⇒ 該環境 BE 重新部署,順序錯即「未知的 kid」
  • 不做「非 production 只能簽 trial」的強制規則:曾考慮此防呆,最終不採——主站管理員即決策者本人、LC 環境未來也會與產品環境分開部署;token 白名單的目的是管理便利與可追溯(可撤銷、發照紀錄看得出來源環境),不是防範內部誤用
  • API token 白名單設計刻意保持最小:只做「新增 / 列表 / 停用」,不做完整 CRUD 編輯。撤銷用停用不刪列(保留歷史發照的 issuer 追溯);token 只存 hash 不存明文
  • 產品端不因來源是 LC 就跳過驗章lc-issuelc-extend 回照仍走完整 _verify_signed_license + _store_verified_license,BE 是唯一信任邊界;手動上傳路徑保留不動,作為 LC 不可達時的天然 fallback
  • 請照時產品必須自報 deployment_mode:未指定時帶本站台 config,不讓 LC 用預設值猜(POC 首發曾因此簽出 saas 記載錯誤的照)
§7

環境座標

本機開發

  • 主產品 BE:~/Projects/Billows/Audit-Manager/compliance-manager-be(branch main);:8000(main_app.py,重啟必 kill -9)
  • 簽發站:~/Projects/Billows/Audit-Manager/license_center(branch main);web :5062(PYTHONPATH=src poetry run python -m license_center.web.app);帳密/passphrase/DB 連線都在 LC .env;測試 PYTHONPATH=src poetry run pytest
  • FE dev::5180(npm run dev;多開殘留曾佔 5181-5183,砍舊 PID 再起)
  • DB:guidant_ai_devlicense_center_dev(188:25432,cmmgr)
  • 文件站預覽:repo 根 python3 -m http.server 8100

STG(192.168.50.188)

  • BE:/opt/compliance-manager-be(:8000)
  • LC:/opt/license_centersystemd 常駐 license-center.service(enabled+active,ExecStart=/opt/license_center/.venv/bin/python -m license_center.web.appBIND_HOST=0.0.0.0,:5062)
  • FE:nginx /var/www/html/audit-manager(:80)
  • DB:guidant_ai_stglicense_center_stg(188:25432)
  • 簽發鑰 kid 04b1e65f100c6c8f;LC API token Staging(environment=stg)

POC(192.168.50.189)

  • BE:/opt/compliance-manager-be(:8000)
  • LC:/opt/license_center(:5062,已起)
  • FE:nginx /var/www/html/audit-manager(:80)
  • DB:guidant_ai_poclicense_center_poc(189:25432)
  • 簽發鑰 kid f3b562a6d9755527;LC API token GuidantAI-POC(environment=production)
  • ⚠️ POC 等同 production,唯讀操作可自由做,任何寫入/部署一律等決策者當次放行

LC 部署手冊~/Projects/Billows/Audit-Manager/license_center/docs/deployment-guide.html(以 STG 實戰部署為藍本,含 systemd unit、環境變數、keygen 與公鑰接線步驟)

  • Notion 操作一律 scripts/notion_case.py(get/status/append/query);開卡 script 無 create 子命令——API 直打 POST /pages(database id 23c346da4cd0801d8b67dbd9e73cf246,title 屬性「任務名稱」,本棒開了 10 張全用此法,pattern 見任一次對話或 import notion_case 模組復用其 helpers)
§8

行為規範提醒(每棒都適用)

  • 派工必引用實際 Notion 卡號;完成回寫子卡 修正待驗證 + 白話補充(不等令);母卡/子需求卡收尾等令
  • 顯式 git add、禁 -am、不 push(等 user 明示)、不切 branch
  • migration 只套 DEV;STG/POC 需決策者當次放行(T-7.2 script 已內建 DB 白名單護欄)。2026-08-10 的 STG/POC 套用是決策者當次明示放行的部署動作,不構成常態授權
  • 對 STG/POC 唯讀操作(SELECT / pg_dump / 看容器 / 讀設定)一律可做不需請示;任何寫入(含 migration、重啟、部署)一律停下問決策者
  • root tenant 無條件豁免 license 執法;/license/ 前綴整段豁免唯讀 gate(防死鎖)
  • 首腦驗收紀律(本棒實踐有效,建議沿用):每棒回報後 fetch 卡+看 commit+親跑測試+查 DB 實況,不信自報;user 手測過才收 Done;裁決發生時回填到產生疑問的那張卡(user 會催「有沒有回填」)
  • user 風格備註:驗收很細(親測 UI、抓過 dropdown 裁切/時區/i18n 缺漏),常在驗收中丟出新需求——每條都開卡落痕再派,小修可併棒(如 T-2.4 併 .3、i18n 併 T-1.3)