How OpenMAIC Synchronizes Workbench Views in Server-Backed Deployments
OpenMAIC synchronizes workbench views through a three-layer architecture: a server-side session-store.ts wrapper that persists state via HTTP, a WebSocket-based owner-session-client.ts that broadcasts changes in real time, and the 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. 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
// 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 file manages the WebSocket connection (or Server-Sent Events fallback). It bridges the network layer with the local session store:
// 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. This React hook connects owner-session-client to React's rendering cycle, triggering re-renders when confirmed server state arrives.
// 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) 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.tsacting as the exclusive gateway for all state read/write operationsowner-session-client.tsproviding real-time bidirectional transport via WebSocketuseWorkbenchStream.tsenabling 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 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 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →