Get-ShopdbConfig required both BaseUrl AND ApiToken, so a token-less kiosk
(authorized by the server's IP allowlist) got a null config and never ran.
Now BaseUrl alone is a valid config; X-API-Key is sent only when a token is
present (New-ShopdbAuthHeaders), so token-authorized sites are unchanged and
vaulted-network sites need no per-PC token.
Per decision: displays need none of the fleet-wide common scope's software, so
the gea-shopfloor-display scope carries everything it enforces and does not
inherit common. This avoids repackaging common's SMB-backed payloads for a
share-less display.
- Invert the client common-merge switch: -NoCommon (default-on) becomes
-IncludeCommon (default OFF). A scope now enforces alone unless opted in.
The capability stays for a future share-less non-display PC; displays omit it.
- Drop the common SMB-payload audit + inheritance sections from the display
seed comments and docs (GE-ENFORCE-DISPLAY.md); document self-sufficiency.
- GE-ENFORCE-CLIENT.md: common-scope inheritance is now opt-in.
Get GE-Enforce closer to running on credential-less Intune/Entra display PCs
that pull manifest + payloads over HTTPS instead of SMB.
Server (plugins/geenforce/api/routes.py):
- Rate-limit + 512MB served-size ceiling on GET /payload/<sha256> (reuses the
login limiter's cache pattern, config-overridable via GEENFORCE_PAYLOAD_*).
- New tests: payload hardening, manifestblobs model-vs-migration parity, and a
report-contract test locking the lowercase per-entry report keys.
PS client (plugins/geenforce/client/):
- Fix New-ShopdbReport per-entry key casing to lowercase (name/action/selfhealed/
exitcode/message) to match what the server reads; the engine emits PascalCase.
- Enforce TLS 1.2 in the network functions.
- Fetch + merge the fleet-wide common scope alongside the pctype scope
(pctype wins on conflict; -NoCommon opt-out).
- Normalize whatever the engine returns into a well-formed summary.
- Make the empty-cache fail-safe observable: event-log entry + report ping
instead of a silent exit 0.
Manifest (plugins/geenforce/seed_display_scope.py + docs/GE-ENFORCE-DISPLAY.md):
- Seed a gea-shopfloor-display scope: 4 Edge kiosk drift-heal registry entries
+ 1 data-driven dispatcher (Dashboard/Lobby/3DPrintRoom via display-type.txt).
Kiosk EXEs stay image-baked; the manifest heals policy/config drift only.
- Documents the common SMB-payload audit (entries needing http/inline before a
share-less display can inherit common).
Migration registry (shopdb/plugins/alembic_template.py + test):
- Register the pre-existing manifestblobs and the new printersupplyalerts tables
in PLUGIN_TABLE_OWNERS; update EXPECTED_HEAD_REVISION for geenforce (0002blobs),
printers (0002supplyalerts), and printedparts (0004txnrev) which had drifted.
Recognition edit hid the time fields (grouped with Recertification), so start/end
could not be adjusted even though the backend honors them. Show the time fields
for every type except Recertification (due-date driven); Recognition end still
auto-fills to the next 8 AM reset when blank.
Also GE-Enforce B2 client (HTTPS payload consume): ShopdbEnforceClient.psm1 gains
Get-ShopdbPayload (fetch by sha256, verify, cache) + Resolve-ShopdbPayloads
(rewrite http/inline entries to local staged files so the engine installs from
local, no SMB); Invoke-ShopdbEnforce resolves payloads before running the engine;
importer parses PayloadSource/PayloadSha256/PayloadRef. VM-verified: a SYSTEM
Windows client fetched a payload over HTTP by hash, hash matched.
Found by running the kit under pwsh 7 against the live API: Invoke-WebRequest
returns header values as string ARRAYS in PS7 (scalars in Windows PowerShell
5.1), so X-Manifest-Version came back as @('1') and [int] on it threw - report
build failed. The target scheduled task runs 5.1 (works), but the kit must be
robust under PS7 too (target preinstalls PowerShell 7). Coerce ETag and
X-Manifest-Version with @(...)[0], a clean scalar in both.
Validated end to end on Linux pwsh 7.6.3 against the dev API: fetch (200) ->
cache-304 -> report sent -> landed received=true/status=ok. All 4 client scripts
parse clean; PSScriptAnalyzer shows only cosmetic warnings (Write-Host in a CLI,
intentional log-guard catch).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Closes the "how do sites actually deploy GE-Enforce" gap (esp. OOBE-ppkg sites
without a PXE/WinPE step). Site-neutral + imaging-path independent.
- plugins/geenforce/client/Install-GEEnforce.ps1: a bootstrap that writes the
PC's identity (C:\Enrollment\pc-type.txt is what determines the PC type; plus
machine-number/cmm version/cmm id/site-config as needed), sets the shopdb
BaseUrl + token in HKLM:\SOFTWARE\GE\ShopDB, deploys the client kit, optionally
copies the engine from -EngineSource, and registers the SYSTEM scheduled task
(at logon + every N min). Idempotent; fails loud (installer, not the fail-safe
runtime). Engine is REFERENCED not vendored - it belongs to the GE-Enforce
framework; the script warns if absent but still labels the PC.
- docs/GE-ENFORCE-DEPLOY.md: the deploy contract - the three things a PC needs
(client, identity, credential), the identity table (what determines PC type,
no auto-detection - the provisioner supplies it; shopdb cannot set it at
imaging), and how to invoke per path (PXE step, OOBE ppkg via
ProvisioningCommands, Intune, manual), the engine boundary, and verification.
- Cross-linked from docs/GE-ENFORCE.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>