Orez
Zero on PGLite and SQLite-WASM - An experimental dev server to run Postgres and Zero Sync in dev mode instantly and easily with no configuration.
Install / Use
/learn @natew/OrezREADME
oreZ
Run Zero locally with zero native dependencies. No Postgres install, no SQLite compilation, no Docker.
bunx orez
oreZ makes Zero work on PGlite (Postgres in WASM) and bedrock-sqlite (SQLite in WASM), bundled together so local development is as simple as bun install && bunx orez.
Requirements
- Bun 1.0+ or Node.js 20+
- Zero 0.18+ (tested with 0.18.x)
Limitations
This is a development tool only. Not suitable for production.
- Single-session per database — queries are serialized through a mutex. Fine for development, would bottleneck under load.
- Trigger overhead — every write fires change-tracking triggers.
- Local filesystem — no replication, no HA. Use
orez pg_dumpfor backups.
Features
bunx orez
What oreZ handles automatically:
- Zero native deps — both Postgres and SQLite run as WASM. Nothing to compile, nothing platform-specific.
- Memory management — auto-sizes Node heap (~50% RAM, min 4GB), purges consumed WAL, batches restores with CHECKPOINTs
- Real-time replication — changes sync instantly via
pg_notifytriggers, with adaptive polling fallback (20ms catching up, 500ms idle) - Auto-recovery — finds available ports if configured ones are busy, provides reset/restart controls
- PGlite compatibility — rewrites unsupported queries, fakes wire protocol responses, filters unsupported column types
- Admin dashboard — live logs, HTTP request inspector, restart/reset controls, env viewer
- Production restores —
pg_dump/pg_restorewith COPY→INSERT conversion, auto-coordinates with zero-cache - Extensions — pgvector, pg_trgm, pgcrypto, uuid-ossp, citext, hstore, ltree, fuzzystrmatch, btree_gin, btree_gist, cube, earthdistance enabled by default (lazy-loaded, no overhead unless used)
CLI
bunx orez
--pg-port=6434 postgresql proxy port
--zero-port=5849 zero-cache port
--data-dir=.orez data directory
--migrations=DIR migrations directory (skipped if not set)
--seed=FILE seed file path
--pg-user=user postgresql user
--pg-password=password postgresql password
--skip-zero-cache run pglite + proxy only, skip zero-cache
--log-level=warn error, warn, info, debug
--s3 also start a local s3-compatible server
--s3-port=9200 s3 server port
--force-wasm-sqlite force wasm sqlite even if native is available
--disable-wasm-sqlite force native sqlite (fail if not available)
--on-db-ready=CMD command to run after db+proxy ready, before zero-cache
--on-healthy=CMD command to run once all services healthy
--disable-admin disable admin dashboard
--admin-port=6477 admin dashboard port (default: 6477)
Ports auto-increment if already in use.
Admin Dashboard
Enabled by default at http://localhost:6477.
- Logs — live-streaming logs from zero-cache, filterable by source and level
- HTTP — request/response inspector for zero-cache traffic
- Env — environment variables passed to zero-cache
- Actions — restart zero-cache, reset (wipe replica + resync), full reset (wipe CVR/CDB too)
Logs are also written to separate files in your data directory: zero.log, proxy.log, pglite.log, etc.
bunx orez --disable-admin # disable dashboard
Programmatic API
bun install orez
import { startZeroLite } from 'orez'
const { config, stop, db, instances } = await startZeroLite({
pgPort: 6434,
zeroPort: 5849,
migrationsDir: 'src/database/migrations',
seedFile: 'src/database/seed.sql',
adminPort: 6477, // set to 0 to disable
})
// your app connects to zero-cache at localhost:5849
// database is at postgresql://user:password@localhost:6434/postgres
// db is the postgres PGlite instance (for direct queries)
// instances has all three: { postgres, cvr, cdb }
await stop()
All options are optional with sensible defaults.
Lifecycle hooks
| Hook | CLI | Programmatic | When |
| ----------- | ------------------- | ------------------------------------- | ----------------------------------- |
| on-db-ready | --on-db-ready=CMD | onDbReady: 'CMD' or onDbReady: fn | after db + proxy ready, before zero |
| on-healthy | --on-healthy=CMD | onHealthy: 'CMD' or onHealthy: fn | after all services ready |
Shell commands receive env vars: DATABASE_URL, OREZ_PG_PORT, OREZ_ZERO_PORT. Change tracking triggers are re-installed after onDbReady.
Vite Plugin
import { orezPlugin } from 'orez/vite'
export default {
plugins: [
orezPlugin({
pgPort: 6434,
zeroPort: 5849,
migrationsDir: 'src/database/migrations',
onDbReady: () => console.log('db ready'),
onHealthy: () => console.log('all services healthy'),
}),
],
}
Starts oreZ when vite dev starts, stops on close. Supports all startZeroLite options plus s3 and s3Port.
Backup & Restore
Dump and restore your local database — no native Postgres install needed.
bunx orez pg_dump > backup.sql
bunx orez pg_dump --output backup.sql
bunx orez pg_restore backup.sql
bunx orez pg_restore backup.sql --clean # drop public schema first
Restoring into a running instance
When oreZ is running, restore through the wire protocol:
bunx orez pg_restore backup.sql --pg-port 6434
This automatically:
- Stops zero-cache before restore (via admin API)
- Clears replication state and shard schemas
- Restores the dump
- Adds all public tables to the publication
- Restarts zero-cache
The --direct flag forces direct PGlite access, skipping wire protocol.
What restore handles
- COPY → INSERT — PGlite doesn't support COPY protocol; converted to batched multi-row INSERTs
- Unsupported extensions —
pg_stat_statements,pg_buffercache,pg_cronetc. silently skipped - Idempotent DDL —
CREATE SCHEMA→IF NOT EXISTS,CREATE FUNCTION→OR REPLACE - Oversized rows — rows >16MB skipped with warning (WASM limit)
- Transaction batching — 200 statements per transaction, CHECKPOINT every 3 batches
- Dollar-quoting — correctly parses
$$and$tag$in function bodies
Standard Postgres tools (pg_dump, pg_restore, psql) also work against the running proxy.
Environment Variables
All ZERO_* env vars are forwarded to zero-cache. oreZ provides defaults:
| Variable | Default | Overridable |
| --------------------------- | ------------------ | ----------- |
| NODE_ENV | development | yes |
| ZERO_LOG_LEVEL | from --log-level | yes |
| ZERO_NUM_SYNC_WORKERS | 1 | yes |
| ZERO_ENABLE_QUERY_PLANNER | false | yes |
| ZERO_UPSTREAM_DB | (managed) | no |
| ZERO_CVR_DB | (managed) | no |
| ZERO_CHANGE_DB | (managed) | no |
| ZERO_REPLICA_FILE | (managed) | no |
| ZERO_PORT | (managed) | no |
Common vars you might set:
ZERO_MUTATE_URL=http://localhost:3000/api/zero/push
ZERO_QUERY_URL=http://localhost:3000/api/zero/pull
Local S3
Since Zero apps often need file uploads and MinIO requires Docker:
bunx orez --s3 # with orez
bunx orez s3 # standalone
import { startS3Local } from 'orez/s3'
const server = await startS3Local({ port: 9200, dataDir: '.orez' })
Handles GET, PUT, DELETE, HEAD with CORS. Files stored on disk. No multipart, no ACLs, no versioning.
How It Works
Architecture
oreZ runs three components:
- Three PGlite instances — PostgreSQL 17 in WASM, one per database zero-cache expects (postgres, zero_cvr, zero_cdb)
- TCP proxy — speaks PostgreSQL wire protocol, routes to correct PGlite, handles logical replication
- zero-cache — child process connecting to proxy, thinks it's real Postgres
Why three instances?
zero-cache expects three databases with independent transaction contexts. PGlite is single-session — all connections share one session. Without isolation, CVR transactions get corrupted by postgres queries (ConcurrentModificationException).
| Connection database | PGlite instance | Data directory |
| ------------------- | --------------- | ----------------- |
| postgres | postgres | pgdata-postgres |
| zero_cvr | cvr | pgdata-cvr |
| zero_cdb | cdb | pgdata-cdb |
Replication
PGlite doesn't support logical replication, so oreZ fakes it:
- Triggers capture every mutation into
_orez._zero_changes - Changes are encoded as pgoutput binary protocol
- Streamed to zero-cache through the replication connection
Change notifications use pg_notify for real-time sync. Polling (20ms/500ms adaptive) is fallback only.
SQLite WASM
zero-cache needs SQLite via @rocicorp/zero-sqlite3 (native C addon). oreZ intercepts this at runtime using Node's ESM loader hooks, redirecting to bedrock-sqlite — SQLite's bedrock branch compiled to WASM with BEGIN CONCURRENT and WAL2.
The shim also polyfills the better-sqlite3 API surface zero-cache expects.
Native SQLite mode
For --disable-wasm-sqlite, bootstrap the native addon first:
bun run native:bootstrap
Internal Schema
oreZ stores replica
Related Skills
feishu-drive
340.2k|
things-mac
340.2kManage Things 3 via the `things` CLI on macOS (add/update projects+todos via URL scheme; read/search/list from the local Things database)
clawhub
340.2kUse the ClawHub CLI to search, install, update, and publish agent skills from clawhub.com
postkit
PostgreSQL-native identity, configuration, metering, and job queues. SQL functions that work with any language or driver
