Claude Desktop (MDM)
Claude Desktop (including Cowork) can send inference to Corveil through Developer → Configure Third-Party Inference → Gateway. An MDM profile locks that choice to your Corveil host. People on managed devices do not open People & Access → API Keys, and they do not paste a token.
This page is the fleet setup: Gateway mode, interactive SSO, and managed-key. Anthropic is billed on the organization's API key stored as a Corveil provider credential. The Claude Cowork page is the manual, per-machine setup, on an older config shape. Passthrough there uses an Anthropic API key the customer provisions (sk-ant-api…). The Claude Desktop page is an older environment-variable setup that still requires a pasted Corveil key.
Build the profile in the app on one unmanaged admin machine (Help → Troubleshooting → Enable Developer Mode, then Developer → Configure Third-Party Inference…), test the connection, and export it. Anthropic's Deploy with MDM and gateway pages are the export and encoding reference. The keys below are the ones that matter for Corveil.
What the managed profile locks
Claude Desktop reads a managed configuration from the operating system. When that source sets the inference keys, it wins: values the user writes locally are ignored, and the in-app configuration window opens read-only. The app applies the profile at launch (and, from Desktop 1.46388.1, on its next configuration re-check). Push the profile before the installer so the first launch is already the third-party deployment.
| Platform | Managed location |
|---|---|
| macOS | /Library/Managed Preferences/<user>/com.anthropic.claudefordesktop.plist (per-user; wins over the machine file /Library/Managed Preferences/com.anthropic.claudefordesktop.plist) |
| Windows | HKLM\SOFTWARE\Policies\Claude |
| Linux | /etc/claude-desktop/managed-settings.json |
A few encoding rules, from Anthropic's current reference:
- Windows. Machine policy and user policy are separate. When any value exists directly under
HKLM\SOFTWARE\Policies\Claude, the app ignoresHKCU\SOFTWARE\Policies\Claude. Put the whole profile in one hive. The in-app export writes user policy,HKCU\SOFTWARE\Policies\Claude, with values directly under that key. Booleans come out asDWORD(chatTabEnabled=dword:00000001). Object-typed keys such asbannercome out as oneREG_SZJSON string.REG_SZis also accepted for booleans and integers. - Linux. The file is native JSON, owned by root, not a symlink, and not group- or world-writable.
/etc/claude-desktophas the same ownership rules. A file that fails those checks is rejected and local settings are disabled until it is fixed. - macOS and Windows store object-typed keys (such as
inferenceGatewayOidc) as one JSON string, not as nested keys. Export from the app rather than hand-nesting them.
A profile that sets only the app-behavior keys (auto-update, relaunch window, configuration re-check, network proxy) does not lock inference. To pin Gateway mode, the profile has to set inferenceProvider and the gateway fields below. Also set disableDeploymentModeChooser to true so the device cannot switch back to a claude.ai sign-in.
Set inferenceGatewayBaseUrl to the Corveil origin, with no /v1 suffix — https://corveil.io for the hosted gateway. Desktop calls GET /v1/models and POST /v1/messages on that origin. Confirm with the in-app connection test on one device before the fleet push. Which of those models Cowork keeps is covered under Models Cowork will show.
Managed-key and passthrough
Gateway mode has two routing choices, and they are not interchangeable. Pick one for the fleet. Spend limits count both. Passthrough bills Anthropic on an API key the customer provisions (sk-ant-api…); that credential is per-token pass-through. Managed-key bills Anthropic on the organization's API key stored as a Corveil provider credential. A static sk-citadel-… bearer is managed-key mode.
Add the Anthropic API key under AI Gateway → Providers, and enable a Claude model in the catalog. That key is what Cowork gateway traffic bills. Users never receive it.
| Managed-key | Passthrough | |
|---|---|---|
| Guardrails and request logs | Yes | Yes |
| Budgets | Yes. Spend limits count this traffic. | Yes. An sk-ant-api… key is per-token pass-through, and spend limits count it. |
| Who is billed upstream | The organization's Anthropic API key, stored as a Corveil provider credential | An Anthropic API key the customer provisions (sk-ant-api…) |
Who Desktop signs in as is a separate choice. Use one of the next two sections. When inferenceCredentialKind is helper-script, Desktop uses only that helper.
Signing in to Claude (claude.ai, Claude Desktop, or Claude Code) is a Claude account sign-in. It stays in that app. It does not mint the Corveil virtual key, and it is not the upstream credential for either routing mode. Managed-key bills Anthropic on the organization's provider credential. The passthrough helper below prints an Anthropic API key the customer provisions (sk-ant-api…).
Single sign-on
This is the fleet sign-in. It needs Claude Desktop 1.6889.0 or later. No helper runs, and no long-lived gateway key is stored on the device. This sign-in does not create a virtual key. A claude.ai or Anthropic SSO login is a different sign-in.
Configure SSO
On People & Access → Organization, choose Configure SSO and connect the identity provider people already use. That connects sign-in for Corveil. It does not, by itself, let Claude Desktop use this profile.
Corveil has to accept this profile's sign-in for your organization before Desktop can sign in.
- Hosted gateway. Ask Corveil support or your onboarding contact.
- A gateway you run. An operator on the staff allowlist (
STAFF_EMAILS) adds the sign-in under Staff → Accounts → Desktop SSO. Organization admins can list and remove their organization's sign-ins, but cannot add one.
With provisioning on, the first sign-in creates an account only for an email in one of your organization's verified domains. Someone who already has a Corveil account is added through an invite or directory sync, not by signing in to Desktop.
Profile
The profile is published in corveil/claude-desktop-mdm. The four files encode the same keys. In the .mobileconfig and the .reg, inferenceGatewayOidc and banner are one JSON string each. Set inferenceGatewayBaseUrl to your Corveil origin (https://corveil.io for the hosted gateway), with no /v1 suffix. deploymentDisplayName, the subtitle, and the banner text are sample labels. Replace them before the fleet push.
| Platform | File |
|---|---|
| macOS | corveil.mobileconfig |
| Windows | corveil.reg (HKLM\SOFTWARE\Policies\Claude) |
| Linux | managed-settings.json |
| JSON | corveil.json |
Sign in to your organization
After the profile is on the device, open Claude Desktop and choose Sign in to your organization. The browser opens your organization's sign-in. Return to the app when it finishes.
Virtual-key helper
Corveil does not ship a Desktop login program. corveil login is the CLI, and it is not a credential helper you can point Desktop at. This section is for a fleet that places an sk-citadel- secret on the device. A managed fleet that should not do that uses Single sign-on.
Signing in to the Corveil web app does not hand MDM a key. The dashboard Default API key is shown once, when it is created. Later sign-ins do not reveal it. Directory sync can create the same key and also does not reveal it. Revoking Default API key does not mint a replacement; delete it before a new one can be created. Do not build a profile, a script, or a helper that tries to re-read Default API key.
corveil login is the interactive login that returns a Corveil key. It opens a browser to your organization's Corveil sign-in and returns sk-citadel-… once. The key is named CLI on <hostname>. The plaintext is not shown again. A later login from the same hostname deactivates that key and mints a replacement; it does not reveal the old secret.
corveil login --url https://corveil.ioThat command is the right way for an administrator, or a single user at a terminal, to mint a key without using the dashboard. It always sends the machine hostname, it writes the key into the CLI's config profile, and it does not print a bare token on stdout. Pointing inferenceCredentialHelper at corveil login will not work, and a Desktop install that shared the machine hostname would deactivate the CLI key (and the reverse).
A virtual-key profile can use inferenceCredentialKind: helper-script when you install your own executable. On first launch that program runs the /auth/cli-login + /auth/cli/exchange handshake, caches the new key, and prints it. The user signs in through the browser. They never copy a token. Do not combine the helper with interactive. When the kind is set, Desktop uses only that source.
Illustrative managed-key profile (logical keys; export to plist or registry from the app). The helper path is a placeholder for the executable you install:
{
"inferenceProvider": "gateway",
"inferenceGatewayBaseUrl": "https://corveil.io",
"inferenceGatewayAuthScheme": "bearer",
"inferenceCredentialKind": "helper-script",
"inferenceCredentialHelper": "/usr/local/bin/mint-gateway-key",
"inferenceCredentialHelperWindows": "C:\\Program Files\\YourOrg\\mint-gateway-key.exe",
"inferenceCredentialHelperTtlSec": 3600,
"inferenceCredentialHelperSilentRefreshEnabled": false,
"inferenceCredentialHelperTimeoutSec": 300,
"modelDiscoveryEnabled": true,
"disableDeploymentModeChooser": true
}The helper's contract:
- Stdout is only the credential. Either a bare
sk-citadel-…token, or one JSON object{"token":"sk-citadel-…"}. Banners and logs go to stderr. Exit 0 on success. CLAUDE_HELPER_CONTEXT=interactive(first run, and Desktop's "Sign in again" after a rejected credential) is the only time the helper may call the exchange. Bind127.0.0.1on a port from 1024 through 65535, open{origin}/auth/cli-login?cb_port=<port>&state=<nonce>, and on the loopback redirect immediatelyPOST /auth/cli/exchangewith{"token":"<exchange>","hostname":"<name>"}. The exchange token lives for about a minute and is single-use. Readapi_keyfrom the JSON response, replace the cache, and print it.- Every other context (
mid-session-refresh,background,scheduled-task,setup-test, and the TTL re-run) reprints the cached key, or exits non-zero if the cache is missing. Do not call/auth/cli/exchangeon those runs. A repeat exchange with the same hostname deactivates the key Desktop is already using. - Hostname. Send a name that is unique per device and different from the machine hostname
corveil loginuses, for exampleclaude-desktop-<machine-hostname>. The server truncates it at 64 characters and names the keyCLI on <that name>. One shared literal for the whole fleet makes every desktop rotate the same key. - Cache. Store the plaintext in the user keychain (macOS Keychain, Windows Credential Manager, libsecret) or in a
0600file owned by the user. There is no refresh token to mint a new key without a browser. SetinferenceCredentialHelperSilentRefreshEnabledtofalseso a mid-session refresh does not expect a non-interactive re-login. The TTL re-run still happens; the reprint branch is what serves it. RaiseinferenceCredentialHelperTimeoutSec(default 60, maximum 600) so the first browser sign-in fits. 300 seconds is a reasonable start. - Windows. The in-app registry export writes the helper path in
inferenceCredentialHelper. Anthropic's reference also acceptsinferenceCredentialHelperWindowsfor a Windows executable. Point whichever key you set at the helper installed on that PC.
A revoked key recovers when the user chooses Sign in again. That re-invokes the helper with interactive, which mints a replacement and replaces the cache. If a non-interactive run reprints a key you already know is bad, Desktop accepts the output and the next turn fails again with no prompt — exit non-zero instead.
Static key in the profile
inferenceCredentialKind: static plus inferenceGatewayApiKey also selects managed-key mode. Use it only when you can inject a distinct, freshly minted key per user. The plaintext still has to be captured at mint time (corveil login or the exchange above). The profile cannot be filled from Default API key after the fact. A single key shared by the whole fleet collapses per-user budgets and attribution. A static key is a long-lived secret in the managed profile.
Passthrough helper
Passthrough bills Anthropic on an API key the customer creates in the Anthropic Console (sk-ant-api…). Claude Code, Claude Desktop, and claude.ai sign-in stay in those apps. The token field is the API key you provision.
This profile is the flat form of the manual Claude Cowork JSON. The macOS .mobileconfig and the Windows .reg export carry the same keys. inferenceCustomHeaders is absent. Desktop treats that map as plain configuration, drops a string value, and then sends no custom headers. The virtual key is supplied in the helper's JSON headers.
{
"inferenceProvider": "gateway",
"inferenceGatewayBaseUrl": "https://corveil.io",
"inferenceCredentialKind": "helper-script",
"inferenceCredentialHelper": "/Users/you/bin/anthropic-passthrough.sh",
"chatTabEnabled": true
}On macOS the export is a user-scoped configuration profile whose payload type is com.anthropic.claudefordesktop. chatTabEnabled is a boolean. On Windows the same export is HKCU\SOFTWARE\Policies\Claude, and chatTabEnabled is a DWORD. A banner object, when set, is one JSON string on both. A macOS export stores a macOS path in inferenceCredentialHelper. On Windows, set that value to the helper installed on the PC. Anthropic's reference also accepts inferenceCredentialHelperWindows for that path.
Stdout is one JSON object. token is the Anthropic API key you provision (sk-ant-api…). headers carries the Corveil virtual key:
{"token":"sk-ant-api-your-key-here","headers":{"x-citadel-api-key":"sk-citadel-your-key-here"}}The helper holds no secrets. It reads them at runtime. On a shared Mac, a world-readable script under /usr/local/bin would let any local user read the Anthropic API key. Keep the keys in one of:
- a
0600file owned by the user - the user keychain (macOS Keychain, Windows Credential Manager, libsecret)
- an MDM-injected secret written into one of those stores
#!/bin/sh
# The helper may live on a shared path only when it contains no secret.
# Mode 0700 when it is installed per user. Keys stay in the 0600 file.
set -eu
secrets="${HOME}/.config/corveil/anthropic-passthrough"
token=$(awk 'NR==1 { print; exit }' "$secrets")
key=$(awk 'NR==2 { print; exit }' "$secrets")
printf '{"token":"%s","headers":{"x-citadel-api-key":"%s"}}\n' "$token" "$key"The secrets file is two lines: the Anthropic API key from the Console, then the Corveil virtual key minted ahead of time (corveil login or People & Access → API Keys). Both are ASCII only. A literal ellipsis character in either placeholder fails the connection test. Create the file mode 0600 before the first launch. The script prints that JSON and nothing else. It does not emit ANTHROPIC_CUSTOM_HEADERS. Quit Cowork after you change the secrets file so Desktop drops the cached helper output. Helper output is cached for inferenceCredentialHelperTtlSec (default 3600). Spend limits count this traffic.
Models Cowork will show
Cowork fills the picker from GET /v1/models. It keeps a row only when the model id contains claude, anthropic, sonnet, opus, haiku, fable, or mythos. Kimi, Gemini, and other non-Claude catalog ids are dropped. The log line is Gateway /v1/models returned 0 usable models with a non-zero rawCount, and the connection test reports an empty picker.
Enable a Claude model for the organization under AI Gateway → Providers → Catalog, for example claude-opus-4-8. Run the connection test again after the catalog change, or relaunch Cowork. Cowork keeps the previous model list until then. On a virtual-key helper, that key has to belong to the organization.