# How OpenMAIC Synchronizes Workbench Views in Server-Backed Deployments

> Discover how OpenMAIC synchronizes workbench views using its three-layer architecture. Learn about real-time updates and state persistence in server-backed deployments. Maximize your team's collaboration now.

- Repository: [MAIC/OpenMAIC](https://github.com/THU-MAIC/OpenMAIC)
- Tags: internals
- Published: 2026-09-06

---

**OpenMAIC synchronizes workbench views through a three-layer architecture: a server-side [`session-store.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/session-store.ts) wrapper that persists state via HTTP, a WebSocket-based [`owner-session-client.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/owner-session-client.ts) that broadcasts changes in real time, and the [`useWorkbenchStream.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/useWorkbenchStream.ts) React hook that re-renders UI components when updates arrive.** This system ensures every connected client sees the exact same pane layout, active course, and scroll positions without page reloads.

OpenMAIC's collaborative workbench is designed for multi-user and multi-device scenarios. When deployed behind a Node.js or Fastify server, the framework replaces local-only state with a centralized synchronization pipeline. Every view mutation travels to the server first, gets persisted to a database (MongoDB or PostgreSQL), then propagates to all connected clients through WebSocket channels.

## The Three Core Components of OpenMAIC View Synchronization

### session-store.ts: The Server-Side State Gateway

All workbench state operations flow through [`packages/@openmaic/editor/src/lib/workbench/session-store.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/packages/@openmaic/editor/src/lib/workbench/session-store.ts). This thin wrapper enforces **single source of truth** by ensuring no client can modify local state without server confirmation.

The store handles three responsibilities:

- **Fetching** initial session state via `GET /api/session/:id`
- **Posting** state deltas via `POST /api/session/:id`
- **Applying** confirmed server payloads to the local React context

```typescript
// session-store.ts – thin wrapper around the backend API
export const sessionStore = {
  async getCurrent() {
    const resp = await fetch(`/api/session/${SESSION_ID}`);
    return resp.json() as Promise<WorkbenchSession>;
  },

  async updateCourseVisibility(courseId: string, visible: boolean) {
    await fetch(`/api/session/${SESSION_ID}`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ type: 'course-visibility', courseId, visible })
    });
    // The server will push the updated session back to us via WebSocket
  },

  setCurrent(newSession: WorkbenchSession) {
    // Called by WebSocket client; updates React context used by useWorkbenchStream
    workbenchContext.setState({ session: newSession });
  }
};

```

### owner-session-client.ts: Real-Time Bidirectional Transport

The [`packages/@openmaic/editor/src/lib/workbench/owner-session-client.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/packages/@openmaic/editor/src/lib/workbench/owner-session-client.ts) file manages the WebSocket connection (or Server-Sent Events fallback). It bridges the network layer with the local session store:

```typescript
// owner-session-client.ts – WebSocket handling
import { sessionStore } from '@/lib/workbench/session-store';

const socket = new WebSocket(
  `${location.origin.replace(/^http/, 'ws')}/ws/session/${SESSION_ID}`
);

socket.addEventListener('message', ev => {
  const payload = JSON.parse(ev.data);
  if (payload.type === 'session:update') {
    sessionStore.setCurrent(payload.session);
  }
});

export const ownerSessionClient = {
  sendDelta(delta: any) {
    socket.send(JSON.stringify({ type: 'session:delta', delta }));
  }
};

```

This client enables **sub-millisecond latency** for state propagation. When the server broadcasts a `session:update` event, every connected workbench receives the identical payload simultaneously.

### useWorkbenchStream.ts: Reactive UI Subscription

UI components subscribe to state changes through [`packages/@openmaic/editor/src/lib/workbench/use-workbench-stream.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/packages/@openmaic/editor/src/lib/workbench/use-workbench-stream.ts). This React hook connects `owner-session-client` to React's rendering cycle, triggering re-renders when confirmed server state arrives.

```tsx
// WorkspaceRail.tsx – component participating in synchronization
import { useWorkbenchStream } from '@/lib/workbench/use-workbench-stream';
import { sessionStore } from '@/lib/workbench/session-store';

export const WorkspaceRail = () => {
  const { session } = useWorkbenchStream(); // receives live updates

  const toggleCourse = (courseId: string) => {
    sessionStore.updateCourseVisibility(
      courseId,
      !session.visibleCourses[courseId]
    );
  };

  return (
    <div className="rail">
      {Object.entries(session.visibleCourses).map(([id, visible]) => (
        <button key={id} onClick={() => toggleCourse(id)}>
          {visible ? 'Hide' : 'Show'} {id}
        </button>
      ))}
    </div>
  );
};

```

## The OpenMAIC Synchronization Flow in Practice

The complete data flow spans five distinct stages, with the server serving as the authoritative arbiter of all state changes.

### 1. Initial Session Hydration

When a workbench mounts, `useWorkbenchStream` calls `sessionStore.getCurrent()`. The server responds with a JSON payload containing the full view model: pane tree structure, active course identifier, scroll positions, and UI flags.

### 2. Optimistic Local Updates

User interactions trigger store methods like `updatePaneLayout`, `activateCourse`, or `saveScroll`. These functions:
- POST a delta to the server immediately
- Emit a local optimistic update for responsive UI feedback

### 3. Server-Side Merge and Persistence

The backend ([`packages/@openmaic/editor/src/server/routes/session.routes.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/packages/@openmaic/editor/src/server/routes/session.routes.ts)) receives the delta, merges it into the stored session using **last-write-wins** or custom de-duplication logic, and persists to the database.

### 4. Broadcast to All Clients

The server pushes the complete new session state to **every** connected client through the WebSocket channel, using the `ws` library's broadcast capability.

### 5. Synchronized Re-render

Each client's `owner-session-client` received the `session:update` event, forwards it to `sessionStore.setCurrent()`, and `useWorkbenchStream` propagates the change through React's context system. All workbench views update simultaneously.

## Conflict Resolution and Session Consistency

OpenMAIC's server-backed synchronization avoids divergent client states through a strict **server-authoritative** design. Clients never assume their local mutations are final; they always await the confirmed server payload before treating state as committed.

- **Last-write-wins** for simple scalar values
- **Custom merge logic** for complex structures like pane ID lists
- **Type-safe contracts** via TypeScript interfaces (`WorkbenchSession`, `PaneLayout`) prevent schema drift

## Summary

OpenMAIC achieves reliable workbench view synchronization in server-backed deployments through:

- **[`session-store.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/session-store.ts)** acting as the exclusive gateway for all state read/write operations
- **[`owner-session-client.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/owner-session-client.ts)** providing real-time bidirectional transport via WebSocket
- **[`useWorkbenchStream.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/useWorkbenchStream.ts)** enabling reactive UI updates across all connected components
- **Server-authoritative validation** ensuring every client receives identical, confirmed state
- **Persistent session storage** supporting cross-device and post-reload continuity

## Frequently Asked Questions

### How does OpenMAIC handle network interruptions during synchronization?

The [`owner-session-client.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/owner-session-client.ts) WebSocket implementation includes automatic reconnection with exponential backoff. Missed updates are recovered through a full session refetch when the connection re-establishes, ensuring clients resynchronize with the server state without manual intervention.

### Can OpenMAIC synchronize workbench views without WebSocket support?

Yes. The architecture falls back to **Server-Sent Events (SSE)** when WebSocket connections are blocked by proxies or firewalls. The [`owner-session-client.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/owner-session-client.ts) abstraction detects transport capability and degrades gracefully, though SSE only supports server-to-client streaming—client mutations still use HTTP POST.

### What data format does OpenMAIC use for session state?

All session payloads use **JSON** with TypeScript-defined interfaces. The core types include `WorkbenchSession` (root object), `PaneLayout` (hierarchical pane structure), and specialized delta formats for specific mutations like `CourseVisibilityDelta`. These contracts are shared between client and server packages.

### Where is the session state physically stored in a production deployment?

The server-side implementation in `packages/@openmaic/editor/src/server/routes/session.routes.ts` persists sessions to either **MongoDB** or **PostgreSQL** based on environment configuration. The database choice is abstracted behind a repository pattern, allowing operators to select storage that matches their infrastructure.