# 4D PluginLoader 邊界分析

本文件承接 [4A WebApi / ServiceHost / Plugin Loader 邊界分析](phase-four-a-boundary-analysis.md)、[第四階段 4B / 4C / 4D 執行切分表](phase-four-execution-split-plan.md)、[第四階段標準 / 文件 / 施作範圍 / 驗收條件總表](phase-four-standards-scope-acceptance.md)、4B WebApi 第一版與 4C ServiceHost 第一版結果，整理 4D PluginLoader 進入任何程式實作前必須確認的信任模型、metadata contract、版本策略、隔離策略、卸載與回復策略。

本文件只做邊界分析與後續決策依據，不代表已同意建立 PluginLoader、掃描 plugin folder、載入外部 DLL、執行外部程式碼、修改 `IDeviceAdapter` public contract、修改 config schema、導入 plugin framework、保存正式 DLL 路徑、接真實硬體、執行正式 DB DDL 或改變程式啟動方式。

## 0. 文件狀態

| 項目 | 內容 |
|---|---|
| 文件狀態 | 已整理 |
| 整理日期 | 2026-06-06 |
| 對應階段 | 第四階段 4D PluginLoader |
| 文件 repo | `hs-device-control-template-docs` |
| 程式 repo | `hs-device-control-template` |
| 程式 branch | `poc/nmodbus-tcp` |
| 程式基準 commit | `a846212 新增 4C ServiceHost 第一版與驗收測試` |
| 已完成前置 | 4A 邊界與決策確認、4B WebApi 第一版、4C ServiceHost 第一版 |
| 本次目標 | 固定 4D PluginLoader 的信任、版本、metadata、隔離、卸載、回復、測試與停止線 |
| 本次不做 | 不建立 Loader、不載入 DLL、不掃描資料夾、不執行 plugin、不改 Adapter / config / DB |

## 1. 4D 定位

4D PluginLoader 的目標不是讓任何 DLL 立刻被載入，而是先把「什麼可以被視為 plugin、誰可以信任它、版本如何判定、失敗如何隔離、如何停用與回復」整理成可測試、可驗收的 contract。

| 原則 | 說明 |
|---|---|
| Metadata 不等於可執行 | 3D 已有 `PluginDescriptor` / `PluginCatalogService`，但目前只代表 metadata catalog，不代表允許載入 DLL。 |
| Loader 不寫流程邏輯 | PluginLoader 不得寫 Workflow node、Retry、NextNode 或 TaskStateMachine 規則。 |
| Adapter contract 仍是設備邊界 | 即使未來 plugin 提供設備模組，也必須遵守 `IDeviceAdapter` / command contract，不直接繞過 Adapter 邊界。 |
| ServiceHost 承接 lifecycle | 4C 已建立 ServiceHost 第一版；未來 Loader 若要常駐管理，應由 ServiceHost 組裝，不由 WebApi 直接載入 DLL。 |
| WebApi 只查詢 / 控制 | WebApi 可以查詢 plugin catalog 或提交啟停請求，但不得在 Controller 內直接載入 DLL。 |
| 外部程式碼需先不可信 | 未建立來源、簽章、checksum、版本與隔離策略前，所有外部 DLL 一律視為不可執行。 |

## 2. 目前程式基準

| 項目 | 目前狀態 |
|---|---|
| Application plugin metadata | 已有 `HS.DeviceControl.Application.Plugins`、`PluginDescriptor`、`PluginCapabilityDescriptor`、`IPluginCatalogService`、`PluginCatalogService`。 |
| Plugin metadata 測試 | 已有 `PluginCatalogServiceTests`，驗證 metadata、缺少欄位、敏感資訊與 capability 檢查。 |
| WebApi | 4B 已建立 `HS.DeviceControl.WebApi`，但未新增 plugin loader route。 |
| ServiceHost | 4C 已建立 `HS.DeviceControl.ServiceHost`，可作為未來常駐 lifecycle 承接點。 |
| Loader 程式 | 尚未存在 `PluginLoader`、plugin folder scanner、assembly loading 或 unload 管理。 |
| 外部 DLL 載入 | 尚未授權；唯讀搜尋未找到已建立的外部 DLL Loader。 |

## 3. 4D 邊界總表

| 邊界 | 可整理內容 | 目前停止線 |
|---|---|---|
| Plugin identity | `PluginId`、`Version`、`DisplayName`、`Capabilities`、`Checksum`、來源與狀態。 | 不把 identity 直接當成執行授權。 |
| Trust model | 來源白名單、簽章、checksum、版本相容、人工核准狀態。 | 不信任任意 folder 或使用者提供的 DLL。 |
| Metadata contract | catalog、capability、device type、command name、相容版本與停用原因。 | 不在 metadata 中保存密碼、IP、Port、完整 DLL 路徑或正式環境資訊。 |
| Loader lifecycle | `Discovered`、`Verified`、`Loaded`、`Disabled`、`Faulted` 等狀態語意。 | 不建立實際 loader、不呼叫 assembly loading API。 |
| Isolation | 錯誤隔離、禁載、停用、recover、審計紀錄。 | 不讓 plugin 例外中斷 Core / ServiceHost terminal state。 |
| Adapter integration | 未來 plugin 若提供 Adapter，仍需走已確認的 Adapter factory / dispatcher 邊界。 | 不修改 `IDeviceAdapter` public contract。 |
| Config schema | 可整理候選欄位與風險。 | 不修改 `devices.json`、`workflows.json`、`appsettings.json` schema。 |
| DB / Audit | 可整理 read-only audit log 欄位。 | 不執行 DB DDL、ALTER TABLE、正式 Apply。 |
| 真實硬體 | 只列出後續驗收需求。 | 不接真實設備、不執行案場 plugin。 |

## 4. 與 3D / 4B / 4C 的承接關係

| 前置 | 可承接內容 | 4D 使用方式 |
|---|---|---|
| 3D Plugin metadata | `PluginDescriptor`、capability、catalog service、`APP-0331` metadata error。 | 作為 4D metadata contract 的起點，但不升級為 loader。 |
| 3D ControlUnit / Resource / Command | 多設備單元、Resource Lock、Command planning。 | 未來 plugin capability 需對應設備單元與命令能力，不可繞過 resource / command 規則。 |
| 4B WebApi | HTTP 查詢與控制入口。 | 可在後續新增 read-only catalog API，但不得在 API 內載入 DLL。 |
| 4C ServiceHost | 背景生命週期與 Adapter lifecycle。 | 未來 Loader lifecycle 應由 ServiceHost 組裝與監控，不由 ConsoleHost 或 WebApi 直接負責。 |

## 5. 信任模型草案

| 檢查項 | 建議規則 | 失敗處理 |
|---|---|---|
| 來源 | 第一版只允許受控來源，例如 repository 內測試 fixture 或明確白名單資料夾。 | 標記 `Rejected`，不載入。 |
| Checksum | metadata 宣告 checksum 需與實際 artifact 比對。 | 回傳標準錯誤並寫 audit。 |
| 簽章 | 若進入正式 DLL，需定義簽章或發佈者驗證策略。 | 未通過不可進入 `Verified`。 |
| 版本相容 | plugin 需宣告目標 contract version 與 capability version。 | 版本不符需禁載，不做自動降級。 |
| 敏感資訊 | metadata 不得包含 password、token、connection string、正式 IP / Port。 | 直接視為 metadata invalid。 |
| 人工核准 | 正式載入前需有 operator / approver / reason。 | 無核准不可載入。 |

## 6. Plugin 狀態語意草案

| 狀態 | 語意 | 是否可執行 |
|---|---|---|
| `Discovered` | 已發現 metadata 或候選 artifact，但尚未驗證。 | 否 |
| `Verified` | metadata、來源、checksum、版本與簽章通過。 | 仍需另行確認 |
| `Loaded` | 已載入並可由受控 lifecycle 管理。 | 僅未來授權後 |
| `Disabled` | 已被人工或策略停用。 | 否 |
| `Rejected` | 驗證失敗或來源不可信。 | 否 |
| `Faulted` | 載入或執行期間發生錯誤。 | 否，需隔離與 audit |

本文件階段只允許整理狀態語意，不允許把任何狀態轉成實際 DLL 執行。

## 7. 第一版候選 contract

以下只是後續 4D contract 草案可討論的方向，尚未授權程式實作。

| 類型 | 候選命名 | 說明 |
|---|---|---|
| Metadata | `PluginDescriptor` extension 或 `PluginManifest` | 描述 plugin identity、version、checksum、capability 與相容性。 |
| Trust result | `PluginVerificationResult` | 回傳驗證是否通過、錯誤碼、原因與安全摘要。 |
| State | `PluginLoadState` | 描述 discovered / verified / loaded / disabled / faulted。 |
| Catalog | `IPluginCatalogService` extension | 只讀查詢 metadata，不載入 DLL。 |
| Audit | `PluginAuditRecord` | 記錄 operator、source、checksum、decision、reason、time。 |
| Loader | `IPluginLoader` | 只可在使用者另行確認後討論；本文件不建立。 |

## 8. 測試與驗收策略

| 測試 | 目的 | 本階段是否執行 |
|---|---|---|
| 文件驗證 | 確認 4D 邊界、信任模型與停止線已整理。 | 是 |
| Metadata validation tests | 驗證缺欄位、敏感資訊、capability 與 checksum 語意。 | 可在 contract 階段討論 |
| Trust model tests | 驗證來源不可信、checksum 不符、版本不符、未核准不可載入。 | 待確認 |
| Loader lifecycle tests | 驗證 load / unload / fault / disable / recover。 | 否，需另行確認 |
| External DLL tests | 使用測試 plugin assembly 驗證載入。 | 否，需另行確認 |
| 真實硬體 tests | 驗證 plugin device adapter 對真實設備。 | 否 |
| DB audit tests | 驗證 audit 寫入 DB。 | 否，不執行 DDL |

## 9. 主要風險

| 風險 | 說明 | 建議處理 |
|---|---|---|
| 外部 DLL 信任不足 | 未驗證來源、簽章或 checksum 就載入，可能執行不可信程式碼。 | 先建立 trust model 與禁載規則。 |
| Adapter contract 被破壞 | Plugin 若直接操作硬體或改 Adapter contract，會破壞共用骨架。 | 保持 Adapter contract 不變，先做 wrapper / registry 邊界。 |
| Config schema 過早固定 | plugin folder、manifest、binding 若太早寫入正式 schema，後續難改。 | 先整理候選欄位，不改既有 schema。 |
| 卸載與隔離困難 | .NET 5.0 下 assembly unload 與隔離策略需要謹慎設計。 | 第一版先不載入 DLL，先定義狀態與失敗策略。 |
| 錯誤影響 ServiceHost | plugin 例外若未隔離，可能拖垮背景服務。 | 定義 `Faulted`、禁載、停用與 audit。 |
| 敏感資訊外洩 | metadata 若包含正式路徑、IP、Port、token 會進 repo 或 log。 | metadata sanitizer 與測試必須保留。 |

## 10. 本階段不做事項

- 不建立 `PluginLoader` 程式。
- 不建立 `IPluginLoader` public contract。
- 不掃描 plugin folder。
- 不載入外部 DLL。
- 不呼叫 `Assembly.Load`、`LoadFrom`、`LoadFile` 或類似 API。
- 不執行外部 plugin 程式碼。
- 不新增或更換 plugin framework / DI framework / sandbox 套件。
- 不修改 `IDeviceAdapter` public contract。
- 不修改 Core / Adapters / Application / Infrastructure 既有 public method 簽章。
- 不修改 `devices.json`、`workflows.json`、`appsettings.json` schema。
- 不保存密碼、Token、IP、Port、完整 connection string、正式 DLL 路徑或正式環境資訊。
- 不執行 DB 寫入、DDL、ALTER TABLE 或正式 Apply。
- 不接真實硬體或案場客製 plugin。

## 11. 建議下一步

4D 邊界分析完成後，建議下一步整理 `4D PluginLoader 決策確認表`，讓使用者逐項確認：

1. 4D 第一版是否先做 trust model / metadata contract，不載入 DLL。
2. 是否沿用 3D `PluginDescriptor` / catalog 作為 metadata 起點。
3. 是否要求 checksum / 版本相容 / 來源白名單。
4. 是否保留 Adapter public contract 不變。
5. 是否把 Loader lifecycle 放在 ServiceHost 承接，不讓 WebApi 直接載入 DLL。
6. 是否本輪仍不改 config schema、不保存正式路徑或敏感資訊。
7. 是否所有 Loader 實作、外部 DLL 測試、真實硬體與 DB audit 皆需另行確認。
