Architecture
OpenJam follows a client-server architecture with WebSocket for real-time communication.
System Overview
┌──────────────────┐ HTTP + WS ┌──────────────────┐
│ │ ◄───────────────► │ │
│ React Frontend │ │ Go Backend │
│ (SPA) │ │ (Gin) │
│ │ │ │
└──────────────────┘ └────────┬─────────┘
│
┌───────────────────┼───────────────────┐
│ │ │
┌──────┴──────┐ ┌───────┴───────┐ ┌───────┴───────┐
│ PostgreSQL │ │ Redis │ │ MinIO │
│ (Primary) │ │ (Optional) │ │ (Optional) │
└─────────────┘ └───────────────┘ └───────────────┘Frontend Architecture
The frontend is a single-page React application built with Vite.
Key Components
- OpenJamCanvas - Main canvas component handling rendering, interaction, and tool management
- ElementStore - CRDT-based state management for canvas elements with vector clocks
- useCollaboration - React hook bridging ElementStore with WebSocket for real-time sync
- WebSocketClient - WebSocket connection with auto-reconnect and message queuing
Data Flow
- User action (e.g., add sticky note)
- ElementStore creates operation with vector clock
- useCollaboration intercepts and broadcasts via WebSocket
- Remote clients receive operation and apply to their ElementStore
- React re-renders affected components
Backend Architecture
The backend is structured following Go conventions with internal/ packages.
Package Structure
- config - Environment variable loading and validation
- db - PostgreSQL connection pool and Redis client
- handler - HTTP route handlers and WebSocket upgrade
- middleware - Authentication middleware
- model - Data models with database operations
- storage - MinIO/S3 file storage
- ws - WebSocket hub (room management) and client (message handling)
WebSocket Hub
The hub manages connected clients and rooms:
- Clients register/unregister on connect/disconnect
- Room-based message broadcasting (only clients in the same room)
- Redis pub/sub for multi-server broadcasting
- Automatic cleanup of disconnected clients
Database Schema
sql
-- Users with bcrypt password hashing
users (id UUID, email, password_hash, display_name, avatar_color, ...)
-- Session tokens with expiration
sessions (token VARCHAR(64), user_id, expires_at, ...)
-- Rooms (boards) with JSONB board data
rooms (id VARCHAR(36), name, owner_id, board_data JSONB, ...)
-- Operations for CRDT sync
operations (id BIGSERIAL, room_id, user_id, op_id, op_type, payload JSONB, vector_clock JSONB, ...)Multi-Server Scaling
With Redis enabled, OpenJam supports horizontal scaling:
- Each server instance connects to the shared Redis pub/sub channel
- When a client sends an operation, the server broadcasts locally AND publishes to Redis
- Other server instances receive the pub/sub message and broadcast to their local clients
- All server instances share the same PostgreSQL database
