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