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 adapter · runnable demo · 19 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.

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.

Supabase adapter included

Manifest count + keyset (cursor) pagination, scoped per-user by Row-Level Security. 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. Waitlist members get an early-bird discount.

Single app

$39 one-time
  • ✓ Full source — core + Supabase adapter
  • ✓ Use in one commercial app
  • ✓ Lifetime updates
  • ✓ Runnable example app
Best value

Unlimited

$89 one-time
  • ✓ Everything in Single app
  • ✓ Use in unlimited apps you own
  • ✓ Priority email support
  • ✓ Early access to new adapters
Lock in the early-bird price →

Be first in line

One email at launch + the early-bird discount. 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.

When does it launch?

Soon — join the waitlist to get the launch email and the early-bird price.