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
| type | checked | drifted |
|---|---|---|
| product | 182 | 0 |
| price | 304 | 0 |
| customer | 41,209 | 0 |
| subscription | 8,417 | 0 |
| invoice | 96,532 | 0 |
| charge | 102,114 | 0 |
| total | 248,758 | 0 |
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.
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/.
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.
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.
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.
You are on the list. No newsletter; one message if it ships.