Skip to content

Feature Design

Proposed multi-device sync architecture using op logs.

This document is about future sync architecture only.

Current local architecture:

  • the local daemon is a Go background process
  • local clients talk to it over a Unix socket
  • this document does not change that local IPC model

Future sync is a separate concern layered on top of the local daemon.

Crona is local-first with a SQLite database per device. Multiple devices create divergent state. Direct DB sync isn’t viable because SQLite files are not merge-friendly, and there is no natural conflict resolution at the row level.

Every mutation in Crona already produces an immutable Op record:

{ id, user_id, device_id, entity, entity_id, action, payload, timestamp }

The op log is append-only, ordered by timestamp, and tagged by device. Syncing Crona across devices reduces to sharing the op log, replaying it on each device, and deriving consistent state.

The model is structurally similar to how distributed databases, CRDTs, and event-sourced systems handle sync. No central authority is required.

Three sync modes, each building on the previous:

Export the op log as an append-only NDJSON file to a shared folder watched by any cloud sync provider:

~/<cloud-provider>/crona-sync/
ops-<device-id>.ndjson
ops-<device-id-2>.ndjson

On startup and periodically, each device:

  1. reads all ops-*.ndjson files from other devices
  2. filters ops it has not seen yet
  3. replays them against the local DB in timestamp order
  4. appends its own new ops to ops-<its-device-id>.ndjson

Works with iCloud Drive, Dropbox, Google Drive, Syncthing, or any folder-sync tool.

A lightweight relay server that acts as a dumb op store:

POST /ops
GET /ops?since=

Devices push their ops on mutation and pull on startup or reconnect.

When devices are on the same network, sync directly without a relay:

  • kernel advertises itself via mDNS
  • devices discover peers
  • peers exchange ops directly

Since ops are immutable and timestamped, conflicts are resolved deterministically:

Entity Strategy Rationale
Repo / Stream / Issue fields Last-write-wins by timestamp Simple fields, low conflict risk
Issue status transitions Ordered by timestamp State-machine style updates
Active context Per device, never merged Context is device-local
Sessions Device-scoped, no merge needed Sessions are owned by one device
Deletes Soft-delete wins deleted_at is terminal
  • active context
  • timer state
  • auth tokens
  • Op replay must be idempotent.
  • Minor clock skew is acceptable for last-write-wins.
  • The local DB remains derivable from the op log.
  • Every mutation must produce an op.