The PgPaw unb node owns the application data plane: migrations, schema, raw SQL, and realtime. Worldant retains only its private durability kernel and generates no application data client.
The root worldant.ts configures the data plane only
through the strict pgpaw object. Embedded mode (builtin: true) uses dataDir and databaseName;
databaseName applies to the embedded PostgreSQL
engine. Remote mode (builtin: false) uses
remoteAddress, whose DSN reaches the PgPaw-owned
PostgreSQL carrying the durability kernel. Both modes require
minConnections and maxConnections.
In embedded mode, PgPaw owns the primary and exposes its DSN
through its Rust API. Worldant opens a direct SQLx PostgreSQL pool
from that DSN for kernel SQL; application SQL binds in-process to
the primary's SQL operations under the neutral
pgpaw_public role. The primary also attaches PgPaw's
services to the Worldant node as a listenerless child over a
private Unix socket. This adds the routed
pgpaw.sql and pgpaw.live services
without creating another database, replica, or public PgPaw
listener.
Applications define schema with ordered SQL migrations, applied
through PgPaw's schema surface at build. A migration is immutable
once applied and must remain present on disk; each file commits in
its own transaction, so a mid-chain failure keeps earlier files
applied. A destructive contraction (DROP TABLE,
DROP COLUMN) is refused unless the file carries the
-- pgpaw: acknowledge-destructive comment —
acknowledge only after every run and snapshot depending on the
prior schema has retired.
The client
Commands and Steps reach application data by subject over the activation-bound session:
import { session } from "worldant/client"
type SqlReply<Row> = { command: string; rows: Row[]; rowsAffected: number }
const reply = (await session.request("pgpaw.sql", {
sql: "select id from toys where owner = $1",
params: [input.owner],
})) as SqlReply<{ id: string }>
// reply = { command: "SELECT", rows: [...], rowsAffected: 0 }
session.request("pgpaw.sql", { sql, params })
executes exactly one parameterized statement and always answers
{ command, rows, rowsAffected }; you supply the row
type. Each call commits independently — there is no handler-wide
or multi-statement transaction, and a retried Step may re-issue a
committed write, so mutations must be idempotent. Row-returning
SQL must expose unique output column names. Realtime-safe live
reads are served by the PgPaw node's own
pgpaw.live root;
session.subscribe throws inside an activation, so
pgpaw.live is a client-session subscription.
Parameter binding
Parameters bind as JSON values against the statement's described types. Three consequences:
Types outside the JSON families need a text cast. Bind the value as text and cast in SQL:
await session.request("pgpaw.sql", {
sql: "select * from toys where id = ($1::text)::uuid",
params: [input.id],
})
A JS array binds as jsonb, not a Postgres array.
= any($1) fails. Unnest the jsonb value instead:
await session.request("pgpaw.sql", {
sql: "select * from toys where id in (select jsonb_array_elements_text($1)::uuid)",
params: [input.ids],
})
Absence is an empty rows array.
Check reply.rows.length when zero rows is a normal
outcome.
Authorization
Worldant does not define users, JWTs, claims, tenants, or token validation. Applications bring their own authentication and authorization model.
Application SQL executes under the fixed neutral
pgpaw_public role (NOLOGIN,
NOBYPASSRLS). The activation's ingress headers are
projected onto every outgoing request, so row-level-security
policies keyed on request.headers hold across the
wire. For served requests, the activation's preserved headers
reach SQL as transaction-local JSON:
current_setting('request.headers', true)::jsonb
Application-table authorization is normal PostgreSQL row-level security:
ALTER TABLE todos ENABLE ROW LEVEL SECURITY;
ALTER TABLE todos FORCE ROW LEVEL SECURITY;
CREATE POLICY todos_owner ON todos
TO pgpaw_public
USING (
owner_id = current_setting('request.headers', true)::jsonb->>'authorization'
)
WITH CHECK (
owner_id = current_setting('request.headers', true)::jsonb->>'authorization'
);
Do not trust raw client headers in production unless they have been validated by a trusted gateway, middleware, or SQL function.
Root worldant.policy.json can select kernel event
visibility with events and ownerHeader.
Application-table RLS belongs in migrations.