A backend in one crate.

Collections, auth, files, realtime and an admin dashboard in one Rust binary. Speaks the PocketBase API — the official SDK just works. SQLite today, Postgres when you need it.

$ curl -fsSL https://cratebase.dev/install.sh | sh
Downloading Cratebase 0.1.0 for aarch64-apple-darwin...
cratebase 0.1.0 installed to ~/.local/bin/cratebase

$ cratebase serve
listening on http://127.0.0.1:8090
dashboard:   http://127.0.0.1:8090/_/
api:         http://127.0.0.1:8090/api/
→ open http://localhost:8090

Try it without installing

A live public instance isn't running yet — this section is a placeholder for a real, always-on demo (a guestbook backed by realtime + rules + cron, driven by the exact SDK calls shown below) rather than something to fake with a canned response.

Demo instance coming soon. In the meantime:

import PocketBase from "pocketbase";

const pb = new PocketBase("http://localhost:8090");
await pb.collection("guestbook").create({ text });
pb.collection("guestbook").subscribe("*", (e) => render(e.record));

Three things it does that PocketBase doesn't

Faster under load

22 of 24 benchmark cells were faster on Cratebase in the latest idle-host run, several by an order of magnitude.

CategoryConc.CratebasePocketBaseRatio
search5049,986 req/s4,774 req/s10.47x
search-auth10038,768 req/s3,989 req/s9.72x
search-wide10016,414 req/s1,924 req/s8.53x
create10011,960 req/s6,606 req/s1.81x

The auth win is a security-parameter difference (Argon2id vs. bcrypt cost-12), not throughput work — read honestly, not as an engineering claim. Two delete cells at low concurrency read 0.95x/0.97x, within noise of parity.Full methodology and results →

Reproduce it: benchmarks/run.sh

Postgres, zero code change

The same API, the same filter syntax, the same rules — switch backends with one environment variable:

DATABASE_URL=sqlite://./data.db
DATABASE_URL=postgres://user:pass@host/db

On Postgres, realtime also becomes cross-node viaLISTEN/NOTIFY — a write on one app instance reaches a client subscribed on another instance in ~95–100ms measured, HTTP-in to SSE-out.

An AI backend, not just a database

A vector field, auto-embedding, and an LLM gateway that streams over the realtime connection you already have:

nearestTo(pb, "chunks", "embedding", vec, { limit: 5, filter: 'docId = "abc"' })
chat(pb, messages, { onDelta })   // streams as llm_chunk over /api/realtime
POST /api/mcp {"method":"tools/list"}  // -> list_memories, create_memories, ...

Ranking is application-side cosine similarity over up to 20,000 candidates — honest about not being a vector database at massive scale.AI overview → ·docmind example

Everything else, plainly

  • a crate is a collection
  • a locked crate is an auth collection
  • a stack is a database
  • the belt is realtime
Dynamic collections
Define fields through the API/dashboard; a real SQL table is created and migrated for you. POST /api/collections
API rules
list/view/create/update/delete access, enforced in SQL, not application code you have to trust. listRule, viewRule, ...
Auth
Password, refresh, verification, reset, email change, OTP, MFA, OAuth2 (Google/GitHub), impersonation, login alerts. POST .../auth-with-password
Realtime
Subscribe to a collection or a single record over SSE; create/update/delete events as they happen. GET /api/realtime
Files
Local disk or any S3-compatible bucket, plus thumbnails and protected-file access tokens. ?thumb=100x100
Batch
Several record writes in one HTTP round trip and one SQL transaction. POST /api/batch
JS hooks
pb_hooks/*.pb.js — PocketBase-parity lifecycle hooks and routerAdd, on an embedded QuickJS runtime. onRecordCreate, routerAdd
SQL cron jobs
A _cron_jobs record is the whole job: name, schedule, and raw SQL. Reactive, no redeploy. _cron_jobs
Webhooks
Outgoing (_webhooks, signed) and a worked pattern for verifying inbound HMAC-signed webhooks. X-Cratebase-Signature
Push notifications
Web Push, FCM, and APNs, via _push_subscriptions. _push_subscriptions
Schema as code
Export collections to checked-in JSON; apply with a dry-run plan that flags destructive changes. POST /api/schema/apply?dryRun=1
API keys
Dashboard-minted keys as ordinary identities — for agents, CI, and services. Settings → API keys
Teams
_teams / _team_members for scoping an app collection to a workspace via rules. _team_members
Audit log
Append-only log of schema changes, settings updates, and superuser account changes. _audit_log
Metrics
Prometheus exposition, unauthenticated and outside /api by design. GET /metrics
Backups
Streaming VACUUM INTO snapshots to local disk or S3, from the dashboard. Settings → Backups

Moving from PocketBase

cratebase migrate-from-pocketbase pb_data --dir cratebase_data
collections created: 2
  + categories
  + books
collections updated (existing schema, fields merged): 1
  ~ users
records migrated:
  _superusers: 1
  books: 3
  categories: 2
  users: 1
files copied: 4
  • Passwords keep working — bcrypt hashes verify directly, re-hashed to Argon2id on next login.
  • Collection and record ids are preserved (deterministic id derivation, verified identical to PocketBase's own).
  • Not migrated: sessions, OAuth2 secrets, _mfas/_otps/_externalAuths/_authOrigins, pb_hooks.
- const pb = new PocketBase("https://pocketbase.example.com");
+ const pb = new PocketBase("https://cratebase.example.com");
Read the full migration guide →