S
Store

Sync Engine

How Store replicates data across devices using a queue-based sync engine with pluggable conflict resolution.

Architecture

The sync engine sits between your local adapter and one or more remote targets. Every write operation produces an operation-log entry. Manual profiles flush that queue on demand; continuous profiles can be wired to background workers.

App → Store → Local Adapter → Sync Queue → Remote(s)

                          Pull + Merge

Configuring sync

Sync is configured in the active profile through remote targets, routes, and sync policy.

import { createStore } from "@cuitty/store/client";

const store = await createStore({
  module: "tasks",
  profile: {
    id: "team",
    name: "Team",
    local: { adapter: "local.sqlite", path: ".cuitty/store/tasks.sqlite" },
    remotes: [{ id: "team-postgres", adapter: "postgres.record", dsnRef: "secret://pg" }],
    routes: [{ namespace: "tasks", class: "record", localAdapter: "local.sqlite", remoteAdapters: ["team-postgres"] }],
    encryption: { atRest: "preferred", remote: "required", p2p: "disabled" },
    sync: { mode: "manual", flushEveryMs: 5000 },
  },
  schema: { tasks: { class: "record" } },
});

Conflict strategies

When two devices modify the same record before syncing, a conflict can occur. Current public client APIs expose the local operation log and sync controller; conflict policy is encoded by the sync service and profile route implementation.

Last-write-wins

The write with the most recent timestamp takes precedence. This is simple and suitable for most cases.

await store.record("tasks").put("task:1", { title: "Ship", updatedAt: Date.now() });
await store.sync.flush();

Custom merge function

Custom merge belongs in the application or sync service. Model the merged record explicitly and write it back with an idempotency key.

await store.record("tasks").put("task:1", {
  title: "Ship",
  tags: ["client", "server"],
}, {
  idempotencyKey: "merge:task:1",
});

Manual resolution

Manual resolution is represented as a normal write with application-selected content.

await store.record("tasks").put("task:1", selectedResolution, {
  idempotencyKey: "resolve:task:1",
});

Sync queue and planner

The sync planner optimizes network usage by batching writes, deduplicating consecutive updates to the same key, and compressing payloads. If the same record is updated five times while offline, only the final state is transmitted during sync.

The queue persists to disk so pending operations survive process restarts.