Managed mode
Zero-touch deployment for MDM-managed fleets. When the configuration profile sets shft.deploymentMode = managed, shft hides its setup wizard entirely and runs the migration from profile values.
Scope
- Skip the entire wizard (Welcome → Role → Discovery → Pairing → User Mapping → Manifest → Preflight → Progress → Complete).
- Validate required profile keys at launch; surface missing ones as a clear "Contact IT" UI rather than crashing.
- Local self-attestation when
requireManagedSource = true— a destination demanding the source be MDM-enrolled has to be enrolled itself. - Drive
ConnectionManagerprogrammatically through discovery → peer selection → pairing. - Derive a
UserMappingfromuserPrincipal(no user prompt). - Apply
migrationCategoriesto the data picker, then run the engine. - Post a JSON telemetry envelope to
telemetryEndpointon every lifecycle transition.
Profile keys
See Configuration profile reference for the canonical key table. The keys this feature reads:
| Key | Required when managed | Notes |
|---|---|---|
shft.deploymentMode | — | managed activates this whole flow |
shft.deploymentRole | — | destination (default) or source |
shft.userPrincipal | yes | MDM variable, typically $EMAIL |
shft.migrationSource | — | auto / bonjour / thunderbolt |
shft.migrationCategories | — | Subset of category IDs |
shft.requireManagedSource | — | Boolean; gates on local self-attestation |
shft.telemetryEndpoint | — | URL for managed-mode events |
shft.prefRestoreMode | — | See Staged pref restore |
shft.brandingName | — | Surfaced as "Managed by …" |
Missing shft.userPrincipal (the only required-when-managed key) puts the coordinator into a missingKeys state. The UI shows Contact IT and the lifecycle event managed.missing_keys POSTs to the telemetry endpoint.
State machine
ManagedModeCoordinator.State:
validating
↓
├─ missingKeys([String]) → UI: Contact IT, telemetry: managed.missing_keys
├─ attestationFailed(reason) → UI: Contact IT, telemetry: managed.attestation_failed
│
↓ (validation + attestation pass)
awaitingPeer → telemetry: managed.awaiting_peer
↓
pairing(peerName)
│ ├─ failed(reason) → telemetry: managed.pairing_failed
↓
ready(peerName) → telemetry: managed.ready
↓
transferring → telemetry: managed.transfer_start
↓
├─ complete → telemetry: managed.complete
└─ failed(reason) → telemetry: managed.failed
ManagedStatusView reflects each state with a self-explanatory label and never shows a control beyond "Quit shft" (on completion) or "Contact IT" (hard errors). By design the user never clicks anything.
Source attestation
SourceAttestation.attestLocalDevice() checks two MDM signals:
-
/Library/Managed Preferences/populated — the strongest signal. MDM provisions a per-user subdirectory the moment a profile is installed; an empty or missing directory is a "not managed" tell. -
/usr/bin/profiles status -type enrollment— catches devices where the user-scoped preferences haven't been delivered yet but the device record is good. Matches a line likeMDM enrollment: Yes ….
When requireManagedSource = true, the coordinator runs this check on the destination before doing anything else. A device that can't prove its own enrollment can't credibly require it of a peer.
Telemetry envelope
TelemetryClient POSTs a JSON body for every event:
{
"timestamp": "2026-05-20T00:57:00Z",
"event": "managed.complete",
"device": "mac-1234",
"principal": "alice@example.com",
"appVersion": "1.0.0",
"details": {
"files": "12847",
"bytes": "53483392"
}
}Failures log a warning and are dropped — point the endpoint at a webhook or logging collector that owns durability. No retry queue, no local buffering: the migration never blocks on the telemetry path.
Example: minimal managed profile
<key>shft.deploymentMode</key>
<string>managed</string>
<key>shft.userPrincipal</key>
<string>$EMAIL</string>
<key>shft.migrationCategories</key>
<array>
<string>userFiles</string>
<string>applicationData</string>
<string>browserData</string>
</array>
<key>shft.requireManagedSource</key>
<true/>
<key>shft.telemetryEndpoint</key>
<string>https://webhook.acme.example/shft</string>
<key>shft.brandingName</key>
<string>Acme</string>$EMAIL is the standard MDM variable for the enrolled user's email/UPN — Jamf, Kandji, Mosyle, and Intune all resolve it per-device.