Skip to content
Ownfold
Esc
navigateopen⌘Jpreview
On this page

Database adapters

Integrate Ownfold vault metadata using Drizzle, Prisma, direct PostgreSQL, SQLite, or the shared storage-adapter contract.

Ownfold stores only vault coordination metadata. Encrypted application records remain in the host application’s own tables. Every official adapter implements VaultAdapter and VaultRotationAdapter and runs the shared checkVaultRotationAdapterCompliance suite for idempotent creation, optimistic concurrency, and atomic root-key rotation cutover.

Adapter Best fit Detailed guide
SQLite One Node.js process, local or modest coordination traffic SQLite
Direct PostgreSQL Applications using pg or another query layer PostgreSQL
Drizzle PostgreSQL applications with a Drizzle schema Drizzle
Prisma Applications with a generated Prisma Client Prisma

SQLite

@ownfold/sqlite targets Node.js 22.13 or newer and uses the built-in node:sqlite driver, so it does not add a native third-party runtime dependency:

import { DatabaseSync } from "node:sqlite"
import { applyOwnfoldSqliteSchema, SqliteVaultAdapter } from "@ownfold/sqlite"

const database = new DatabaseSync("ownfold.sqlite")
applyOwnfoldSqliteSchema(database)

const adapter = new SqliteVaultAdapter(database)

Run the idempotent schema function during application migration or startup before serving vault requests. The adapter validates every JSON record read from SQLite, uses BEGIN IMMEDIATE for serialized metadata updates, and commits rotation cutover atomically. Give it a dedicated database connection and do not call it from inside another transaction on that connection. Because DatabaseSync is synchronous, use PostgreSQL for high-write, horizontally scaled deployments; SQLite is intended for single-process and edge-local Node deployments with modest coordination traffic.

Drizzle and PostgreSQL

Apply packages/drizzle/migrations/0000_ownfold_vaults.sql followed by 0001_ownfold_devices.sql, 0002_ownfold_pairings.sql, 0003_ownfold_rotations.sql, and 0004_ownfold_device_last_active.sql. Include the four Ownfold tables in the Drizzle schema and construct the adapter:

import { drizzleVaultAdapter } from "@ownfold/drizzle"
import {
  ownfoldDevices,
  ownfoldPairings,
  ownfoldRotations,
  ownfoldVaults,
} from "@ownfold/drizzle/schema"

const db = drizzle(pool, {
  schema: { ownfoldVaults, ownfoldDevices, ownfoldPairings, ownfoldRotations },
})
const adapter = drizzleVaultAdapter(db)

The adapter uses parameterized Drizzle queries, ON CONFLICT DO NOTHING for idempotent creation, and a revision predicate for atomic recovery-status updates. Device activity updates use a server-issued timestamp and intentionally leave the security revision unchanged. Its generic database input preserves compatibility with schema-registered Drizzle clients without exposing host schema objects through Ownfold’s shared adapter contract.

Prisma

Copy the Ownfold models from packages/prisma/prisma/schema.prisma into the application’s schema. For an existing installation, apply the packaged device_last_active migration or create an equivalent backfill migration before regenerating the client. Then pass the generated client:

import { prismaVaultAdapter } from "@ownfold/prisma"

const adapter = prismaVaultAdapter(prisma)

The adapter depends structurally on the generated Ownfold delegates and does not bundle a Prisma client or engine. Rotation completion uses an interactive transaction. Copy all four models before running the application’s migration workflow.

Direct PostgreSQL

Apply PostgreSQL migrations 0000 through 0004 in order, then provide a parameterized query executor. A node-postgres pool can be exposed directly or through a small wrapper matching PostgresExecutor:

import { nodePostgresVaultAdapter } from "@ownfold/postgres/node-postgres"

const adapter = nodePostgresVaultAdapter(pool)

All returned database rows are validated before becoming VaultRecord values. Malformed stored metadata becomes a typed StorageAdapterError; raw driver errors and SQL details are not exposed to the browser.

Pairing approval must register the device and approve the request atomically. Rotation completion must update every active device envelope, the Recovery Kit ID, the vault key version, and the rotation state in one transaction. The shared compliance suite deliberately supplies one stale device revision after another device could have been updated, then verifies that every prior value was preserved.

Real PostgreSQL release gate

pnpm test:postgres runs the complete lifecycle and rotation compliance suite through direct node-postgres, Drizzle, and a generated Prisma Client against a real PostgreSQL server. It is separate from the fast PGlite suite and is required by pnpm verify. The pull-request workflow provisions PostgreSQL 16.

Set OWNFOLD_TEST_POSTGRES_URL to a local database named exactly ownfold_test. The test harness refuses remote hosts and every other database name before issuing its schema reset, so an accidental production URL is not touched. It drops and recreates the public schema on every run; the named database must contain test data only.

OWNFOLD_TEST_POSTGRES_URL="postgresql://postgres:postgres@127.0.0.1:5432/ownfold_test" \
  pnpm test:postgres

This lane executes the checked-in adapter migrations before each direct and Drizzle compliance run. For Prisma, it copies the packaged model fragment into an isolated schema, generates a Prisma 7.6 client, applies that schema to the disposable database, proves the generated client satisfies Ownfold’s structural adapter contract without a type assertion, and runs the same compliance suite. Prisma remains a peer dependency of the published adapter; the pinned client is test infrastructure only. PGlite remains valuable for fast tests, but passing PGlite alone is not presented as PostgreSQL production proof.

Last updated on August 4, 2026

Was this page helpful?