Add GE-Enforce P4 client kit: fetch + report + shadow mode (reference)
Client-side integration kit for sourcing manifests from shopdb and reporting results back. Site-neutral reference a site adapts into its GE-Enforce.ps1; the live dispatcher and engine are NOT touched (they are read-only reference under projects/pxe). Only the manifest JSON source moves from a share file to shopdb, plus a result report. - plugins/geenforce/client/ShopdbEnforceClient.psm1: Sync-ShopdbManifest (GET with ETag -> local cache; falls back to last-known-good when shopdb is unreachable so a PC is never left unmanaged), Compare-ShopdbShadow (behavioral diff vs the on-share manifest), Send-ShopdbReport / New-ShopdbReport (best- effort POST /report), Get-ShopdbConfig (BaseUrl + token from HKLM:\SOFTWARE\GE\ShopDB). - plugins/geenforce/client/Invoke-ShopdbEnforce.ps1: orchestrator. Fetches, optionally shadow-compares (installs from the share, only logs the diff), runs the unchanged engine, and reports. Fail-safe: any error exits 0. - docs/GE-ENFORCE-CLIENT.md: the fetch + report contracts, config, cache/fail- safe behavior, the staged shadow -> read-cutover -> payload-migration runbook, and TLS/payload-integrity notes. The report JSON shape matches the POST /api/geenforce/report contract already covered by the reporting tests. Nothing here runs the live client; shadow mode and cutover stay a site decision after Milestone 1 sign-off. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
126
docs/GE-ENFORCE-CLIENT.md
Normal file
126
docs/GE-ENFORCE-CLIENT.md
Normal file
@@ -0,0 +1,126 @@
|
||||
# GE-Enforce client integration (shopdb manifest source + reporting)
|
||||
|
||||
This is the client-side contract for the GE-Enforce manifest-store plugin: how a
|
||||
PC sources its install manifest from shopdb instead of a share file, and how it
|
||||
reports its enforcement result back. It pairs with the plugin proposal in
|
||||
`docs/proposals/ge-enforce-plugin.md`.
|
||||
|
||||
The reference kit lives in `plugins/geenforce/client/`:
|
||||
|
||||
- `ShopdbEnforceClient.psm1` - fetch (with ETag + last-known-good cache),
|
||||
shadow compare, and report helpers.
|
||||
- `Invoke-ShopdbEnforce.ps1` - a reference orchestrator that fetches a manifest,
|
||||
runs the UNCHANGED engine against it, and reports the result.
|
||||
|
||||
These are site-neutral references, not the live dispatcher. A site adapts them
|
||||
into its GE-Enforce.ps1 flow. The engine (`Install-FromManifest.ps1`),
|
||||
detection, self-heal, and SMB payload resolution are untouched - only the source
|
||||
of the manifest JSON moves, plus a result report.
|
||||
|
||||
## What does NOT change
|
||||
|
||||
- The engine and its four filters, all detection methods, self-heal, marker
|
||||
files, and SMB payload staging.
|
||||
- Payload transport for `smb` rows: the client still mounts the share and
|
||||
resolves `apps/...` paths exactly as today. Only the manifest JSON source moves.
|
||||
- The fail-safe posture: any error exits 0. A PC is never blocked or broken
|
||||
because shopdb is unreachable.
|
||||
|
||||
## Configuration
|
||||
|
||||
Registry (provisioned by Azure DSC, same channel as the SFLD credentials):
|
||||
|
||||
```
|
||||
HKLM:\SOFTWARE\GE\ShopDB
|
||||
BaseUrl https://shopdb.<site>.geaerospace.net
|
||||
ApiToken <a geenforce.fetch (+ geenforce.report) managed service token>
|
||||
```
|
||||
|
||||
Mint the token in shopdb: Settings > API Tokens, scopes `geenforce.fetch` and
|
||||
`geenforce.report`. It is a service token (owner must hold those permissions).
|
||||
|
||||
## Fetch contract
|
||||
|
||||
```
|
||||
GET /api/geenforce/manifest?pctype=<scope>[&phase=runtime]
|
||||
X-API-Key: <token>
|
||||
If-None-Match: <cached ETag> (optional)
|
||||
```
|
||||
|
||||
- `200` - body is the full published manifest JSON for the scope (fat client:
|
||||
the engine filters locally, exactly as today). Response headers carry `ETag`
|
||||
and `X-Manifest-Version`. Cache the body + ETag + version.
|
||||
- `304` - your cached copy is current; use it.
|
||||
- `404` - no such scope, or the scope has no published version yet.
|
||||
- Network failure - enforce from the last-known-good cached manifest (the kit
|
||||
does this automatically) and log a warning.
|
||||
|
||||
The served manifest is always the current PUBLISHED snapshot, never a live draft
|
||||
being edited in shopdb, so a half-finished edit can never reach a PC.
|
||||
|
||||
## Report contract
|
||||
|
||||
Each enforcement cycle, POST the result (best-effort; a failed report never
|
||||
fails the cycle):
|
||||
|
||||
```
|
||||
POST /api/geenforce/report
|
||||
X-API-Key: <token>
|
||||
Content-Type: application/json
|
||||
{
|
||||
"hostname": "WJCMM01",
|
||||
"scopename": "gea-shopfloor-cmm",
|
||||
"appliedversion": 3, // the published version you actually ran
|
||||
"enforcerversion": "2.6",
|
||||
"counts": { "installed": 1, "skipped": 3, "failed": 0, "filtered": 2 },
|
||||
"results": [
|
||||
{ "name": "PC-DMIS 2019 R2", "action": "installed", "selfhealed": true },
|
||||
{ "name": "Protect Viewer", "action": "skipped" },
|
||||
{ "name": "eDNC", "action": "failed", "exitcode": 1603,
|
||||
"message": "MSI 1603" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- `appliedversion` lets shopdb show which PCs received the latest manifest
|
||||
(`receivedlatest` in the fleet view).
|
||||
- `action` per entry: `installed` (fired - a self-heal when it should already be
|
||||
present), `skipped` (detected present), `failed`, `filtered`. `selfhealed`
|
||||
marks a drift correction.
|
||||
- shopdb keeps the latest report per (hostname, scope, phase) plus history, and
|
||||
surfaces it under Settings > Enforcement Reports.
|
||||
|
||||
The engine already computes these counts (`installed/skipped/failed/pcFiltered`
|
||||
at the end of its main loop) and knows each entry's action; shape them into the
|
||||
`results` list at the call site (`New-ShopdbReport` in the kit takes a summary
|
||||
with `Installed/Skipped/Failed/Filtered` + a `Results` list).
|
||||
|
||||
## Cutover (safe, staged)
|
||||
|
||||
1. **Configure** the registry values on a canary PC; mint the token.
|
||||
2. **Shadow mode**: run `Invoke-ShopdbEnforce.ps1 -ShadowMode -ShareManifestPath
|
||||
<current share manifest>`. It installs from the SHARE (no behavior change),
|
||||
fetches the shopdb manifest, logs any diff, and reports. Watch for zero diffs
|
||||
across one PC of every pctype for ~20 cycles.
|
||||
3. **Read cutover**: drop `-ShadowMode`. The engine now runs against the
|
||||
shopdb-sourced manifest; payloads still come from the share. Rollback is a
|
||||
one-line revert to the share-sourced call. Keep exporting manifests from
|
||||
shopdb to the share (Settings > Imaging PC Types > Export to Share) so the
|
||||
share stays a break-glass copy.
|
||||
4. **Payload migration** (optional, later): move small scripts/configs to
|
||||
`http`/`inline` payloads, verified by `payloadsha256`. Big MSIs stay on SMB.
|
||||
|
||||
Do not cut a fleet over before the shadow diffs are clean. Preinstall
|
||||
(`phase=preinstall`) stays share-sourced until its own cutover is planned - it
|
||||
runs before enrollment provisions a token.
|
||||
|
||||
## Security notes
|
||||
|
||||
- The client runs as SYSTEM, so shopdb's TLS certificate must be in the machine
|
||||
trust store (air-gapped/self-signed sites provision the CA via the same DSC
|
||||
step as the token).
|
||||
- `http`/`inline` payloads are verified against `payloadsha256` before running,
|
||||
independent of how the entry detects install state. This is the real integrity
|
||||
guarantee and holds even over plain HTTP inside a trusted segment.
|
||||
- The token is a scoped service token: it can fetch manifests and report, and
|
||||
nothing else.
|
||||
Reference in New Issue
Block a user