protobase
v0.1.0
Published
Run a fleet of Supabase Lite (supalite) projects in the background on a single Postgres cluster. One daemon, one cluster, many databases — with instant PG18 file-copy database forks.
Maintainers
Readme
⚡ litekit — @supabase/lite-kit
Run a whole fleet of Supabase Lite ("supalite") projects in the background on one local Postgres cluster.
One shared Postgres cluster + one tiny gateway process serve every project. Each project gets its own real Postgres database in the cluster, and is routed to by hostname/path — so you can spin up dozens of isolated Supabase-compatible backends without a container or a spare port per project.
npm install -g @supabase/lite-kit # exposes the `litekit` CLI (needs Bun + PostgreSQL)
litekit start # boot the Postgres cluster + gateway
litekit new my-project # new database + live REST/Auth API
litekit fork my-project experiment # instant clone of a database (PG18 CoW)
litekit list # see the fleet
litekit ui --open # web UI to explore projects, branches & dataPoint @supabase/supabase-js straight at a project:
import { createClient } from "@supabase/supabase-js";
// in a browser, *.localhost resolves to 127.0.0.1 automatically:
const supabase = createClient("http://my-project.lite.localhost:54320", "any-key-works");
const { data } = await supabase.from("todos").select("*");How it works
┌──────────────────────────────────────────┐
my-project.lite. │ litekit gateway (one Bun.serve process) │
localhost:54320 ───► │ │
/@my-project/... │ routes by host / path / header to an │
X-Litekit-Project │ in-memory supalite App per project │
└───────┬───────────────┬───────────────┬───┘
│ │ │
┌──────▼──────┐ ┌──────▼──────┐ ┌──────▼──────┐
│ litekit_ │ │ litekit_ │ │ litekit_ │ one shared
│ my_project │ │ experiment │ │ blog │ PostgreSQL
└─────────────┘ └─────────────┘ └─────────────┘ cluster :5488Supalite is effectively stateless per request — everything lives in its
Postgres database — so litekit doesn't spawn a process per project. Instead a
single gateway holds one lightweight in-memory App per project and routes
each request to the right one. That's dramatically cheaper than a
process-and-port per project, and it's what lets litekit start + litekit new
scale to a large fleet on a laptop.
Everything lives under ~/.litekit/ (override with LITEKIT_HOME):
~/.litekit/
├── config.json # chosen ports + resolved postgres binary
├── cluster/ # the one shared PGDATA
├── registry.json # the fleet (source of truth for the gateway)
├── daemon.json # gateway pid + ports
├── daemon.log
└── projects/<name>/ # a supabase/ scaffold per project (config, schema, migrations)Routing — three ways to reach any project
Every project is served on the single gateway port (default 54320):
| Method | Example | Best for |
| ------------ | ------------------------------------------ | ---------------------- |
| Subdomain | http://my-project.lite.localhost:54320 | Browsers (auto DNS) |
| Path prefix | http://localhost:54320/@my-project | Anything, no DNS setup |
| Header | X-Litekit-Project: my-project | API clients |
Browsers resolve *.localhost to loopback automatically. For curl or a
Node/Bun client where arbitrary *.localhost names may not resolve, use the
path prefix or the header.
Instant forks (PostgreSQL 18)
litekit fork <source> <name> runs CREATE DATABASE <name> TEMPLATE <source>.
On PostgreSQL 18 litekit sets file_copy_method = clone, so the copy is a
copy-on-write reflink clone on APFS / Btrfs / XFS — near-instant regardless of
database size, and space-efficient until the fork diverges. See
boringsql: instant database clones.
litekit degrades gracefully:
| Server | Fork strategy | Notes |
| ------------- | ---------------------------- | ------------------------------ |
| PG 18+ | STRATEGY file_copy + clone | copy-on-write, instant |
| PG 15–17 | STRATEGY file_copy | full block copy |
| PG < 15 | default TEMPLATE | wal_log copy |
Install PG18 for the fast path: brew install postgresql@18. litekit prefers
postgresql@18 if present and falls back to the newest install it finds
(override with LITEKIT_PG_BIN=/path/to/pg/bin).
Commands
CLUSTER
litekit start [--pg-port N] [--gateway-port N] init + start Postgres and the gateway
litekit stop stop the gateway and Postgres
litekit restart stop then start
litekit status cluster + gateway + project state
PROJECTS
litekit new <name> [--schema <file>] create a project (new database + route)
litekit new <name> --fork <source> create a project by forking a database
litekit fork <source> <name> shorthand for the above
litekit list | ls list projects (status, route, size)
litekit info <name> routes + connection string for one project
litekit pause <name> stop serving a project (keeps its database)
litekit resume <name> resume a paused project
litekit delete <name> [--yes] drop the database and remove the project
DATA
litekit ui [--port N] [--open] web UI to explore projects, branches & data
litekit sql <name> [query] run SQL against a project (or pipe via stdin)
litekit exec <name> -- <lite args…> run any @supabase/lite CLI command
litekit logs [--daemon|--pg] [--follow] view gateway or Postgres logsWeb UI (Supabase Platform Kit)
litekit ui serves a local web app (default http://localhost:54330) for
exploring the fleet visually: projects → branches → tables → data, plus a
SQL editor per branch.
It's built on Supabase's Platform Kit.
The Platform Kit sits on top of the Supabase Management API — its data layer
runs SQL through POST /v1/projects/{ref}/database/query and introspects schema
with Supabase's own pg-meta queries. litekit implements that exact contract
locally:
src/web/client/pg-meta.tsis vendored verbatim from the Platform Kit (the realpg_catalogintrospection SQL), and the Management-API client is the sameopenapi-fetchclient pointed at/api/supabase-proxy.src/web/server.tsis the local proxy target: instead of forwarding toapi.supabase.com, it runs the SQL against the right database in the shared cluster. Because litekit uses real Postgres, every Supabase introspection query works natively.
litekit maps its model onto the Platform Kit's world: a root project is a
project, and each fork is a branch (main = the root database). The UI's
New branch button forks the selected branch's database — an instant PG18
copy-on-write clone. Reads run in READ ONLY transactions.
The stock Platform Kit ships as Next.js + shadcn/ui components. This repo runs on Bun with HTML imports (no Next.js/Vite, per
CLAUDE.md), so litekit reuses the Platform Kit's data engine and Management-API contract and renders the explorer as a Bun-bundled React app in the same visual language. The/api/supabase-proxy/...surface is drop-in compatible if you later want to mount the stock components.
Schema & migrations
litekit new --schema file.sql installs your Postgres DDL as the project's first
migration. After that, manage schema with the real supalite CLI via exec:
litekit exec my-project -- migration new add_users
litekit exec my-project -- migration up
litekit exec my-project -- db diff -f tweak
litekit sql my-project "select count(*) from todos"Requirements
- Bun ≥ 1.1 (the CLI runs on Bun).
- PostgreSQL installed locally (18 recommended for instant forks; 14+ works). litekit runs your Postgres binaries directly — it does not bundle a server.
Programmatic use
The pieces are exported for scripting your own fleets:
import { ensureConfig, startCluster, createDatabase } from "@supabase/lite-kit";
const cfg = await ensureConfig();
await startCluster(cfg);
await createDatabase(cfg, "litekit_scratch");Notes & limitations
- The cluster uses trust auth on 127.0.0.1 — it's a local dev tool, not for exposure to a network.
- Forking evicts the source project's live connection momentarily (the template database must have no active sessions), then reloads it lazily.
- Supalite is alpha; see its
LIMITATIONS.mdfor feature coverage (PostgREST + GoTrue subset).
Releasing (maintainers)
The package is published as Bun-native TypeScript — no build step; src/*.ts
and index.ts ship as-is and run under Bun (the litekit bin uses a
#!/usr/bin/env bun shebang).
bun run typecheck # gate (also runs automatically on publish via prepublishOnly)
npm run release:dry # preview the tarball (npm publish --dry-run)
npm run release # npm publish (scoped public via publishConfig.access)npm publish runs prepublishOnly (typecheck) first and refuses to publish if
it fails. The published tarball is an allowlist (files): src/, index.ts,
README.md, LICENSE only.
Update
repository/homepage/bugsinpackage.jsonto the real repo URL, and publishing under the@supabasescope requires membership in that npm org (or rename the scope). Bump the version withnpm version <patch|minor|major>before releasing.
