Skip to content
Ownfold
Esc
navigateopen⌘Jpreview
On this page

Framework adapter architecture

How Ownfold keeps framework bridges thin by sharing one Web-standard handler, stable contracts, and reusable compliance tests.

Ownfold follows the integration shape proven by Better Auth: a canonical Web-standard handler, thin framework bridges, independent database adapter packages, and one reusable compliance suite. The reference implementation was reviewed from the official Better Auth repository, including its core adapter factory, framework integrations, standalone ORM packages, and adapter test utilities.

Ownfold deliberately does not copy Better Auth’s generic database CRUD layer. Vault coordination has a smaller domain contract, and keeping that contract explicit makes ownership checks, optimistic concurrency, and the absence of decryption APIs auditable.

Canonical server boundary

@ownfold/fetch exposes a handler with one portable contract:

type VaultFetchHandler = (request: Request) => Promise<Response>

Framework integrations adapt their request object to this contract and return its response. They must not reproduce route validation or vault business logic. The handler provides strict JSON validation, request-size limits, same-origin mutation checks, authentication through the configured VaultServer, safe error serialization, and Cache-Control: no-store responses.

import { createVaultFetchHandler } from "@ownfold/fetch"

export const vaultHandler = createVaultFetchHandler({
  server: vaultServer,
})

The matching browser transport implements both VaultTransport and VaultRotationTransport and only contacts the configured application URL:

import { createFetchVaultTransport } from "@ownfold/fetch"
import { createVaultClient } from "@ownfold/browser"

export const vault = createVaultClient({
  transport: createFetchVaultTransport({
    baseURL: "/api/ownfold",
  }),
})

Compatibility groups

Integration group Strategy Official package plan
Next.js, React Router, Remix, TanStack Start Route handler delegates Web Request directly Framework-specific route helpers
Hono, Elysia, Bun, Deno, Cloudflare Workers Delegate the runtime’s Web Request directly @ownfold/hono, @ownfold/elysia, then runtime examples
Express, Fastify, NestJS Convert Node request/response objects at one tested bridge @ownfold/node, @ownfold/fastify, plus small framework helpers
tRPC Typed procedures call VaultServer directly; client adapter implements VaultTransport @ownfold/trpc
SvelteKit, SolidStart, Nuxt/Nitro Delegate platform requests and isolate browser imports Framework-specific examples and helpers
Custom TypeScript stacks Use VaultFetchHandler, VaultTransport, and VaultAdapter directly Supported by core contracts

Database and authentication packages

Database packages implement the narrow VaultAdapter and VaultRotationAdapter contracts and run the same compliance suite. ORM types never cross the shared boundary. Official PostgreSQL-first adapters are Drizzle, Prisma, direct PostgreSQL, and Node.js SQLite; MySQL and MongoDB remain planned.

Authentication packages only resolve the authenticated user ID from the host request. Better Auth and Auth.js have official structural resolvers; Clerk, Supabase Auth, Firebase Auth, and custom session systems remain outside the core. No authentication integration may receive a Recovery Kit password, root key, record key, or plaintext application data.

Package rules

  1. Framework and ORM dependencies are peers of their leaf adapter package.
  2. Core, crypto, browser, and server packages never import a framework or ORM.
  3. Every HTTP input is parsed before it reaches VaultServer.
  4. Ownership always comes from getUserId; client-supplied ownership fields are rejected.
  5. Each adapter passes shared contract tests plus framework-specific integration tests.
  6. Published packages are checked from their built ESM and declaration outputs.

Last updated on August 4, 2026

Was this page helpful?