tightrelay

Work offline. Sync later.

tightrelay is the sync service for native apps. Your app keeps its local database. tightrelay delivers the changes to every device and resolves conflicts on the server.

  • macOS
  • iOS
  • Windows
  • Android

One document, three devices

Take Windows offline and edit there, then edit on the Mac and reconnect Windows. The later change wins, everywhere.

api.tightrelay.comserver_seq 1

    What tightrelay handles for you

    The service has no app-specific tables. It stores JSON documents, versions them and distributes changes. A schema is optional, and it is configuration, not server code.

    A glass device with a broken connection; pending changes wait as golden dots.

    Offline first

    The app writes to its own database, whether SwiftData, SQLite or Room. The SDK tracks every change and syncs as soon as the network is back.

    A glass security chip with a key symbol.

    Devices instead of passwords

    The key lives in the Secure Enclave, TPM or Android Keystore. New devices find their account via iCloud Keychain or Block Store; others pair by QR.

    Several glass spheres linked by threads of light to one shared disc.

    Shared data

    Shared spaces for teams or businesses, with invites by QR code and roles to manage, edit or read.

    The server settles conflicts

    Every change carries a hybrid logical clock timestamp. Per document, the later change wins, and every device ends up in the same state.

    Subscriptions from the App Store and Google Play

    If you want, tightrelay only syncs with an active subscription. The server verifies purchases, renewals, expiry and refunds itself. When a subscription lapses, sync pauses and the data stays.

    No schema required

    Collections appear with the first sync. Add a schema whenever you like: in the console form, suggested from your data, or from your code. After that, only changes that don’t break older app versions pass.

    In sync in three steps

    No server to set up, no tables to create, no schema to write. All you need is an app ID and the SDK.

    1. Create an app

      Pick an app ID in the console. No API key, no password: every device signs itself up on its first sync.

    2. Add the SDK

      Get it via Swift Package, NuGet or Maven. A small adapter connects it to the database your app already has.

    3. Write data

      create(collection: "notes", …) and sync(). The collection appears with the first sync, on every device.

    Later, if you want, a schema: in the console form, suggested from the stored data, or from your code with tightrelay-cli schema push, in CI too.

    One SDK per platform

    Swift, C# and Kotlin, with no third-party dependencies. All three run the same test vectors and behave the same.

    • Your database stays. You write a small adapter around one transaction, and the SDK does the rest.
    • Push, pull, retry with backoff and device clock skew are built in.
    • You decide when to sync: at launch, shortly after changes, in the background.
    let client = TightRelayClient(
        configuration: .init(baseURL: api, appId: "stundennachweis",
                             clientSchema: 1, deviceName: "Mac"),
        store: MySwiftDataStore(),
        identities: KeychainIdentityStore(appId: "stundennachweis"),
        recovery: ICloudKeychainRecoveryStore(appId: "stundennachweis"))
    
    try await client.create(collection: "clients", data: ["name": "Miller Ltd"])
    try await client.update(id, set: ["archived": true])  // merge
    try await client.delete(id)                           // tombstone
    let report = try await client.sync()                  // push, then pull

    How a sync works

    Each round is a push and a pull over HTTPS. Every request is signed with the device key.

    1. Change locally

      The app saves as usual. The SDK stamps the change with the device clock and queues it for upload.

    2. Upload

      The server validates the schema if there is one, compares timestamps and only accepts newer data. If the change is older, the device gets the current version back.

    3. Number

      Every accepted change gets the next number in its space, without gaps, even with concurrent uploads.

    4. Download

      The device fetches everything after its last number, page by page, one transaction each. If the connection drops, nothing is lost.

    What tightrelay deliberately leaves out

    A small scope keeps the service easy to understand and predictable. If your app needs one of these, tightrelay isn’t the right fit today.

    No real-time pushDevices sync after changes, at launch and every few minutes. Too slow for chat, plenty for work data.

    No field-level mergingWhen two devices change the same document, the later change wins as a whole.

    No filesPhotos and PDFs don’t belong in the sync. A document is at most 64 KB.

    No web clientBuilt for native apps with their own database, not for browsers.

    In production: Stundennachweis

    The time tracker for freelancers syncs clients, projects and entries through tightrelay. A running timer shows up on the other devices right away.

    Start a timer on two devices at once and a rule on the server stops the older one. Rules like this run in the same transaction as the upload.

    Mac and iPhoneSwiftUI, SwiftData, Secure Enclave
    WindowsWinUI 3, SQLite, TPM
    AndroidJetpack Compose, Room, Keystore
    ServerRust, PostgreSQL 16, Hetzner

    Your app, in sync on every device

    Sign up in the console. Once your account is approved, create your app and add the SDK. Collections appear with the first sync.