This commit is contained in:
Andras Schmelczer 2026-09-27 11:09:36 +01:00
commit ff1f474324

View file

@ -1,6 +1,6 @@
# Sync client (API v4)
The engine uses one serialized loop per vault and requires Web Crypto (SHA-256). Local notifications and WebSocket
The sync coordinator uses one serialized loop per vault and requires Web Crypto (SHA-256). Local notifications and WebSocket
`vaultChanged` hints wake it; complete disk scans and the durable server event log are the
sources of truth. Periodic reconciliation is opt-in: set `syncIntervalMs` to a
positive interval to repair missed notifications. Unset or `0` disables polling.
@ -20,6 +20,46 @@ resuming starts a fresh session. Connection checks can run while sync is disable
WebSocket shutdown retires callbacks immediately and drains message handlers
already running; it does not wait for the transport's close handshake.
## Code layout
Start with `src/sync-operations/syncer.ts` for the sync workflow: resolve the saved
upload, fetch the complete remote history, apply it, and prepare the next upload.
It coordinates the following components:
| Component | Responsibility |
| -------------------------------------- | --------------------------------------------------------------------------------------------- |
| `sync-scheduler.ts` | Serialize attempts, coalesce notifications, stop, and schedule polling or retries. |
| `local-change-queue.ts` | Persist create/delete/move notifications and invalidate plans when identities change. |
| `scan.ts` | Inventory disk files, infer identities, and commit the scan before consuming notifications. |
| `remote-changes.ts` | Fetch and validate every event page before returning the final remote state. |
| `content-sync.ts` | Fetch merge bases, plan content writes, encode uploads, and maintain the optional diff cache. |
| `upload-queue.ts` | Persist exact requests, handle acknowledgements, and retain rejected requests for retry. |
| `file-operations/` | Apply paths and content to disk, incorporating notifications between files. |
| `persistence/database.ts` | Define `StoredDatabase` and atomically commit or reload it. |
| `persistence/client-metadata-store.ts` | Serialize database, settings, checkpoint, and notification saves into the shared store. |
`requestSync()` schedules work; `synchronize()` runs an attempt; `refreshLocalFiles()`
flushes notifications and calls `scanLocalFiles()`. `initializeFromServerSnapshot()`
applies the initial server snapshot, while `applyRemoteChanges()` merges and applies
an observed remote state. These operations keep the same persisted database format.
The scheduler's `SyncPhase` enum makes its transitions explicit:
| Event | Transition |
| ----------------------------------- | ------------------------------------------------------------------- |
| Start or explicit request | Idle, waiting, or blocked → run requested |
| Begin a pass | Run requested → running |
| Notification during a pass | Running → run requested; repeated notifications coalesce |
| Successful completion | Running → idle; optionally schedule the polling timer |
| Transient failure | Running → waiting to retry |
| Authentication or permanent failure | Running → blocked until an explicit request |
| Stop | Running → stopping → stopped; idle timers are cancelled immediately |
From `frontend/`, run `npm run lint -w sync-client`, `npm run check -w sync-client`,
and `npm run test -w sync-client`. `npm run test:harness` covers interrupted saves,
file application, and recovery. Protocol types under `src/services/types/` are
generated from Rust and excluded from ESLint; handwritten TypeScript requires braces.
## State and policy
The persisted state contains the incorporated server file manifest, the current
@ -127,7 +167,7 @@ Power loss may lose bytes or rename identity and may cause a merge to run again.
Metadata is the one atomic boundary: persistence `save` replaces a complete value
and an interrupted/failed save leaves an old or new complete value, never a torn
object. A failed save is reloaded before retrying. No ordering between metadata and
user-file durability is required. Settings and engine saves are serialized.
user-file durability is required. Settings and database saves are serialized.
Keep metadata outside the user-file namespace or in `.vault-link-sync`, which is
excluded from scans. Changing a client's vault once it holds identities, offline
notifications, pending requests or an incorporated event cursor requires a separate state store.