Installation
Build an Ownfold setup by choosing a database, identity resolver, server adapter, and optional browser client.
Ownfold is headless. Choose one option in each step; do not install frontend packages for a backend-only service.
Install the server and CLI
npm install @ownfold/server@beta
npm install --save-dev @ownfold/cli@betapnpm add @ownfold/server@beta
pnpm add -D @ownfold/cli@betayarn add @ownfold/server@beta
yarn add --dev @ownfold/cli@betabun add @ownfold/server@beta
bun add -d @ownfold/cli@betaChoose a database
pnpm add @ownfold/sqlite@beta
pnpm ownfold init --database sqlite --identity custom
pnpm ownfold migrate --database-path ./ownfold.dbimport { DatabaseSync } from "node:sqlite"
import { SqliteVaultAdapter } from "@ownfold/sqlite"
export const adapter = new SqliteVaultAdapter(new DatabaseSync("ownfold.db"))pnpm add @ownfold/postgres@beta pg
pnpm ownfold init --database postgres --identity custom
pnpm ownfold migrate --database-url "$DATABASE_URL"import { nodePostgresVaultAdapter } from "@ownfold/postgres/node-postgres"
import { Pool } from "pg"
export const adapter = nodePostgresVaultAdapter(
new Pool({ connectionString: process.env.DATABASE_URL }),
)pnpm add @ownfold/drizzle@beta drizzle-orm
pnpm ownfold init --database drizzle --identity custom
pnpm ownfold migrate --database-url "$DATABASE_URL"import { drizzleVaultAdapter } from "@ownfold/drizzle"
import { db } from "./db"
export const adapter = drizzleVaultAdapter(db)The generated SQL is also available under ownfold/migrations if Drizzle Kit owns deployment.
pnpm add @ownfold/prisma@beta @prisma/client
pnpm ownfold init --database prisma --identity customMerge ownfold/schema.prisma into your application schema, then choose one migration path:
pnpm prisma migrate dev --name add-ownfold
# or apply the generated SQL directly
pnpm ownfold migrate --database-url "$DATABASE_URL"import { prismaVaultAdapter } from "@ownfold/prisma"
import { prisma } from "./prisma"
export const adapter = prismaVaultAdapter(prisma)init creates ownfold.config.json, ownfold.server.ts, and adapter-specific schema files.
generate refreshes schema files after upgrades; it refuses to replace them unless --force is
explicit. migrate records PostgreSQL migrations in ownfold_migrations and is safe to run again.
Choose identity
pnpm add @ownfold/better-auth@betaimport { betterAuthUserResolver } from "@ownfold/better-auth"
import { auth } from "./auth"
export const getUserId = betterAuthUserResolver(auth)pnpm add @ownfold/auth-js@betaimport { authJsUserResolver } from "@ownfold/auth-js"
import { auth } from "./auth"
export const getUserId = authJsUserResolver(async () => auth())import type { OwnfoldUserResolver } from "@ownfold/server"
export const getUserId: OwnfoldUserResolver = async ({ request }) => {
const session = await sessions.verify(request.headers)
return session?.user.id ?? null
}import { singleUserResolver } from "@ownfold/server"
export const getUserId = singleUserResolver("local-owner")Use this only for a private single-user process, local tool, or desktop companion. It does not authenticate requests and must not protect a public or multi-user service.
Create the server
import { createVaultServer } from "@ownfold/server"
import { adapter } from "./database"
import { getUserId } from "./identity"
export const vaultServer = createVaultServer({ adapter, getUserId })Database and identity are independent. Any database tab above works with any identity tab.
Choose a server adapter
import { createNextVaultHandlers } from "@ownfold/next"
import { vaultServer } from "@/ownfold.server"
const handlers = createNextVaultHandlers({ server: vaultServer })
export const GET = handlers.GET
export const POST = handlers.POSTimport { createVaultFetchHandler } from "@ownfold/fetch"
import { createExpressVaultMiddleware } from "@ownfold/node"
app.use("/api/ownfold", createExpressVaultMiddleware({
handler: createVaultFetchHandler({ server: vaultServer }),
origin: "https://app.example.com",
}))import { createFastifyVaultHandler } from "@ownfold/fastify"
fastify.all("/api/ownfold/*", createFastifyVaultHandler({
server: vaultServer,
origin: "https://app.example.com",
}))import { createHonoVaultHandler } from "@ownfold/hono"
app.all("/api/ownfold/*", createHonoVaultHandler({ server: vaultServer }))import { createElysiaVaultHandler } from "@ownfold/elysia"
app.all("/api/ownfold/*", createElysiaVaultHandler({ server: vaultServer }))import { createVaultFetchHandler } from "@ownfold/fetch"
export const handleOwnfold = createVaultFetchHandler({ server: vaultServer })Install only the selected adapter package. See framework adapters for runtime-specific mounting, origin checks, and deployment notes.
Add a client only when needed
Done. Do not install @ownfold/browser or @ownfold/react.
pnpm add @ownfold/browser@beta @ownfold/fetch@betaimport { createVaultClient } from "@ownfold/browser"
import { createFetchVaultTransport } from "@ownfold/fetch"
export const vault = createVaultClient({
transport: createFetchVaultTransport({ baseURL: "/api/ownfold" }),
})pnpm add @ownfold/browser@beta @ownfold/fetch@beta @ownfold/react@beta@ownfold/react provides a provider and hooks, not components or CSS. Your application owns every
label, screen, dialog, and interaction.
pnpm add @ownfold/core@beta @ownfold/crypto@betaUse this only when the backend intentionally owns plaintext and keys. It is a different trust model from browser E2EE.
Continue with Encrypt application records for complete browser write, server validation, database storage, read, and decrypt code.