# How OpenWork Controls Local Model Access with Desktop Policies

> Learn how OpenWork controls local model access using its centralized desktop policy system. Discover how it calculates effective policies and gates features via a restriction checker.

- Repository: [Different AI/openwork](https://github.com/different-ai/openwork)
- Tags: how-to-guide
- Published: 2026-08-01

---

**OpenWork uses a centralized desktop-policy system that calculates an effective policy from default and organization-assigned rules, then exposes a restriction checker to both UI and backend code for gating local model features.**

OpenWork is an open-source AI workbench that lets organizations manage how users interact with local and cloud models. The **desktop policy system** provides fine-grained control over capabilities like custom provider installation and Zen model access, ensuring security and licensing constraints can be enforced without redeploying the client.

## The Desktop Policy Catalog

Policy definitions live in a static catalog at [`packages/types/src/den/desktop-policies.ts`](https://github.com/different-ai/openwork/blob/main/packages/types/src/den/desktop-policies.ts). Each entry specifies:

- **`id`** – Unique machine-readable identifier (e.g., `"allowCustomProviders"`)
- **`name`** and **`description`** – Human-readable labels for admin dashboards
- **`userNotice`** – Message displayed when the policy blocks a feature
- **`defaultValue`** – Fallback applied when no organization policy exists

```ts
// From desktop-policies.ts
export const desktopPolicyDefinitions = [
  {
    id: "allowCustomProviders",
    name: "Custom providers",
    description: "Allow users to add and use models that are not deployed through OpenWork Cloud.",
    userNotice: "Your organization administrator has disabled adding custom providers.",
    defaultValue: true,
  },
  {
    id: "allowZenModel",
    name: "Zen models",
    description: "Allow access to built-in Zen models.",
    userNotice: "Zen models have been disabled by your administrator.",
    defaultValue: true,
  },
] as const;

```

This catalog serves as the single source of truth for which capabilities can be controlled.

## Policy Storage and Retrieval

Policies are stored in the Den backend as JSON documents typed as `DesktopPolicyDocument`. The desktop client fetches two sources:

1. **Default policy** – Baseline values for all organizations
2. **Assigned policies** – Organization-specific overrides

These raw documents must be normalized before use:

```ts
import { normalizeDesktopPolicyDocument } from "./desktop-policies";

const raw = await fetchDesktopPolicy(); // JSON from Den backend
const policy = normalizeDesktopPolicyDocument(raw);

```

## Calculating the Effective Policy

The `calculateEffectiveDesktopPolicy` function in [`desktop-policies.ts`](https://github.com/different-ai/openwork/blob/main/desktop-policies.ts) merges multiple policy sources into a single authoritative configuration.

```ts
export function calculateEffectiveDesktopPolicy(input: {
  orgPolicyCount: number;
  defaultPolicy?: unknown;
  assignedPolicies: unknown[];
}): Required<DesktopPolicyValue>

```

The algorithm follows two rules:

- **Union semantics** – A feature is enabled if **any** policy sets it to `true`
- **Empty org fallback** – When `orgPolicyCount === 0`, `allDesktopPolicies(true)` grants full access

```ts
import { calculateEffectiveDesktopPolicy } from "./desktop-policies";

const effective = calculateEffectiveDesktopPolicy({
  orgPolicyCount: orgPolicies.length,
  defaultPolicy: defaultPolicy,
  assignedPolicies: orgPolicies,
});
// effective.allowCustomProviders === true if any source enables it

```

This design allows layered policy management: defaults provide safe baselines, while assigned policies can selectively disable or enable features.

## Enforcing Restrictions in the UI

The reconciled policy is exposed through a `DesktopAppRestrictionChecker` interface used throughout the desktop application.

### Building the Checker

The [`policy-provider-reconcile.ts`](https://github.com/different-ai/openwork/blob/main/policy-provider-reconcile.ts) file constructs the checker from the effective policy:

```ts
// From policy-provider-reconcile.ts
type DesktopAppRestrictionChecker = (opts: { restriction: DesktopPolicyKey }) => boolean;

const checkDesktopAppRestriction: DesktopAppRestrictionChecker = ({ restriction }) =>
  effective[restriction] === true;

```

### Using the Checker in Components

UI code queries the checker before rendering gated features. In [`provider-auth/store.ts`](https://github.com/different-ai/openwork/blob/main/provider-auth/store.ts), the model picker uses this pattern:

```ts
const canAddCustom = checkDesktopAppRestriction({ restriction: "allowCustomProviders" });

if (!canAddCustom) {
  // Render disabled state with admin notice
  return <DisabledBanner notice="Custom providers are disabled by your admin." />;
}

```

When a restriction returns `false`, the component displays the `userNotice` from the policy catalog definition.

## Backend Enforcement

The same policy values gate server-side operations. When `allowCustomProviders` is disabled:

- The backend rejects MCP requests attempting to register non-cloud model providers
- API endpoints return `403 Forbidden` with the policy's `userNotice`

This prevents policy bypass through direct API calls.

## Policy Administration and Live Updates

Administrators modify policies via the MCP endpoint:

```bash
PATCH /v1/desktop-policies/:id

```

The update flow works as follows:

1. Admin patches policy via MCP
2. Desktop client re-fetches policies on next sync or foreground event
3. `calculateEffectiveDesktopPolicy` recomputes the effective policy
4. React components re-render with updated `checkDesktopAppRestriction` results
5. UI immediately reflects new restrictions (banners appear, options disappear)

The E2E test flow in [`evals/flows/desktop-policies-cloud-mcp.flow.ts`](https://github.com/different-ai/openwork/blob/main/evals/flows/desktop-policies-cloud-mcp.flow.ts) validates this complete loop, ensuring policy changes propagate correctly.

## Key Policy Types for Local Model Control

| Policy ID | Controls | Default |
|-----------|----------|---------|
| `allowCustomProviders` | Adding non-OpenWork Cloud models (Ollama, LM Studio, etc.) | `true` |
| `allowZenModel` | Access to built-in Zen inference models | `true` |

Organizations running air-gapped or compliance-constrained deployments typically disable `allowCustomProviders` to prevent unapproved model execution.

## Summary

- OpenWork's **desktop policy system** centralizes control of local model capabilities
- Policy definitions in [`desktop-policies.ts`](https://github.com/different-ai/openwork/blob/main/desktop-policies.ts) provide schemas, defaults, and user-facing notices
- `calculateEffectiveDesktopPolicy` merges default and assigned policies with union semantics
- The `DesktopAppRestrictionChecker` exposes results to UI components via [`policy-provider-reconcile.ts`](https://github.com/different-ai/openwork/blob/main/policy-provider-reconcile.ts)
- Both client and backend enforce restrictions, preventing bypass
- Live policy updates propagate without client restarts

## Frequently Asked Questions

### What happens if an organization has no explicit desktop policy assigned?

When `orgPolicyCount` is zero, `calculateEffectiveDesktopPolicy` returns `allDesktopPolicies(true)`, granting full access to all features. This ensures backward compatibility and smooth onboarding.

### Can users override desktop policies locally?

No. Policies are fetched from the Den backend and recomputed server-side. The desktop client has no mechanism to modify effective policy values, and backend endpoints reject operations violating active restrictions.

### How quickly do policy changes take effect?

Policy changes propagate on the next policy sync, typically within seconds when the app is foregrounded. The E2E flow in [`desktop-policies-cloud-mcp.flow.ts`](https://github.com/different-ai/openwork/blob/main/desktop-policies-cloud-mcp.flow.ts) validates sub-minute propagation times under normal network conditions.

### Where is the restriction checker instantiated in the codebase?

The `checkDesktopAppRestriction` function is built in [`policy-provider-reconcile.ts`](https://github.com/different-ai/openwork/blob/main/policy-provider-reconcile.ts) from the effective policy, then passed to React components through the provider-auth domain store in [`provider-auth/store.ts`](https://github.com/different-ai/openwork/blob/main/provider-auth/store.ts).