Vennbase

Build multi-user apps without writing a single access rule.

Vennbase is a TypeScript client-side database for collaborative, local-first web apps — with no backend to run, no server to pay for, and no access control rules to misconfigure. Users sign in with their Puter account. Your app only sees the user's subset of the data stored in Puter.
Write your frontend. Vennbase handles the rest.
  • Zero backend — no server to run, no infrastructure bill
  • No access rules to write — share a link, they're in; that's the whole model
  • Optimistic updates — instant writes built-in
  • Local-first support — app data syncs via CRDT automatically
  • NoSQL, open source
  • Auth, server functions — via Puter, one login for your whole app
  • User-pays AI — Puter's AI APIs are billed to the user, not you; build AI features with zero hosting cost
  • Agent-friendly — the explicit-grant model is simple enough that AI coding agents get it right first time
tsx
// Write
const board = db.create("boards", { title: "Launch checklist" }).value;
db.create("cards", { text: "Ship it", done: false, createdAt: Date.now() }, { in: board });

// Read (React)
const { rows: cards } = useQuery(db, "cards", {
  in: board,
  index: "byCreatedAt",
  order: "asc",
});

// Share
const { shareLink } = useShareLink(db, board);

For coding agents

Paste this into your coding agent so it uses Vennbase and pulls the package docs instead of inventing a backend.

Setup

Create one Vennbase instance for your app and pass it an appBaseUrl so that share links point back to your app:

ts
import { Vennbase } from "@vennbase/core";
import { schema } from "./schema";

export const db = new Vennbase({ schema, appBaseUrl: window.location.origin });

Auth and startup

tsx
import { useSession } from "@vennbase/react";

function AppShell() {
  const session = useSession(db);

  if (session.status === "loading") {
    return <p>Checking session…</p>;
  }

  if (!session.session?.signedIn) {
    return <button onClick={() => void session.signIn()}>Log in with Puter</button>;
  }

  return <App />;
}

Creating rows

ts
// Create a top-level row
const board = db.create("boards", { title: "Launch checklist" }).value;

// Create a child row — pass the parent row or row ref
db.create("cards", { text: "Write README", done: false, createdAt: Date.now() }, { in: board });
db.create("cards", { text: "Publish to npm", done: false, createdAt: Date.now() }, { in: board });

create and update are synchronous optimistic writes. Use .value on the returned receipt when you want the row handle immediately.

To update fields on an existing row:

ts
db.update("cards", card, { done: true });

Membership

Once users have joined a row you can inspect and manage the member list:

ts
// Flat list of usernames
const members = await db.listMembers(board);

// With roles
const detailed = await db.listDirectMembers(board);
// → [{ username: "alice", role: "editor" }, ...]

// Add or remove manually
await db.addMember(board, "bob", "editor").committed;
await db.removeMember(board, "eve").committed;

Membership inherited through a parent row is visible via listEffectiveMembers.

Real-time sync (CRDT)

Vennbase includes a CRDT message bridge. Connect any CRDT library to a row and all members receive each other's updates in real time.

Sending CRDT updates requires "editor" access, but all members can poll and receive them.

In React, here is the recommended Yjs integration:

tsx
import * as Y from "yjs";
import { createYjsAdapter } from "@vennbase/yjs";
import { useCrdt } from "@vennbase/react";

const adapter = createYjsAdapter(Y);
const { value: doc, flush } = useCrdt(board, adapter);

// Write to doc normally, then push immediately when needed
await flush();

@vennbase/yjs uses your app's yjs instance instead of bundling its own runtime, which avoids the multi-runtime Yjs failure mode.

Example apps

packages/todo-app is the code from this README assembled into a working app — boards, recent boards, cards, and share links. Run it with:

bash
pnpm --filter todo-app dev

For a fuller picture of how the pieces fit together in a real app, read packages/woof-app. It uses CRDT-backed live chat, user-scoped history rows for room restore, child rows with per-user metadata, and role-aware UI — the patterns you'll reach for once basic reads and writes are working.

bash
pnpm --filter woof-app dev