FR-066 · Agent Native Installer — 設計文件 · 2026-08-18

檢測 Agent 原生安裝包 設計定案

把檢測 Agent(evidence-agent)從 docker 三容器出貨,改為 Nuitka 編譯+外部工具全包的一顆 tarballinstall.sh 一鍵安裝為 Linux systemd 服務。本文件承接同資料夾需求討論稿的 D1–D9 定案,轉為正式設計:build 管線、tarball 打包與工具 bundle、install.sh 與 systemd、驗收與文件,共 4 子需求 × 11 子任務。

設計定案 2026-08-18・待實作 開放查證項 Q1–Q4(實作期) 前作:FR-063 Nuitka / FR-064 簽章 / FR-065 installer 標的 repo:evidence-agent 拆分:4 子需求 × 11 子任務

狀態:設計定案・待實作(D1–D9 已拍板;Q1–Q4 為實作期查證項)|建立日期:2026-08-18 前作:FR-063 Nuitka 落地版打包 design(build 管線/私房 wheel/resource_path 慣例)/FR-064 防竄改偵測 design(manifest 簽章泛用層)/FR-065 落地版 Installer design(install.sh 七步結構/維運子命令) 討論稿(唯一內容真相源,D1–D9 拍板脈絡與被排除方案全文):discussion.html

給 PM 的三分鐘版

這節不需要任何技術背景就讀得懂。工程細節從下一節起。

這案在做什麼:檢測 Agent 是我們派駐在客戶機房、替我們去做掃描的小程式。它原本包在 docker 容器裡出貨(容器=一個跟外界隔開的獨立小房間)。但 Agent 的工作就是量測它所在 的那台機器,隔著這層隔離會讀不到硬體身分、或每次重啟讀到的值都不一樣——這件事已經 真的出過事(系統認不得同一台機器)。本案把它改成直接裝進機器裡,就像同業的掃描 Agent (Nessus、Qualys、Wazuh)一貫的做法。

為什麼值得做(三個實際好處):

  1. 身分不會漂——機器識別碼直讀主機,不再靠外掛腳本從外面抄值硬撐
  2. 客戶不必先會 docker——解開一顆包、跑安裝腳本、回答三個問題就好
  3. 封閉機房也能裝——掃描工具全部隨包附上,安裝過程不必連外網下載任何東西

順帶做掉的:客戶端少一個資料庫服務、少一個網路轉發服務要維運(都省成 Agent 自己承接), 以及防偽章——出貨前逐個檔案蓋章,有人動過包裡任何檔案,Agent 就拒絕啟動。

影響哪些畫面/操作產品畫面完全不變。這是交付形態的改造,客戶端的變化是「裝的 方式不同了」;雲端後台看到的 Agent 清單、派工、報告全部照舊。唯一要注意的是:改了機器 身分的算法,既有已開通的 Agent 指紋會變、需要重新開通一次(決策 D11,當時存量最少、 最便宜)。

拍板了什麼(決策 D1–D19 的白話版,完整理由見 §3):

題目 定案
程式怎麼給 編譯成機器碼,客戶看不到我們的原始碼
工具怎麼帶 全部塞進同一顆包,不做「核心包+工具包」分層——分開給就有漏帶的風險,封閉機房現場出事沒網路可補救
本地資料怎麼存 從「另起一個資料庫服務」縮成單一檔案(它只服務一張表,養一個服務不值得)
防竄改要不要做 要——Agent 以最高權限跑在客戶機器上、還握著加密憑證,是整條信任鏈最容易被摸的一端
怎麼升級 新版裝到旁邊、切換指向即完成;出錯切回去就是回滾
Windows 版 凍結(D19,PM 拍板)。兩條路線實測都判死(Docker Desktop 使用者一登出整組停掉;另一條方案閒置一分鐘就被系統凍結)。現階段要掃 Windows 主機的解法是:Linux Agent 走遠端連線去掃,這是既有功能
出貨哪一版 docker compose 版(D16)。原生安裝版開發完成但暫不出貨,等真的出現封閉網路/不能裝 docker 的客戶再解凍

現在到哪了:母卡 CM-1284 已於 2026-08-23 收案,compose 版已隨產品出貨。 各子需求「怎麼親手驗它做完了」見下一節的分工概述表。


變更紀錄

日期 變更 對應
2026-08-18 初版設計定案:D1–D9 定案轉入、現況接入點盤點、4 子需求 × 11 子任務拆分、Q1–Q4 歸屬標注、端到端驗收 FR-066
2026-08-18 補 Windows 可行性初判入 D8(附錄:分層難度表+工具鏈盤點+SmartScreen 風險,T-4.3 起點素材) FR-066 D8
2026-08-20 §2 分工概述升級:加「完成怎麼判定(決策者檢查法)」欄+非工程讀者導讀(決策者反饋:原版太工程師、不知道每階段怎麼檢查) FR-066
2026-08-20 §5.2.3 落地調整:manifest 由單份改雙層(agent 層給啟動 gate、bundle 層給交付完整性),圖 2 同步更新;T-2.3(CM-1294)實作時發現單份與啟動 gate 掃描錨點衝突 FR-066.2 D6
2026-08-20 新增 FR-066.5 EL(Rocky/RHEL 9)支援:D10 決策+T-5.1~5.3 拆分(方案 A 低 glibc 容器編譯,單一 tarball 不變) FR-066.5
2026-08-21 新增 FR-066.6 docker compose 形態+SeaweedFS 內建儲存:D11 指紋二源化(全域)/D12 雙形態矩陣/D13 沿用 FR-039 既有 Agent 儲存鏈;T-6.1~6.4 拆分。另記 FR-066.5 PM 決議暫緩 FR-066.6
2026-08-21 T-6.1 落地:D11 補「撞號守門的豁免條件=憑 uid 認出」與 --upgrade 須更新維運指令副本兩則實作教訓(皆為實測抓到的錯,見 D11 註) FR-066.6 D11
2026-08-21 FR-066.6 增 T-6.5 configure-storage 子命令(D14 裁示:本地 CLI 先做、web 下發另開評估卡給 PM) FR-066.6
2026-08-22 FR-066.6 增 T-6.6 compose 版互動安裝程式(D15:交付形態對齊主產品 installer 模式——問答引導,客戶不碰 docker 指令;T-6.3 產物為其積木) FR-066.6
2026-08-22 增 T-6.7 恢復系統內建儲存(出廠快照+configure-storage 選項;主產品對應功能另卡處理) FR-066.6
2026-08-22 D16 出貨形態收斂:原生 tarball 暫不出貨、現階段一律 compose 版(D12 出貨面凍結,開發成果保留);T-4.1 隨之暫緩、T-4.2 範圍改 compose 為主。另收 CM-1335 裁示:compose 交付包廢除 install/upgrade 兩包制、收斂一顆通吃 FR-066 D16
2026-08-22 新增 FR-066.7 Windows(Docker Desktop)支援:D17 裁示(docker 路線先行、原生版不討論;Docker Desktop 承載;install.ps1+薄指引;setup.exe 記 PM 反饋觸發升級項);T-7.1~7.3 拆分。另記 D16 補充:手冊(T-4.2)解凍條件=FR-065 產品本體 Windows 與本案 .7 皆收口後兩本一起寫 FR-066.7
2026-08-23 新增 FR-066.8 原生 Windows 版:D18 裁示(Docker Desktop 判死於出貨〔T-7.1 實測登出全滅 P0〕、B〔WSL+engine 常駐〕否決、原生服務為 Windows 唯一出貨路線;code signing 階段策略=現階段不買憑證走客戶 IT 白名單、第一個正式 Windows 客戶在望時買 OV、救火買 EV);T-8.1~8.5 拆分。FR-066.7 收斂為 demo 限定(T-7.2/T-7.3 降級,見 D18 註) FR-066.8
2026-08-23 D19 Windows 線全面凍結(PM 拍板):先以 Linux 為主,FR-065/FR-066 不再探討 Windows 安裝,客戶真有需求再開卡。T-7.4(CM-1346)B 案生死驗證判死收錄(WSL VM 閒置 60 秒凍結 userspace、四種挽救全滅、外部 watchdog=每分鐘重開機非常駐);.7/.8 全卡樹暫緩、探路資產保留。T-4.2 手冊解凍改為 Linux 版立即撰寫(主產品對齊卡 CM-1347+agent 手冊 CM-1299) FR-066 D19

1. 需求背景與端到端流程

1.1 背景:容器裡的 Agent 量不到宿主

檢測 Agent(evidence-agent,獨立 repo ~/Projects/Billows/Audit-Manager/evidence-agent,v0.2.27,Flask + dependency-injector)目前唯一交付形式是 docker image(python:3.11-slim)+三容器 compose(agent 本體+agent-db postgres:16+nginx mTLS sidecar),出貨走 docker save / docker load

但 agent 的本職是量測宿主機——機器指紋要讀 /sys/class/dmi/id/product_uuid/etc/machine-id、主網卡 MAC,這些在容器裡讀不到或會漂移(已發生 Debian base image /etc/machine-id 空檔退回隨機 MAC 的指紋漂移事故),現行靠 deploy/collect-host-id.sh workaround 從宿主抽值塞進容器硬撐。業界同類產品(Nessus / Qualys / Wazuh / Elastic / Datadog Agent)全部以原生套件(deb/rpm/tarball)+ systemd 服務交付——貼宿主量測的產品,原生安裝就是行規。

FR-065(產品落地 installer)期間決策者裁定:agent 安裝包另起新 FR、非 docker,即本案。

1.2 本案目標

  • Agent 做成 Nuitka standalone 編譯(把 Python 程式編成機器碼、原始碼不可見)的 tarball 安裝包
  • install.sh 一鍵安裝為 Linux systemd 服務
  • 外部檢測工具全包在同一顆 tarball,離線(封閉網路)完全自足
  • Windows 不實作,只出評估文件與前提清單

1.3 前作可複用資產

前作 可直接複用
FR-063 Nuitka 落地版打包 scripts/build/build_release.sh build 管線、dependency-injector 私房 wheel rebuild_wheels.shresource_path() 慣例、DI 靜態清單處理手法
FR-064 防竄改偵測 簽章泛用層 sign_payload / verify_payload(帶 type 欄)、gen_integrity_manifest.pysign_manifest.sh、License Center 簽發
FR-065 產品落地 installer install.sh 七步結構、--check-only、安裝落檔慣例、digest 驗證、維運子命令(status / logs / uninstall / fingerprint)

1.4 端到端安裝時序

客戶側從解 tarball 到心跳上線,mTLS(雙向 TLS 認證)全自動、客戶零 openssl 操作:

%%{init: {'theme':'base','themeVariables':{'primaryColor':'#E2F0F1','primaryTextColor':'#14201F','primaryBorderColor':'#0E7C86','secondaryColor':'#EEF2F3','secondaryTextColor':'#14201F','tertiaryColor':'#FBFCFC','tertiaryTextColor':'#14201F','lineColor':'#4A5A5C','textColor':'#14201F','mainBkg':'#E2F0F1','nodeBorder':'#0E7C86','nodeTextColor':'#14201F','edgeLabelBackground':'#FBFCFC','titleColor':'#14201F','clusterBkg':'#FBFCFC','clusterBorder':'#E4EAEB','actorBkg':'#E2F0F1','actorTextColor':'#14201F','actorBorder':'#0E7C86','actorLineColor':'#C7D1D2','signalColor':'#4A5A5C','signalTextColor':'#14201F','labelBoxBkgColor':'#EEF2F3','labelBoxBorderColor':'#C7D1D2','labelTextColor':'#14201F','loopTextColor':'#14201F','noteBkgColor':'#F6EBD5','noteTextColor':'#14201F','noteBorderColor':'#9C6B12','activationBkgColor':'#EEF2F3','activationBorderColor':'#C7D1D2','sequenceNumberColor':'#FFFFFF'}}}%%
sequenceDiagram
    participant OP as 客戶操作者
    participant IS as install.sh
    participant SD as systemd
    participant AG as guidant-agent 服務
    participant CL as 雲端產品(FR-065 已含 PKI)
    OP->>IS: 解 tarball、執行 install.sh
    IS->>IS: 七步:前置檢查→驗 manifest 簽章→落檔 versions/<ver>→裝 bundle 工具→建 symlink→裝 systemd unit→啟動
    IS->>OP: 互動問答三題(雲端位址/註冊 token/本機對外位址)
    IS->>IS: 生成 /etc/guidant-agent/agent.env
    IS->>SD: systemctl enable --now guidant-agent
    SD->>AG: 啟動(啟動時驗 manifest 簽章)
    AG->>AG: 直讀宿主指紋(product_uuid / machine-id / MAC)
    AG->>CL: enroll:產 keypair+CSR(SAN=本機位址)+token 註冊
    CL-->>AG: CA 簽回 client 憑證+8443 server 憑證
    AG->>AG: 憑證存 cert_dir,gunicorn/ssl 自起 8443 資料面
    loop 心跳
        AG->>CL: mTLS 心跳(404 → 自動重註冊)
    end
圖 1 — 客戶側一鍵安裝到心跳上線(D4/D5 全自動 mTLS)

2. 分工概述(30 秒版)

本節寫給決策者與非工程讀者:每個大項在做什麼、產出是什麼、以及你要怎麼親手判定它完成了。工程細節從 §3 起。

整案一句話:把檢測 Agent 從「docker 三容器出貨」改成「Nuitka 編譯+外部工具全包的一顆 tarball,install.sh 一鍵裝成 Linux systemd 服務」——宿主指紋直讀、SQLite 取代 PG、agent 自起 TLS 取代 nginx sidecar、build 期 manifest 簽章+啟動驗章。

子需求 做什麼(白話) 產出 完成怎麼判定(決策者檢查法)
FR-066.1 Nuitka build 管線與程式改造 把 agent 的 Python 程式編成機器碼,順手把「只有在 docker 裡才成立」的地方改掉:資料庫從要另起一個 PG 容器改成單檔 SQLite、版號不再讀 pyproject.toml、檔案定位改編譯相容寫法、指紋直接讀宿主 agent 版 build_release.sh+可跑的 Nuitka standalone 產物 在 build 機(188)跑 smoke_agent_release.sh5/5 全綠——產物翻不到 .py 原始碼、服務起得來、健康檢查有回應。✅ 已達成(2026-08-20,決策者親跑)
FR-066.2 tarball 打包與工具 bundle 把掃描工具(CINC / sonar-scanner / LibreOffice / 中文字型…)全部塞進同一個壓縮包,客戶不用連網也裝得起來;出貨前逐檔算 hash 簽章,啟動時驗章防竄改 guidant-agent-<ver>.tar.gz(含 manifest.sig)+build_bundle 打包腳本 一鍵產出 guidant-agent-<ver>.tar.gz+校驗碼、解包目錄結構與 §5.2.3 圖一致;且親手改包內任一個檔案 → agent 拒絕啟動(防偽章生效)、斷網容器內工具裝得起
FR-066.3 install.sh 與 systemd 客戶拿到 tarball 後的一鍵安裝腳本:問三個問題就裝成開機自起的系統服務,升級換版切個 symlink、出錯切回去就回滾,還附 status / logs / uninstall 等維運子命令 install.sh(安裝/升級/回滾/維運)+systemd unit 找一台沒裝過的乾淨 Linux:解包 → sudo ./install.sh 回答三題 → systemctl status guidant-agent 顯示 active;升一版再退回,服務都活著
FR-066.4 驗收與文件 找一台乾淨 Linux 機從頭裝一遍、斷網環境也裝一遍,確認真的能跑檢測任務;寫客戶安裝手冊;Windows 版可不可行寫成評估文件 真機驗收紀錄+客戶手冊+Windows 評估文件 斷網真機從安裝到跑完一個檢測任務、報告回到雲端全程通;手冊給沒參與過的人照著做能裝成功;Windows 評估文件能支撐「做/不做」的商務判斷
FR-066.5 EL(Rocky/RHEL 9)生態支援(暫緩) 讓同一顆安裝包也能在政府金融常用的 Rocky Linux / RHEL 9 上裝起來、跑得動:agent 編譯搬進低版本函式庫容器(debian:11)做,一份產物通吃三家族;安裝腳本認得 EL 的 SELinux/firewalld 並給指引(只檢測+輸出指令,不自動改客戶機);最後在真的 Rocky 9 機器驗一輪 容器化編譯的 build 管線+EL 適配的 install.sh+EL9 真機驗收紀錄(tarball 仍是同一顆,不分家) 找一台 Rocky Linux 9 機器(SELinux 開啟狀態)照同一份 tarball 裝:sudo ./install.sh 裝完 systemctl status guidant-agent 顯示 active、雲端後台看得到 agent 上線、派一單 CINC 檢測任務跑完報告回到雲端
FR-066.6 docker compose 形態+SeaweedFS 內建儲存 出一版 docker compose 形態的 agent 安裝包:同一顆編譯產物裝進容器、SeaweedFS 證據儲存服務一起出貨,agent 收到的證據預設存進 SeaweedFS;客戶自有 SeaweedFS/MinIO 也能設定指過去;機器指紋改用兩個掛載可得的來源 compose 交付包+agent seaweedfs 儲存支援+指紋二源化+configure-storage 設定命令+compose 版互動安裝程式 找一台有 docker 的 Linux 機:docker compose up -d 起來後雲端後台看得到 agent 上線;到儲存設備設定頁選「使用 Agent 服務」存檔,上傳一個證據檔,能在 SeaweedFS 的管理網頁看到那個檔、產品端能正常預覽下載
FR-066.7 Windows(Docker Desktop)支援 讓 Windows 客戶也能裝檢測 Agent——不重編程式,客戶在 Windows 上裝 Docker Desktop(圖形介面的容器執行軟體),拿同一顆 compose 安裝包,用 Windows 版互動安裝程式(install.ps1,PowerShell 腳本)問答三題裝起來;網路連入、機器指紋在 Windows 環境的差異由安裝程式與指引文件處理 install.ps1(Windows 版互動安裝程式)+Windows 安裝前置指引文件+160 真機驗收紀錄 找一台裝好 Docker Desktop 的 Windows 機(192.168.50.160):解開同一顆安裝包 → 跑 install.ps1 回答三題 → Docker Desktop 看到兩個容器綠燈 → 雲端後台看得到 agent 上線 → 派一單檢測任務跑完報告回雲端;重置 Docker Desktop 後照指引重新開通能接回
FR-066.8 原生 Windows 版 Windows 正式出貨版——agent 與內建儲存(SeaweedFS)編譯/打包成 Windows 原生程式,裝成兩個開機自起的 Windows 系統服務,不經 docker;問答式安裝程式(install.ps1)、升級回滾、防竄改簽章對齊 Linux 版。機器指紋改讀 Windows 本機識別碼(重灌才變,不受容器/登出影響) Windows build 管線+agent.exe/weed.exe 雙服務+install.ps1+乾淨機驗收紀錄 找一台沒裝過的 Windows 機:跑 install.ps1 回答三題 → 服務清單看到兩個 Guidant 服務「執行中」→ 登出、重開機後服務自動回來、雲端 agent 仍在線(這是 docker 版做不到的關鍵差異)→ 派一單檢測任務跑完報告回雲端 → 改包內任一檔案後服務拒絕啟動

依賴一句:.1 → .2 → .3 串行(build 產物是打包的輸入、打包產物是安裝的輸入),.4 收口;與 FR-063/064/065 不同,本案各階段產物互為輸入,無平行空間。.6 依賴 .1 編譯產物,與 .4 收口平行;.5 暫緩最後做。


3. 決策定案(D1–D9)

九項全部已由決策者拍板;每項含定案理由與被排除方案(全文脈絡見討論稿 §2)。

# 決策 定案與理由 被排除方案
D1 打包形式 Nuitka standalone 編譯 → tarball → systemd 服務。standalone 同為機器碼編譯,自家 code 不可見,保護強度與 FR-063 BE 打包相同;第三方套件以原始 .py 附帶(EXCLUDE 名單機制),皆為公開開源套件、不構成洩漏 onefile 模式:①啟動先解壓暫存目錄有延遲;②FR-064 逐檔 manifest 驗章在單一自解壓檔上難做;③外部工具反正要目錄形式,onefile 省不了目錄
D2 本地 DB agent-db(postgres:16)退役,換 SQLite。現況 agent-db 只服務 jedi-file-upload 的 upload_files 一張表(core/app_factory.py:37 註解明寫;create(checkfirst=True) 自建、無 migration 體系);單表、單進程寫入、無並發、無 RLS,正是 SQLite 甜蜜點;init_db() 吃 SQLAlchemy URL,換 sqlite:/// 即可。前置守門=Q2(PG 方言掃描) ①要求宿主裝 PG:加重客戶前置需求,違背一鍵安裝;②bundle PG 進 tarball:包更肥、多一個服務要維運(起停/備份/密碼),為一張表不值得
D3 外部工具交付 全包一顆 tarball,install.sh 自動安裝,離線完全自足。封閉網路情境(FR-059.3 已驗證為既定支援情境)下,全包才不存在漏帶/版本錯配的現場事故;體積 ~1GB+ 已明示接受。bundle 清單見 §5.2 方案 B「核心包+工具附加包分層」:多檔交付有漏帶風險,封閉網路現場出事無網路可補救;方案 C「宿主前置需求清單」:把安裝品質丟給客戶,且客戶自裝 InSpec 會踩 CINC 授權紅線
D4 mTLS 承接 agent 以 gunicorn/ssl 自行承接 8443 資料面 TLS,nginx sidecar 退役。mTLS 驗證邏輯本來就在 agent code 內(core/data_plane_auth.py),nginx 只是轉發層,砍掉不損失任何安全能力;8443 server 憑證由 enrollment(agent 向雲端自我註冊)時 CA 簽出(CSR SAN=base_url host),客戶不需自備憑證 —(保留 nginx 意味多一個要 bundle 與維運的服務,無對價)
D5 設定模型 /etc/guidant-agent/agent.env 作為 systemd EnvironmentFile。沿用現有全環境變數設計(config/config.py,agent 無設定檔)——零 code 改動;install.sh 互動問答三題(雲端位址/註冊 token/本機對外位址)生成該檔。客戶側 mTLS 全自動:自我註冊流程已存在(core/enroll.py:產 keypair+CSR → token 註冊 → 憑證簽回存 cert_dir → 心跳走 mTLS → 404 自動重註冊),客戶零 openssl 操作;雲端側 PKI 由 FR-065 產品 install.sh 已涵蓋(install.sh:1936 AGENT_CERT_DIR —(引入新設定檔格式=無謂 code 改動)
D6 簽章/防竄改 本 FR 一起做,瘦身版:build 期 manifest(逐檔 hash 清單)簽章+啟動時驗章。複用 FR-064 泛用層:簽章加一個 agent_manifest type、gen_integrity_manifest.py 改常數、sign_manifest.sh 近零改,複用成本僅 1–2 張小卡。值得做的理由:agent 以 root 服務跑在客戶機、握 mTLS 憑證,是整條信任鏈最容易被摸的一端 FR-064 runtime 全家桶(抽查/lockdown/unlock token):對 agent 過重,不做
D7 升級/回滾 /opt/guidant-agent/versions/<ver>/current symlink 切換。升級=解新版本目錄、切 symlink、重啟;回滾=切回舊 symlink。SQLite 檔與 mTLS 憑證放資料目錄、不隨版本目錄走;agent 無 DB migration 負擔(D2 單表自建),不需 FR-065 那套 pg_dump 備份模型,升級模型比產品端輕得多 —(複製 FR-065 備份模型=為不存在的 migration 負擔付維運成本)
D8 Windows 只出評估文件+前提清單,不實作不承諾(比照 FR-065 D9;FR-065 對應工作 T-4.3 於 2026-08-18 裁定跳過、評估文件未產出——agent 版評估文件從零寫,不假設有前稿可抄)。評估三題:①指紋來源(WMI / registry)②服務形式(Windows Service / NSSM)③工具鏈可用性(CINC / sonar-scanner 等在 Windows 的形態)。脫離 docker 後 Docker Desktop 商業授權疑慮消失;但 machine-id 等指紋問題在 Windows 依然存在,是評估重點 實作 Windows 版:需求未定案,不投
D9 指紋 只做 Linux 原生直讀,不預抽 OS 抽象層。直讀 /sys/class/dmi/id/product_uuid/etc/machine-id、主網卡 MAC——core/fingerprint.py 已有伏筆註記(「原生封裝後直接讀系統路徑,模型不變」);deploy/collect-host-id.sh workaround 隨之廢除 先抽 OS 抽象層:Windows 需求未定案(D8 只出評估文件),為未定需求預先抽象是過度設計;等 Windows 真要做再抽,重構成本可控
D10 EL(Rocky/RHEL)支援方式 方案 A:agent 編譯搬進 debian:11(glibc 2.31)容器,與 syslibs 採集同一基準邏輯——低 glibc 編譯向上相容,一份產物通吃 Debian 11+/Ubuntu 20.04+/EL9;工具 bundle 層已於 T-2.1 三家族實測通過,故只補編譯層+安裝腳本 EL 適配+EL 真機驗收;firewalld/SELinux 只檢測與指引、不自動改 方案 B 雙 build lane 出兩顆 tarball:QA/出貨負擔雙倍、違背 D3 單包精神;方案 C 宣告不支援 EL:Rocky/RHEL 是政府金融常態需求,把需求推走
D11 指紋二源化(全域) device_uuid 由三源 sha256(product_uuid|machine-id|mac) 改二源 sha256(product_uuid|machine-id),全域生效(原生版一起改)。docker 形態下 MAC 量不到宿主(bridge 網路虛擬網卡),machine-id 與 product_uuid 可唯讀掛載解決;只改 docker 版會讓同機兩種裝法算出兩個身分,故全域統一。代價:既有已 enroll agent(123 真機+DEV 測試筆)指紋全變需重 enroll——現在存量最小最便宜。VM 模板複製撞號風險(machine-id 整批相同)改由 BE enroll 端撞號偵測守門(同指紋再註冊→拒絕並提示)承接,冗餘從演算法搬到檢核 ①僅 docker 版二源:同機雙身分;②保留三源+collect-host-id.sh 人工抄 MAC:已被 D9 廢除的 workaround,會過期、靜默錯

D11 實作註(T-6.1 落地,2026-08-21)——兩則都是實測才現形、且都「看起來已經做對了」:

① 撞號守門的豁免條件必須是「憑 agent_uid 認出來的」,不是「resolve 到同一列」。 BE _resolve_agent 在 agent 沒帶 uid 時會退用指紋去重,於是複製出來的機器必然 resolve 到被撞的那一列、uid 自然相等——守門若比對 resolved.uid 就會判定「就是它 自己」而放行。首版即如此,實測(DEV 真打 register)回 HTTP 200 且原機那一列的 base_url 被複製品覆寫,正是本守門要防的情境。指紋 fallback 這條路徑就是複製品會走 的,拿它當「同一台」的證據等於把守門前提交給攻擊面本身。已改為 _resolve_agent 回傳 (entity, matched_by_uid),只豁免前者。

② 指紋演算法一改,--upgrade 必須一併更新 guidant-agent-ctl 該檔是安裝當下 複製出去的獨立腳本(讓客戶刪掉 2GB 安裝包後仍能用 status/logs),升級只換 /opt 下的 程式、不會動它。其 fingerprint 子命令是 agent 演算法的 shell 複刻,版本不一致 就印出另一個值——而那正是客戶要抄去雲端開通的那串。123 真機實撞:服務報二源值、舊 ctl 報三源值。原有的「與執行中服務對帳」機制有出聲(正是這樣抓到的),但它在服務沒 跑時(開通前,恰是 fingerprint 的主要使用時機)無從對帳,故不能當解法。

共同教訓:凡是「同一套邏輯存在兩份實作」(Python↔︎shell 複刻、演算法↔︎守門豁免), 改一邊就要指名另一邊,且要對真端點/真機各跑一次——單元層 fixture 會繞過真實 解析路徑(本例即因 fixture 直餵 resolved=None 而漏掉 ①)。 | D12 | 雙形態支援矩陣 | compose 版=Linux 有 docker 環境首選(含 SeaweedFS 全套);原生 tarball=封閉網路/無 docker/未來 Windows 路線。兩版共用同一顆 Nuitka 產物與同一套簽章,build_all 加 compose 打包出口,不開第二條產線 | —(雙產線=雙倍 QA/出貨負擔,違背單一產物精神) | | D13 | 產品端「使用 Agent 服務」 | =FR-039 既有功能,BE/FE 零新功能。雲端儲存設備設定頁本就有該選項(storage config 指 agent base_url/agent_uid → RemoteAgentAdapter → agent blob API → agent 依自身 STORAGE_TYPE 落地)。本案只做 agent 端:內建 SeaweedFS 預設接上+自有後端 env 設定+驗收既有鏈在新編譯 agent 上仍通 | —(在 BE/FE 另做新儲存選項=重複造輪子,鏈路早已存在) | | D14 | 自有儲存後端的設定介面 | 本地 CLI 先做(T-6.5):agent 加 configure-storage 維運子命令——secret 不離機、連通驗證當場做、量級小且是 web 下發的必要兜底(下發失敗/無雲端連線時的現場設定路徑)。web 設定下發開獨立評估卡給 PM 排優先度(前置資安題:客戶儲存憑證入雲端 DB 是否可接受) | 直接做 web 下發(三端成本+secret 治理題未決,不該擋住立即可用的 CLI) | | D15 | compose 版交付形態 | 配互動安裝程式(T-6.6),照主產品 FR-065 installer 同款模式:環境檢查→載 image→問答→生成設定→起服務→維運子命令。受眾是一線工程師/業務,不假設會 docker;SeaweedFS 憑證自動生成不讓客戶發明密碼 | 手填 .env+docker compose up(T-6.3 首版形態——僅適合熟 docker 者,不符落地版交付受眾) | | D16 | 出貨形態收斂(2026-08-22) | 原生 tarball 版暫不出貨——現階段對客戶出貨一律 compose 版。原生開發成果(.1~.3 全部產物與腳本)保留不廢,何時解凍等商務需求(封閉網路/無 docker 客戶出現時)。連動:T-4.1(原生乾淨機+斷網驗收)暫緩;T-4.2 手冊改 compose 為主、原生降附錄。同日另一裁示(CM-1335):compose 交付包廢除 install/upgrade 兩包制、收斂一顆通吃(兩包只差 82MB seaweedfs image,維護兩條路不值;客戶端以「本機已有該 image」為升級放行條件)。⚠️ 遺留:斷網封閉網路驗證至今無人跑過(D3 立案根基)——若 compose 版也出給封閉網路客戶,此驗證應轉移到 compose 版做,不隨原生凍結 | 雙形態同時出貨(D12 原案):原生版每出一版都要 QA 兩形態,現階段無原生需求客戶,QA 成本無對價 | | D17 | Windows 支援路線(2026-08-22) | docker(Docker Desktop)路線先行,原生 Windows 版暫不討論(T-4.3 評估為據)。承載選 Docker Desktop(決策者定案):安裝體驗是「裝一個 Windows 軟體」,客戶不碰 Linux 層;其商用授權(>250 人或年營收 >$10M 組織需付費訂閱)為客戶側前置條件,寫入指引與售前須知。指紋風險接受度(決策者裁示):WSL/Docker Desktop 重置後指紋漂移=可接受的維運事件,處理=log 記錄+「重置後重新開通」SOP,不做 host 值注入 workaround(維持 D9/D11 立場)。安裝程式出 install.ps1(PowerShell 互動版,與 D15「不假設客戶會 docker」同一受眾邏輯——也不假設會 Linux 終端);交付物=同一顆 onepack 包(一個位元組不改)+install.ps1+薄前置指引。setup.exe 安裝精靈記為 PM 反饋觸發的升級項(不做的理由:SmartScreen 攔無簽章 exe,消警告需買 OV/EV code signing 憑證——docker 路線本已繞開此成本,不為包裝形式請回來)⚠️ 2026-08-23 D18 修訂:Docker Desktop 降 demo/PoC 限定(T-7.1 實測登出全滅),出貨路線改 FR-066.8 原生版;T-7.2/T-7.3 降級為 demo 支援用途。 | ①WSL2+docker engine(免授權費但客戶要自管 WSL,違背交付受眾設定)②路線甲「客戶在 WSL 終端跑現有 install.sh」(零開發但要求客戶進 Linux 世界,與 D15 受眾邏輯矛盾)③首版就做 setup.exe(SmartScreen+憑證成本) | | D18 | Windows 出貨路線定案(2026-08-23) | 原生 Windows 服務版(FR-066.8)為 Windows 唯一出貨路線。依據:T-7.1 實測證實 Docker Desktop 登出後 engine 全滅、SYSTEM 排程與 com.docker.service 兩條無登入自起路徑皆失敗(probe §8,P0)——「常駐」與「綁登入 session」本質矛盾,Docker Desktop 降為 demo/PoC 限定(有人值守場景仍可用,T-7.1 已驗全鏈通)。B 案(WSL2+docker engine+排程器常駐)否決:常駐機制未驗、四層架構(Windows→排程器→WSL→docker→容器)查修成本高、指紋仍是 VM 值、產出對原生版零複用——demo 用不到(A 夠)、出貨不如 C,兩頭不沾。Code signing 階段策略(行規=B2B agent 類幾乎全簽,但按階段投入):現階段(Windows 客戶數 0)不買憑證——manifest 簽章(自有鑰)照做、SmartScreen 靠客戶 IT 白名單(GPO/AppLocker)指引承接,與客群(封閉網路、IT 管控)相容;第一個正式 Windows 客戶在望即買 OV(年費數百美金級,含數週申請+信譽爬升期,不可等簽約後);大客戶臨時需求買 EV(免爬升)。年費列為 Windows 版固定成本讓售前/PM 知情 | ①Docker Desktop 出貨(登出全滅 P0 無解)②B 案(見上)③現在就買憑證(為未驗證市場預付訂閱)④Azure Trusted Signing(台灣組織不可申請) | | D19 | Windows 線凍結(2026-08-23,PM 拍板) | FR-065/FR-066 不再探討 Windows 安裝,先以 Linux 為主;客戶真有需求再開卡。決策鏈收束:A(Docker Desktop)=demo/有人值守可用但出貨不可(T-7.1 登出全滅);B(WSL+engine 常駐)=T-7.4 實測判死——WSL VM 閒置約 60 秒凍結整個 distro userspace(SYSTEM 帳戶被硬拒、S4U 可跑但照凍、vmIdleTimeout=-1 無效、distro 內 anchor 連坐被凍、外部 watchdog 只能做到「每分鐘重開機」會腰斬檢測任務),死因=WSL VM 生命週期需要 Windows 側永不退出的進程握持,而該進程的正規形態就是 Windows 服務——推導出唯一活路 C(原生版);C 需第二個編譯出口(Windows build 機+MSVC),PM 裁示此投資等客戶訊號。凍結時保留資產:T-4.3 評估、T-7.1/T-7.4 兩份實測紀錄、FR-066.8 卡樹(CM-1340~1345 暫緩);解凍首步=build 機佈建。Windows 掃描需求的現行解答=Linux agent 走 CINC WinRM 遠端掃(既有功能) | ①續投 B 唯一殘存路線(WinSW 握 wsl.exe——工同 C 的 T-8.3、換到六層架構、對 C 零複用)②Hyper-V appliance(等同要求客戶出一台 Linux,非解法)③立即投 C(PM:build 產線投資等客戶訊號) |

D8 附錄:Windows 可行性初判(2026-08-18 口頭討論)

性質:決策者 2026-08-18 口頭討論確認的初判,非定案——定案要等 T-4.3 正式查證。此節作為 T-4.3 評估文件的起點素材。

核心結論:本案技術路線(Nuitka+SQLite+paramiko+環境變數設定)每一項都恰好沒堵死 Windows 的路;原生路線在 Windows 反而比 docker 路線順(免 Docker Desktop 企業授權)。粗估 Windows 版工作量約 Linux 版 40–60%(源碼層幾乎共用)。

分層可行性表

Windows 對應 難度
Nuitka 編譯 原生支援,編成 .exe+standalone 目錄(MSVC),同套 build 邏輯換 Windows build 機
常駐服務 Windows Service:NSSM 包裝(業界標準,Wazuh 等同款)或 pywin32 原生 service
SQLite 完全跨平台(D2 的額外紅利;PG 在 Windows 會很痛) 零成本
機器指紋 WMI Win32_ComputerSystemProduct.UUID+registry MachineGuid+MAC——即 D9 延後的抽象層,屆時再抽 中低
mTLS/enrollment 純 Python(cryptography/httpx),跨平台 零改
安裝腳本 PowerShell 或 MSI/Inno Setup 重寫,流程邏輯(問三題→佈檔→裝服務)照搬
外部工具 bundle 真正瓶頸所在,見下

工具鏈逐項:CINC Auditor 有 Windows msi ✅/sonar-scanner 有 windows-x64 zip(自帶 JRE)✅/git 有 portable 版 ✅/LibreOffice 有 Windows 版(離線佈署形式待查)🟡/xsltproc 無原生、可改用 Python lxml 取代(反而更乾淨)🟡/SSH 掃描走 paramiko 純 Python ✅。工具面無死路,但每項都要重做一次離線化整備。

新識別風險(Linux 版沒有的)

Windows SmartScreen/Defender 會攔沒有 code signing 憑證的陌生 exe——可能需要購買 Windows code signing 憑證,屬 T-4.3 必查項。

待 T-4.3 正式查證項:LibreOffice Windows 離線佈署形式、NSSM vs pywin32 原生 service 取捨、code signing 憑證需求與成本、Windows build 機規格。


4. 現況接入點盤點

evidence-agent repo 內每個會被本案觸動的元件,逐項列現況與本案動作:

元件 現況 本案動作
Dockerfile / rebuild.sh docker image build 入口 退役,換 agent 版 Nuitka build 腳本(.1)
deploy/docker-compose.yml agent + agent-db + nginx 三服務編排 退役,換 systemd unit(.3)
agent-db(postgres:16) 只服務 jedi-file-upload upload_files 一張表 → SQLite(D2,.1)
nginx mTLS sidecar 8443 TLS 終結+轉發 退役,agent 自起 TLS(D4,.3)
deploy/collect-host-id.sh 容器讀不到宿主指紋的 workaround 廢除(D9,.1)
config/config.py 版本上報 pyproject.toml 取版號 Nuitka 後檔案不存在 → 改 version bake(build 期把版號寫死進程式,同 FR-063 D6 手法,.1)
connector 硬編工具路徑 /usr/bin/* 寫死(image 內裝好) 配置化或 install.sh 建 symlink 指向 bundle 工具(.2/.3)
__file__ 定位三處 inspec.py:145 / nmap.py:101 / config.py:85 resource_path() 慣例(FR-063 同款,.1)
assets/ 資料檔 nmap.xsl 等隨 repo Nuitka --include-data-dir 帶入(.1)
dependency-injector C extension,Nuitka 需特製 wheel 複用 FR-063 私房 wheelrebuild_wheels.sh,.1)
jedi-common / jedi-file-upload 私服(Nexus)套件 編進 binary;build 機需可達 Nexus(.1)
DI wiring 本來就是硬編靜態清單(無動態掃描) 免改——FR-063 的 DI 掃描雷不會踩
eventlet / WeasyPrint agent 沒有這兩個依賴 免處理——FR-063 另兩大雷也不會踩
心跳排程 threading.Thread(非 APScheduler) 免改,Nuitka 相容

好消息:FR-063 的三大 Nuitka 雷(eventlet monkey patch / WeasyPrint 動態載入 / DI 動態掃描)agent 一個都不會踩

agent 沒有 eventlet、沒有 WeasyPrint,DI wiring 本來就是硬編靜態清單。.1 的風險面遠小於當初 BE 打包(冷 build 亦預期遠低於 BE 的 56 分鐘,見 Q3)。


5. 詳細設計

5.1 FR-066.1 Nuitka build 管線與程式改造

白話:這階段做「讓 agent 能被編成機器碼、且編出來在裸機上跑得起來」的所有程式面工作——它是整案起點,後面的打包(.2)與安裝(.3)都以這裡的 build 產物為輸入。docker 時代靠容器環境撐著的假設(旁邊有一個 PG、版號讀得到 pyproject.toml、__file__ 指得到源碼樹、指紋由 collect-host-id.sh 餵進來)在這一棒全部改掉。

5.1.1 SQLite 化(D2)

  • Q2 前置查證先做:掃 jedi-file-upload 的 model 定義與所有查詢,確認無 jsonb / ON CONFLICT 等 PG 方言;有的話評估改寫成本再定落地細節。
  • init_db() 的 SQLAlchemy URL 換 sqlite:///<資料目錄>/agent.dbupload_files 表沿用 create(checkfirst=True) 自建,無 migration 體系要搬。
  • SQLite 檔落在資料目錄(不在版本目錄,配合 D7 升級模型)。
  • agent-db 容器、其在 compose 的編排、對應連線環境變數隨之退場。

5.1.2 程式改造

  • version bakeconfig/config.pypyproject.toml 取版號的邏輯,改為 build 期把版號 bake 進常數(FR-063 D6 同款手法)——Nuitka 產物內沒有 pyproject.toml。
  • resource_path() 三處inspec.py:145 / nmap.py:101 / config.py:85__file__ 定位改 resource_path() 慣例(FR-063 同款),配合 --include-data-dir 帶入 assets/(nmap.xsl 等)。
  • 指紋直讀(D9)core/fingerprint.py 直讀 /sys/class/dmi/id/product_uuid/etc/machine-id、主網卡 MAC;廢除 deploy/collect-host-id.sh 與其在部署流程的掛點。
  • 工具路徑配置化:connector 內 /usr/bin/* 硬編路徑改為可由環境變數/設定覆寫(預設值指向 install.sh 建立的 bundle 工具 symlink,見 .3)。

5.1.3 agent 版 build_release

  • 以 FR-063 scripts/build/build_release.sh 為藍本開 agent 版:dependency-injector 私房 wheel 直接複用 rebuild_wheels.sh;jedi-common / jedi-file-upload 從 Nexus 拉入編進 binary(build 機需可達 Nexus)。
  • Q3:EXCLUDE_COMPILE_PKGS 名單依 agent 依賴集合(paramiko / python-gvm / zaproxy / cryptography 等)重評,不照抄 FR-063。
  • build 完跑 smoke 驗證:產物在無 Python 環境的裸機(或乾淨容器)啟動、心跳邏輯 dry-run、SQLite 建表成功。

5.2 FR-066.2 tarball 打包與工具 bundle

白話:這階段把「.1 編好的 agent」跟「它工作時要呼叫的所有外部工具」組成一顆客戶拿了就能離線安裝的壓縮包,並在出貨前蓋上防竄改簽章。沒有這一棒,客戶機上要自己裝 CINC、LibreOffice、中文字型……封閉網路根本裝不了;有了它,一顆 tarball 就是完整交付物。

5.2.1 工具 bundle(D3)

工具 體積 備註
CINC Auditor(Ruby omnibus,全家桶自足安裝形式) ~275MB ⚖️ 授權紅線:絕不可換成官方 InSpec 商業 binary
sonar-scanner(含 JRE) ~147MB amd64 專屬
LibreOffice + fonts-noto-cjk ~400MB+ 檔案預覽轉 PDF;⚠️ 字型家族名是 Noto Sans CJK TC 不是 Noto Sans TC;離線化形式=Q1(自帶 deb 組 dpkg -i vs portable / AppImage,實測選定,判準:離線可裝、字型能一起落、體積可接受)
xsltproc + assets/nmap.xsl nmap 報告轉換
git
  • 不 bundle:ZAP / OpenVAS(客戶自備 daemon)、OpenSCAP / Nmap 引擎(裝在目標主機、agent SSH 過去跑,不在 agent 機上)。
  • Q4:查證 image 內 openscap-utils / sshpass 是否實際已無用(OpenSCAP connector 已改 paramiko 直連、不用官方 oscap-ssh 腳本)——確認殘留就不搬進 tarball,包更瘦。

5.2.2 manifest 簽章+啟動驗章(D6)

  • 簽章泛用層(FR-064)加 agent_manifest type;gen_integrity_manifest.py 改常數(掃描根目錄/輸出路徑)產逐檔 SHA-256 manifest;sign_manifest.sh 近零改,走 License Center 簽發。
  • agent 啟動時以編譯進 binary 的公鑰驗 manifest.sig,逐檔比對;驗章失敗拒絕啟動(瘦身版:不做 runtime 抽查/lockdown)。

5.2.3 build_bundle 打包腳本

  • 組 tarball 骨架:agent/(Nuitka 產物)+tools/(bundle 工具)+install.shsystemd/+manifest 簽章(雙層,見下)。
  • 產出 guidant-agent-<ver>.tar.gz 並附 sha256 檔(交付完整性驗證,FR-065 digest 慣例同款)。
  • 實作:evidence-agent scripts/build/build_agent_bundle.sh(打包六步)+smoke_agent_bundle.sh(六項交付驗證,含 gate 正反例突變測試)。

🔄 落地調整(2026-08-20,T-2.3/CM-1294):manifest 由單份改雙層。 初版設計是骨架根一份 manifest 涵蓋全部;實作時發現它與啟動 gate 的掃描錨點衝突——gate(T-2.2)讀 binary 所在目錄agent/)的 manifest.sig 並逐檔比對該樹,而工具樹安裝後散落 /opt/cinc-auditor/opt/libreoffice25.8/usr/share/fonts,不在版本目錄、gate 掃不到也不該掃(那些路徑不歸版本目錄管)。單份 manifest 的結果是:gate 在 agent/ 下找不到 sig、路徑也對不上 → 正常包每次啟動必拒。拆成雙層後各管各的:

落點 用途 驗證時機
agent 層 agent/manifest.jsonagent/manifest.sig runtime 啟動 gate(掃 agent/ dist:binary、.so、assets) 每次啟動全驗
bundle 層 骨架根 manifest.jsonmanifest.sig 交付完整性(掃整個骨架,含 agent 層 sig 形成互鎖 install.sh 安裝前驗一次

簽章順序:agent 層先簽(它是交付檔的一部分)→ 骨架全組完 → bundle 層才產 manifest 送簽 → tar。打包腳本有可執行斷言擋順序倒置(bundle manifest 必含 agent/manifest.sig)。兩層同走 LC agent_manifest type、同一把鑰。

連帶兩個配套調整(同 commit a11ccf2):

  • gen_agent_manifest.py--external-symlinks {abort,skip}:工具樹帶指向安裝後路徑的 symlink(CINC binstub、LibreOffice desktop 檔),屬 omnibus/deb 打包形式,tar 原樣保留出貨;bundle 掃描以 skip 略過不收錄(對 build 機上恰好同名的檔算 hash 既無意義也不穩定),dist 掃描維持 abort 原判準。
  • startup_gate.py 錨點與 resource_root() 分家(_manifest_root()):打包模式固定=binary 所在目錄、不吃 AGENT_RESOURCE_ROOT——驗證錨點可被環境變數改指等於自廢,且 smoke 第 4c 項(拿該變數做 resource_path 反證)會讓簽章產物在 gate 假紅。源碼模式保留覆寫(fake-root 突變測試靠它)。
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#E2F0F1','primaryTextColor':'#14201F','primaryBorderColor':'#0E7C86','secondaryColor':'#EEF2F3','secondaryTextColor':'#14201F','tertiaryColor':'#FBFCFC','tertiaryTextColor':'#14201F','lineColor':'#4A5A5C','textColor':'#14201F','mainBkg':'#E2F0F1','nodeBorder':'#0E7C86','nodeTextColor':'#14201F','edgeLabelBackground':'#FBFCFC','titleColor':'#14201F','clusterBkg':'#FBFCFC','clusterBorder':'#E4EAEB','actorBkg':'#E2F0F1','actorTextColor':'#14201F','actorBorder':'#0E7C86','actorLineColor':'#C7D1D2','signalColor':'#4A5A5C','signalTextColor':'#14201F','labelBoxBkgColor':'#EEF2F3','labelBoxBorderColor':'#C7D1D2','labelTextColor':'#14201F','loopTextColor':'#14201F','noteBkgColor':'#F6EBD5','noteTextColor':'#14201F','noteBorderColor':'#9C6B12','activationBkgColor':'#EEF2F3','activationBorderColor':'#C7D1D2','sequenceNumberColor':'#FFFFFF'}}}%%
flowchart LR
  T["guidant-agent-&lt;ver&gt;.tar.gz<br/>~1GB+(D3 已接受)"]
  T --> A["agent/<br/>Nuitka standalone 產物<br/>自家 code 已編機器碼<br/>+manifest.json/.sig(agent 層:<br/>啟動 gate 每次啟動全驗)"]
  T --> B["tools/<br/>外部工具 bundle"]
  T --> C["install.sh<br/>七步安裝+互動問答<br/>upgrade / rollback / 維運子命令"]
  T --> D["manifest.json + manifest.sig<br/>(bundle 層:install.sh 安裝前驗整包,<br/>涵蓋 agent 層 sig 互鎖)<br/>FR-064 泛用層簽章 type=agent_manifest<br/>🔄 雙層拆分見 §5.2.3"]
  T --> E["systemd/<br/>guidant-agent.service<br/>EnvironmentFile=/etc/guidant-agent/agent.env"]
  B --> B1["cinc-auditor(~275MB)<br/>⚖️ 不可換官方 InSpec"]
  B --> B2["sonar-scanner+JRE(~147MB)"]
  B --> B3["LibreOffice+fonts-noto-cjk(~400MB+)<br/>形式待 Q1 實測"]
  B --> B4["xsltproc+nmap.xsl / git"]
圖 2 — tarball 交付物結構(D1/D3/D6 落地形狀)

出貨側 build 管線(FR-063/064 資產複用點):

%%{init: {'theme':'base','themeVariables':{'primaryColor':'#E2F0F1','primaryTextColor':'#14201F','primaryBorderColor':'#0E7C86','secondaryColor':'#EEF2F3','secondaryTextColor':'#14201F','tertiaryColor':'#FBFCFC','tertiaryTextColor':'#14201F','lineColor':'#4A5A5C','textColor':'#14201F','mainBkg':'#E2F0F1','nodeBorder':'#0E7C86','nodeTextColor':'#14201F','edgeLabelBackground':'#FBFCFC','titleColor':'#14201F','clusterBkg':'#FBFCFC','clusterBorder':'#E4EAEB','actorBkg':'#E2F0F1','actorTextColor':'#14201F','actorBorder':'#0E7C86','actorLineColor':'#C7D1D2','signalColor':'#4A5A5C','signalTextColor':'#14201F','labelBoxBkgColor':'#EEF2F3','labelBoxBorderColor':'#C7D1D2','labelTextColor':'#14201F','loopTextColor':'#14201F','noteBkgColor':'#F6EBD5','noteTextColor':'#14201F','noteBorderColor':'#9C6B12','activationBkgColor':'#EEF2F3','activationBorderColor':'#C7D1D2','sequenceNumberColor':'#FFFFFF'}}}%%
flowchart LR
  S["evidence-agent 源碼<br/>+ jedi-common / jedi-file-upload<br/>(build 機需可達 Nexus)"] --> W["dependency-injector<br/>私房 wheel<br/>(複用 FR-063 rebuild_wheels.sh)"]
  W --> N["Nuitka standalone 編譯<br/>version bake + resource_path()<br/>--include-data-dir assets/<br/>EXCLUDE 名單重評(Q3)"]
  N --> P["組 tarball 骨架<br/>agent/ + install.sh + systemd/"]
  TB["工具 bundle 下載/整備<br/>CINC / sonar-scanner /<br/>LibreOffice(Q1)/ xsltproc / git"] --> P
  P --> M["gen_integrity_manifest.py<br/>(FR-064 改常數)逐檔 hash"]
  M --> SG["sign_manifest.sh → License Center 簽章<br/>type=agent_manifest(D6)"]
  SG --> OUT["guidant-agent-&lt;ver&gt;.tar.gz<br/>出貨"]
圖 3 — 出貨側 build 管線(.1 → .2 產物流)

5.3 FR-066.3 install.sh 與 systemd

白話:這階段做客戶手上唯一會碰到的東西——安裝腳本。客戶解開 tarball、跑 install.sh、回答三個問題,agent 就變成一個開機自起、由 systemd 看管的系統服務並自動向雲端註冊上線;日後升級換版、出問題回滾、日常看狀態看 log,也全走同一支腳本的子命令。它吃 .2 的 tarball 當輸入,是 .1/.2 成果對客戶的「出口」。

5.3.1 install.sh 主流程

七步(承 FR-065 七步結構):前置檢查(root / systemd / 磁碟空間 / amd64;支援 --check-only)→ 驗 manifest 簽章(D6 交付完整性)→ 落檔 /opt/guidant-agent/versions/<ver>/裝 bundle 工具(含 Q1 定案的 LibreOffice 落地形式與 fonts-noto-cjk 字型)→ 建 symlinkcurrent → 版本目錄;工具指令 symlink 供 connector 預設路徑取用)→ 裝 systemd unitguidant-agent.serviceEnvironmentFile=/etc/guidant-agent/agent.env)→ 啟動systemctl enable --now)。

互動問答三題生成 agent.env(D5):①雲端位址 ②註冊 token ③本機對外位址(CSR SAN 用)。生成後 agent 首次啟動即走既有 core/enroll.py 自我註冊,並以 gunicorn/ssl 自起 8443 資料面 TLS(D4)——nginx unit/設定完全不存在於交付物。

5.3.2 upgrade / rollback(D7)

  • install.sh --upgrade <新tarball>:驗章 → 解進 versions/<新ver>/ → 停服務 → 切 current symlink → 起服務;資料目錄(SQLite 檔、mTLS 憑證、agent.env)不動。
  • install.sh --rollback:切回前一版 symlink、重啟。無 DB migration、無備份還原步驟——這是刻意的輕量模型(D7)。

5.3.3 維運子命令

比照 FR-065:status(服務狀態+版本+心跳連線)/logs(journalctl 包裝)/uninstall(停服務、移除 unit 與版本目錄;資料目錄詢問後才刪)/fingerprint(印出本機三指紋值,供雲端綁定核對)。

5.4 FR-066.4 驗收與文件

白話:前三棒各自驗過自己,這棒證明「整條路對客戶真的成立」——拿一台從沒裝過任何東西的 Linux 機,只給它一顆 tarball,從安裝到跑完一輪真實檢測任務;再模擬客戶的封閉網路(斷網)環境重來一遍。最後把過程寫成客戶看得懂的安裝手冊,並把 Windows 可不可行的評估寫成文件收口 D8。

  • 真機驗收:乾淨 Linux 機(非 build 機、非開發機)→ tarball → install.sh → 心跳上線 → 派一輪實際檢測任務(至少涵蓋一個用到 bundle 工具的 connector,如 CINC)→ 證據回收成功;另驗 upgrade → rollback 一輪。
  • 封閉網路驗證:斷外網環境完整重跑安裝與任務(工具 bundle 自足性的最終證明)。
  • 客戶安裝手冊:比照 FR-065 T-4.1 手冊體例(安裝/開通/升級/回滾一本通),agent 版另含目標主機前提(OpenSCAP / Nmap 引擎裝在受測機的說明沿用 FR-057/058 setup_guide 內容)。
  • Windows 評估文件(D8):從零寫,覆蓋指紋來源(WMI / registry)、服務形式(Windows Service / NSSM)、工具鏈可用性三題,結論給「可行性+若做的前提清單」,不承諾時程。

6. 拆分(6 子需求 × 21 子任務)

依賴鏈:FR-066.1 → .2 → .3 串行,.4 收口

FR-066.1 Nuitka build 管線與程式改造 — 依賴:無(起點)

# 子任務 內容 驗收 依賴
T-1.1 SQLite 化 Q2 掃描先行(jedi-file-upload PG 方言)→ init_db()sqlite:///、SQLite 檔落資料目錄、agent-db 退場 Q2 掃描結果落文件;agent 以 SQLite 起動、檔案上傳/查詢功能綠;無任何 PG 連線需求
T-1.2 程式改造 version bake 取代讀 pyproject.toml;__file__ 三處(inspec.py:145 / nmap.py:101 / config.py:85)改 resource_path();指紋直讀+廢 collect-host-id.sh(D9);connector 工具路徑配置化 源碼樹不存在的環境下版號/資源定位正確;裸機直讀三指紋值與宿主實值一致;collect-host-id.sh 掛點全移除 T-1.1
T-1.3 agent 版 build_release 以 FR-063 管線為藍本:私房 wheel 複用、Nexus 套件編入、--include-data-dir assets/、EXCLUDE 名單重評(Q3)、smoke 驗證 無 Python 環境的乾淨機上產物可啟動、建 SQLite 表、心跳邏輯 dry-run 通過;build 腳本可重跑 T-1.2

FR-066.2 tarball 打包與工具 bundle — 依賴:FR-066.1(build 產物是輸入)

# 子任務 內容 驗收 依賴
T-2.1 工具 bundle CINC omnibus/sonar-scanner+JRE/LibreOffice 離線化(Q1 實測選定 deb 組 vs portable)+fonts-noto-cjk/xsltproc+nmap.xsl/git;Q4 openscap-utils・sshpass 殘留查證 斷網環境下工具全數可安裝且可執行(cinc-auditor version / sonar-scanner -v / libreoffice 轉檔+fc-match 'Noto Sans CJK TC' 正確);Q1/Q4 結論落文件 T-1.3
T-2.2 manifest 簽章+啟動驗章 簽章泛用層加 agent_manifest type;gen_integrity_manifest.py 改常數;sign_manifest.sh 接 License Center;agent 啟動驗章、失敗拒起 正常包啟動綠;任改一檔啟動即拒(突變驗證);簽驗流程可重跑 T-1.3
T-2.3 build_bundle 打包腳本 tarball 骨架組裝(agent/ + tools/ + install.sh + systemd/ + manifest)+sha256 產出 一鍵產出 guidant-agent-<ver>.tar.gz+sha256;解包結構與圖 2 一致 T-2.1, T-2.2

FR-066.3 install.sh 與 systemd — 依賴:FR-066.2(tarball 是輸入)

# 子任務 內容 驗收 依賴
T-3.1 install.sh 主流程 七步安裝+--check-only+互動問答三題生成 agent.env+systemd unit+自起 TLS 8443(D4/D5) 乾淨機一鍵裝完 → 服務 active → enroll 成功 → mTLS 心跳上線 → 8443 憑證為 CA 簽發(SAN=填答位址) T-2.3
T-3.2 upgrade / rollback --upgrade(驗章→解新版→切 symlink→重啟)+--rollback(切回前版);資料目錄不動(D7) 升級後版本上報為新版且既有資料(SQLite / 憑證)保留;回滾後服務以舊版正常心跳 T-3.1
T-3.3 維運子命令 status / logs / uninstall / fingerprint(FR-065 同款體例) 四子命令輸出正確;uninstall 後系統無殘留 unit;fingerprint 輸出與雲端綁定值一致 T-3.1

FR-066.4 驗收與文件 — 依賴:FR-066.1–.3(收口)

# 子任務 內容 驗收 依賴
T-4.1 乾淨機真機驗收+封閉網路驗證 乾淨 Linux 機端到端(§7 全清單)+斷網環境重跑安裝與任務 §7 端到端驗收全數通過並留紀錄 T-3.3
T-4.2 客戶安裝手冊 安裝/開通/升級/回滾一本通(FR-065 手冊體例),含目標主機前提說明 依手冊可由非開發者完成安裝(以 T-4.1 過程校驗步驟完備) T-4.1
T-4.3 Windows 評估文件 從零寫:指紋(WMI/registry)/服務形式(Windows Service/NSSM)/工具鏈可用性三題(D8) 三題各有結論與前提清單;明確標注「不實作不承諾」

FR-066.5 EL(Rocky/RHEL 9)生態支援(PM 決議暫緩,最後做) — 依賴:FR-066.1–.3(產物與腳本是輸入);於 .4 收口前完成

# 子任務 內容 驗收 依賴
T-5.1 編譯環境降 glibc(容器化編譯) build_agent_release 改於 debian:11(glibc 2.31)容器內編譯(Python 版本對齊、私房 wheel/Nexus 憑證進容器、build_all 接上);產物向上相容三家族 smoke_agent_release 5/5 綠;rockylinux:9 容器內產物可啟動、無 GLIBC 缺符號;build_all 一鍵重出 tarball
T-5.2 install.sh EL 適配 --check-only 加發行版辨識(os-release)+SELinux 狀態(getenforce)+firewalld 8443 檢測;輸出對應指引指令,不自動改防火牆/安全政策;SELinux enforcing 下服務起不來的最小適配(runner 查證後定案,查證先於修) Rocky 9 上 --check-only 輸出正確指引;deb 家族輸出不回歸;enforcing 下服務可 active T-5.1
T-5.3 Rocky 9 真機端到端驗收 SELinux enforcing 的 EL9 機器走全鏈:install → TOFU → enroll → 心跳 → 8443 mTLS → 派一單 CINC 檢測任務 → 報告回雲端、LibreOffice 轉 PDF 中文不缺字(驗收機由決策者提供) §7 驗收清單於 EL9 全數通過並留紀錄 T-5.2

FR-066.6 docker compose 形態+SeaweedFS 內建儲存 — 依賴:FR-066.1(編譯產物是輸入);與 .4 平行

# 子任務 內容 驗收 依賴
T-6.1 指紋二源化(D11,先行) device_uuid 改 sha256(product_uuid|machine-id) 二源(agent 端演算法+原生/容器兩形態一致);BE enroll 端加同指紋撞號偵測(拒絕並提示,含 error code);既有 agent 重 enroll SOP 落文件;collect-host-id.sh 殘跡確認全清 二源指紋在原生與容器內算值一致且等於宿主直讀;BE 撞號註冊被拒且訊息可辨;123 重 enroll 成功
T-6.2 agent SeaweedFS 儲存支援 config.py 補 seaweedfs 分支(複用套件 SeaweedfsUploadConfigDTO);env 契約定案(STORAGE_TYPE=seaweedfs 預設值與 SEAWEEDFS_* 變數,沿用既有 MINIO_* 命名精神、先 grep 全 codebase 確認無同義變數);自有 SeaweedFS/MinIO 設定文件;防篡改成立條件查證(SeaweedFS S3 Object Lock/WORM 支援度+威脅模型邊界,查證結論落文件、宣稱強度依結論定) STORAGE_TYPE=seaweedfs 時檔案實際落 SeaweedFS 且 storage_type 記 seaweedfs;改 env 指自有 MinIO 亦通;查證結論落卡與文件
T-6.3 compose 交付包 Nuitka 產物進薄 base image(非源碼版)+SeaweedFS service(官方 image,weed server -s3 -filer,admin UI 曝露走內網介面綁定)+指紋二源掛載(product_uuid/machine-id 唯讀)+資料 volume 佈局(D7 精神:資料與版本分離)+build_all 加 compose 打包出口(同一顆產物同一套簽章);舊 deploy/docker-compose.yml 汰換或改造 乾淨 docker 機 compose up 一鍵起(agent+SeaweedFS),agent 8443 mTLS 自起、enroll 上線;image 內翻不到 .py 源碼;build_all 一鍵出 compose 包 T-6.1, T-6.2
T-6.4 端到端驗收(含 D13 既有鏈路) compose 版裝起 → 雲端儲存設備設定選「使用 Agent 服務」→ 上傳證據 → 檔案落 agent 側 SeaweedFS(admin UI 可見、DB storage_type 正確)→ 產品端預覽/下載通;升級(換 image tag)資料存活;驗收紀錄落文件 全鏈通並留紀錄;既有 FR-039 讀寫路徑無回歸 T-6.3
T-6.5 configure-storage 子命令 agent 維運子命令加 configure-storage:互動問答/帶參數設定儲存後端三型,寫 agent.env、實打端點驗連通、重啟生效;compose 形態對應操作路徑(exec 或 .env 說明);deploy README 同步 三型各設定一輪皆通(連通驗證擋得住錯 endpoint/憑證);設定後上傳實際落新後端;原生與 compose 皆有可操作路徑 T-6.2
T-6.6 compose 版互動安裝程式 交付包補 install.sh(照主產品 FR-065 installer 模式):①環境檢查(docker/compose plugin/磁碟/machine-id/product_uuid 可讀)②docker load+digest 比對③互動問答(雲端位址/註冊 token/本機對外位址三題,SeaweedFS 憑證自動生成)④產 .env(600)⑤compose up 等 healthcheck⑥印上線結果;維運子命令 status/logs/restart/stop/start/uninstall+configure-storage 轉呼叫容器內 ctl;--check-only/--config 非互動模式;升級模式(load 新 image→換 tag→up -d,資料 volume 不動);README/INSTALL.txt 對齊 乾淨 docker 機解包跑 install.sh 問答三題→兩容器 healthy→enroll 上線,全程零 docker 指令;--check-only 在缺 docker 機器上正確擋下;維運子命令可用;upgrade 換版資料存活 T-6.3

| T-6.7 | 恢復系統內建 SeaweedFS(出廠快照+configure-storage 選項) | 安裝生成 SEAWEEDFS 帳密時快照 factory-defaults.env 到資料目錄(600、只含 SEAWEEDFS_* 五項,D7 資料目錄語意跨版本存活);原生 scripts/install/install.sh 與 compose deploy/install.sh 兩處同步;configure-storage 第一題改四選項(1 系統內建恢復預設/2 自有 SeaweedFS/3 自有 MinIO/4 本機磁碟),選 1 從快照讀回→照舊連通驗證→寫入→重啟、user 零輸入;快照不存在時選項照顯但給明確指引(舊站點已改自有後端補不回,文件註明找原廠);upgrade 路徑補快照(現行設定仍為內建值時);「舊檔不搬家」紅字警告在恢復路徑同樣觸發(T-6.5 機制);deploy README 對齊。主產品對應功能另卡處理 | 原生與 compose 各走一輪「內建→自有→恢復內建」循環,恢復後上傳實落內建 SeaweedFS;快照檔 600;無快照場景指引正確;deb 家族不回歸 | T-6.5, T-6.6 |

FR-066.7 Windows(Docker Desktop)支援 — 依賴:FR-066.6(compose onepack 包是輸入)

D19(2026-08-23):本節全卡樹暫緩——PM 拍板 Windows 不再探討;T-7.1/T-7.4 已完成的探路結論保留(見 t71-windows-probe.md/t74-wsl-engine-probe.md)。

# 子任務 內容 驗收 依賴
T-7.1 160 真機探路實測 在 192.168.50.160(Win11+Docker Desktop)手動走通全鏈並記錄實況:onepack 解包→(暫用 WSL 終端跑現有 install.sh 或手動等效步驟)→兩容器起→enroll 上線→派一單檢測任務。重點查證四項落文件:①指紋二源掛載在 Docker Desktop 的實際行為(product_uuid 有無/machine-id 是誰的/守門與 log 表現)②雲端連回 8443 的網路路徑(Docker Desktop 端口映射行為,需不需要額外設定)③安裝第三題 IP 自動偵測的錯法與正確填法④重置 Docker Desktop 後重新開通 SOP 實測。實測記錄=T-7.2 的規格輸入 全鏈通並留實測紀錄;四項查證各有結論;擋路項(若有)逐條列出
T-7.2 install.ps1 互動安裝程式 照 deploy/install.sh 的流程邏輯(環境檢查→load image→問答三題→生成 .env→compose up 等 healthy→印上線結果)出 PowerShell 版;環境檢查改查 Docker Desktop(裝了沒/跑了沒/版本);第三題預設值改 Windows 主機 IP 偵測;TOFU 憑證確認、SeaweedFS 帳密生成、factory-defaults.env 快照對等實作;維運子命令(status/logs/restart/stop/start/uninstall/configure-storage 轉呼叫容器內 ctl)對等;T-7.1 查證出的 Windows 專屬步驟(8443/防火牆等)內建進流程或明確指引 160 上解包→install.ps1 問答三題→兩容器 healthy→enroll 上線,全程不開 Linux 終端不手打 docker 指令;維運子命令可用;configure-storage 恢復循環通 T-7.1
T-7.3 Windows 前置指引+驗收收口 薄指引文件(Docker Desktop 安裝與授權須知/機器前提/install.ps1 用法/重置後重新開通 SOP/已知限制含指紋漂移邊界);160 端到端終驗(含升級路徑:同 onepack 跑 upgrade 資料存活);build 側若需(onepack 內附 install.ps1)調整打包腳本 指引可由未參與者照做裝成;終驗全鏈+升級通留紀錄;PM 展示可用(setup.exe 升級項的反饋入口) T-7.2
T-7.4 B 案生死驗證(已完成,判死) WSL2+docker engine+排程器常駐可行性實測 ✅ 完成(2026-08-23):不可行——閒置 60 秒凍結,詳 t74-wsl-engine-probe.md

FR-066.8 原生 Windows 版 — 依賴:FR-066.1(Nuitka 產物源碼同源);T-4.3 評估+T-7.1 實測為規格輸入

D19(2026-08-23):本節全卡樹暫緩——PM 拍板 Windows 不再探討;T-7.1/T-7.4 已完成的探路結論保留(見 t71-windows-probe.md/t74-wsl-engine-probe.md)。

# 子任務 內容 驗收 依賴
T-8.1 Windows build 管線 Windows build 機佈建(MSVC Build Tools+Python 3.11+Nexus 可達;機器待決策者定案,160〔Server 2025, 8vCPU/24GB〕可兼任候選);dependency-injector 私房 wheel MSVC 重編;Nuitka standalone 編 agent.exe;build 腳本 PowerShell 載體;smoke 對應(產物翻不到 .py/可啟動/健康檢查)。參考 Linux 版教訓:1303~1309 七輪除錯,預留試錯棒數 無 Python 的乾淨 Windows 機上 agent.exe 可啟動、建 SQLite、心跳 dry-run 通過;build 可重跑 build 機定案
T-8.2 指紋 OS 抽象層+程式改造 fingerprint provider 化(D9 延後項正式開工):Linux=讀檔(現行)、Windows=WMI Win32_ComputerSystemProduct.UUID+registry MachineGuid(winreg 標準庫);WMI 佔位值(FFFFFFFF…)處理策略;工具路徑預設值 Windows 化(AGENT_CINC_BIN 等);設定落點 C:\ProgramData\GuidantAgent\ 二源指紋在 Windows 讀值與 WMI/registry 直查一致;佔位值場景有明確行為;Linux 版行為零回歸(既有測試綠) T-8.1
T-8.3 雙服務化+install.ps1 WinSW(或同族 wrapper,實測選定)包 agent.exe+weed.exe(SeaweedFS Windows 版)成兩個 Windows 服務(開機自起/崩潰重啟/log 輪替/優雅停止);install.ps1 七步(環境檢查→驗章→落檔 versions\→裝服務→問答三題〔IP 偵測用 Find-NetRoute,T-7.1 已驗〕→防火牆規則自動加〔T-7.1 已驗一條 inbound 即通〕→啟動);TOFU 憑證確認 PowerShell 重寫;SeaweedFS 帳密生成+factory-defaults 快照+configure-storage 對等;升級回滾(versions\+junction 切換,D7 模型);維運子命令 乾淨 Windows 機 install.ps1 問答三題裝成→兩服務執行中→enroll 上線;登出+重開機後服務自起、雲端仍在線;升級回滾資料存活;維運子命令可用 T-8.1(可與 T-8.2 平行起工)
T-8.4 工具鏈 Windows 離線整備 CINC msi silent(msiexec /qn)+安裝後路徑;sonar-scanner windows-x64 zip(自帶 JRE);LibreOffice 選型實測(msi silent vs Portable,Q1-Windows)+Noto CJK 字型落地+headless 中文轉檔驗證;MinGit;weed.exe 釘版;Windows 版 tools-manifest(釘版+sha256+Nexus 第二來源)整套 斷網 Windows 機上工具全數可裝可執行(cinc-auditor version/sonar-scanner -v/LibreOffice 轉 PDF 中文不缺字);manifest 完整 可與 T-8.1~8.3 平行
T-8.5 簽章+乾淨機端到端驗收 manifest 簽章泛用層接 Windows 產物(agent_manifest type 沿用)+啟動驗章+突變測試;打包腳本出 Windows 交付包;乾淨機端到端:裝→enroll→跑 CINC+SSH 遠端型任務→證據落本機 SeaweedFS→登出/重開機存活→升級回滾→防竄改;SmartScreen 白名單指引寫入文件(D18 策略) §7 對應項全綠留紀錄;改任一檔啟動被拒;白名單指引可操作 T-8.2, T-8.3, T-8.4

開放項歸屬(Q1–Q4)

# 查證項 歸屬子任務 性質
Q1 LibreOffice 離線化形式(deb 組 vs portable/AppImage) T-2.1 實測選定,判準:離線可裝/字型一起落/體積可接受
Q2 jedi-file-upload PG 方言依賴掃描 T-1.1 D2 落地的前置守門,.1 開工先做
Q3 Nuitka EXCLUDE_COMPILE_PKGS 名單重評 T-1.3 agent 依賴集合與 BE 不同,不照抄 FR-063;冷 build 預期遠低於 BE 56 分鐘
Q4 openscap-utils / sshpass 是否已無用 T-2.1 殘留即不搬進 tarball,包更瘦

7. 端到端驗收

  1. 乾淨 Linux 機(無 Python、無 docker、非 build 機)解 tarball → install.sh 七步全綠 → 互動三題填答 → 服務 active、開機自起。
  2. Agent 直讀宿主三指紋(product_uuid / machine-id / MAC)與實機值一致;enroll 全自動拿到 mTLS 憑證;心跳上線、雲端 agent 清單可見。
  3. 一輪實際檢測任務跑通:至少含一個吃 bundle 工具的 connector(CINC)+一個 SSH 遠端型(OpenSCAP)→ 報告回收進證據池;LibreOffice 預覽轉 PDF 中文不缺字。
  4. 封閉網路情境:斷外網環境完整重跑 1–3(雲端產品在同內網),無任何步驟需要對外連線。
  5. 升級→回滾--upgrade 新版心跳上報新版號、SQLite 資料與憑證保留;--rollback 後舊版正常服務。
  6. 防竄改:竄改任一交付檔後啟動被拒(manifest 驗章生效)。
  7. 文件收口:客戶安裝手冊、Windows 評估文件產出。
  8. EL 家族:以上 1–3 於 Rocky Linux 9(SELinux enforcing)重跑一輪通過(FR-066.5)。
  9. compose 形態:docker 機上 compose 版完成上述 2/3/6 對應項+「使用 Agent 服務」證據上傳落 SeaweedFS 全鏈通(FR-066.6)。
  10. Windows(Docker Desktop)形態:160 真機上 install.ps1 全程問答安裝→enroll 上線→跑一單檢測任務→報告回雲端;重置 Docker Desktop 後照 SOP 重新開通成功(FR-066.7)。
  11. 原生 Windows 形態:乾淨 Windows 機 install.ps1 裝成雙服務→登出/重開機服務存活且雲端在線→跑檢測任務→證據落本機 SeaweedFS→升級回滾→防竄改拒起(FR-066.8)。

附註

  • 本案標的 repo 是 evidence-agent;主專案(compliance-manager-be)只承載文件與簽章工具鏈的複用來源。
  • Q1–Q4 不阻擋開工,但 Q2 是 T-1.1 的前置守門。
  • Notion 開卡(母案+子卡)於派工時依 big-feature-workflow skill Step 4/5 進行,本文件拆分表即開卡依據。