Skip to content

Stores

Choose a PayKernel storage adapter from declared manifests. Redis is optional; local SQLite is single-host; D1 is not Durable Objects.

Updated View as Markdown

Pick a store adapter by package + subpath and the adapter’s StorageAdapterManifest — not by marketing copy. All production adapters implement the same store contracts (idempotency, webhook inbox, reconciliation). Inject stores at the app layer; @paykernel/core and @paykernel/webhooks never depend on a database package.

All store adapters are published: store-postgres/redis/sqlite/turso/d1/durable-objects 0.1.1, store-contracts/sql-foundation 0.1.0 on npm.

Honesty

Fact Implication
Redis is optional You do not need Redis to use @paykernel/core or the webhook engine
Local SQLite is single-host Bun / Node / better-sqlite3 file DBs are not multi-host coordination
:memory: is one process Lost on process restart; not the file-backed durable claim
Turso is remote multi-host No /sync export; not local store-sqlite
D1 ≠ Durable Objects ≠ Turso ≠ local SQLite Separate packages, APIs, and consistency models
Memory stores are NON-PRODUCTION MEMORY_STORAGE_ADAPTER_MANIFEST is single-process + ephemeral
No adapter declares coordinationScope: "multi-region" Do not invent multi-region strong consistency

@paykernel/internal-sql-store is private. Do not install it. Relational adapters depend on @paykernel/sql-foundation.

Inbox stores claim work. handleWebhook only verifies and normalizes — it does not claim, lease, or set HTTP status. Never fulfill in onWebhookVerified. Fulfill after an inbox claim, and only when the rematched event is payment.succeeded or capture.completed and payment.status === "paid", bound to gatewayPaymentId. See Webhooks.

Capability matrix

Adapter Package Distributed Durable audit Atomic claim Best use Limitation
PostgreSQL @paykernel/store-postgres Yes (multi-host) Yes (durable) Yes (strong) General default when you have PostgreSQL Multi-primary without consensus is out of scope
Redis/Valkey @paykernel/store-redis /bun /ioredis /node-redis Yes (multi-host); Bun: no Cluster/Sentinel Configuration-dependent Yes (Lua) Low-latency coordination Optional infra; not automatic long-term audit
Upstash Redis @paykernel/store-redis/upstash Yes (multi-host) Configuration-dependent Yes (EVAL) Serverless coordination over HTTP Same hybrid-audit caveats; HTTP/network model
Bun SQLite @paykernel/store-sqlite/bun No — single host Yes with a durable file (:memory: is process-local) Yes (BEGIN IMMEDIATE) Bun local / single-server Not cross-host; no network FS sharing
Node SQLite @paykernel/store-sqlite/node No — single host Yes (file-backed) Yes Node local / single server node:sqlite is experimental; Node ≥ 22.5.0
better-sqlite3 @paykernel/store-sqlite/better-sqlite3 No — single host Yes (file-backed) Yes Mature Node SQLite Native dependency; sync API
Turso serverless @paykernel/store-turso/serverless Yes (multi-host remote) Yes (durable) Yes Shared remote SQLite-compatible Not local SQLite; no /sync
libSQL @paykernel/store-turso/libsql Yes remote; local file: is single-host testing Yes remote Yes Existing @libsql/client projects Not interchangeable with /serverless; no /sync
Cloudflare D1 @paykernel/store-d1 Yes (multi-host, shared D1) Yes (durable) Yes Worker-native shared relational Not SQLite/Turso/DO; readAfterWrite: "session"
Durable Objects @paykernel/store-durable-objects Yes, partitioned Yes (SQLite-backed DO) Yes within a partition Per-key / per-partition serialization Never one global DO; not D1
Memory (testkit) @paykernel/testkit No — single process No (ephemeral) Strong only in one isolate Tests / examples NON-PRODUCTION

Manifest cheat sheet

Adapter coordinationScope durability claims readAfterWrite staleReadsPossible
postgres multi-host durable strong strong false
redis (all bindings) multi-host configuration-dependent strong strong false
sqlite (all bindings) single-host durable strong strong false
turso (both bindings) multi-host durable strong strong false
cloudflare-d1 multi-host durable strong session true
cloudflare-do multi-host (partitioned) durable strong strong (per DO) false (per partition)
memory single-process ephemeral strong (isolate only) strong false

Decision (stop at the first fit)

  1. Already run PostgreSQL that all workers share?@paykernel/store-postgres
  2. Cloudflare Workers? (never @paykernel/store-sqlite — Workers are multi-isolate with no durable local FS)
    • Need strong per-key / per-partition serialization (including if you already use D1)? → @paykernel/store-durable-objects with explicit sharding. Never one global Durable Object.
    • Shared relational / greenfield? → @paykernel/store-d1. Prefer Sessions (first-primary) when read replication is on.
  3. Ephemeral serverless FS / no durable local disk (not Workers)? Do not use store-sqlite. Continue to Turso, optional Redis, or STOP.
  4. Bun, single host, durable local file OK?@paykernel/store-sqlite/bun
  5. Node, single host, local file OK?/node or /better-sqlite3 (prefer better-sqlite3 until node:sqlite is stable)
  6. Need multi-host remote SQLite-compatible storage?@paykernel/store-turso (/serverless or /libsql). Not interchangeable with local SQLite or D1.
  7. Already operate Redis / Valkey / Upstash? → matching store-redis binding; optionally hybrid with SQL for audit. Bun + Cluster/Sentinel → not /bun; use /ioredis or /node-redis.
  8. No Redis today and moderate load? Pick the primary relational / D1 / DO adapter. Do not add Redis only because PayKernel exists.

Fail-closed: if multi-host or multi-isolate coordination is required and the only option is local SQLite → STOP. Choose PostgreSQL, Turso (remote), D1, or sharded Durable Objects. Do not share the SQLite file across hosts.

Real subpaths only

Package Exports
@paykernel/store-postgres . /bun-sql /postgres-js /pg /drizzle
@paykernel/store-redis . /bun /upstash /ioredis /node-redis
@paykernel/store-sqlite . /bun /node /better-sqlite3
@paykernel/store-turso . /serverless /libsqlno /sync
@paykernel/store-d1 . only
@paykernel/store-durable-objects . only

Root entries never statically import optional drivers.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close