getdriftless.dev

Your Postgres never drifts from Stripe.

Driftless is a single binary that receives Stripe webhooks, finds the events Stripe never delivered, backfills history, and materializes everything into a stripe schema in your own Postgres. Then it proves the result: driftless verify reconciles your database against Stripe and exits nonzero on drift.

Apache-2.0 · self-hosted · read-only against Stripe · no third party touches your payment data

the problem

Webhook delivery is at-least-once, unordered, and silently droppable.

Stripe's own engineering blog says so, and teaches you to build queues and reconciliation jobs around it (their words). Teams discover the damage late: 3% of subscriptions drifted, eight months of leaked revenue, a webhook marked delivered that never arrived.

And the evidence expires: Stripe's event log only goes back roughly 30 days. After that, a gap is not recoverable from events at all.

quickstart

Ten minutes from zero to a proven-correct mirror.

You need Postgres 17+, a Stripe API key (a restricted read-only rk_... key is recommended), and a webhook signing secret.

# 1. Install: one static binary, no runtime
#    https://github.com/quyumkehinde/driftless/releases
tar xzf driftless_*.tar.gz && sudo install driftless /usr/local/bin/

# 2. Point it at your Postgres and your Stripe account
export DRIFTLESS_DATABASE_URL='postgres://...'
export DRIFTLESS_STRIPE_API_KEY='rk_test_...'
export DRIFTLESS_STRIPE_WEBHOOK_SECRET='whsec_...'

# 3. First-run setup: environment checks, migrations, offer of a backfill
driftless init

# 4. Run it, and point a Stripe webhook endpoint at
#    https://<your-host>/webhooks/stripe
driftless serve

# 5. Prove it
driftless verify --full

Exit 0 means your database provably matches Stripe. Exit 3 means drift, with a per-object report, and verify --repair fixes it. Put that exit code in CI and billing drift becomes a failing check.

Prefer containers? A Docker Compose quickstart, a distroless image at ghcr.io/quyumkehinde/driftless, and Kubernetes manifests ship in deploy/.

what it does

The whole receiver side, owned end to end.

receive
Signature-verified webhooks; Stripe is acknowledged only after the event is durably committed.
dedupe
Every event recorded exactly once; state converges regardless of duplicates, ordering, or same-second ties.
gap sweep
A sweeper finds events Stripe generated but never delivered, before the 30-day cliff erases them.
backfill
Full or incremental history, resumable across kill -9, including the canceled subscriptions the default listing hides.
materialize
Typed, indexed stripe.* tables with the raw JSON preserved alongside.
prove
A reconciliation report with an exit-code contract, and a repair mode that trues up whatever it finds.

At-least-once delivery, exactly-once processing, provable convergence. That is the honest version of the promise; nobody can give you exactly-once delivery.

alternatives

Existing tools cover fragments of the problem.

Driftless stripe/sync-engine Hookdeck / Svix Airbyte / Fivetran
Dedupe guaranteed yes partial best-effort, documented as such n/a
Out-of-order and same-second safe yes known open issue disclaimed n/a
Detects never-delivered events yes no no no
Backfill incl. canceled subscriptions yes known open issue no polling sync
Reconciliation proof verify, exit-code contract no no no
Where your data lives your Postgres your Postgres their cloud, then yours your warehouse, hours later

Already running stripe/sync-engine? driftless import --from-sync-engine migrates a database in place: one schema rename, one import, one verify --repair.

managed driftless

Rather not be on call for it?

The OSS core is complete and stays that way. If you would pay for a hosted control plane on top (fleet dashboard, drift history, alert routing), leave an email. No newsletter; one message if it ships.