Flutter · offline-first · cloud restore

Your app ships the reinstall bug. RestoreKit fixes it.

A user reinstalls your app, the local database is empty, and your launch logic treats them as brand new — even though their data is safe in your cloud. RestoreKit detects that exact moment and rehydrates from your source of truth: resumably, idempotently, and crypto-aware.

Pure-Dart engine · Supabase, drift & secure-storage adapters · runnable demo · 36 passing tests

The "looked like data loss" bug

The naive fix — "if local is empty, pull from cloud" — breaks in four ways, and each one is its own outage.

🆕

New vs. returning is ambiguous

A genuinely new user also has an empty local DB. Always-pull hammers your backend; never-pull loses returning users. After a reinstall the auth session is gone too — so you can't even check until they sign back in.

🔑

The encryption key is gone

If your local DB is encrypted, the key lived in secure storage — also wiped. You must recover it before writing a single row. That's a real design decision, not an afterthought.

⏸️

It isn't resumable

A 4,000-record pull dies at 2,200 on hotel wifi. Without a checkpoint, the next launch can't tell "done" from "half-full" — so you either lose rows forever or restart from zero.

📴

A failed check becomes "new user"

A timed-out manifest request falls through to onboarding, and a returning user stares at a fresh-install screen. A transient blip must mean "retry" — never data loss.

How it works

Four steps, behind the scenes — your user just sees their data come back.

📱
STEP 1

Reinstall → empty device

The OS wiped local storage, so the on-device database is empty.

🔍
STEP 2

Spot a returning user

After sign-in, RestoreKit sees an empty device + an account that has data in your cloud → returning, not new.

☁️
STEP 3

Pull from your cloud

It restores from your own backend (Supabase, Firebase, your API) in resumable batches.

STEP 4

Data restored

Safely, no duplicates — even if the download is interrupted. No blank screen, ever.

🔒 RestoreKit has no server and stores nothing — it moves your data, from your cloud, on the device. It never sees or keeps your users' data.

A small state machine, not an if

RestoreKit resolves what a launch means from the four signals that matter, then runs a resumable restore you can watch.

  • Tells a new user apart from a returning one with an empty device
  • Recovers your encryption key (your strategy, abstracted)
  • Checkpoints after every batch — resume, never restart
  • Idempotent upserts, so a re-delivered batch is harmless
  • Treats failures as retryable-into-resume, never as data loss
// 1. What does this launch mean?
switch (await resolver.resolve()) {
  case LaunchDecision.normalLaunch:    goToApp();
  case LaunchDecision.freshOnboarding: onboard();
  case LaunchDecision.restoreAvailable:
  case LaunchDecision.resumeRestore:   await restore();
}

// 2. Run (or resume) it — observable + safe
coordinator.states.listen(updateProgressUI);
await coordinator.run();
// → restored | nothingToRestore | alreadyComplete

Built like production, tested like it matters

Pure-Dart core

Zero Flutter / DB / network dependencies. Fully unit-testable and portable. Your stack plugs in through six small interfaces.

Resumable by design

A durable checkpoint advances after every batch. An interrupted restore continues from the last cursor — no duplicates.

Crypto-agnostic

Bring your own key recovery — passphrase-derived, cloud-wrapped, KMS, biometric-gated. RestoreKit asks for the key, never dictates it.

Adapters included

Supabase (manifest + keyset pagination via RLS), drift (idempotent upserts), and a secure-storage checkpoint — drop-in for your stack. Firebase / REST are a small interface away.

No data-loss shortcuts

Probe failures throw and force a retry — they're never collapsed into "new user." The one mistake that ruins trust, designed out.

Runnable demo

Wipe the device, watch it restore, interrupt it, hit retry. Every guarantee on this page is backed by a test you can run.

Simple, one-time pricing

Buy the weeks, not the glue. Full source, lifetime updates, instant access via GitHub.

Single app

$39 one-time
  • ✓ Full source — core + all 3 adapters
  • ✓ Use in one commercial app
  • ✓ Lifetime updates
  • ✓ Runnable example app
Buy Single App — $39
Best value

Unlimited

$89 one-time
  • ✓ Everything in Single app
  • ✓ Use in unlimited apps you own
  • ✓ Priority email support
  • ✓ Early access to new adapters
Buy Unlimited — $89

Secure checkout via Polar · pay in your local currency · instant GitHub repo access + license key

Stay in the loop

Get notified about new adapters, releases, and deep-dive posts on offline-first & mobile security. No spam.

Building in public — follow along as the security/offline patterns get extracted.

FAQ

Is this just another sync library?

No. Sync keeps two live stores in agreement. RestoreKit solves the cold-start case sync ignores: an empty device that must rehydrate from the cloud after a reinstall — including key recovery and resumable, idempotent restore.

Which backend / database does it need?

Any. The core is backend-agnostic via six interfaces. A Supabase adapter ships in the box (keyset pagination + RLS); Firebase, a custom API, drift, sqflite, Hive, or Isar are a thin adapter away.

Do I get the source?

Yes — full source, yours to use under the license tier you buy, with lifetime updates.

My app isn't encrypted. Still useful?

Absolutely. Key recovery is optional — omit the KeyVault and you get the reinstall detection + resumable restore on their own.

Is it available now?

Yes — buy above and you'll get GitHub repo access plus your license key instantly, with lifetime updates included.