TinySync Understand Integrate Decide FAQ Playground ↗
Understand · Chapter 2 of 5

Inside the Engine

The containers, components, and crates that make up the sync engine.

Container view

Each device runs several co-operating processes. The engine is separated from the OS integration layer, and a background daemon owns all mutable engine state.

C4 Level 2 — Containers (per device)
graph TB subgraph Device["Device (macOS or Windows)"] direction TB subgraph UX["Control Plane"] Tauri["🖥️ Tauri Desktop App\n(tray, status, vault management)"] CLI["⌨️ CLI\n(recovery, headless ops)"] end Daemon["⚙️ TinySync Daemon\n(per-user background service)\nOwns: SQLite · credentials · event stream"] subgraph Providers["OS Native Providers"] FPExt["🍎 File Provider Extension\n(macOS)"] CFProc["🪟 CFAPI Provider Process\n(Windows)"] end end subgraph Cloud["Cloud"] Server["🔄 TinySync Server"] PG["PostgreSQL"] Blobs["Blob Store"] Server --- PG Server --- Blobs end Tauri & CLI -- "JSON / IPC" --> Daemon FPExt -- "JSON over Unix socket" --> Daemon CFProc -- "JSON over named pipe" --> Daemon Daemon -- "HTTPS + WebSocket" --> Server
ContainerOwnsDoes not own
TinySync DaemonSQLite engine state, credentials, server polling, WebSocket, pending ops queue, retry, panic-resync, unified event streamOS placeholder state, shell callbacks, Finder/Explorer presentation
File Provider Ext (macOS)Placeholder registration, Finder enumeration, hydration callbacks, own-write suppressionCredentials, SQLite, server protocol, conflict policy
CFAPI Process (Windows)Sync root registration, placeholder operations, Explorer callbacks, byte-range hydrationSame as above
Tauri AppSetup UI, tray, status display, device management, process supervisionSync semantics — it is a control plane only
CLIRegister, attach, sync-once, status, devices, admin (groups/grants), panic-resyncOwns no state — reads from daemon or engine directly
Why a separate daemon?
The macOS File Provider extension has an OS-controlled lifecycle. The Tauri app can be quit by the user. Neither is the right owner of SQLite, credentials, or WebSocket connections. A per-user background daemon (macOS LaunchAgent, Windows user-mode service) gives sync one durable, crash-recoverable owner. Quitting the UI does not stop sync.

Engine components

The daemon contains tinysync-engine — the heart of the system. It is isolated behind two traits: one facing the cloud, one facing the OS.

C4 Level 3 — Engine Component View
graph TB subgraph Engine["tinysync-engine"] SE["SyncEngine<A>\nOrchestrates all sync logic"] LS["LocalStore (SQLite)\nItem state · pending ops · engine_state kv"] CC["CloudClient trait\n(HTTP abstraction)"] PA["PresentationAdapter trait\n(OS abstraction)"] SE --> LS SE --> CC SE --> PA end subgraph Adapters["Adapter Implementations"] FSA["FolderWatcherAdapter\n(prototype, eager)"] FPA["FileProviderAdapter\n(macOS, lazy)"] CFA["CFAPIAdapter\n(Windows, lazy)"] MockA["MockAdapter (tests)"] end subgraph CloudImpls["CloudClient Implementations"] HC["HostedCloudClient (HTTPS)"] TestC["TestCloudClient (tests)"] end PA -.-> FSA & FPA & CFA & MockA CC -.-> HC & TestC

The Two Trait Boundaries

CloudClient — server-facing

trait CloudClient {
  get_snapshot(vault_id) → Snapshot
  get_log(vault_id, after: Seq)
    → LogPage
  upload_blob(vault_id, hash, bytes)
  download_blob(vault_id, hash)→Bytes
  submit_mutation(vault_id, req)
    → MutationResponse
}

The engine has no HTTP dependency. Swapping in a test double is trivial — and that's how all engine tests run.

PresentationAdapter — OS-facing

trait PresentationAdapter {
  hydration_strategy()→Eager|Lazy
  apply_create(item, content)
  apply_update(item, content)
  apply_delete(item)
  apply_move_rename(from, to)
  enumerate_local()→Vec<LocalEntry>
  read_local_content(path, hash)→Bytes
  preserve_conflict_copy(path,
    op_id, device_id)→Option<VPath>
}

The engine knows nothing about notify events, File Provider callbacks, or CFAPI requests. It speaks in sync intent.

The crate map

CrateLayerContents
tinysync-coreSharedAll protocol DTOs, ID types, content hashing, path/name validation, Unicode normalization.
tinysync-serverServerAxum HTTP API, Postgres queries (sqlx), blob store trait + FS impl, WebSocket wake hub (in-process + Postgres LISTEN/NOTIFY), token auth.
tinysync-engineClientSyncEngine, LocalStore (SQLite), PresentationAdapter + CloudClient traits, all sync state machine logic, conflict detection, pending ops queue, panic-resync.
tinysync-adapter-fsClientPrototype eager adapter: filesystem watcher (notify), file materialization, content hashing, own-write suppression, inode-based rename detection.
tinysync-provider-macos-ffiClientThin Swift-Rust FFI shim for macOS File Provider callback bridge.
tinysync-provider-windowsClientWindows CFAPI sync root registration, placeholder management, hydration callback handler.
tinysync-provider-ipcSharedLength-prefixed JSON IPC protocol (4-byte big-endian + JSON body, max 1 MiB/frame) between native providers and daemon.
tinysync-daemonClientPer-user background daemon: IPC server, engine orchestration, multi-vault routing, client event stream, wake subscription.
tinysync-clientClientHTTP implementation of CloudClient, device identity storage, sync loop primitives.
tinysync-desktopUITauri shell, tray, daemon lifecycle management, settings UI.
tinysync-cliUICLI: register, attach, sync-once, status, devices, admin (groups/grants), panic-resync.

Engine lifecycle

The engine has five states, persisted in the engine_state SQLite table. Transitions are explicit and logged.

Engine Lifecycle State Machine
stateDiagram-v2 [*] --> Bootstrapping : process start Bootstrapping --> OnlineIdle : snapshot + log complete OnlineIdle --> OnlineSyncing : changes_pending OnlineSyncing --> OnlineIdle : queue_drained OnlineIdle --> Offline : connection_lost OnlineSyncing --> Offline : connection_lost Offline --> OnlineSyncing : reconnect OnlineIdle --> PanicResync : integrity_violation OnlineSyncing --> PanicResync : integrity_violation Offline --> PanicResync : local_state_corrupt PanicResync --> Bootstrapping : local_index_rebuilt
StateDescription
BootstrappingFetches server snapshot, enumerates local adapter state, reconciles, replays log events after snapshot seq. Also the entry state after panic-resync.
OnlineIdleFully caught up. Listening on WebSocket for wake notifications.
OnlineSyncingActively processing: replaying remote log, hashing local changes, uploading blobs, submitting mutations, satisfying hydration requests.
OfflineServer unreachable. Local changes queue in pending_ops. Remote changes fetched on reconnect via GET /log?after=last_seq_processed.
PanicResyncLocal state suspected corrupt. Engine halts. Recovery: fetch fresh snapshot, hash local files, preserve divergent content as conflict copies, rebuild SQLite from scratch. Re-runnable if interrupted.