Skip to main content

Events

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

1. Envelope

Standard. retentionClass = operational (30 days hot; no long-term archive for sync operational events).

2. Events Published

2.1 sync.mutation.applied.v1

interface MutationAppliedV1 { clientMutationId: string; tenantId: TenantId; userId: UserId; deviceId: DeviceId; service: string; entityType: string; entityId: string; op: string; appliedAt: ISODate; }

2.2 sync.mutation.conflicted.v1

interface MutationConflictedV1 { clientMutationId: string; conflictId: ULID; tenantId: TenantId; userId: UserId; entityType: string; entityId: string; conflictPolicy: string; baseVersion: number; serverVersion: number; conflictedAt: ISODate; }

2.3 sync.mutation.rejected.v1

interface MutationRejectedV1 { clientMutationId: string; tenantId: TenantId; userId: UserId; entityType: string; entityId: string; reason: string; rejectedAt: ISODate; }

2.4 sync.delta.projected.v1

interface DeltaProjectedV1 { scope: string; tenantId: TenantId; newLamport: number; entityCount: number; projectedAt: ISODate; }

2.5 sync.conflict.resolved.v1

interface ConflictResolvedV1 { conflictId: ULID; tenantId: TenantId; userId: UserId; entityType: string; resolution: string; resolvedBy?: UserId; resolvedAt: ISODate; }

2.6 sync.push.applied.v1 (outbox)

Emitted alongside sync.mutation.applied.v1 on successful push apply (backlog / analytics alias; see SyncEvents.PUSH_APPLIED in implementation).

interface PushAppliedV1 { clientMutationId: string; tenantId: TenantId; userId: UserId; deviceId: DeviceId; appliedAt: ISODate; }

2.7 sync.pull.completed.v1

One emission per completed pull projection (EP-20 / US-100).

interface PullCompletedV1 { tenantId: TenantId; userId: UserId; deviceId: DeviceId; scope: string; lamport: number; deltaCount: number; completedAt: ISODate; }

2.8 sync.device.wiped.v1

Remote wipe recorded; consumer devices must evict and stop sync until re-bound.

interface DeviceWipedV1 { tenantId: TenantId; userId: UserId; deviceId: DeviceId; wipeRequestId: ULID; wipedAt: ISODate; }

2.9 content.play_package.bundle.revoked.v1

Published by content flows; sync-service re-emits on admin revoke path when recording revoked_bundles for pull (EP-17 / EP-20 alignment).

interface BundleRevokedV1 { tenantId: TenantId; bundleId: string; licenseId: string; revokedAt: ISODate; reason?: string; }

2.10 identity.device.removed.v1

Emitted when a device row is removed after wipe (may originate from sync-service outbox in coordinated wipe flow; identity-service remains owner of identity events).

interface DeviceRemovedV1 { tenantId: TenantId; userId: UserId; deviceId: DeviceId; removedAt: ISODate; }

2.11 sync.full_resync.initiated.v1

interface FullResyncInitiatedV1 { tenantId: TenantId; userId: UserId; deviceId: DeviceId; scope: string; reason: 'cursor_out_of_range' | 'admin_force' | 'device_rebound'; initiatedAt: ISODate; }

3. Events Consumed (ALL replicable domain events)

  • enrollment.created.v1, .completed.v1, .revoked.v1 — project enrollment deltas.
  • progress.statement.stored.v1, .attempt.closed.v1, .completion.recorded.v1 — project progress deltas.
  • certification.certificate.issued.v1, .revoked.v1 — project cert deltas.
  • content.play_package.bundle.published.v1, .revoked.v1 — project bundle deltas.
  • marketplace.license.granted.v1, .revoked.v1 — project license deltas.
  • assignment.window.opened.v1, .completed.v1, .overdue.v1 — project assignment deltas.
  • authoring.course_draft.updated.v1 (M5 — offline authoring Yjs deltas).
  • identity.device.bound_for_offline.v1 — register device for sync.
  • gdpr.subject_request.received.v1 — erase all sync data for user.
  • notification.sent.v1 — project in-app notification deltas.

4. Versioning

v1 additive. New entity types additive.

5. Idempotency

  • clientMutationId PK dedups.
  • Delta projections idempotent on (scope, lamport).

6. Outbox / Inbox

Standard.

7. Partition Key

  • (tenantId, userId) for per-user ordering.

8. DLQ

sync.dlq — alerts on non-empty.