PGB Hub Documentation & Architecture Guides

PGB Hub Documentation

Technical guides, client SDK references, and runnable recipes for all 8 subsystems: functions, containers, PostgreSQL, auth, storage, transactional mail, realtime, and the MCP server for AI agents.

PGB Hub Architecture & AI Agent Guide

Quickstarts for TypeScript, Python and Go, type generation, MCP setup for AI agents, and production recipes.

Read Master Guide

Cloud Categories

PGB Hub Console

Create projects, keys, databases and services.

Open Console →
Getting Started

Projects, Endpoints & SDKs

A PGB Hub project bundles a PostgreSQL database with functions, containers, end-user auth, object storage, transactional mail, realtime channels and an MCP server for AI agents. Project services answer on https://<project>.pgbhub.app; account-level APIs answer on https://console.pgbhub.com.

# Package names for each SDK. They are not on the public registries yet:
# these commands start working with each SDK's first published release.
npm install @pgb/sdk                    # TypeScript / Node.js 18+ (also installs the pgb CLI)
pip install pgbhub                      # Python (Pydantic v2 + httpx)
go get github.com/PGBHub/pgb-sdk-go     # Go
cargo add pgbhub                        # Rust

Subsystem Architecture

  • One account, one bill: the database, compute, auth, storage, mail and realtime for a project are managed from the same console.
  • Two kinds of project key: the secret key (pgb_sec_...) is for servers and has full access; the publishable key (pgb_pub_...) can ship in browsers and apps, and its SQL runs under Row-Level Security.
  • Consistent configuration: the SDKs, the pgb CLI and the MCP server all read the same PGBHUB_* environment variables (PGBHUB_ENDPOINT, PGBHUB_API_KEY, PGBHUB_PROJECT_ID, PGBHUB_DATABASE).
  • Which host to use: storage needs the project gateway (https://<project>.pgbhub.app); the table API, cron, jobs and realtime need https://console.pgbhub.com. Auth, mail and SQL work on either when you send a project key.
Production Note: Never put the secret key in frontend code or log it. Keep it in a server-side environment variable and give browsers and mobile apps the publishable key.
Compute & Functions

Serverless Functions

Deploy JavaScript (Node.js) or Python functions that are served over HTTPS on your project domain, run on a schedule, or receive signed webhooks.

// Function "orders" (runtime: javascript)
// The handler gets one context object: { req, db, env, auth, ... }
export default async function handler({ req, db }) {
  if (req.method !== "POST") {
    return Response.json({ error: "Use POST" }, { status: 405 });
  }

  const { items, total } = req.body;
  const rows = await db.query(
    "INSERT INTO orders (items, total) VALUES ($1, $2) RETURNING id",
    [JSON.stringify(items), total]
  );

  return Response.json({ orderId: rows[0].id }, { status: 201 });
}

Subsystem Architecture

  • Runtimes: JavaScript on Node.js and Python. Each call runs in its own sandboxed process with a 5-second default timeout (configurable) and 128 MB of memory by default.
  • Context: req carries method, headers, query, body and the caller's IP; db.query / db.execute run parameterized SQL against the project database; env holds the function's encrypted environment variables; auth holds the end user when a valid user token is sent.
  • Routing: a function named orders answers at https://<project>.pgbhub.app/functions/orders/<subpath>. The subpath reaches the handler in the x-subpath header.
  • HTTP functions are public: check auth or a shared secret inside the handler. Functions deployed with the webhook trigger type require a valid X-PGBHub-Signature header instead.
Production Note: 'pgb dev' starts a local mock gateway on 127.0.0.1:4000 that returns canned responses, which is useful for wiring up a client offline. It does not run your function code, so test real behavior against your project.
Containers

Containers & Scale-to-Zero

Run a container image or a Git repository with a Dockerfile on its own HTTPS address. Idle containers are stopped and start again on the next request.

# Deploy a container from an image (control plane, Bearer API key)
curl -X POST "https://console.pgbhub.com/api/v1/projects/$PGBHUB_PROJECT_ID/containers" \
  -H "Authorization: Bearer $PGBHUB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "myapp-api",
    "image": "ghcr.io/myorg/myapp-api:2.4.1",
    "port": 8080,
    "health_check_path": "/health",
    "env_vars": {"NODE_ENV": "production"},
    "min_replicas": 0,
    "max_replicas": 3
  }'

# The service is then served at its own subdomain
curl "https://myapp-api.ct.pgbhub.app/health"

Subsystem Architecture

  • Scale to zero: with min_replicas set to 0, a container with no traffic is stopped after its idle timeout (60 seconds by default, configurable per container), so it uses no RAM. The next request starts it again. That is a cold start: in-memory state is not kept.
  • Replica autoscaling: the replica count follows concurrent requests (target_concurrency, default 80 per replica) between min_replicas and max_replicas. More than one replica requires a card on file.
  • Addressing: each container gets https://<container-name>.ct.pgbhub.app (names are unique across the platform), and you can attach your own domain.
  • Secrets: keep credentials such as DATABASE_URL in the container's environment settings in the console rather than in deploy scripts.
  • Availability: containers are enabled per account on request from the project's Containers tab.
Production Note: Use min_replicas: 0 for staging, internal tools and low-traffic services. Keep at least one replica for latency-sensitive production APIs, since waking a stopped container takes a cold start.
Auth & Identity

End-User Auth & Row-Level Security

Sign users in with email and password, 6-digit email codes, magic links or Google/GitHub OAuth, and use their identity in PostgreSQL Row-Level Security policies.

import { createClient } from "@pgb/sdk";

// In a browser or app, use the publishable key, never the secret key
const pgb = createClient({
  endpoint: "https://<project>.pgbhub.app",
  apiKey: process.env.PGBHUB_PUBLISHABLE_KEY,
});

const email = "[email protected]";

// 1. Email the user a 6-digit code (valid for 10 minutes)
await pgb.auth.requestOtp({ email });

// 2. Verify the code the user typed in
const codeFromUser = "849201";
const { user } = await pgb.auth.verifyOtp({ email, token: codeFromUser });
console.log("Signed in as", user.id);

// 3. The client keeps the session and sends the access token on later
//    auth calls (as X-PGB-User-Token)
const me = await pgb.auth.getUser();

// Magic links work the same way
await pgb.auth.sendMagicLink({ email, redirectTo: "https://myapp.example.com/auth/callback" });

Subsystem Architecture

  • Built in: users, sessions, OTP codes, magic links, password reset, MFA and OAuth (Google, GitHub) are part of the project. No separate identity provider is needed.
  • Sessions: sign-in returns an access token (1 hour) and a refresh token (30 days). Send the access token as X-PGB-User-Token, or as Authorization: Bearer on auth routes.
  • RLS: for SQL sent with the publishable key, the gateway verifies X-PGB-User-Token and sets request.jwt.claims for that transaction. auth.uid(), auth.role() and auth.jwt() read it. Without a valid user token the query runs as the anon role.
  • SDK note: the TypeScript SDK currently sends the user token only on auth calls. For queries that must run under a user's RLS policies, call /sql with the header as shown in the cURL tab.
Object Storage

Object Storage & Presigned Uploads

Per-project object storage on Cloudflare R2. Files are uploaded directly to storage through a presigned URL that is valid for 15 minutes.

import { readFile } from "node:fs/promises";
import { createClient } from "@pgb/sdk";

// Storage is served by the project gateway and needs the secret key
const pgb = createClient({
  endpoint: "https://<project>.pgbhub.app",
  apiKey: process.env.PGBHUB_API_KEY, // pgb_sec_...
});

const fileBytes = await readFile("./driver_984.jpg");

// upload() requests a presigned URL (valid 15 minutes) and PUTs the bytes to it
const { path } = await pgb.storage.upload("avatars/driver_984.jpg", fileBytes, {
  contentType: "image/jpeg",
});

// List and delete
const objects = await pgb.storage.list("avatars/");
await pgb.storage.delete(path);

Subsystem Architecture

  • Per-project partitions: every project's objects live under their own prefix, and uploads count toward the project's storage quota.
  • Direct uploads: the presigned URL lets a client send the bytes straight to object storage, so large files never pass through your server. The URL expires after 15 minutes (expires_in: 900).
  • Keys: uploading and deleting need the secret key. Listing and usage accept any key for the project.
  • Buckets: buckets and files created in the console's Storage tab are served at https://<project>.pgbhub.app/storage/<bucket>/<path> with long-lived cache headers.
Transactional Mail

Transactional Mail

Send transactional email from your project's verified domain with one API call. Bounces and complaints are tracked for you.

import { createClient } from "@pgb/sdk";

// Sending needs the project's secret key, so call this from your server
const pgb = createClient({
  endpoint: "https://<project>.pgbhub.app",
  apiKey: process.env.PGBHUB_API_KEY, // pgb_sec_...
});

const res = await pgb.mail.send({
  to: "[email protected]",
  subject: "Your ride receipt #9842",
  html: "<h1>Thanks for riding with us</h1><p>Total: $24.50</p>",
  text: "Thanks for riding with us. Total: $24.50",
});

// success means the mail provider accepted the message for delivery
console.log("Accepted for delivery:", res.success);

Subsystem Architecture

  • Accepted, not delivered: a successful response (status: sent) means the provider accepted the message for delivery. Delivery, bounce and complaint events arrive later from the provider.
  • Bounce and complaint handling: provider webhooks record bounces and complaints, which feed the project's sending reputation and automatic quarantine.
  • Your own domain: add a sending domain in the console and publish the DNS records it shows to send from your own address.
  • Sandbox first: new accounts can send only to their own team and verified addresses (up to 100 messages a day) until production sending access is approved.
Realtime

Realtime Channels

Broadcast JSON messages to named channels and receive them over Server-Sent Events. Channels are scoped to your project.

import { createClient } from "@pgb/sdk";

// Realtime is served by the control plane; use a project key
const pgb = createClient({
  endpoint: "https://console.pgbhub.com",
  apiKey: process.env.PGBHUB_API_KEY,
});

// Subscribe: returns a function that closes the stream
const unsubscribe = pgb.realtime.subscribe(
  "orders",
  ({ event, data }) => console.log(event, data),
  (err) => console.error("stream error", err)
);

// Broadcast from your server
await pgb.realtime.broadcast("orders", { orderId: 42, status: "paid" }, { event: "order.paid" });

unsubscribe();

Subsystem Architecture

  • Transport: Server-Sent Events over HTTPS (not WebSockets). A client that reconnects with Last-Event-ID gets the recent messages it missed.
  • Project-scoped channels: channel names are namespaced per project, so two projects using the same channel name never see each other's messages.
  • Publishing rules: a secret key may publish as the server. A publishable key may publish only together with a signed-in user's token (X-PGB-User-Token), and the message is attributed to that user.
  • Scope: this is publish/subscribe messaging. It does not stream database changes.
AI & MCP Integration

MCP Server for AI Agents (@pgb/mcp)

Connect Claude Desktop, Claude Code, Cursor and other MCP clients to your project so agents can read the schema, run parameterized SQL and deploy functions and containers.

# @pgb/mcp is not on npm yet. Build it from the public repository:
git clone https://github.com/PGBHub/pgb-mcp.git
cd pgb-mcp
npm install
npm run build

# Then point your MCP client at dist/index.js (see the MCP Config tab).
# Once @pgb/mcp is published, "command": "npx", "args": ["-y", "@pgb/mcp"] works too.

Subsystem Architecture

  • Where the config goes: .cursor/mcp.json for Cursor, claude_desktop_config.json for Claude Desktop.
  • Transport: the server speaks the Model Context Protocol over stdio.
  • Configuration: it reads PGBHUB_API_KEY, PGBHUB_PROJECT_ID and PGBHUB_ENDPOINT (default https://console.pgbhub.com), the same variables the SDKs use. The --key, --project and --endpoint flags do the same job.
  • Sandbox mode: started without both an API key and a project id, it provisions a temporary sandbox project that you can claim later.
  • Permissions: agents act with the API key you give them. Use a project-scoped key so an agent can reach only that project.
Production Note: Ask the agent to call pgbhub_get_project_graph first, so it reads the real tables and column types before it writes SQL.
PostgreSQL & JSONB Core

PostgreSQL 18, JSONB, PostGIS & pgvector

Every project is a real PostgreSQL database: store JSON documents in JSONB columns, run geospatial queries with PostGIS and similarity search with pgvector.

-- Enable postgis and vector for your database from the console's Extensions list.

-- 1. JSONB containment, accelerated by a GIN index
CREATE INDEX IF NOT EXISTS venues_data_gin ON venues USING GIN (data jsonb_path_ops);

SELECT id, data->>'name' AS venue, data->'pricing'->>'tier' AS tier
FROM venues
WHERE data @> '{"status": "OPEN", "category": "restaurant"}'::jsonb;

-- 2. PostGIS: venues within 5 km, nearest first
SELECT id, data->>'name' AS name,
  ST_Distance(geom::geography, ST_SetSRID(ST_MakePoint(-122.4194, 37.7749), 4326)::geography) AS distance_meters
FROM venues
WHERE ST_DWithin(geom::geography, ST_SetSRID(ST_MakePoint(-122.4194, 37.7749), 4326)::geography, 5000)
ORDER BY distance_meters ASC
LIMIT 10;

-- 3. pgvector: nearest neighbours by cosine distance
CREATE TABLE IF NOT EXISTS documents (
  id bigserial PRIMARY KEY,
  title text NOT NULL,
  embedding vector(1536)
);
CREATE INDEX IF NOT EXISTS documents_embedding_hnsw
  ON documents USING hnsw (embedding vector_cosine_ops);

SELECT id, title, 1 - (embedding <=> $1::vector) AS similarity
FROM documents
ORDER BY embedding <=> $1::vector
LIMIT 5;

Subsystem Architecture

  • PostgreSQL 18: databases on the shared server run PostgreSQL 18. Databases on dedicated pods may run an earlier major version; the console shows the version of each database.
  • Standard transactions: ACID transactions, MVCC, foreign keys and joins work exactly as in any PostgreSQL database.
  • GIN with jsonb_path_ops: index JSONB columns so containment (@>) queries use the index instead of scanning the table.
  • Extensions: PostGIS and pgvector (with HNSW and IVFFlat indexes) are available alongside an allowlist of other extensions such as hypopg and pg_partman.
  • Table API filters: pgb.table().find() matches top-level columns by equality only. For JSONB paths, ranges or vectors, use pgb.db.query with parameters as shown.
Infrastructure & Backups

Connections, Pooling & Backups

Connect with any PostgreSQL driver over TLS, directly or through PgBouncer. Automated daily snapshots are copied to offsite object storage.

# Copy the exact connection strings from the console. Keep the password in
# PGPASSWORD (or ~/.pgpass) rather than in the URL.
export PGPASSWORD="<database password>"

# Direct connection (TLS required)
psql "postgresql://[email protected]:5433/$PGDATABASE?sslmode=require"

# PgBouncer, transaction pooling (TLS required)
psql "postgresql://[email protected]:6432/$PGDATABASE?sslmode=require"

Subsystem Architecture

  • Two ports: 5433 connects straight to PostgreSQL; 6432 goes through PgBouncer in transaction mode, which accepts up to 1,000 client connections on the shared server and multiplexes them over a small pool. Use the pooler for serverless workers; use the direct port for migrations and session features such as LISTEN or session-level prepared statements.
  • Hostnames: shared databases use db.pgbhub.app. Databases on a dedicated pod get their own hostname, shown in the console.
  • Automated snapshots: one snapshot a day, copied to offsite object storage. The free tier keeps up to 2 snapshots for 7 days. Hourly snapshots and longer retention are a paid backup add-on.
  • One-click restore: restore a database from any retained snapshot in the console.
  • Not available for shared databases: point-in-time recovery, branching and read replicas.