# How OpenWork Cloud Synchronization Works: Desktop-to-Cloud Workspace Sync Explained

> Discover how OpenWork Cloud Synchronization powers desktop-to-cloud workspace sync. Learn about its event-driven MCP protocol for seamless data migration and updates.

- Repository: [Different AI/openwork](https://github.com/different-ai/openwork)
- Tags: internals
- Published: 2026-07-31

---

**OpenWork synchronizes local and remote workspaces via an event-driven MCP protocol that pulls cloud state, migrates unsynced local data, and polls for incremental updates through dedicated HTTP endpoints.**

OpenWork (the open-source workspace manager maintained by different-ai) implements a robust cloud synchronization mechanism that keeps desktop session groups consistent with remote cloud workspaces. The system uses a combination of client-side sync handlers and server-side MCP (Multiple Connection Protocol) routes to manage state merging, initial migrations, and real-time event polling.

## The Cloud Sync Architecture

The synchronization mechanism relies on two primary components: a **client-side sync handler** that manages local state transitions, and **server-side MCP endpoints** that expose atomic operations for fetching, updating, and streaming workspace changes.

### Client-Side Sync Handler ([`use-session-group-sync.ts`](https://github.com/different-ai/openwork/blob/main/use-session-group-sync.ts))

Located at [`apps/app/src/react-app/shell/use-session-group-sync.ts`](https://github.com/different-ai/openwork/blob/main/apps/app/src/react-app/shell/use-session-group-sync.ts), this handler orchestrates the desktop side of the sync process. It creates an endpoint reference via `endpointForWorkspaceRef.current(workspace)`, initiates version tracking with `beginSessionGroupServerSync(workspace.id)`, and executes the migration logic. The handler decides whether to upload existing local groups based on the `migrateLocal` flag and the presence of `readMigrationComplete` state.

### Server-Side MCP Endpoints ([`cloud-mcp.ts`](https://github.com/different-ai/openwork/blob/main/cloud-mcp.ts))

The server implementation in [`apps/server/src/routes/cloud-mcp.ts`](https://github.com/different-ai/openwork/blob/main/apps/server/src/routes/cloud-mcp.ts) exposes JSON-RPC style HTTP routes under the `/cloud-mcp` path. These endpoints implement **provider actions** that the desktop client consumes:

- **`POST /cloud-mcp/state`** – Retrieves the current workspace state from `runtimeDb.getWorkspaceState(workspaceId)`.
- **`PUT /cloud-mcp/state`** – Persists local changes via `runtimeDb.saveWorkspaceState(workspaceId, state)`.
- **`POST /cloud-mcp/events`** – Returns incremental changes using `eventStore.getEvents(workspaceId, since)`.

## Step-by-Step Synchronization Flow

When a workspace activates, the desktop app executes a four-phase synchronization protocol to ensure consistency between local `SessionGroup` data and the OpenWork Cloud.

### 1. Initialize the Sync Handler

The process begins when the React shell mounts the `useSessionGroupSync` hook for a specific `RouteWorkspace`. The handler acquires a version token from `beginSessionGroupServerSync` to track this sync attempt and establishes the MCP endpoint connection.

### 2. Pull Remote State

The client calls `endpoint.client.getSessionGroups(endpoint.workspaceId)`, which maps to the server's `POST /cloud-mcp/state` route. This returns the canonical cloud state, which the client stores in the `nextState` variable. If the sync is cancelled mid-flight, the handler aborts to prevent partial updates.

### 3. Migrate Unsynced Local Session Groups

If the `migrateLocal` parameter is true and `readMigrationComplete` returns false—indicating a first-time sync—the handler checks for existing local data via `localGroupStateForWorkspace`. When `hasGroupData` confirms the cloud state is empty but local state exists, the client transforms local groups using `serverStateFromWorkspaceState` and uploads them via `endpoint.client.putSessionGroups`. After successful upload, `writeMigrationComplete` persists a flag to the **workspace KV store** to prevent redundant migrations.

### 4. Poll for Incremental Events

Following initial sync, the handler enters a polling loop against `listSessionGroupEvents`. New events trigger subsequent calls to `getSessionGroups`, allowing the desktop to merge remote changes incrementally without full-state transfers. The `applySessionGroupServerState` function reconciles these updates into the local UI state using the version token for optimistic concurrency.

## Data Persistence and Storage Layer

The server persists synchronized data through a dedicated runtime database and workspace-scoped key-value storage.

### Runtime Database ([`runtime-db.ts`](https://github.com/different-ai/openwork/blob/main/runtime-db.ts))

[`apps/server/src/runtime-db.ts`](https://github.com/different-ai/openwork/blob/main/apps/server/src/runtime-db.ts) provides the primary storage interface for cloud state. It implements `getWorkspaceState` and `saveWorkspaceState`, ensuring atomic writes of session group data. This layer isolates workspace state from other runtime concerns and supports the transactional semantics required for conflict-free synchronization.

### Workspace KV Store and Migration Flags

[`apps/server/src/workspace-kv-store.ts`](https://github.com/different-ai/openwork/blob/main/apps/server/src/workspace-kv-store.ts) manages metadata such as the migration-complete flag written by `writeMigrationComplete`. This key-value store tracks per-workspace synchronization metadata without polluting the main session group state, enabling the sync handler to idempotently determine whether initial migration has occurred.

## Implementation Code Examples

The following excerpt from [`apps/app/src/react-app/shell/use-session-group-sync.ts`](https://github.com/different-ai/openwork/blob/main/apps/app/src/react-app/shell/use-session-group-sync.ts) demonstrates the client-side orchestration logic:

```typescript
// apps/app/src/react-app/shell/use-session-group-sync.ts
const syncWorkspace = async (workspace: RouteWorkspace, migrateLocal: boolean) => {
  const endpoint = endpointForWorkspaceRef.current(workspace);
  if (!endpoint) return;
  const version = beginSessionGroupServerSync(workspace.id);

  // Pull the current state from the cloud
  const response = await endpoint.client.getSessionGroups(endpoint.workspaceId);
  if (cancelled) return;

  let nextState = response.state;
  const localState = localGroupStateForWorkspace(workspace, endpoint);

  // Migrate any unsynced local groups to the cloud
  if (migrateLocal && !readMigrationComplete(endpoint) && !hasGroupData(response.state) && localState !== undefined) {
    const migrated = await endpoint.client.putSessionGroups(
      endpoint.workspaceId,
      serverStateFromWorkspaceState(localState)
    );
    nextState = migrated.state;
  }

  writeMigrationComplete(endpoint);
  applySessionGroupServerState(workspace.id, nextState, version);
};

```

The server-side route implementation in [`apps/server/src/routes/cloud-mcp.ts`](https://github.com/different-ai/openwork/blob/main/apps/server/src/routes/cloud-mcp.ts) handles the underlying RPC calls:

```typescript
// apps/server/src/routes/cloud-mcp.ts
router.post("/cloud-mcp/state", async (req, res) => {
  const { workspaceId } = req.body;
  const state = await runtimeDb.getWorkspaceState(workspaceId);
  res.json({ state });
});

router.put("/cloud-mcp/state", async (req, res) => {
  const { workspaceId, state } = req.body;
  await runtimeDb.saveWorkspaceState(workspaceId, state);
  res.json({ success: true });
});

router.post("/cloud-mcp/events", async (req, res) => {
  const { workspaceId, since } = req.body;
  const events = await eventStore.getEvents(workspaceId, since);
  res.json({ events });
});

```

## Summary

- **OpenWork Cloud synchronization** uses an MCP-based protocol via the `/cloud-mcp` routes to exchange session group state between desktop clients and the cloud.
- The **client-side handler** ([`use-session-group-sync.ts`](https://github.com/different-ai/openwork/blob/main/use-session-group-sync.ts)) manages versioned sync cycles, initial data migration, and event-driven updates.
- **Server endpoints** ([`cloud-mcp.ts`](https://github.com/different-ai/openwork/blob/main/cloud-mcp.ts)) expose atomic fetch, save, and event-listening operations backed by [`runtime-db.ts`](https://github.com/different-ai/openwork/blob/main/runtime-db.ts).
- **Migration safety** is enforced through the workspace KV store via `readMigrationComplete` and `writeMigrationComplete`, ensuring local data uploads only occur once per workspace.
- The system supports **incremental synchronization** by polling `listSessionGroupEvents` and applying deltas via `applySessionGroupServerState`.

## Frequently Asked Questions

### How does OpenWork handle conflicts between local and remote workspace state?

OpenWork resolves conflicts through versioned synchronization tokens. The `beginSessionGroupServerSync` function generates a unique version identifier for each sync cycle. When applying updates via `applySessionGroupServerState`, the system validates this version against the current local state, ensuring that only the most recent cloud state overwrites local data, and preventing stale updates from clobbering newer changes.

### What MCP endpoints manage cloud synchronization?

The synchronization protocol relies on three primary endpoints defined in [`apps/server/src/routes/cloud-mcp.ts`](https://github.com/different-ai/openwork/blob/main/apps/server/src/routes/cloud-mcp.ts): `POST /cloud-mcp/state` for retrieving workspace state, `PUT /cloud-mcp/state` for persisting local changes to the cloud, and `POST /cloud-mcp/events` for polling incremental updates. These endpoints implement the JSON-RPC interface that the desktop client consumes through methods like `getSessionGroups` and `putSessionGroups`.

### Where is synchronized workspace state stored?

The canonical state resides in the **runtime database** managed by [`apps/server/src/runtime-db.ts`](https://github.com/different-ai/openwork/blob/main/apps/server/src/runtime-db.ts), which provides `getWorkspaceState` and `saveWorkspaceState` methods. Migration metadata and sync flags are stored separately in the **workspace KV store** ([`workspace-kv-store.ts`](https://github.com/different-ai/openwork/blob/main/workspace-kv-store.ts)), allowing the system to track sync health and migration status independently from the session group data itself.

### Does OpenWork support offline-first synchronization?

While the desktop app can operate on local state offline, the cloud synchronization mechanism requires an active connection to reach the MCP endpoints. The `use-session-group-sync` handler queues local changes and retries synchronization once connectivity restores, but the protocol itself is designed for online consistency via the `eventStore` polling mechanism rather than robust offline queueing with conflict resolution at reconnect.