diff --git a/docs/GE-ENFORCE-CLIENT.md b/docs/GE-ENFORCE-CLIENT.md index 59874f2..524a70e 100644 --- a/docs/GE-ENFORCE-CLIENT.md +++ b/docs/GE-ENFORCE-CLIENT.md @@ -104,12 +104,13 @@ return code, `$null`, or several emitted objects) into the count/results shape `New-ShopdbReport` expects, so a not-yet-compliant engine still produces a valid report. -## Common-scope inheritance +## Common-scope inheritance (opt-in, OFF by default) -Every PC inherits the fleet-wide `common` scope on top of its own pctype scope, -mirroring the real GE-Enforce.ps1 (which applies `common\manifest.json` first, -then the pctype's). `Invoke-ShopdbEnforce.ps1` fetches the `common` scope in -addition to `-Scope` and merges it in via `Merge-ShopdbManifests`: +By default a PC enforces its `-Scope` ALONE. Pass `-IncludeCommon` to also fetch +the fleet-wide `common` scope and merge it on top, mirroring the real +GE-Enforce.ps1 (which applies `common\manifest.json` first, then the pctype's). +When enabled, `Invoke-ShopdbEnforce.ps1` fetches `common` in addition to +`-Scope` and merges it via `Merge-ShopdbManifests`: - entries are keyed by `Name` (case-insensitive); - common's unique entries come first, then all pctype entries (common enforces @@ -117,11 +118,16 @@ addition to `-Scope` and merges it in via `Merge-ShopdbManifests`: - on a `Name` conflict the pctype entry wins (its override replaces common's). Common is fetched over the same fail-safe path (ETag + last-known-good cache). -Pass `-NoCommon` to enforce the pctype alone, or `-CommonScope ` to inherit -a different fleet scope. A run whose `-Scope` already is the common scope does -not merge itself. This is how the three display subtypes (Dashboard, Lobby, 3D -Print Room), selected by `C:\Enrollment\display-type.txt`, pick up shared policy -without duplicating it per subtype. +`-CommonScope ` inherits a different fleet scope; a run whose `-Scope` +already is the common scope does not merge itself. + +Displays do NOT use this: the `gea-shopfloor-display` scope is self-sufficient, +so the display scheduled task omits `-IncludeCommon`. Common-merge exists for a +future share-less non-display PC that genuinely needs the fleet-wide entries +(which would first require repackaging common's SMB payloads as http/inline). +The three display subtypes (Dashboard, Lobby, 3D Print Room), selected by +`C:\Enrollment\display-type.txt`, carry their shared policy inside the display +scope itself, not via common. ## Fail-safe is observable, not silent diff --git a/docs/GE-ENFORCE-DISPLAY.md b/docs/GE-ENFORCE-DISPLAY.md index 630f5af..b0f166b 100644 --- a/docs/GE-ENFORCE-DISPLAY.md +++ b/docs/GE-ENFORCE-DISPLAY.md @@ -5,7 +5,8 @@ credential-less kiosk PCs that pull their manifest over HTTPS on port 443 and authenticate with a read-only service PAT scoped `geenforce.fetch`, sent as `X-API-Key`. They have no SMB share mount. The kiosk engine and the kiosk browser are baked into the display image, not shipped over HTTPS, so the display -manifest heals POLICY / CONFIG drift and inherited common entries, never EXEs. +manifest heals POLICY / CONFIG drift only, never EXEs. It is self-sufficient and +does not inherit the fleet-wide `common` scope (see below). There are three display subtypes, selected by `C:\Enrollment\display-type.txt`: `Dashboard`, `Lobby`, and `3DPrintRoom`. @@ -53,58 +54,18 @@ creates a new version). | `Lobby` | `/tv` | slides plugin TV dashboard (surface `lobby`) | | `3DPrintRoom` | `/parts-kiosk` | **PLACEHOLDER, TODO-confirm** printedparts parts kiosk route; confirm the real 3D-print-room target with the floor team before publishing to production displays | -## Inheritance: the client merges common +## Self-sufficient: displays do NOT inherit common -The manifest model has no inheritance column. The display scope is a plain -(non-common) runtime scope carrying only display-specific entries. The CLIENT -merges the fleet-wide `common` scope underneath the display scope at fetch time. -So `common` is where the fleet-wide policy/config/self-update entries live, and -`gea-shopfloor-display` adds the kiosk-only entries on top. +The `gea-shopfloor-display` scope carries everything a display enforces. It does +NOT inherit the fleet-wide `common` scope. Displays run the enforcer with +common-merge off (the client default; common-merge is opt-in via +`Invoke-ShopdbEnforce.ps1 -IncludeCommon`), so `common`'s SMB-backed fleet +entries (Adobe, Oracle, OpenText, Defect Tracker, EventSaver, printer map, +self-update, asset-reporting, ...) never reach a share-less display. -## Blocker: common carries SMB payloads that break on share-less displays - -Before a share-less display can safely inherit `common`, every `common` entry -that pulls a payload file from the SMB share must first be given an `http` or -`inline` payload (with a `payloadsha256`). A display has no share mount, so any -inherited entry whose `Installer` / `Source` / `Script` resolves to a -share-relative path will fail its fetch. - -Registry entries in `common` carry no payload (they write inline reg values) and -are safe to inherit as-is. The entries below reference a share file and must be -converted first. This audit is the authoritative to-convert list; the payload -bytes themselves are not converted here (that needs the real payload files). - -Common entries that reference an SMB/share payload (as of the on-share -`common/manifest.json`, 22 entries, Version 2.0): - -| # | Common entry | Type | Share payload | Field | -| --- | --- | --- | --- | --- | -| 1 | Adobe Acrobat Reader DC | CMD | `scripts/Install-AcroReader.cmd` | Installer | -| 2 | Migrate pc-type.txt to gea-shopfloor-* taxonomy | PS1 | `scripts/Migrate-PCType.ps1` | Script | -| 3 | WJF Defect Tracker | MSI | `apps/WJF_Defect_Tracker.msi` | Installer | -| 4 | 3OF9 barcode font | File | `configs/3OF9.ttf` | Source | -| 5 | Edge IE-Mode site list | File | `configs/enterprise-mode-site-list.xml` | Source | -| 6 | Ensure VNC firewall rule | PS1 | `scripts/ensure-vnc-firewall.ps1` | Script | -| 7 | FMS hosts pin (WJFMS3.AE.GE.COM) | PS1 | `scripts/Set-FmsHostsEntry.ps1` | Script | -| 8 | Oracle Client 11.2 | CMD | `scripts/Install-Oracle11r2.cmd` | Installer | -| 9 | PrinterInstallerMap (site-map printer installer) | File | `apps/PrinterInstallerMap.exe` | Source | -| 10 | OpenText HostExplorer ShopFloor | CMD | `scripts/Setup-OpenText.cmd` | Installer | -| 11 | GE-Enforce dispatcher (self-update) | File | `GE-Enforce.ps1` | Source | -| 12 | Install-FromManifest lib (self-update) | File | `lib/Install-FromManifest.ps1` | Source | -| 13 | Report asset (host + IP + machine number) to ShopDB | PS1 | `apps/Report-AssetToShopDB.ps1` | Script | -| 14 | EventSaver screensaver (binary) | File | `apps/EventSaver.scr` | Source | -| 15 | EventSaver screensaver (config) | File | `configs/EventSaver.ini` | Source | -| 16 | EventSaver enable (per-user screensaver) | PS1 | `scripts/Set-EventSaverScreensaver.ps1` | Script | -| 17 | EventSaver power (keep monitor awake) | PS1 | `scripts/Set-EventSaverPower.ps1` | Script | -| 18 | EventSaver disable (measuring-tool bays) | PS1 | `scripts/Set-EventSaverDisable.ps1` | Script | -| 19 | EventSaver disable (specific hostnames) | PS1 | `scripts/Set-EventSaverDisable.ps1` | Script | - -Safe to inherit as-is (Registry entries, no share payload): `3OF9 barcode font -registry entry`, `Edge IE-Mode policy level`, `Edge IE-Mode policy site list -pointer`. - -Not every entry above is relevant to a display (a display needs no Oracle -client, OpenText, or Defect Tracker), so a follow-up decision is which common -entries a display should actually run (via `PCTypes` targeting) versus which -must be repackaged as `http`/`inline`. But any that survive targeting must have -a non-SMB payload before displays inherit common. +This was a deliberate decision: a display needs none of common's software, and +inheriting common would have forced repackaging every SMB `common` payload as +`http`/`inline` for a share-less box. Keeping the display scope self-sufficient +avoids all of that. If a future non-display share-less PC genuinely needs the +fleet-wide entries, that is what `-IncludeCommon` plus a per-entry SMB->http +payload conversion would be for -- but displays do not use it. diff --git a/plugins/geenforce/client/Invoke-ShopdbEnforce.ps1 b/plugins/geenforce/client/Invoke-ShopdbEnforce.ps1 index eb112e8..66da901 100644 --- a/plugins/geenforce/client/Invoke-ShopdbEnforce.ps1 +++ b/plugins/geenforce/client/Invoke-ShopdbEnforce.ps1 @@ -24,15 +24,16 @@ .PARAMETER ShadowMode Fetch + compare + report, but install from the share (no behavior change). -.PARAMETER CommonScope - The fleet-wide scope every PC inherits (default 'common'). Its manifest is - fetched in addition to -Scope and merged in, so a display enforces its own - scope entries PLUS common's. On a Name conflict the -Scope (pctype) entry - wins. Set -NoCommon to disable, or point at a different common scope name. +.PARAMETER IncludeCommon + Also fetch the fleet-wide CommonScope and merge it into -Scope, so the PC + enforces its own scope entries PLUS common's (on a Name conflict the -Scope + entry wins). OFF by default: a scope is enforced ALONE (self-sufficient). + Displays are self-sufficient and do NOT set this. Only a share-less non-display + PC that genuinely needs the fleet-wide common entries over HTTPS turns it on. -.PARAMETER NoCommon - Do not fetch or merge the common scope; enforce -Scope alone (the original - single-scope behavior). +.PARAMETER CommonScope + Name of the fleet-wide scope merged when -IncludeCommon is set (default + 'common'). Ignored without -IncludeCommon. .NOTES Fail-safe: any error exits 0 so a bad web app never blocks or breaks a PC. @@ -44,8 +45,8 @@ param( [Parameter(Mandatory)] [string]$EnginePath, [string]$ShareManifestPath, [switch]$ShadowMode, + [switch]$IncludeCommon, [string]$CommonScope = 'common', - [switch]$NoCommon, [string]$BaseUrl, [string]$ApiToken, [string]$LogFile = "C:\Logs\Shopfloor\shopdb-enforce-$(Get-Date -Format yyyyMMdd).log" @@ -126,12 +127,13 @@ try { # no common merge - the share already carries its own common scope). $manifestToRun = $ShareManifestPath } else { - # Common-scope inheritance: a display enforces its own scope PLUS the - # fleet-wide common scope. Fetch common too (best-effort, same fail-safe - # cache) and merge it in with the pctype winning on conflict. Skipped - # when -NoCommon, or when this run IS the common scope. + # Optional common-scope inheritance (OFF by default; displays are + # self-sufficient). Only when -IncludeCommon is set do we fetch the + # fleet-wide common scope (best-effort, same fail-safe cache) and merge + # it in with the pctype winning on conflict. Skipped when this run IS + # the common scope. $manifestToMerge = $sync.Path - if (-not $NoCommon -and $CommonScope -and ($CommonScope -ine $Scope)) { + if ($IncludeCommon -and $CommonScope -and ($CommonScope -ine $Scope)) { $commonSync = Sync-ShopdbManifest -Scope $CommonScope -Config $config if ($commonSync.Path) { $manifestToMerge = Merge-ShopdbManifests -PrimaryManifestPath $sync.Path -CommonManifestPath $commonSync.Path diff --git a/plugins/geenforce/seed_display_scope.py b/plugins/geenforce/seed_display_scope.py index b2cb5f7..a257aaf 100644 --- a/plugins/geenforce/seed_display_scope.py +++ b/plugins/geenforce/seed_display_scope.py @@ -15,12 +15,11 @@ What this scope contains: launches the kiosk target for the subtype. The display-type -> target map is a data-driven table (DISPLAY_TYPE_TARGETS) so the targets are easy to edit. -Inheritance: the manifest model has no inheritance column. The display scope is -a plain (non-common) runtime scope; the CLIENT merges the fleet-wide 'common' -scope with the display scope at fetch time. So this scope carries display-only -entries and relies on the client to layer 'common' underneath. See the common -SMB-payload audit in docs/GE-ENFORCE-DISPLAY.md before letting a share-less -display inherit common. +Self-sufficient: the display scope carries EVERYTHING a display enforces and +does NOT inherit the fleet-wide 'common' scope. Displays run the enforcer with +common-merge off (the client default), so common's SMB-backed fleet entries +never reach a share-less display. This keeps the display path simple and needs +no common-payload repackaging. Authoring path mirrors how every other scope is created: build a manifest dict and hand it to service.replace_scope_draft (the same call the import-share CLI @@ -35,8 +34,6 @@ from . import service SCOPE_NAME = 'gea-shopfloor-display' SCOPE_PHASE = 'runtime' -# Match the fleet-wide 'common' manifest version so a merged display + common -# document stays internally consistent. SCOPE_VERSION = '2.0' # Edge kiosk relaunch policy key. Values below mirror 09-Setup-Display.ps1 @@ -222,8 +219,8 @@ def build_display_manifest(): 'gea-shopfloor-display runtime scope. Heals Edge kiosk relaunch ' 'policy drift and dispatches the kiosk to the subtype target. No ' 'EXE payloads: kiosk engine and browser are baked into the display ' - 'image. The client merges the fleet-wide common scope underneath ' - 'this one at fetch time.'), + 'image. Self-sufficient: displays do NOT inherit the common ' + 'scope.'), 'Applications': applications, } diff --git a/tests/test_plugins/test_geenforce_display_seed.py b/tests/test_plugins/test_geenforce_display_seed.py index 698ba47..9d58fb7 100644 --- a/tests/test_plugins/test_geenforce_display_seed.py +++ b/tests/test_plugins/test_geenforce_display_seed.py @@ -21,7 +21,8 @@ def test_seed_creates_display_scope(db): scope = ManifestScope.query.filter_by( scopename=SCOPE_NAME, phase='runtime').first() assert scope is not None - # Not the common scope: the client merges common underneath at fetch time. + # A plain runtime scope, not the common scope. Displays are self-sufficient + # and do NOT inherit common. assert scope.iscommon is False # Four Registry drift-heal entries + one PS1 dispatcher, in order.