Real-Time Collaboration¶
IFClite supports real-time collaborative BIM editing. The @ifc-lite/collab package puts the model in a CRDT (a Yjs document mirroring the IFCX data model), so any number of peers can edit concurrently and converge without a central lock, online or offline. @ifc-lite/collab-server is the reference websocket sync server.
Collaboration is IFCX/IFC5-canonical: the CRDT schema mirrors the IFCX wire shape (path-keyed nodes, flat namespaced attributes, layer composition), so seeding from and snapshotting to IFCX are lossless, and every peer's edits form a composable IFCX layer. STEP-flavored IFC (IFC2X3/IFC4) is converted through IFCX to participate.
Quick Start¶
import { createCollabSession, setAttribute } from '@ifc-lite/collab';
const session = await createCollabSession({
roomId: 'project-abc/model.ifcx',
user: { id: 'louis', name: 'Louis', color: '#5b8def' },
provider: 'indexeddb+websocket', // 'memory' (default) | 'indexeddb' | 'websocket' | 'indexeddb+websocket'
serverUrl: 'ws://localhost:1234', // required for websocket providers
});
// Wait for persistence + websocket sync to complete
await session.whenSynced;
// Seed the shared document from an IFCX file (string, buffer, or parsed
// IfcxFile). ifcxContent is caller-provided, e.g.
// await fetch('model.ifcx').then(r => r.text())
session.seed(ifcxContent);
// Capture a baseline state vector now if you later want to extract just
// this peer's edits as a layer (see extractUserLayer below).
const baseline = session.captureBaseline();
// Make an edit; all peers see it in real time
session.transact(() => {
setAttribute(session.doc, 'wall-uuid', 'bsi::ifc::v5a::Pset_WallCommon::FireRating', 'EI60');
});
// Live presence: selections and cursors of every peer
session.presence.setSelection(['wall-uuid']);
session.presence.onUpdate((peers) => {
// render avatars / cursors / selection outlines
});
// Undo is scoped to your own edits only
session.undo();
session.redo();
// React to concurrent-edit conflicts (last-writer-wins has already resolved them)
const off = session.onConflict((conflict) => {
console.log('conflict on', conflict);
});
// Snapshot the whole document back to IFCX
const file = session.snapshot();
// Or extract just this peer's edits as an IFCX layer. The baseline is the
// state vector captured with session.captureBaseline() before the edits.
const myLayer = session.extractUserLayer(baseline);
session.dispose();
Key points:
createCollabSessionis async and returns aCollabSessionsession.whenSyncedis a promise property, not a methodsession.presencecarries live cursors and selections between peerstokenin the options is forwarded to the server as a bearer token for authentication
Running the Sync Server¶
The reference server is a plain Node websocket server with file persistence:
Configuration is via environment variables:
| Variable | Default | Description |
|---|---|---|
COLLAB_PORT |
1234 |
Listen port |
COLLAB_HOST |
0.0.0.0 |
Bind address |
COLLAB_DATA_DIR |
./.collab-data |
Room persistence directory |
COLLAB_MAX_ROOMS |
1024 |
Maximum loaded rooms |
COLLAB_METRICS_TOKEN |
unset | Token protecting the /metrics endpoint |
The server also exposes /healthz, Prometheus /metrics, and a content-addressed blob store under /blobs/.
For programmatic use, import startCollabServer(opts) from @ifc-lite/collab-server. Pluggable persistence backends include FilePersistence, MemoryPersistence, S3Persistence, and RedisPersistence. For production hardening (TLS, JWT authentication, per-role rate limits, path locks, JSONL audit logging), use startSecureCollabServer - see Testing collab end-to-end for a full example.
Beyond Basic Sync¶
These ship in @ifc-lite/collab today (mention-level; see the package README for details):
- Branching and history -
forkSessionforks a session into a branch,mergeBranchmerges it back (op-based or layer-based strategies), andbuildBranchTreereconstructs the branch graph from a history sidecar (MemoryHistorySidecarorAutomergeHistorySidecar, attached withattachHistorySidecar) - Federation -
createFederationSessioncomposes one session per model plus a shared federation document for cross-model relationships - End-to-end encryption - AES-GCM-256 per-room keys:
generateRoomKey/deriveRoomKey,encryptFrame/decryptFrame, andcreateKeyRingfor rotation - Privacy/GDPR -
exportAndLeaveandredactAuthorMeta
Try It: Two Runnable Demos¶
From the repository root:
Builds the packages, starts the sync server on ws://localhost:1234 and a demo app on http://localhost:5174. Open it in two browser tabs: live peer cursors, shared selection, "Add wall" entity edits mirrored instantly, forced conflicts with keep-mine/accept-theirs, per-tab undo, and IFCX history snapshots.
Same server, but serves the Three.js variant (examples/threejs-collab) on http://localhost:5175: every entity is a real 3D box whose position, size, rotation, and color are CRDT attributes. Drag a wall in tab A and it slides in tab B.
Conflict Semantics¶
Concurrent edits converge automatically. Attribute writes resolve last-writer-wins by Lamport clock; the conflict detector then surfaces what happened through session.onConflict so the UI can offer keep-mine/accept-theirs. Geometry replacement keeps both candidates for explicit resolution, and delete-versus-edit prompts a restore. The full semantics table is in the package README.
Testing¶
For everything from unit suites to latency simulation, curl smoke tests, audit-log inspection, and the determinism harness, see the dedicated guide: Testing @ifc-lite/collab end-to-end.
Status¶
The collab stack is early (0.x). APIs are functional and tested, but expect breaking changes between minor versions.