#Managed Device Policies (MDM)
cmux supports MDM-enforceable policies for managed Macs. Administrators deliver them as forced preference values through a macOS configuration profile (a Custom Settings payload). Forced values are tier 0: they win over environment variables, user settings, ~/.config/cmux/cmux.json imports, and built-in defaults.
Forced values cannot be changed from inside the app: the Settings UI shows the control as “Managed by your organization”, the command palette hides the matching commands, and the cmux CLI refuses with a managed-policy error.
#Payload domain
Target the preference domain of the release app:
com.cmuxterm.appChannel builds (debug, nightly, staging) run under their own bundle identifiers but also honor profiles targeting com.cmuxterm.app, so one profile governs every channel. A value forced in the app's own domain wins over the release-domain fallback.
#Policy keys
| Key | Type | Default | Behavior when forced to true |
|---|---|---|---|
DisableEmbeddedBrowser | Boolean | false | Disables every embedded-browser surface: browser panes and tabs, terminal-link interception, browser creation from automation, layouts, and session restore. Live browser panes are closed when the policy activates; links open in the system default browser instead. WebKit-based local viewers riding the same gate (the diff viewer, agent-chat pane, in-app upgrade pages) are also unavailable, and the Mac stops advertising browser capabilities to the iOS app. |
DisableRemoteControl | Boolean | false | Disables the Mac acting as a remote view/control host for the cmux iOS app: the Iroh host runtime (including its local-network advertisement), the legacy TCP pairing listener, connection admission, and device pairing. Live phone connections are closed when the policy activates, the pairing trust broker refuses to mint new pair grants, local attach-ticket minting refuses, and relayed phone replies are no longer typed into terminals. Outbound-only notification forwarding, app updates, the local automation socket, and Mac-as-client SSH remain unaffected. |
DisableCloud | Boolean | false | Disables cmux Cloud Machines and cmux-managed private-network access: the Cloud sidebar tab, Settings, palette and new-workspace entries, session restore of Cloud workspaces, every Cloud VM service call (create, open, attach, exec, SSH, desktop, ports, publications), the vm socket/CLI commands, the Cloud control-plane verbs that share that backend (remotes, aiAccounts including credential upload, coderouter, workspace.cloud_vm_*), the cmux vm-pty-connect direct dial, the Cloud tab-bar button, and the cmux.cloudvm cmux.json action fail closed with a managed-policy error. The app never enrolls, starts, or reconnects its private-network tunnel; cmux vpn up fails closed while cmux vpn status, down, and revoke stay available for cleanup. When the policy activates mid-session, live Cloud workspaces and links are torn down and the managed VPN configuration is removed. Local terminals, local automation, and ordinary user-configured SSH are unaffected. |
DisableRemoteConnections | Boolean | false | Disables cmux-created remote connections: SSH, Mosh, and remote tmux sessions plus the remote registry, from every entry point (command palette, menus, the cmux CLI, socket automation, and session restore). ssh:// links are refused up front, and a profile pushed mid-session disconnects every live cmux-created remote workspace and remote tmux mirror. Cloud Machines attach over the same mechanism, so this key blocks them too. Terminals on this Mac are unaffected, and a user's own ssh typed into a shell is deliberately out of scope: this control governs the connections cmux creates, not the shell. |
DisableFileTransfer | Boolean | false | Disables cmux-mediated file transfer: drag-and-drop and pasted-image uploads into a remote terminal (including a configured custom upload command), remote-file previews in the SSH file explorer, phone↔Mac transfers over the iOS app (attachment upload, artifact and changed-file fetch, image paste), and cmux vm push / cmux vm pull. A refused drop or paste shows the policy message. Local drops into local terminals still work, and a user's own scp or rsync typed into a shell is deliberately out of scope. |
DisableIrohNetworking | Boolean | false | Disables cmux-managed Iroh networking: the host runtime never activates, so no endpoint is created, no relay traffic flows, and no routes are published. Local terminal work, the local automation socket, and ordinary SSH are unaffected. DisableRemoteControl disables the Mac's role as an iOS remote-control host; this key disables the transport itself. |
DisableTelemetry | Boolean | false | Disables analytics and crash reporting (PostHog, Sentry) and the remote feature-flag fetch; all three carry the install's anonymous id off the Mac, and flags then use their built-in defaults. Read once at launch like the user opt-in it overrides. Settings → App shows the telemetry toggle locked. |
DisableAutoUpdate | Boolean | false | Disables Sparkle: no scheduled or launch-time update checks and no downloads, and Check for Updates explains the managed state. Read at launch. Deploy app versions through your MDM instead. |
DisableAutomationWebhooks | Boolean | false | Disables the webhook action of automation rules, which posts event payloads with caller-supplied headers to any http(s) URL. The action fails with a managed-policy message in the automation log; run and notify actions are unaffected. |
DisableTLSTrustBypass | Boolean | false | Disables the embedded browser's click-through on certificate errors: the error page offers no bypass and no earlier 24-hour grant is honored. |
DisableComputerUse | Boolean | false | Disables Computer Use: new agent launches never receive the computer-use tools, the bundled helper stops (also when the policy is pushed mid-session), and Settings → Computer Use locks the toggle. Lifting the policy re-applies the user's own setting. |
DisableCustomSidebars | Boolean | false | Disables interpreted custom sidebars from ~/.config/cmux/sidebars (user- or agent-authored code that can dispatch cmux commands): none are listed or mounted, and Settings → Beta Features locks the toggle. |
DisableAICredentialUpload | Boolean | false | Disables uploading local AI credentials (Claude/Codex OAuth tokens, Anthropic/OpenAI API keys) to the cmux tenant: cmux ai-accounts upload and cmux coderouter claude add/update fail closed at the socket and inside their clients. Listing and removing accounts still work. Independent of DisableCloud, which refuses these families entirely. |
- Values must be Boolean. A key forced to false (or to a non-Boolean value) does not enforce the policy, but the key still counts as managed for write-locking purposes.
- DisableTelemetry and DisableAutoUpdate are the two launch-time reads: Settings shows their managed state within a minute of a push, and the telemetry and updater processes honor them at the next launch. Every other key applies live.
- Only forced (profile-delivered) values are honored as policy. A plain defaults write of these keys has no effect.
- Policies apply at app launch, on preference changes, on app activation, and on a periodic re-check (about once a minute) while cmux runs — a profile pushed mid-session takes effect within roughly a minute even if the user never leaves the app.
- An MDM policy cannot remove a signed entitlement from an installed binary. Builds that carry the Network Extension entitlement keep it; DisableCloud instead makes the Cloud and VPN capability unreachable at runtime. Builds without the entitlement continue to use the existing unavailable-backend path.
- The capability keys compose rather than override one another: each is a separate gate, and a capability is unavailable when any key covering it is forced. Every Disable key defaults to false and the two Browser Allow keys default to true, so an unmanaged Mac — and a managed Mac whose profile omits the key — behaves exactly as a standard install.
#Browser allowlist
BrowserURLAllowlist restricts the embedded browser to the sites you list. By default a managed list still permits localhost and local files, because those never leave the Mac; two allow-style keys turn each of those defaults off. Most deployments only need the list of company domains.
| Key | Type | Default | Behavior |
|---|---|---|---|
BrowserURLAllowlist | Array of strings | unset (allow all web origins) | Restricts every embedded-browser top-level navigation (address bar, links, redirects, window.open, automation, deep links, restored panes) to matching rules. A forced empty array denies every external web origin; cmux-owned internal pages, localhost, and local files remain available unless the allow keys below turn them off. |
BrowserAllowLocalhost | Boolean | true | When true (the default), a managed list permits localhost, *.localhost, 127.0.0.1, ::1, and 0.0.0.0 on any port without a rule, so local development servers keep working. Force false to block loopback origins, even ones the list names. |
BrowserAllowLocalFiles | Boolean | true | When true (the default), local file: documents opened through cmux, dropped onto a browser pane, or linked from another local file stay available. Force false to block local files, with or without a BrowserURLAllowlist. |
Each entry is a host or an HTTP(S) URL pattern:
git.example.com # exactly this host, any port, http or https
*.example.com # every subdomain of example.com (not example.com itself)
https://issues.example.com
http://localhost:3000 # only needed when BrowserAllowLocalhost is false- localhost and local files do not need entries; add rules only for the web origins your users should reach. If you force BrowserAllowLocalhost to false, list the exact loopback origins you still want to allow with a port-qualified rule.
- A forced empty array is a valid policy that blocks every external web origin. Paths, queries, fragments, credentials, and non-HTTP(S) schemes are rejected; a list containing only invalid rules fails closed.
- The allowlist governs page navigations. It does not filter subresource requests (images, scripts, fetch), and DisableEmbeddedBrowser takes precedence: when the browser is disabled the allowlist is not consulted.
- A blocked navigation shows an in-page explanation with the blocked origin and the next step; Settings → Browser shows the effective rules and whether localhost and local files are allowed, and cmux browser status --json reports the same.
#Lockability
Configuration-profile forced values are locked by macOS itself: no user-level write (including Reset All Settings) can change the effective value, and removing the profile restores normal behavior. cmux additionally suppresses its own writers: the cmux.json importer skips every profile-forced key, and for the browser, remote-control, and Cloud controls the Settings UI shows the managed state, the command palette hides the matching commands, and the CLI refuses with a managed-policy error — including when an administrator forces the user-level browserDisabledOverride key directly.
#Supported platforms and versions
- macOS 14 (Sonoma) and later, matching the cmux system requirements.
- cmux for macOS builds that include this feature (see the changelog entry that shipped it). All release channels honor the release payload domain.
- These are macOS-side controls. The iOS app needs no separate policy: a Mac with DisableRemoteControl enforced refuses admission and pairing, so the phone cannot attach to it.
#Sample configuration profile
Deploy via your MDM as a Custom Settings payload for com.cmuxterm.app, or install the profile below manually for testing (System Settings → General → Device Management).
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PayloadContent</key>
<array>
<dict>
<key>PayloadType</key>
<string>com.apple.ManagedClient.preferences</string>
<key>PayloadVersion</key>
<integer>1</integer>
<key>PayloadIdentifier</key>
<string>com.example.cmux.managed-policies</string>
<key>PayloadUUID</key>
<string>6D4A3E9C-1B2F-4C8D-9E0A-5F6B7C8D9E0F</string>
<key>PayloadDisplayName</key>
<string>cmux managed policies</string>
<key>PayloadContent</key>
<dict>
<key>com.cmuxterm.app</key>
<dict>
<key>Forced</key>
<array>
<dict>
<key>mcx_preference_settings</key>
<dict>
<key>DisableEmbeddedBrowser</key>
<true/>
<key>DisableRemoteControl</key>
<true/>
<key>DisableCloud</key>
<true/>
<key>DisableRemoteConnections</key>
<true/>
<key>DisableFileTransfer</key>
<true/>
<key>DisableIrohNetworking</key>
<true/>
<key>DisableTelemetry</key>
<true/>
<key>DisableAutoUpdate</key>
<true/>
<key>DisableAutomationWebhooks</key>
<true/>
<key>DisableTLSTrustBypass</key>
<true/>
<key>DisableComputerUse</key>
<true/>
<key>DisableCustomSidebars</key>
<true/>
<key>DisableAICredentialUpload</key>
<true/>
<key>BrowserURLAllowlist</key>
<array>
<string>https://git.example.com</string>
<string>*.example.com</string>
</array>
</dict>
</dict>
</array>
</dict>
</dict>
</dict>
</array>
<key>PayloadType</key>
<string>Configuration</string>
<key>PayloadVersion</key>
<integer>1</integer>
<key>PayloadIdentifier</key>
<string>com.example.cmux.managed-policies.profile</string>
<key>PayloadUUID</key>
<string>2A1B3C4D-5E6F-4A7B-8C9D-0E1F2A3B4C5D</string>
<key>PayloadDisplayName</key>
<string>cmux Managed Policies</string>
<key>PayloadScope</key>
<string>System</string>
</dict>
</plist>#Verifying on a managed Mac
Read the effective forced values and the CLI's managed state:
defaults read com.cmuxterm.app DisableEmbeddedBrowser
defaults read com.cmuxterm.app DisableRemoteControl
defaults read com.cmuxterm.app DisableCloud
defaults read com.cmuxterm.app DisableRemoteConnections
defaults read com.cmuxterm.app DisableFileTransfer
defaults read com.cmuxterm.app DisableIrohNetworking
defaults read com.cmuxterm.app DisableTelemetry
defaults read com.cmuxterm.app DisableAutoUpdate
defaults read com.cmuxterm.app DisableAutomationWebhooks
defaults read com.cmuxterm.app DisableTLSTrustBypass
defaults read com.cmuxterm.app DisableComputerUse
defaults read com.cmuxterm.app DisableCustomSidebars
defaults read com.cmuxterm.app DisableAICredentialUpload
defaults read com.cmuxterm.app BrowserURLAllowlist
defaults read com.cmuxterm.app BrowserAllowLocalhost # absent or 1 = allowed
defaults read com.cmuxterm.app BrowserAllowLocalFiles # absent or 1 = allowed
cmux browser status --json # url_allowlist, url_allowlist_managed, url_allowlist_allows_localhost, url_allowlist_allows_local_files
cmux vm list # refused: Cloud Machines are disabled by your administrator.