MDM Configuration

The app reads a managed configuration from the OS at launch, letting an IT admin enforce settings via any MDM (Mobile Device Management) provider — Intune, Workspace ONE, Jamf, SOTI, MobileIron, and so on. It uses the vendor-agnostic OS mechanisms — Android Enterprise Managed Configurations and Apple Managed App Configuration — so no per-vendor SDK is involved.

The config is read once at startup. Changes pushed to a running app take effect on the next launch, which is the standard behaviour for managed apps.

How it works

File Responsibility

Assets/Scripts/SystemNative/MDM.cs

Static native bridge — fetches the raw managed config per-platform (mirrors SecureStorage) and deserializes it into a caller-supplied type via GetConfig<T>(). Does not depend on the config shape.

Assets/Scripts/SystemNative/ManagedConfig.cs

Typed data contract defining the individual keys — the C# mirror of app_restrictions.xml. All fields nullable. Read via MDM.GetConfig<ManagedConfig>(). Lives beside the bridge rather than with any one consumer, because the keys are read from several layers.

Assets/Scripts/Settings/SettingsStore.cs

ApplyManagedConfig() pre-seeds the backing prefs so enforced values flow through the normal apply pipeline.

Assets/Scripts/App/State/AppState.cs

TryGetManagedProjectKey() resolves projectUrl into a ProjectKey; EnsureProjects() seeds and selects it at startup; IsProtectedProject() answers whether a project may be removed.

Keys

All keys are optional. A key that is not set leaves the corresponding setting under the user’s own control. The settings keys map to Settings.ManagedConfig; projectUrl is read by the app layer.

Key Type Effect

projectUrl

string

Project link the app enforces — see [Enforced project].

anonymousMode

bool

Forces anonymous mode on or off.

conversationMode

string

Forces a conversation mode by enum name.

bundleBranch

string

Forces the asset bundle branch by enum name.

uploadDebugLogs

bool

Forces uploading of debug logs on or off.

[[Enforced project]] == Enforced project

Setting projectUrl makes that project the device’s project. Concretely:

  • It is added to the saved project list at startup, before any network call — so it is present and selectable even on a cold, offline first launch. Its name, tenant and logo are placeholders until the first successful fetch fills them in.

  • It becomes the project opened on launch, overriding whichever project the user last had open. An incoming app link or deep link still wins, as before.

  • It cannot be removedAppOrchestrator.RemoveProject() rejects it and ProjectsUI hides the remove button on its card. The built-in default project is protected the same way.

The URL must be a project link using the project’s refhttps://eu.promethist.ai/p/{ref}. A bare engine host, an agent (/a/…) link or a relay (/q/…) link is not accepted here: those resolve to a project only after a network round-trip, which is too late to seed and protect the entry at startup. An unusable value is ignored with a warning in the device log rather than failing the launch.

Enforcement is re-derived from the managed configuration on every launch and never persisted. Clearing or repointing projectUrl therefore releases the previously enforced project on the next launch — it stays in the user’s list as an ordinary, removable project rather than being stranded as undeletable.

Android

Keys are declared in Assets/Plugins/Android/systemnative.androidlib/src/main/res/xml/app_restrictions.xml and registered via the android.content.APP_RESTRICTIONS meta-data in the androidlib manifest. They show up automatically in the MDM console’s app-configuration UI.

They are read at runtime through RestrictionsManager.getApplicationRestrictions() in ai.promethist.systemnative.ManagedConfiguration.readJson().

Test with Google’s Test DPC app to push managed configs to a work profile, or use a real MDM trial tenant.

iOS

Apple has no in-app schema mechanism — the keys above are the contract. The MDM writes them into NSUserDefaults under com.apple.configuration.managed; the app reads them in Assets/Plugins/iOS/ManagedConfiguration.mm.

Deliver a Managed App Configuration payload via a real MDM trial tenant, or push a .mobileconfig with Apple Configurator. Example payload (ManagedApplicationConfiguration):

<dict>
    <key>projectUrl</key><string>https://eu.promethist.ai/p/default</string>
    <key>anonymousMode</key><true/>
    <key>bundleBranch</key><string>Default</string>
    <key>uploadDebugLogs</key><true/>
</dict>

Editor

Drop a ManagedConfig.dev.json file in the project root to simulate a managed config without device enrollment:

{ "anonymousMode": true, "bundleBranch": "Default", "uploadDebugLogs": true, "projectUrl": "https://eu.promethist.ai/p/default" }