Skip to main content

Sync Contract

:::info Source Sourced from services/sync-service/SYNC_CONTRACT.md in the documentation repo. :::

1. Self-Reference

This IS the sync service. This document describes the platform-wide sync protocol that all replicable services conform to.

2. Protocol Summary (Frozen F07)

EndpointMethodPurpose
/sync/v1/pullPOSTServer → client delta since cursor
/sync/v1/pushPOSTClient → server mutations
/sync/v1/ackPOSTClient acknowledges applied deltas
/sync/v1/resolve-conflictPOSTResolve detected conflict
/sync/v1/registrationsGETList replicable entity types
/sync/v1/statusGETPer-device sync health

3. Registered Entity Types (at platform launch)

ServiceEntityPolicySync Direction
progressStatementappend_onlypush + pull
progressAttemptserver_authoritativepull
progressCompletionRecordserver_authoritativepull
assessmentQuizBankserver_authoritativepull
assessmentBranchingScenarioserver_authoritativepull
assessmentAttemptResultappend_onlypush + pull
certificationCertificateserver_authoritativepull
certificationOfflineIssuanceClaimappend_onlypush
enrollmentEnrollmentserver_authoritativepull
contentPlayPackageBundleserver_authoritativepull
marketplaceListingserver_authoritativepull
marketplaceLicenseserver_authoritativepull
marketplaceOrderserver_authoritativepull
notificationNotification (in-app)append_onlypull
authoringCourseDraft (M5)crdt_yjspush + pull
identityDeviceserver_authoritativepull
tenantRole + Permissionserver_authoritativepull
ai_gatewayPrompt (offline subset)server_authoritativepull
billingInvoice + Subscription (view-only)server_authoritativepull

4. Client-Side Responsibilities

  • Maintain statements_outbox, mutations queue in IndexedDB/SQLite.
  • Push on: app foreground, reconnect, 15-min timer.
  • Pull after push completes.
  • Ack after applying pulled deltas.
  • Display pending conflicts in UI for manual resolution.
  • Encrypt sensitive payloads in local store.
  • 7-day offline max before read-only mode + forced sync prompt.
  • 14-day max before local data purge.

5. Cursor Rules

  • Opaque from client perspective.
  • Server-generated; client stores + returns on next pull.
  • Cursor includes: { lamport, v (format version) }.
  • Cursor format change via v field — server produces compatible cursors.

6. Delta Window

  • Server retains 30 days of deltas.
  • Cursor older than 30 days → sync.cursor.out_of_range (410) → client must full-resync.
  • Full resync: client purges local entities + re-pulls entire scope.

7. VectorClock Semantics (F06)

  • Each device tracks its own counter.
  • On push: client sends its VectorClock.
  • Server merges: max(server[d], client[d]) per device.
  • LWW comparison: total order via VectorClock comparison (lexicographic on max(values) tie-broken by deviceId).

8. Conflict Resolution UX

  • append_only: never conflicts.
  • server_authoritative: auto-resolved (server wins); client notified.
  • lww: auto-resolved; most recent writer wins.
  • crdt_yjs: Yjs auto-merges; on rare merge failure → manual conflict UI with side-by-side diff.
  • AI-assisted merge suggestion (M5): AI proposes merge; author accepts/rejects.

9. Background Sync Behavior

  1. App foreground → push pending mutations.
  2. Push completes → pull deltas (all scopes).
  3. Pull completes → ack cursors.
  4. Prioritize: revocations > license changes > new content > statements.
  5. Low-bandwidth mode: pull metadata first; defer large payloads.
  6. Online steady-state: push/pull every 15 min.
  7. Connectivity change: immediate sync.

10. Tamper Resistance

  • Mutations include HMAC (per device key) for integrity.
  • Server verifies HMAC; tamper → reject + content.bundle.tamper_detected.v1.
  • Client-side IndexedDB/SQLite encrypted with per-tenant key.

11. Multi-Device

  • Each device has independent cursor per scope.
  • Deltas scoped per (tenant, user) — all devices for a user see same deltas.
  • Device-specific deltas (e.g., bundle for specific device): scope includes deviceId.