PostgreSQL Wire Protocol (PGWire) Compatibility
SpacetimeDB supports the PostgreSQL wire protocol ( PGWire), enabling compatibility with PostgreSQL clients and tools.
The PostgreSQL wire protocol is a network protocol used by PostgreSQL clients to communicate with compatible servers. It defines how messages are formatted and exchanged between client and server. The protocol is agnostic to the query dialect, meaning it can be used with different SQL engines and feature sets, in concrete, SpacetimeDB.
This allows users to leverage the existing PostgreSQL ecosystem, including drivers, ORMs, IDEs, CLI tools, and GUI clients that support PostgreSQL.
When using PGWire with SpacetimeDB, consider the following:
Feature Support
SpacetimeDB is progressively adding PostgreSQL client compatibility. Some features are unsupported, partially implemented, or behave differently:
- Protocol Version: Only PGWire protocol version 3.0 is supported, and only the Simple Query Protocol, and without parameterized queries.
- SQL Features: Only the subset of SQL features documented in the SQL documentation are supported. Subscription queries do not update in real time.
- Authentication: SpacetimeDB does not implement database users or roles. The connection string
user_name@database_nameignoresuser_name; onlydatabase_nameis used. Authentication is based on the auth token provided via thepasswordfield. - SSL/TLS: SSL is supported only for
SpacetimeDB Clouddeployments (without mutual TLS). Other deployments (such asSpacetimeDB Standalone) do not support SSL/TLS connections. - System Tables and Views: SpacetimeDB provides its own system tables (e.g.,
SELECT * FROM st_table) for introspection. These are not PostgreSQL-compatible, so tools relying on PostgreSQL system catalogs will not work. - Port and Host:
- In
SpacetimeDB Standalonedeployments, specify the port withspacetime start --pg-port <port>. Without this flag, connections using the PostgreSQL protocol are not enabled. - In
SpacetimeDB Clouddeployments, the port is always5432.
- In
- Transactions: User-defined transactions (
BEGIN TRANSACTION,COMMIT, etc.) are not supported. Each SQL statement executes in its own transaction context. Client libraries should disable automatic transaction handling. - Special Data Types: Some SpacetimeDB data types map to PostgreSQL types as:
- Simple enums are displayed as
Enum. - Algebraic Data Types (ADTs) & records are displayed as
JSON. Durationis displayed asInterval.Identity,ConnectionId,U8,[U8],Bytes&Hexis displayed asBytea.
- Simple enums are displayed as
Connection Parameters
To connect to SpacetimeDB using a PostgreSQL client, use the following parameters:
- Host:
localhostforSpacetimeDB Standalonedeploymentsmaincloud.spacetimedb.comforSpacetimeDB Clouddeployments
- Port:
5432forSpacetimeDB Cloud- The value passed with
--pg-portforSpacetimeDB Standalone
- Database: The target SpacetimeDB database
- User: Any string (ignored by SpacetimeDB)
- Password: The
auth token - SSL Mode:
require(only forSpacetimeDB Cloud)
Auth Token
The auth token is sensitive. Do not expose it in logs, version control, or insecure locations.
SpacetimeDB uses the password field to pass the auth token. Obtain the token with:
spacetime login show --token
To export the token to PGPASSWORD:
For bash:
export PGPASSWORD="$(spacetime login show --token | sed -n 's/^Your auth token.*is //p')"
For PowerShell:
$env:PGPASSWORD = (spacetime login show --token | Select-String 'Your auth token.*is (.*)' | % { $_.Matches[0].Groups[1].Value })
Enabling PGWire in SpacetimeDB Standalone
PGWire is disabled by default when starting a SpacetimeDB Standalone server.
To enable it, start the server with the --pg-port option:
spacetime start --pg-port 5432 [ARGS]
Examples
In the following example, we assume you are using the quickstart-chat database created in
the Rust Module Quickstart or C# Module Quickstart,
and have set the auth token as shown above.
Using psql
SpacetimeDB Standalone deployment:
psql "host=localhost port=5432 user=any dbname=quickstart-chat"
SpacetimeDB Cloud deployment:
psql "host=maincloud.spacetimedb.com port=5432 user=any dbname=quickstart-chat sslmode=require"
Introspection commands such as \dt will not work, as SpacetimeDB does not support PostgreSQL schemas.
Now for example:
quickstart=> select * from message;
sender | sent | text
--------------------------------------------------------------------+----------------------------------+-------
\xc200da2d6ddb6c0beef0bbaafacffe5f0649c86b8d19411e3219066a6d0e5123 | 2025-09-29T22:29:14.271647+00:00 | hello
(1 row)
quickstart=> update message set text = 'world';
updated: 1, server: 1.72ms
quickstart=> select text from message;
text
-------
world
(1 row)
Using Python (psycopg2)
import psycopg2
import os
conn = psycopg2.connect(
host="localhost", # or "maincloud.spacetimedb.com" for SpacetimeDB Cloud
port=5432,
dbname="quickstart-chat",
user="any",
password=os.getenv("PGPASSWORD"),
sslmode="disable" # use "require" for SpacetimeDB Cloud
)
conn.set_session(autocommit=True) # disable transactions
print("Running query:")
with conn.cursor() as cur:
cur.execute("SELECT * FROM message;")
for row in cur.fetchall():
print(row)
conn.close()
print("Done.")
Using Rust (tokio-postgres + rustls)
We use the tokio-postgres-rustls because is stricter, so we can show how disables certificate verification.
# Cargo.toml
[dependencies]
anyhow = "1.0.71"
tokio-postgres = "0.7.14"
tokio-postgres-rustls = "0.13.0"
tokio = { version = "1.47.1", features = ["full"] }
rustls = "0.23.32"
// main.rs
use std::env;
use std::sync::Arc;
use rustls::client::danger::{ServerCertVerified, ServerCertVerifier};
use rustls::pki_types::{CertificateDer, ServerName, UnixTime};
use rustls::{ClientConfig, Error, RootCertStore, SignatureScheme};
use tokio_postgres_rustls::MakeRustlsConnect;
#[derive(Debug)]
struct NoVerifier;
impl ServerCertVerifier for NoVerifier {
fn verify_server_cert(
&self,
_: &CertificateDer<'_>,
_: &[CertificateDer<'_>],
_: &ServerName<'_>,
_: &[u8],
_: UnixTime,
) -> Result<ServerCertVerified, Error> {
Ok(ServerCertVerified::assertion())
}
fn verify_tls12_signature(
&self,
_: &[u8],
_: &CertificateDer<'_>,
_: &rustls::DigitallySignedStruct,
) -> Result<rustls::client::danger::HandshakeSignatureValid, Error> {
Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
}
fn verify_tls13_signature(
&self,
_: &[u8],
_: &CertificateDer<'_>,
_: &rustls::DigitallySignedStruct,
) -> Result<rustls::client::danger::HandshakeSignatureValid, Error> {
Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
}
fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
vec![
SignatureScheme::ECDSA_NISTP384_SHA384,
SignatureScheme::ECDSA_NISTP256_SHA256,
SignatureScheme::RSA_PSS_SHA512,
SignatureScheme::RSA_PSS_SHA384,
SignatureScheme::RSA_PSS_SHA256,
SignatureScheme::ED25519,
]
}
}
#[tokio::main]
async fn main() -> Result<(), anyhow::Error> {
let password = env::var("PGPASSWORD").expect("PGPASSWORD not set");
let mut config = ClientConfig::builder()
.with_root_certificates(RootCertStore::empty())
.with_no_client_auth();
config.dangerous().set_certificate_verifier(Arc::new(NoVerifier));
let connector = MakeRustlsConnect::new(config);
let (client, connection) = tokio_postgres::connect(
// Note: use "maincloud.spacetimedb.com" and sslmode=require for SpacetimeDB Cloud
&format!(
"host=localhost port=5432 user=any sslmode=disable dbname=quickstart-chat password={password}"
),
connector,
).await?;
tokio::spawn(async move { connection.await.expect("connection error") });
println!("Running query:");
let rows = client.simple_query("SELECT * FROM message;").await?;
for row in rows {
println!("Row: {:?}", row);
}
println!("Done.");
Ok(())
}