Server
Workspace Sync Module File sync, blob storage, and SSE change feed
The Workspace Sync module provides cloud storage for all content files (profiles, onboarding data, memory, artifacts) and a real-time SSE change feed. Source: packages/server/src/modules/workspace-sync/.
All endpoints are prefixed with /api/v1/sync and require ClerkAuthGuard.
Method Path Description PUT /sync/profileStore user profile JSON GET /sync/profileRetrieve user profile JSON PUT /sync/org-onboardingStore org onboarding JSON GET /sync/org-onboardingRetrieve org onboarding JSON
Method Path Description PUT /sync/memory/:scopeStore memory (scope: user or org) GET /sync/memory/:scopeRetrieve memory PUT /sync/memory/workspace/:workspaceId/:fileNameStore workspace memory file GET /sync/memory/workspace/:workspaceId/:fileNameGet workspace memory file GET /sync/memory/workspace/:workspaceIdList workspace memory files
Method Path Description POST /sync/filesUpload a file GET /sync/files/:idDownload file content GET /sync/filesList files (with filters) DELETE /sync/files/:idDelete a file PUT /sync/files/:idUpdate file content
Query params for GET /sync/files:
Param Description kindStorageKind filterscopeSHARED or PERSONALworkspaceIdFilter by workspace prefixKey prefix filter
Method Path Description GET /sync/manifestGet metadata index for the whole org GET /sync/changesSSE change feed
All blobs are stored in R2/S3 under an org-scoped path:
/{orgId}/shared/hub-memory/ ← Org-level AI memory
/{orgId}/shared/brand/ ← Logos, icons
/{orgId}/shared/templates/ ← Shared templates
/{orgId}/teams/{teamId}/memory/ ← Team memory files
/{orgId}/workspaces/{workspaceId}/ ← Workspace artifacts + memory
/{orgId}/personal/{userId}/ ← Personal files + user memory
Method Description putUserProfile(orgId, userId, data)Write profile.json to personal scope getUserProfile(orgId, userId)Read profile.json; returns parsed object or null putOrgOnboarding(orgId, data, userId)Write org.json to shared scope getOrgOnboarding(orgId)Read org.json; returns parsed object or null
Method Description putMemory(orgId, userId, scope, content)Store user-memory.md or org-memory.md getMemory(orgId, userId, scope)Retrieve memory file content putWorkspaceMemory(orgId, workspaceId, fileName, content, userId)Store named file in workspace memory dir getWorkspaceMemory(orgId, workspaceId, fileName)Retrieve workspace memory file mergeWorkspaceMemory(orgId, workspaceId, fileName, content, userId, expectedVersion)Merge with optimistic version check; throws 409 on conflict listWorkspaceMemory(orgId, workspaceId)List all memory files in workspace
Method Description putFile(orgId, blobPath, buffer, mimeType, metadata)Upload blob + create StorageObject metadata record getFile(orgId, id)Returns { body: Buffer, metadata: StorageObject } deleteFile(orgId, id, userId)Remove blob + delete metadata record listFiles(orgId, filters)Query StorageObject table with optional filters updateFile(orgId, id, buffer, userId)Replace blob + bump version + update checksum
getManifest(orgId) returns an array of all StorageObject metadata records for the org. Used by the desktop client to detect stale local files without downloading them.
GET /api/v1/sync/changes opens a Server-Sent Events connection.
Server sends a heartbeat comment every 30 seconds to keep the connection alive through proxies.
When any file changes (via putFile, deleteFile, updateFile, putMemory, etc.), the service calls notifyChange(orgId, event).
All connected clients for that orgId receive the event.
Change event structure:
{
type : 'file-changed' | 'file-deleted' ;
storageKey : string; // R2/S3 key
path : string; // Logical path
checksum ?: string; // SHA-256 (on change)
version ?: number; // New version number
changedBy ?: string; // userId who made the change
timestamp : string; // ISO timestamp
}
Kind Description ARTIFACTAI-generated documents, reports, dashboards HUB_MEMORYOrg-level AI memory TEAM_MEMORYTeam-scoped AI memory WORKSPACE_MEMORYWorkspace-scoped AI memory TEMPLATEReusable templates BRAND_ASSETLogos, icons, brand images
Scope Description SHAREDVisible to all team members PERSONALVisible only to the owning user
Wraps the R2/S3 SDK with a consistent interface:
Method Description getBlob(key)Download a blob by key putBlob(key, body, metadata)Upload a blob deleteBlob(key)Delete a blob listBlobs(prefix)List blobs under a prefix headBlob(key)Get blob metadata without downloading