**Overview & goals**Design lightweight REST/JSON APIs so mobile clients can bootstrap full state, then do incremental pulls using opaque change-tokens (cursors), upload large blobs resumably, let server compact change logs, and return conflict hints / partial-apply responses so the app can surface UX choices.**Key server concepts / bookkeeping**- ChangeLog table: ordered entries (id, stream_id, seq, op_type, resource_id, payload, tombstone, timestamp).- Stream state: stream_id, current_seq, compacted_until_seq.- Token = base64({ stream_id, seq, checksum, nonce, expiry }).- UploadSessions: session_id, user_id, blob_meta, offset_received, parts[], expires_at.- Conflict hints: per-change metadata { conflict: boolean, fields: [name], reason }.- Compaction: background job merges historical changes into snapshot entries, advances compacted_until_seq; clients with older tokens must bootstrap.**APIs & examples**1) BootstrapRequest:http
POST /sync/bootstrap
Authorization: Bearer <token>
Content-Type: application/json
{ "client_id":"ios-1", "last_known_seq": null }
Response:json
{ "stream_id":"s1","snapshot":{"items":[...]}, "token":"<token>", "server_time":"2026-03-01T..." }
2) Incremental pullRequest:http
GET /sync/poll?token=<token>&limit=200
Response (partial-apply/conflict hints included):json
{
"token":"<next_token>",
"changes":[
{"seq":125, "op":"update","id":"r1","payload":{...}, "conflict":{"conflict":true,"fields":["title"],"reason":"server-wins"}},
{"seq":126, "op":"delete","id":"r2"}
],
"more": true
}
- Client sends token; server validates. If token.seq < compacted_until_seq → return 412 Precondition Failed with recommended bootstrap endpoint + reason.3) Resumable uploads (large blobs)Start:http
POST /uploads
{ "filename":"video.mp4", "size": 104857600 }
Response:json
{ "upload_id":"u_abc", "chunk_size": 4_194_304, "offset":0, "upload_url":"/uploads/u_abc/append" }
Append chunks:http
PUT /uploads/u_abc/append
Headers: Content-Range: bytes 0-4194303/104857600
Body: <binary chunk>
Server responds:json
{ "upload_id":"u_abc","offset":4194304,"ok":true }
Finalize:http
POST /uploads/u_abc/complete
Server verifies checksum, writes blob, creates resource change entry in ChangeLog referencing upload_id.- Server supports re-checking offset and range; client retries from reported offset.**Compaction & token expiry**- Compaction reduces ChangeLog older than N days into snapshots; compacted_until_seq updated.- Tokens include expiry; server rejects expired tokens with 401 or 412 and points to /sync/bootstrap.**Conflict hints & partial-apply**- When applying client-sent deltas or server-side merges, response includes per-change status: - applied: true/false/partial - conflict: { fields, server_value, client_value, resolution_suggestion }- Example apply endpoint returns partial applied changes and new token.**Mobile client behavior (practical notes)**- Persist token securely; on network loss tolerate retries using token and resumable upload offsets.- On 412 due to compaction, show “sync required — may take longer” UI and call bootstrap.- Present conflict hints in UI: “Server changed title to X — keep server, keep mine, or merge.”**Trade-offs**- Opaque tokens simplify client; server must store stream and compaction metadata.- Compaction saves storage but forces occasional full syncs.- Resumable uploads use server state (UploadSessions); can also use S3 multipart with signed URLs to offload bandwidth.This design balances mobile reliability (resumable, small incremental pulls) with server efficiency (compaction, tokens) and provides enough metadata for good UX around conflicts.