pgvpd 1.0.0

Virtual Private Database for PostgreSQL — tenant isolation at the connection level
# ──────────────────────────────────────────────────────────────
# Pgvpd Configuration
# ──────────────────────────────────────────────────────────────
# Copy to pgvpd.conf and edit for your environment.
#
# All values shown are defaults. Uncomment and change as needed.
# Environment variables (PGVPD_*) and CLI flags override these.
# ──────────────────────────────────────────────────────────────

# ─── Listener ────────────────────────────────────────────────

# Port Pgvpd listens on for client connections
# port = 6432

# Address to bind to (use 0.0.0.0 to listen on all interfaces)
# listen_host = 127.0.0.1

# ─── Upstream PostgreSQL ─────────────────────────────────────

# Postgres host
# upstream_host = 127.0.0.1

# Postgres port (use your Supabase local port if applicable)
# upstream_port = 5432

# ─── Tenant Extraction ──────────────────────────────────────

# Character separating the role from the tenant payload in the username.
# With separator '.', username 'app_user.acme' yields:
#   role = 'app_user', tenant = 'acme'
# tenant_separator = .

# Postgres session variable(s) set for each tenant connection.
# Your RLS policies should reference these via current_setting().
# Comma-separated for multiple variables.
# context_variables = app.current_tenant_id

# Separator for multiple values in the tenant payload.
# With context_variables = app.current_list_id,app.current_user_id
# and value_separator = :, username 'app_user.list123:user456' yields:
#   app.current_list_id = 'list123'
#   app.current_user_id = 'user456'
# value_separator = :

# ─── Role Override ──────────────────────────────────────────

# Override which role pgvpd switches to after authentication.
# By default, pgvpd does SET ROLE {rewritten_username}.
# Set this to use an existing NOLOGIN role (e.g., Supabase's
# "authenticated") so existing RLS policies work unchanged.
# Requires: GRANT <set_role> TO <login_role> on the database side.
# set_role = authenticated

# ─── Access Control ─────────────────────────────────────────

# Usernames that bypass tenant extraction entirely.
# These connections are passed through to upstream as-is.
# Comma-separated. Used for admin, migration, superuser access.
# superuser_bypass = postgres

# ─── TLS Termination (client → Pgvpd) ────────────────────

# Port for TLS-encrypted client connections.
# Requires tls_cert and tls_key. Plain listener stays on `port`.
# tls_port = 6433

# Path to PEM certificate file
# tls_cert = /etc/pgvpd/server.crt

# Path to PEM private key file
# tls_key = /etc/pgvpd/server.key

# ─── TLS Origination (Pgvpd → upstream Postgres) ────────

# Connect to upstream Postgres over TLS
# upstream_tls = false

# Verify upstream TLS certificate (set to false for self-signed)
# upstream_tls_verify = true

# Custom CA certificate for upstream verification
# upstream_tls_ca = /etc/pgvpd/upstream-ca.crt

# ─── Timeouts ────────────────────────────────────────────────

# Max seconds for startup + auth + injection handshake.
# Active queries (transparent pipe) are not affected.
# handshake_timeout = 30

# ─── Context Resolvers ───────────────────────────────────────
#
# Path to a TOML file defining context resolvers.
# Resolvers are SQL queries that run after authentication to derive
# additional session variables from database state (org memberships,
# team grants, ACLs). See resolvers.toml.example for format.
# resolvers = resolvers.toml

# ─── Connection Pooling ──────────────────────────────────────
#
# Session pooling reuses upstream connections across clients.
# In pool mode, pgvpd authenticates clients itself (cleartext)
# and connects to upstream using upstream_password.
#
# Superuser bypass connections are never pooled.
#
# Pool key: (database, role) — all tenants sharing a role/db
# reuse connections from the same bucket.

# Pool mode: none (default, passthrough) or session (pooling)
# pool_mode = none

# Max upstream connections per (database, role)
# pool_size = 20

# Password clients must provide when pool_mode = session.
# Required for session mode.
# pool_password = changeme

# Password pgvpd uses to authenticate to upstream Postgres.
# Required for session mode.
# upstream_password = changeme

# Seconds an idle pooled connection lives before being closed.
# pool_idle_timeout = 300

# Seconds to wait for a connection when the pool is full.
# pool_checkout_timeout = 5

# ─── Tenant Isolation ───────────────────────────────────────
#
# Per-tenant controls: allow/deny lists, connection limits, rate limits.
# These apply to the tenant payload extracted from the username.

# Only allow these tenants (comma-separated). All others rejected.
# Cannot be used with tenant_deny.
# tenant_allow = tenant_a, tenant_b

# Block these tenants (comma-separated). All others allowed.
# Cannot be used with tenant_allow.
# tenant_deny = blocked_tenant

# Max concurrent connections per tenant.
# tenant_max_connections = 50

# Max new connections per tenant per second.
# tenant_rate_limit = 100

# Seconds of inactivity before a tenant connection is terminated.
# In passthrough mode: connection lifetime timeout.
# In pool mode: idle timeout (resets on each data transfer).
# tenant_query_timeout = 300

# ─── Admin API ──────────────────────────────────────────────
#
# HTTP port for the admin API (health, metrics, pool status).
# Disabled by default. Enable for load balancer health checks
# and Prometheus metrics scraping.
# admin_port = 9090

# ─── Logging ─────────────────────────────────────────────────

# Log level: debug, info, warn, error
# log_level = info