# FlowerCore DeviceManagement on GX10 This adopted GX10 app hosts `FlowerCore.DeviceManagement.Web` at `https://devices.iamworkin.lan`. Agent-only REST/SignalR callbacks can use `https://devices-agent.iamworkin.lan`, which is a separate Traefik router that requires a TLS client certificate and forwards the presented certificate to the app. Traefik v3.6 currently forwards raw base64 DER in `X-Forwarded-Tls-Client-Cert`; the app also accepts URL-escaped PEM for compatibility with older/alternate Traefik shapes. ## Apple MDM Runtime Contract Apple MDM is enabled in NanoHUB mode, but enrollment remains unavailable until the runtime secret contains real Apple-side material. Do not use placeholder values to clear readiness checks. `Secret/fc-devicemgmt-runtime` supports these Apple MDM keys: | Key | Purpose | | --- | --- | | `DEVICE_MANAGEMENT_OPERATOR_API_KEY` | Required operator API key for authenticated REST/MCP write operations, including Android command queueing. | | `DEVICE_MANAGEMENT_ADMIN_API_KEY` | Required admin API key for privileged DeviceManagement operations. | | `DEVICE_MANAGEMENT_AGENT_API_KEY` | Required scoped agent credential for REST agent callbacks when TLS terminates before Kestrel; maps to `Auth:AgentApiKey` and `FlowerCore:Auth:AgentApiKey`. | | `DEVICE_MANAGEMENT_ENROLLMENT_CA_CERTIFICATE_PEM` | Optional persistent enrollment CA certificate PEM; maps to `FlowerCore:DeviceManagement:EnrollmentCertificateAuthorityCertificatePem`. Live on GX10 for the agent client-cert chain currently trusted by Traefik. | | `DEVICE_MANAGEMENT_ENROLLMENT_CA_PRIVATE_KEY_PEM` | Optional private key PEM matching the persistent enrollment CA certificate; maps to `FlowerCore:DeviceManagement:EnrollmentCertificateAuthorityPrivateKeyPem`. | | `NANOHUB_API_KEY` | NanoHUB API password for HTTP Basic user `nanohub`. | | `APPLE_MDM_APNS_TOPIC` | MDM APNs topic returned after uploading the Apple MDM push certificate to NanoHUB/NanoMDM. | | `APPLE_MDM_SCEP_URL` | Live SCEP URL included in the enrollment profile. | | `APPLE_MDM_SCEP_CHALLENGE` | SCEP challenge shared with the SCEP provisioner. | | `APPLE_MDM_PROFILE_SIGNING_CERTIFICATE_PEM` | PEM certificate used to CMS-sign `.mobileconfig` profiles. | | `APPLE_MDM_PROFILE_SIGNING_PRIVATE_KEY_PEM` | PEM private key matching the profile-signing certificate. | | `APPLE_MDM_REQUIRE_MANAGED_WIFI_PAYLOAD` | Set to `true` only when Wi-Fi payload delivery should gate enrollment readiness. | | `APPLE_MDM_MANAGED_WIFI_SSID` | Managed Wi-Fi SSID for the iPad profile. | | `APPLE_MDM_MANAGED_WIFI_PASSWORD` | Managed Wi-Fi password when the network is not open. | Non-secret profile constants stay in GitOps: NanoHUB base URL, MDM server URL, check-in URL, organization/display names, the HTTPS trust anchor certificate, managed Wi-Fi encryption type, auto-join, and MAC-randomization disablement. DeviceManagement auth is enabled on GX10. The deployment maps `DEVICE_MANAGEMENT_OPERATOR_API_KEY` to both `Auth__ApiKey` and `FlowerCore__Auth__ApiKey`; the unprefixed key keeps the MCP API key post-config path aligned with REST auth. Agent heartbeat, inventory, command poll, app-catalog, and command-result callbacks use the agent-specific authorization boundary: the server validates a direct device client certificate when Kestrel receives one, validates Traefik-forwarded client certificates only on `devices-agent.iamworkin.lan`, and also accepts only the scoped `DEVICE_MANAGEMENT_AGENT_API_KEY` via `Authorization: Bearer` or `X-Agent-Api-Key` as the fallback path. Operator write endpoints must use `X-Api-Key`. The agent-only Traefik route uses `RequireAndVerifyClientCert` with `Secret/devicemgmt-agent-client-ca`, derived from the persistent DeviceManagement enrollment CA. The application still matches the forwarded client certificate thumbprint to the enrolled device record, but unauthenticated clients are now rejected during TLS before reaching the agent REST route. ## Readiness Check After changing the runtime secret and letting the pod roll, verify: ```bash curl -sk https://devices.iamworkin.lan/api/v1/apple-mdm/enrollment-profile/status ``` Configurator enrollment must wait until this status reports `available=true` and an empty `missingRequirements` array.