# Deploying DictateSI with managed preferences

DictateSI reads its settings from the standard macOS preferences domain
`com.dictatesi.mac`. Any key an MDM delivers in that domain becomes **managed**:
macOS makes it win over whatever the user set, and the app locks the matching
control and shows "Managed by your organization." There is no agent, no SDK,
and no account; it is the same mechanism Jamf, Kandji, and Intune use for
every other Mac app.

Policy can only ever narrow toward on-device. If a profile disallows the
backend a user had chosen, the app falls back to on-device cleanup; it never
falls back to a cloud backend.

## Key reference

| Key | Type | Meaning |
|---|---|---|
| `allowedCleanupBackends` | array of strings | Backends users may pick: `local`, `ollama`, `byok` (Anthropic; aliases `anthropic`, `cloud`), `endpoint` (OpenAI-compatible). Absent = all. |
| `aiCleanupBackend` | string | Pin the backend to one of the identifiers above. |
| `aiCleanupEnabled` | boolean | Force cleanup on or off. |
| `allowCustomEndpoint` | boolean | `false` locks the endpoint URL, model and provider so only pushed values are used. |
| `aiCleanupEndpointURL` | string | Pre-set the OpenAI-compatible endpoint (an API root such as `https://api.openai.com/v1`, or a full Azure deployment URL). |
| `aiCleanupEndpointModel` | string | Pre-set the model (or Azure deployment name). |
| `aiCleanupEndpointProvider` | string | Preset shown in the UI: `openai`, `azure`, `gemini`, `groq`, `mistral`, `openrouter`, `custom`. |
| `auditLogEnabled` | boolean | Force the tamper-evident audit log on. |
| `auditRetentionDays` | integer | `0` keeps audit events forever (the compliance default). |
| `historySaveEnabled` | boolean | `false` stops the local dictation history. |
| `historyRetentionDays` | integer | Auto-purge window for history; `0` keeps forever. |

API keys are never set by profile: a profile is readable on the device, so a
shared key would not stay secret. Users enter keys themselves (stored in the
macOS Keychain), or you point the endpoint at a gateway that needs no key.

## Sample profiles

- `DictateSI-OnDeviceOnly.mobileconfig`: on-device cleanup only, audit log forced on and kept forever, local history off. The profile most regulated reviews ask for.
- `DictateSI-ApprovedEndpoint.mobileconfig`: on-device plus one IT-approved OpenAI-compatible endpoint (an Azure OpenAI deployment in the example), endpoint fields locked, audit log forced on.
- `com.dictatesi.mac.example.plist`: the same keys as a plain plist, for tools that take a preference file rather than a profile.

Replace the placeholder URL and deployment name before use.

## Jamf Pro

Computers → Configuration Profiles → New → Application & Custom Settings →
Upload. Upload `com.dictatesi.mac.example.plist` (edited), preference domain
`com.dictatesi.mac`, scope to the target computers. Or upload one of the
`.mobileconfig` files as a custom profile.

## Kandji

Library → Add → Custom Profile → upload a `.mobileconfig` → assign to a
Blueprint.

## Microsoft Intune

Devices → macOS → Configuration profiles → Create → Templates → Custom →
upload a `.mobileconfig`. (The "Preference file" settings-catalog option also
works with `com.dictatesi.mac.example.plist` and domain `com.dictatesi.mac`.)

## Verifying on a Mac

```bash
sudo profiles show -type configuration | grep -A2 dictatesi
defaults read com.dictatesi.mac        # user layer
```

Open DictateSI → Settings → AI Cleanup: locked controls show
"Managed by your organization." The audit log's export records which
backend ran for each dictation and whether text left the machine.
