/** * SyncServer singleton — shared across server/index.ts and modules. * * Participant mode: server maintains its own Automerge docs. * On any doc change, debounced-save to disk via doc-persistence. * * When a doc belongs to an encrypted space (meta.encrypted === true), * the save is encrypted at rest using the space's encryptionKeyId. * * Relay mode: for encrypted spaces, the server stores opaque blobs * it cannot decrypt, enabling cross-device restore. */ import { SyncServer } from "./local-first/sync-server"; import { saveDoc, saveEncryptedBlob, loadEncryptedBlob } from "./local-first/doc-persistence"; import { getDocumentData } from "./community-store"; /** * Look up the encryption key ID for a doc's space. * DocIds are formatted as "spaceSlug:module:collection[:itemId]". * Returns the encryptionKeyId if the space has encryption enabled, else undefined. */ function getEncryptionKeyId(docId: string): string | undefined { const spaceSlug = docId.split(":")[0]; if (!spaceSlug || spaceSlug === "global") return undefined; const data = getDocumentData(spaceSlug); if (data?.meta?.encrypted && data.meta.encryptionKeyId) { return data.meta.encryptionKeyId; } return undefined; } export const syncServer = new SyncServer({ participantMode: true, onDocChange: (docId, doc) => { const encryptionKeyId = getEncryptionKeyId(docId); saveDoc(docId, doc, encryptionKeyId); }, onRelayBackup: (docId, blob) => { saveEncryptedBlob(docId, blob); }, onRelayLoad: (docId) => { return loadEncryptedBlob(docId); }, });