# How to Set Up OpenWork Desktop Policies for Enterprise‑Wide Configuration Management

> Learn how to set up OpenWork desktop policies for enterprise-wide configuration management. Enforce consistent security and feature settings across all clients with Den backend.

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

---

**OpenWork uses desktop policies backed by the Den backend to enforce consistent security and feature settings across every desktop client in an organization through a version‑stable REST API.**

OpenWork's **desktop policy system** gives enterprise administrators centralized control over how desktop clients behave throughout their organization. This guide walks through the complete setup—from policy definition to client enforcement—using the actual source code implementation in the `different-ai/openwork` repository.

---

## Core Concepts of OpenWork Desktop Policies

Understanding the policy architecture starts with four fundamental building blocks defined in [`packages/types/src/den/desktop-policies.ts`](https://github.com/different-ai/openwork/blob/main/packages/types/src/den/desktop-policies.ts).

### The Desktop‑Policy Catalog

OpenWork maintains a **canonical catalog** of all possible policy items at [`packages/types/src/den/desktop-policies.ts`](https://github.com/different-ai/openwork/blob/main/packages/types/src/den/desktop-policies.ts) (lines 11–15). Each catalog entry includes a safe default value, ensuring organizations without explicit configuration still receive predictable behavior.

```typescript
// Canonical desktop policy catalog.
// Each key represents a configurable behavior with enterprise-safe defaults

```

### Policy Value Normalization

All policy values—whether from API payloads or the KV store—pass through normalization functions to guarantee type safety. The `normalizeDesktopPolicyValue` function (lines 326–330) coerces and validates raw data:

```typescript
const policy = normalizeDesktopPolicyValue(coerced);
// Guarantees proper shape and types regardless of input source

```

For default values, use `normalizeDefaultDesktopPolicyValue` instead.

### The Default Policy Requirement

Every organization must maintain exactly **one default policy** flagged `isDefault: true`. When clients cannot locate a specific policy assignment, they fall back to this default (lines 371–376):

```typescript
if (policy.isDefault === true) {
  // This policy serves as the organization-wide fallback
}

```

### Policy Boolean Conventions

Policy flags follow an **"allow‑style" naming convention**: `allowFileSharing`, `requireSignIn`, `allowAutoUpdates`. The evaluation logic treats any absent or unknown flag as `false` (lines 22–25), making policies secure by omission.

---

## Policy Storage and API Architecture

### Backend Storage: Den KV Store

Policies persist in **Den's KV store** via the `WorkspaceKvStore` class:

```typescript
// apps/server/src/workspace-kv-store.ts (lines 1-20)
export class WorkspaceKvStore {
  // Handles persistence for desktop policies and other workspace data
}

```

### REST API Endpoints

The `/v1/desktop-policies` endpoints expose full CRUD operations, protected by organization‑level authentication:

| Method | Path | Purpose |
|--------|------|---------|
| GET | `/v1/desktop-policies` | List all organization policies |
| GET | `/v1/desktop-policies/:policyId` | Retrieve specific policy |
| POST | `/v1/desktop-policies` | Create new policy |
| PATCH | `/v1/desktop-policies/:policyId` | Update existing policy or change default |
| DELETE | `/v1/desktop-policies/:policyId` | Remove non‑default policy |

Implementation details reside in [`apps/server/src/workspaces.ts`](https://github.com/different-ai/openwork/blob/main/apps/server/src/workspaces.ts).

---

## Step‑by‑Step: Setting Up Enterprise Desktop Policies

### Step 1: Define Your Policy Configuration

Select values from the catalog. Common enterprise settings include:

```typescript
const enterprisePolicy = {
  policyName: 'Enterprise-Standard',
  isDefault: false,      // Will set as default in Step 3
  isEnabled: true,
  // Security and feature flags
  allowFileSharing: true,
  requireSignIn: true,
  allowAutoUpdates: false,
  allowExternalIntegrations: false,
};

```

### Step 2: Create the Policy via API

```typescript
const response = await fetch('https://your-den.example.com/v1/desktop-policies', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    Authorization: `Bearer ${ORG_ADMIN_TOKEN}`,
  },
  body: JSON.stringify(enterprisePolicy),
});

const createdPolicy = await response.json();
// Save createdPolicy.id for Step 3

```

### Step 3: Designate the Default Policy

```typescript
const { id: policyId } = createdPolicy;

await fetch(`https://your-den.example.com/v1/desktop-policies/${policyId}`, {
  method: 'PATCH',
  headers: {
    'Content-Type': 'application/json',
    Authorization: `Bearer ${ORG_ADMIN_TOKEN}`,
  },
  body: JSON.stringify({ isDefault: true }),
});

```

### Step 4: Verify Client‑Side Consumption

Desktop clients poll `/v1/desktop-policies/:orgId` at startup. Implement policy reading with normalization:

```typescript
import { normalizeDesktopPolicyValue } from '@/packages/types/src/den/desktop-policies';

export function useDesktopPolicy() {
  const [policy, setPolicy] = useState(null);

  useEffect(() => {
    fetch('/v1/desktop-policies')
      .then(r => r.json())
      .then(policies => {
        const raw = policies.find((p) => p.isDefault) ?? policies[0];
        setPolicy(normalizeDesktopPolicyValue(raw));
      });
  }, []);

  return policy;
}

```

### Step 5: Conditional UI Enforcement

```tsx
const policy = useDesktopPolicy();

return (
  <>
    {policy?.allowFileSharing ? (
      <FileSharingPanel />
    ) : (
      <DisabledFeature message="File sharing is disabled by your organization." />
    )}
    
    {policy?.requireSignIn && <MandatorySignInGate />}
  </>
);

```

---

## Client‑Side Policy Evaluation

The desktop client implementation in [`packages/ui/src/react/platform-detect.ts`](https://github.com/different-ai/openwork/blob/main/packages/ui/src/react/platform-detect.ts) demonstrates how policy values drive runtime behavior. Always normalize API responses before use:

```typescript
import { normalizeDesktopPolicyValue } from '@/packages/types/src/den/desktop-policies';

// rawPolicy comes from /v1/desktop-policies endpoint
const policy = normalizeDesktopPolicyValue(rawPolicy);

if (policy.allowFeatureX) {
  // Enable corresponding UI or capability
}

```

This pattern ensures **forward compatibility**—new policy fields added to the catalog won't break existing clients.

---

## Enterprise Roll‑Out Best Practices

### Adding New Policy Fields

The catalog design enables safe extension through three steps:

1. Add comment documentation in [`desktop-policies.ts`](https://github.com/different-ai/openwork/blob/main/desktop-policies.ts)
2. Define a secure default value
3. (Optional) Add UI hook for client-side enforcement

Existing clients ignore unknown fields, making enterprise-wide rollouts predictable.

### Policy Update Propagation

Changes propagate automatically:

- **New launches**: Clients fetch current policy on startup
- **Running clients**: Receive updates via live-reload channel (if implemented)
- **Verification**: Check [`evals/specs/models-available.slow.test.ts`](https://github.com/different-ai/openwork/blob/main/evals/specs/models-available.slow.test.ts) for CRUD and default-policy test patterns

---

## Summary

- **Desktop policies** in OpenWork centralize configuration through Den's KV-backed REST API at `/v1/desktop-policies`
- **Every organization requires exactly one default policy** (`isDefault: true`) as defined in [`packages/types/src/den/desktop-policies.ts`](https://github.com/different-ai/openwork/blob/main/packages/types/src/den/desktop-policies.ts) lines 371–376
- **Always normalize values** using `normalizeDesktopPolicyValue` before consumption to guarantee type safety
- **Secure by omission**: Unknown or missing boolean flags evaluate to `false`
- **Three-step extension**: Adding catalog fields requires only documentation, default value, and optional UI hook

---

## Frequently Asked Questions

### How does OpenWork handle policy conflicts when multiple policies exist?

Organizations may create multiple policies for different use cases, but only one can be `isDefault: true`. When clients request their policy assignment, the backend returns either a directly assigned policy or the default. The `normalizeDesktopPolicyValue` function ensures consistent interpretation regardless of which policy applies.

### Can I migrate an existing policy to become the default?

Yes. Use the PATCH endpoint to set `isDefault: true` on any existing policy. The backend automatically removes the default flag from the previous default policy. Reference the implementation in [`apps/server/src/workspaces.ts`](https://github.com/different-ai/openwork/blob/main/apps/server/src/workspaces.ts) and the test verification in [`evals/specs/models-available.slow.test.ts`](https://github.com/different-ai/openwork/blob/main/evals/specs/models-available.slow.test.ts).

### What happens if a client receives a policy with unknown fields?

Unknown fields are preserved through normalization but ignored by client code. This forward-compatibility design—implemented in `normalizeDesktopPolicyValue` at lines 326–330—allows enterprises to preview new policy features before updating desktop clients organization-wide.

### How do I validate policy changes before enterprise deployment?

Create policies with `isDefault: false` initially, then assign them to test users or groups. Once validated, PATCH the policy to `isDefault: true` or assign it to production users. The normalization functions in [`packages/types/src/den/desktop-policies.ts`](https://github.com/different-ai/openwork/blob/main/packages/types/src/den/desktop-policies.ts) ensure test and production policies behave identically.