Skip to content

Docs Resources

Repository layout

The monorepo has three product layers: the Rust kernel, the language bridges, and the app clients. Architecture covers what those layers do while an application runs.

Every workspace is version 0.2.6-alpha.3. Apps install one npm package, kizunasync, and packages/kizunasync is that published package. Six npm packages exist in total: kizunasync and the five per-platform @kizunasync/<platform> packages that npm installs as its optional dependencies. scripts/prepare-npm-release.ts assembles kizunasync from the private workspaces core, supabase, web, react, vue, expo, and rn-uniffi, which apps reach only as kizunasync subpaths. @kizunasync/utilities, @kizunasync/ui, @kizunasync/protocol, and @kizunasync/supabase-pack stay private too, and Rust crates carry publish = false. Project status records the state surface by surface.

Three layers#

LayerWhat it isWhere it lives
KernelSyncEngine: apply, pull, push, query, outbox, attachmentscrates/kizunasync-engine
BridgeJSON plus a handle over the kernelcrates/kizunasync-ffi (UniFFI), crates/kizunasync-napi (N-API), and crates/kizunasync-wasm (browser worker)
App clientTyped API in the app languagecreateKizunaSync in kizunasync; KizunaSyncClient in Swift and Kotlin

Swift and Kotlin are FFI hosts rather than kernel hosts. KizunaSyncClient wraps the generated UniFFI KizunaSyncEngine, serializes KizunaSyncClientConfig to JSON, and hops off the calling thread. It is the native counterpart of createKizunaSync, not of SyncEngine.

selectEngine runs inside the app client createKizunaSync returns, on its first engine call, and it exists in JavaScript alone. It chooses which artifact reaches the one kernel: a transport the driver already carries, a React Native UniFFI handle that the host injects or the driver's loadNativeEngine loads, or the N-API addon. The browser is the first case. kizunasync/web hands a transport to the engine in its worker, so nothing loads on the page and there is no second implementation to weigh. Swift and Kotlin never reach the choice, because their bridge is fixed when the package is linked. Engine selection lists the conditions, and no environment variable changes them.

A bridge is a language crossing in front of the kernel, so a Rust program needs none: it already stands on the kernel's side of that crossing. kizunasync-engine is embedder SPI, the interface for code that hosts the engine rather than for an application, and kizunasync is the SQL-pack CLI. Neither is a Rust KizunaSyncClient, and no public Rust app client exists.

flowchart TB
  Kernel["kizunasync-engine kernel"]
  Ffi["kizunasync-ffi UniFFI bridge"]
  Napi["kizunasync-napi bridge"]
  Wasm["kizunasync-wasm worker engine"]
  Swift["Swift KizunaSyncClient"]
  Kotlin["Kotlin KizunaSyncClient"]
  JS["JS createKizunaSync"]
  Select["selectEngine JS only"]
  RN["rn-uniffi"]

  Swift --> Ffi
  Kotlin --> Ffi
  Ffi --> Kernel
  Napi --> Kernel
  Wasm --> Kernel
  JS --> Select
  Select -->|"browser driver transport"| Wasm
  Select -->|"Node or Bun"| Napi
  Select -->|"RN through the Expo driver loader"| RN
  RN --> Ffi
  Select -->|"nothing loadable"| Fail["ENGINE_UNAVAILABLE on the first engine call"]

The kernel owns the local SQLite store through rusqlite on every runtime. Swift and Kotlin are generated UniFFI packages. In those packages rusqlite and HTTP run in Rust behind database_path and the http feature, and the host keeps the JWT and the typed app client. On the JavaScript N-API path the same rusqlite store opens inside the compiled addon. HTTP is the exception there, because Rust calls back into the host remote so supabase-js can attach the session. The JavaScript locator reports databasePath so the kernel opens the same file the app named. Drivers and the TCK documents what an implementation of each port owes the engine.

One kernel answers every app client, and the native clients are peers of the JavaScript one rather than descendants of it. The browser is no exception: kizunasync-wasm compiles the kernel to WebAssembly and kizunasync/web runs it in a dedicated worker, which is what Vite apps and Expo web both open.

The product kizunasync CLI is a sibling surface: it provisions the SQL pack and never sits on the app-client path. Readers invoke it with npx kizunasync, pnpm dlx kizunasync, yarn dlx kizunasync, or bunx kizunasync, and packages/kizunasync is the Node shim that execs that binary.

Tree#

apps/website            documentation and marketing site
apps/sync-inspector     read-only local sync inspector
apps/demo               two-pane browser demo (not under examples/)
crates/                 Rust kernel, bridges, CLI, conformance
packages/core           JS app client (createKizunaSync), host layer, wire types, ports
packages/kizunasync     the published npm package: CLI shim, SQL pack copy, one entry point per subpath
packages/supabase       RPC remote, Storage/TUS transfer, Realtime wakeup
packages/supabase-pack  installable SQL pack plus local fixtures
packages/web            worker driver over kizunasync-wasm, OPFS file store, connectivity
packages/expo           Expo SQLite and op-sqlite drivers, file store, connectivity
packages/rn-uniffi      React Native Turbo Module loader for UniFFI
packages/protocol       wire schemas, decisions, corpus, TypeScript oracle
packages/react          React provider and hooks
packages/vue            Vue composables
packages/ui             shared theme tokens and cross-app components
packages/utilities      query log, live-sync gate and wrappers, demo accounts, todo-board predicates
scripts/                XCFramework, AAR, bindgen, and npm release scripts
branding/               logo, app marks, favicons, and the brand book
docs/                   public documentation rendered by the website
examples/               five reference apps (React, Vue, Expo, iOS, Android)

Crates#

The workspace builds twelve crates.

CrateRole
kizunasync-protocolWire types and cursor codec
kizunasync-queryLocal query filter evaluation
kizunasync-storeSQLite local store (_kizunasync_*)
kizunasync-transferAttachment transfer (single-shot and TUS)
kizunasync-engineKernel
kizunasync-remote-httpLive HTTP adapters for Supabase RPC and Storage
kizunasync-conformanceProtocol corpus harness for the Rust engine
kizunasync-ffiUniFFI and C-ABI bridge; Swift and Kotlin packages under bindings/
kizunasync-napiNode and Bun N-API bridge
kizunasync-wasmBrowser bridge; the kernel as WebAssembly behind kizunasync/web's worker
kizunasync-bindgenUniFFI bindgen CLI
kizunasync-cliProduct CLI crate; binary name kizunasync

An application imports one app client from the kizunasync package, never a crate.

AppImport
React / Vue / Vite / vanilla browsercreateKizunaSync or createSupabaseKizunaSync, plus kizunasync/web
Expo / React NativecreateKizunaSync plus kizunasync/expo; UniFFI when the React Native module in kizunasync is linked
Native iOSimport KizunaSync, then KizunaSyncClient
Native Androidcom.kizunasync.kizunasync.KizunaSyncClient
Provisioningthe kizunasync CLI through a package-manager runner

Found a problem? Edit this page on GitHub