Sync Contract
:::info Source
Sourced from services/certification-service/SYNC_CONTRACT.md in the documentation repo.
:::
1. Applicability
Certificates are read-replicable offline (learner sees earned certs). OfflineIssuanceClaim is push-only (client creates → server verifies).
2. Registration
{ service: 'certification', entityType: 'Certificate', conflictPolicy: 'server_authoritative', deltaProjector: 'certs_by_user', pushHandler: 'none', versionField: 'issuedAt', schemaRef: 'schemas://certification/certificate/v1' }
{ service: 'certification', entityType: 'OfflineIssuanceClaim', conflictPolicy: 'append_only', deltaProjector: 'claims_by_user', pushHandler: 'SubmitClaim', versionField: 'claimedAt', schemaRef: 'schemas://certification/offline_claim/v1' }
3. Delta Format
{
"cursor": { "lamport": 12, "scope": "certification:user:u_01H..." },
"upserts": { "Certificate": [ ... ] },
"deletes": { "Certificate": [ /* revoked → still present with state='revoked' */ ] }
}
4. Conflict Resolution
- Certificate:
server_authoritative. Client never edits. - OfflineIssuanceClaim:
append_only. Each claim has unique ULID.
5. Cursor
Scope certification:user:{userId}. Advances on issued/revoked.
6. LocalStore
certificates: (id PK, userId, courseId, state, issuedAt, verificationToken, artifactsCache)
offline_claims_outbox: (id PK, clientMutationId, payload, createdAt, pushedAt?)
Artifacts (PDF) cached for offline viewing. Downloaded via signed URLs when online.
7. Offline Issuance Flow
Learner completes offline via PlayPackage:
1. Local player verifies completion criteria.
2. Local signature over (attemptId, completedAt, score) using bundle key.
3. Certificate draft rendered locally from cached template (not verifiable yet).
4. OfflineIssuanceClaim queued.
5. On reconnect: claim pushed.
6. Server verifies local signature against bundle key; verifies completion via progress-service.
7. If valid: issue Certificate with evidence including local signature.
8. Sync back to device; local preview replaced with verified certificate.
9. If invalid: reject; learner sees reason (rare — usually clock skew or tamper).
8. Security
- OfflineIssuanceClaim signature verified server-side against bundle key.
- Claim rejected if attempt not found in progress-service or completion time suspicious.
- No race on duplicate claims:
(tenant_id, enrollment_id, course_version_id)unique constraint on resulting Certificate.
9. Background Sync
- On reconnect, claims pushed first (critical path for learners).
- New certificates pulled in batch per user.