Storage
Copy page
import { postgresStorage, buildPostgresProvisionPlan } from "@nizhal/server/adapters";
const storage = postgresStorage({ connectionString: process.env.DATABASE_URL! });await storage.provision({ schema, syncRules });StorageAdapter interface
Section titled “StorageAdapter interface”getChanges— cursor pull with bucket scope, tombstones,removedBucketstransaction— atomic mutator executionauthorizeMutatorTx— fail-closed write scoping from the actor’s sync-rule membership inside that transactionclaimMutation/recordApplied— idempotent push + client-id reconciliationprovision— DDL from schema + sync rules
buildPostgresProvisionPlan returns raw SQL statements for inspection or CI diffing.
What provision creates
Section titled “What provision creates”updated_at/deleted_atplus_nizhal_row_versionon synced tables_nizhal_sync_controlloop-gate + per-table change log_nizhal_mutations— idempotency + client→server ID map_nizhal_tombstones— delete propagation_nizhal_client_buckets— scope tracking- Bucket + row-version indexes for cursor scans
No logical replication. Runs on managed Postgres (Neon, RDS, Supabase) without superuser WAL settings.
nizhal migrate --config nizhal.config.jsSee CLI reference.
Custom adapters
Section titled “Custom adapters”StorageAdapter is the seam for alternate backends (D1/SQLite backlog). Phase 0 ships Postgres only; the interface exists so a second backend is a new adapter, not a rewrite.