# How OpenWork Den Manages Organization Policies: Architecture and Implementation

> Discover how OpenWork Den manages organization policies with its centralized desktop-policy system. Learn about its architecture, JSON rules, REST endpoints, and WebSocket updates.

- Repository: [Different AI/openwork](https://github.com/different-ai/openwork)
- Tags: architecture
- Published: 2026-08-17

---

**TLDR:** **OpenWork Den manages organization policies through a centralized **desktop‑policy** system that stores JSON‑based rules in PostgreSQL, exposes REST endpoints for CRUD operations, and enforces permissions via WebSocket‑driven updates to the desktop client.**

OpenWork Den acts as the control plane for enterprise OpenWork installations, governing feature access through granular organization policies defined in the `different-ai/openwork` repository. This technical guide examines how OpenWork Den manages organization policies, from TypeScript type definitions in `packages/types` to real‑time enforcement in the desktop application.

## Policy Schema and Type Definitions

The foundation of the policy system lives in [`packages/types/src/den/desktop-policies.ts`](https://github.com/different-ai/openwork/blob/main/packages/types/src/den/desktop-policies.ts). This file exports TypeScript interfaces that define the structure of an organization’s desktop policy, including boolean flags such as `allowModelAccess` and `allowToolX`, along with metadata fields like `policyName`, `isEnabled`, and `assignments`.

```typescript
// packages/types/src/den/desktop-policies.ts
export interface DesktopPolicy {
  id: string;
  orgId: string;
  policyName: string;
  isEnabled: boolean;
  isDefault: boolean;
  assignments?: string[]; // User or team IDs
  allowModelAccess: boolean;
  allowedTools: string[];
  // Additional feature flags...
}

```

The type system ensures that every policy object consumed by the API or desktop client adheres to a strict contract, preventing runtime errors caused by missing fields.

## Database Storage and Default Policies

Policies persist in the Den server’s PostgreSQL database within the `desktop_policies` table. A database migration located at [`packages/den-api/migrations/2023...-desktop-policies.sql`](https://github.com/different-ai/openwork/blob/main/packages/den-api/migrations/2023...-desktop-policies.sql) creates this table with a JSON blob column for the policy payload alongside relational metadata (`orgId`, `isDefault`, `createdAt`).

Every organization must maintain exactly one **default policy** where `isDefault: true`. This policy serves as the fallback for any member whose profile lacks an explicit policy assignment, ensuring consistent security enforcement during onboarding or migrations.

## REST API Implementation

### Route Endpoints

The Den server exposes a full CRUD interface through [`packages/den-api/src/routes/desktop-policies.ts`](https://github.com/different-ai/openwork/blob/main/packages/den-api/src/routes/desktop-policies.ts). These endpoints handle policy management for administrators and read‑only access for members:

```typescript
// packages/den-api/src/routes/desktop-policies.ts
router.get('/v1/desktop-policies', listPolicies);
router.get('/v1/desktop-policies/:id', getPolicy);
router.post('/v1/desktop-policies', createPolicy);
router.patch('/v1/desktop-policies/:id', updatePolicy);
router.delete('/v1/desktop-policies/:id', deletePolicy);

```

- **GET** requests filter results by the caller’s organization.
- **POST** and **PATCH** requests validate that the caller is an organization admin.
- **DELETE** operations prevent removal of the default policy to avoid orphaned users.

### Controller Logic and Validation

The controller at [`packages/den-api/src/controllers/desktop-policies.ts`](https://github.com/different-ai/openwork/blob/main/packages/den-api/src/controllers/desktop-policies.ts) implements the business logic behind these routes. It performs **authorization checks** to restrict mutating operations to admins while allowing members to read their assigned policies.

Incoming payloads undergo normalization via the `normalizeDesktopPolicyValue` helper function. This utility guarantees that every boolean flag has a deterministic `true` or `false` value, protecting downstream components from schema drift when older policy objects lack newer feature flags.

```typescript
// packages/den-api/src/controllers/desktop-policies.ts
export function normalizeDesktopPolicyValue(policy: Partial<DesktopPolicy>): DesktopPolicy {
  return {
    ...policy,
    allowModelAccess: policy.allowModelAccess ?? false,
    allowedTools: policy.allowedTools ?? [],
    isEnabled: policy.isEnabled ?? true,
  } as DesktopPolicy;
}

```

## Client-Side Enforcement

### Policy Retrieval and Caching

When the desktop application initializes, it contacts Den to fetch the organization’s default policy from `/v1/desktop-policies/:id`. The module [`packages/desktop/src/policy/desktopPolicy.ts`](https://github.com/different-ai/openwork/blob/main/packages/desktop/src/policy/desktopPolicy.ts) manages this lifecycle:

- **Fetches** the policy JSON from the Den server.
- **Normalizes** the payload using the same `normalizeDesktopPolicyValue` function defined in the shared types package.
- **Caches** the result in local storage to eliminate network round‑trips during UI rendering.
- **Exposes** helper functions like `isModelAllowed(modelId: string): boolean` and `isToolEnabled(toolName: string): boolean` for synchronous access.

```typescript
// packages/desktop/src/policy/desktopPolicy.ts
export async function loadPolicy(orgId: string): Promise<DesktopPolicy> {
  const response = await fetch(`/v1/desktop-policies/${orgId}`);
  const raw = await response.json();
  const policy = normalizeDesktopPolicyValue(raw);
  localStorage.setItem('desktop_policy', JSON.stringify(policy));
  return policy;
}

```

### UI Feature Gating

UI components import the policy cache to conditionally render features. The model selector component checks `policy.allowModelAccess` before displaying available LLM options, while the toolbar component iterates over `policy.allowedTools` to determine which buttons to enable. This client‑side gating ensures that the organization’s security stance is enforced at the presentation layer, independent of server‑side validation.

## Real-Time Updates and Synchronization

Den maintains open WebSocket connections with desktop clients to push policy changes immediately. When an administrator modifies a policy via the PATCH endpoint, the server emits a `policy:update` event through [`packages/den-api/src/websocket/policy-updates.ts`](https://github.com/different-ai/openwork/blob/main/packages/den-api/src/websocket/policy-updates.ts).

Clients receiving this event invalidate their local cache and re‑fetch the latest policy, causing the UI to re‑render without requiring an application restart. If a user’s profile lacks an explicit policy assignment, the client automatically falls back to the organization’s default policy, ensuring uninterrupted access control.

## Summary

- **Type definitions** in [`packages/types/src/den/desktop-policies.ts`](https://github.com/different-ai/openwork/blob/main/packages/types/src/den/desktop-policies.ts) provide the strict schema for policy objects.
- **PostgreSQL** stores policies in the `desktop_policies` table, with every organization required to maintain one default policy.
- **REST endpoints** at `/v1/desktop-policies` support full CRUD operations with admin‑only write restrictions.
- **Normalization** via `normalizeDesktopPolicyValue` ensures backward compatibility as the policy schema evolves.
- **Desktop clients** cache policies locally and expose helper functions for UI feature gating.
- **WebSocket events** deliver real‑time updates to connected clients when policies change.

## Frequently Asked Questions

### How does OpenWork Den handle users without an assigned policy?

When a user profile lacks an explicit policy reference, the desktop client automatically falls back to the organization’s default policy (where `isDefault: true`). This guarantees that new members or users undergoing migrations still operate under the organization’s security baseline without manual intervention.

### Can regular members modify organization policies?

No. The controller logic in [`packages/den-api/src/controllers/desktop-policies.ts`](https://github.com/different-ai/openwork/blob/main/packages/den-api/src/controllers/desktop-policies.ts) enforces role‑based access control that restricts POST, PATCH, and DELETE operations to organization administrators. Regular members can only read policies through the GET endpoints, ensuring that feature access remains centrally governed.

### What triggers a policy update on desktop clients?

Den pushes real‑time notifications via WebSocket connections defined in [`packages/den-api/src/websocket/policy-updates.ts`](https://github.com/different-ai/openwork/blob/main/packages/den-api/src/websocket/policy-updates.ts). When an administrator updates a policy through the PATCH endpoint, the server emits a `policy:update` event to all connected clients, which then invalidate their cache and reload the policy to reflect changes immediately.

### How does the system prevent schema drift in older policies?

Both the server and client use the `normalizeDesktopPolicyValue` function (located in the shared types package and imported by [`packages/den-api/src/controllers/desktop-policies.ts`](https://github.com/different-ai/openwork/blob/main/packages/den-api/src/controllers/desktop-policies.ts) and [`packages/desktop/src/policy/desktopPolicy.ts`](https://github.com/different-ai/openwork/blob/main/packages/desktop/src/policy/desktopPolicy.ts)). This utility assigns default values to any missing boolean flags or arrays, ensuring that legacy policy objects remain compatible with newer UI components that expect specific fields to exist.