Database adapter API reference
Complete SQLite, PostgreSQL, Drizzle, and Prisma adapter reference with constructors, schemas, driver contracts, transactions, migrations, errors, and compliance requirements.
Database adapters persist vault coordination metadata: vault identity, recovery verification status, devices, pairing requests, and rotation checkpoints. They do not store application encrypted records unless the host creates separate application tables and routes.
All official adapters implement VaultAdapter & VaultRotationAdapter and return typed Result
values.
Choose an adapter
| Package | Database/API | Factory or class | Schema source |
|---|---|---|---|
@ownfold/sqlite |
Node DatabaseSync |
SqliteVaultAdapter |
ownfoldSqliteSchema |
@ownfold/postgres |
Driver-neutral SQL executor | PostgresVaultAdapter, postgresVaultAdapter |
CLI migration |
@ownfold/postgres/node-postgres |
pg.Pool |
nodePostgresVaultAdapter |
CLI migration |
@ownfold/drizzle |
Drizzle PostgreSQL database | DrizzleVaultAdapter, drizzleVaultAdapter |
@ownfold/drizzle/schema |
@ownfold/prisma |
Structurally typed Prisma client | PrismaVaultAdapter, prismaVaultAdapter |
CLI Prisma schema fragment |
SQLite
pnpm add @ownfold/sqlite@beta
import { DatabaseSync } from "node:sqlite"
import {
applyOwnfoldSqliteSchema,
SqliteVaultAdapter,
} from "@ownfold/sqlite"
const database = new DatabaseSync("ownfold.db")
applyOwnfoldSqliteSchema(database)
export const adapter = new SqliteVaultAdapter(database)
Exports
| Export | Type | Purpose |
|---|---|---|
ownfoldSqliteSchema |
string |
Idempotent strict-table SQL for all coordination tables and indexes. |
applyOwnfoldSqliteSchema(database) |
(DatabaseSync) => void |
Executes the bundled schema on an existing connection. |
SqliteVaultAdapter |
class | Synchronous-driver implementation wrapped in async result methods. |
The adapter enables foreign keys in the schema and stores each validated domain record as JSON in strict tables keyed by its primary identity. Mutations that span records run inside SQLite transactions. Use one controlled migration step before serving requests; do not call the schema helper in every request.
DatabaseSync is a Node runtime API. Browser SQLite or unrelated WASM database objects are not
accepted by this package.
PostgreSQL
pnpm add @ownfold/postgres@beta
Driver-neutral executor
query<Row extends Record<string, unknown>>(text: string, values?: readonly unknown[] | undefined) => PromiseLike<PostgresQueryResult<Row>>
<Row extends Record<string, unknown>>(text: string, values?: readonly unknown[] | undefined) => PromiseLike<PostgresQueryResult<Row>>transaction<T>(operation: (executor: PostgresExecutor) => Promise<T>) => Promise<T>
<T>(operation: (executor: PostgresExecutor) => Promise<T>) => Promise<T>import { postgresVaultAdapter } from "@ownfold/postgres"
export const adapter = postgresVaultAdapter({
query: (text, values) => database.query(text, values),
transaction: (operation) => database.transaction((tx) => operation(tx)),
})
query must preserve parameter binding; never interpolate identifiers or values from Ownfold
inputs. transaction must commit only when the callback completes and roll back on thrown or
rejected failure. Rotation completion and pairing approval depend on this atomic boundary.
rowsreadonly Row[]
readonly Row[]rowCount?number | null
number | nullpg convenience entry point
import { Pool } from "pg"
import { nodePostgresVaultAdapter } from "@ownfold/postgres/node-postgres"
const pool = new Pool({ connectionString: process.env.DATABASE_URL })
export const adapter = nodePostgresVaultAdapter(pool)
The wrapper obtains a dedicated client for transactions and always releases it. Configure TLS, pool size, timeouts, and shutdown in the host application.
Class and factory
new PostgresVaultAdapter(executor) exposes the concrete implementation.
postgresVaultAdapter(executor) returns the narrower contract intersection. Prefer the factory
unless tests or dependency injection need the concrete class identity.
PostgresVaultAdapter: No interface or type named "PostgresVaultAdapter" found.Drizzle
pnpm add @ownfold/drizzle@beta drizzle-orm
import { drizzleVaultAdapter } from "@ownfold/drizzle"
import * as ownfoldSchema from "@ownfold/drizzle/schema"
import { drizzle } from "drizzle-orm/node-postgres"
const db = drizzle(pool, { schema: ownfoldSchema })
export const adapter = drizzleVaultAdapter(db)
Adapter exports
| Export | Purpose |
|---|---|
DrizzleVaultAdapter |
Concrete PostgreSQL Drizzle adapter class. |
drizzleVaultAdapter(database) |
Factory returning VaultAdapter & VaultRotationAdapter. |
DrizzleVaultAdapter: No interface or type named "DrizzleVaultAdapter" found.Schema entry point
@ownfold/drizzle/schema exports:
ownfoldVaults;ownfoldDevices;ownfoldPairings; andownfoldRotations.
Register these tables in the same schema object used to construct the database. The adapter imports the canonical table objects, so redefining lookalike tables does not replace them. Generate and apply SQL migrations through the CLI or the application’s Drizzle migration workflow.
Prisma
pnpm add @ownfold/prisma@beta @prisma/client
import { prismaVaultAdapter } from "@ownfold/prisma"
import { prisma } from "@/src/lib/prisma"
export const adapter = prismaVaultAdapter(prisma)
ownfoldVaultPrismaVaultDelegate
PrismaVaultDelegateownfoldDevicePrismaDeviceDelegate
PrismaDeviceDelegateownfoldPairingPrismaPairingDelegate
PrismaPairingDelegateownfoldRotationPrismaRotationDelegate
PrismaRotationDelegate$transaction<T>(operation: (client: PrismaTransactionClient) => Promise<T>) => Promise<T>
<T>(operation: (client: PrismaTransactionClient) => Promise<T>) => Promise<T>The package uses structural delegate interfaces instead of importing a generated application
client type. The client must expose ownfoldVault, ownfoldDevice, ownfoldPairing,
ownfoldRotation, and interactive $transaction with the methods in PrismaVaultClient.
Public Prisma types
| Category | Exports |
|---|---|
| Client boundaries | PrismaVaultClient, PrismaTransactionClient |
| Vault | PrismaVaultRow, PrismaVaultDelegate |
| Device | PrismaDeviceRow, PrismaDeviceCreateRow, PrismaDeviceDelegate |
| Pairing | PrismaPairingRow, PrismaPairingCreateRow, PrismaPairingDelegate |
| Rotation | PrismaRotationRow, PrismaRotationDelegate |
| JSON | PrismaJsonValue, PrismaJsonNestedValue |
| Adapter | PrismaVaultAdapter, prismaVaultAdapter |
These interfaces are extension seams, not a second schema. Generate the canonical model fragment with the CLI, merge it into the application schema, migrate, then regenerate Prisma Client.
Shared adapter methods
Every official adapter implements the same operations:
| Family | Methods |
|---|---|
| Vault | getVault, createVault, updateRecoveryStatus, replaceRecoveryKit |
| Devices | listDevices, registerDevice, touchDevice, revokeDevice |
| Pairing | createPairing, getPairing, approvePairing, cancelPairing |
| Rotation | getRotation, beginRotation, updateRotationProgress, completeRotation |
Read methods return StorageAdapterError on driver or stored-data failure. Mutations may additionally
return ConflictError for duplicate identity or stale revision. Adapters do not throw expected
conflicts and never return raw driver errors to the server.
Transaction requirements
| Operation | Atomic requirement |
|---|---|
| Pairing approval | Mark request approved and register the exact offered device together. |
| Rotation completion | Validate revisions, update vault key/Recovery Kit metadata, replace every active device envelope, and complete rotation together. |
| Revision mutation | Include ownership, identity, status, and expected revision in the write predicate. |
A transaction callback succeeding after a partial write is a correctness bug. Timeouts after an unknown commit outcome must be reconciled by reading current state and relying on idempotent identities.
Migrations
pnpm ownfold generate
pnpm ownfold migrate --database-url "$DATABASE_URL"
SQLite may use --database-path. Drizzle and Prisma users may apply the generated artifacts through
their native migration system. Keep migrations in application source control and apply them before
new code begins serving.
Application record tables
The adapter schema does not create tables for notes, messages, documents, or other application
records. Store each encryptedPayload in an application-owned JSON/JSONB/text column with its
server-authorized owner and immutable record ID. Validate writes through
VaultServer.validateEncryptedRecordWrite.
Compliance and custom adapters
Run both official compliance suites against a disposable real database:
import {
checkVaultAdapterCompliance,
checkVaultRotationAdapterCompliance,
} from "@ownfold/testing"
await checkVaultAdapterCompliance(adapter)
await checkVaultRotationAdapterCompliance(adapter)
See Adapter and transport contracts, Testing APIs, and the complete export index.