Skip to content
Ownfold
Esc
navigateopen⌘Jpreview

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@beta
pnpm add @ownfold/server@beta
pnpm add -D @ownfold/cli@beta
yarn add @ownfold/server@beta
yarn add --dev @ownfold/cli@beta
bun add @ownfold/server@beta
bun add -d @ownfold/cli@beta

Choose a database

pnpm add @ownfold/sqlite@beta
pnpm ownfold init --database sqlite --identity custom
pnpm ownfold migrate --database-path ./ownfold.db
import { 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 custom

Merge 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@beta
import { betterAuthUserResolver } from "@ownfold/better-auth"
import { auth } from "./auth"

export const getUserId = betterAuthUserResolver(auth)
pnpm add @ownfold/auth-js@beta
import { 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.POST
import { 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@beta
import { 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@beta

Use 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.

Last updated on August 4, 2026

Was this page helpful?