# Managed deployment templates
These files are administrator templates, not consumer-app features. Validate and
sign edited profiles in the MDM product before deployment. Every example bundle ID,
UUID, server, profile ID and credential must be replaced.
## Qeli per-app VPN
`Qeli-PerApp-VPN.mobileconfig` uses Apple's `com.apple.vpn.managed.applayer`
payload and the Qeli packet tunnel provider. Keep these identifiers aligned with
`Config/Signing.xcconfig`:
- `VPNSubType`: the containing Qeli app bundle ID (`QELI_APP_BUNDLE_ID`).
- `VPN.ProviderBundleIdentifier`: `QELI_TUNNEL_BUNDLE_ID`.
- `VPNUUID`: the stable UUID the MDM service also assigns to each managed app.
- `VendorConfig.profileID`: a profile UUID already present in Qeli's encrypted
App Group store. The example does not provision credentials or profile text.
On iOS, the target app must itself be managed. Associate it with the VPN by setting
the same `VPNUUID` in the managed app's attributes. For an MDM `InstallApplication`
command, the relevant fragment is:
```xml
Attributes
VPNUUID
1653840A-E082-4CC2-877A-A8D188C183B6
```
Declarative Device Management uses the equivalent `Attributes.VPNUUID` property on
the managed app declaration. A normal App Store install can't assign arbitrary apps
to this VPN and Qeli doesn't attempt to enumerate installed apps.
Apple references:
- https://developer.apple.com/documentation/devicemanagement/applayervpn
- https://developer.apple.com/documentation/devicemanagement/installapplicationcommand/command-data.dictionary/attributes-data.dictionary
## Always On VPN
`Apple-IKEv2-AlwaysOn.mobileconfig` documents Apple's actual Always On facility. It
requires a supervised, MDM-managed device and an IKEv2 server. It does **not** make
the Qeli custom Packet Tunnel Provider Always On and is not compatible with the Qeli
wire protocol. For consumer devices use Qeli's VPN On Demand setting instead.
The shared-secret placeholder keeps the example self-contained, but certificate
authentication with a separately deployed identity payload is preferable in a real
organization. Never deploy the placeholder secret or `.invalid` host names.
Apple references:
- https://support.apple.com/guide/deployment/depae3d361d0/web
- https://developer.apple.com/documentation/devicemanagement/vpn
- https://developer.apple.com/documentation/devicemanagement/vpn/alwayson-data.dictionary
## Managed app configuration
`Qeli-Managed-App-Configuration.plist` is the dictionary to send as legacy managed
app configuration, not a configuration profile. Qeli's standalone
`ManagedConfigurationReader` reads it from
`UserDefaults.standard["com.apple.configuration.managed"]` and accepts:
- `configurationVersion` — integer schema version.
- `activeProfileID` — UUID string referring to an existing encrypted profile.
- `onDemandEnabled` — Boolean policy value.
- `widgetControlsEnabled` — Boolean policy value.
The reader itself is side-effect free. `AppModel` gives these managed values
precedence at launch and whenever the app becomes active: it selects the managed
profile for the next connection, enforces the managed On Demand value, and mirrors
the widget-control policy into the App Group so the widget extension can reject
disabled actions. An `activeProfileID` key that is malformed or does not match an
encrypted local profile fails closed: Qeli blocks manual/widget starts, stops the
old tunnel, removes On Demand rules and disables the stale provider configuration.
It never accepts profile text, passwords, or private keys. On newer managed
deployments, Apple's ManagedApp framework can replace this legacy `UserDefaults`
delivery path.
### Bootstrap order (this one bites)
The policy references a profile by UUID; it never carries the profile itself. So the
profile has to exist on the device **before** the configuration arrives, and the order is
not interchangeable:
1. Install Qeli as a **managed** app. Legacy managed app configuration is only delivered
to apps the MDM installed — sideloaded or App Store copies never see the key.
2. Get the profile onto the device and note its UUID. Qeli accepts no profile text,
password or key over MDM by design, so this step is a `qeli://` link, a QR scan or an
INI import — done by the user or seeded alongside a Per-App VPN payload.
3. Only then push the configuration dictionary with `activeProfileID` set to that UUID.
Pushing step 3 first is the common mistake, and it does not fail quietly: an
`activeProfileID` that matches nothing is treated as a policy violation, not as "no
policy". Qeli fails closed — manual and widget starts are blocked, a running tunnel is
stopped, On Demand rules are removed and the stale provider configuration is disabled.
That is deliberate (a managed device must not silently fall back to a user-chosen
profile), but it means a mis-ordered rollout looks like a broken app.
### Rollback
Remove the managed configuration — or just the `activeProfileID` key — and control returns
to the user: the app goes back to the locally selected profile, and an On Demand value
that came from policy reverts to the app's own setting. Removing the policy does **not**
delete profiles, credentials or the provider configuration; it only stops enforcing them.
To hand a device back permanently, remove the policy first and confirm the app connects on
a local profile, then unmanage or remove the app. Doing it the other way round leaves the
last enforced state in place with nothing left to lift it.
Apple reference:
- https://developer.apple.com/documentation/devicemanagement/configuring-managed-apps-and-extensions