docs/versioned_docs/version-1.12.0/00300-resources/00100-how-to/00200-pg-wire.md
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:
SpacetimeDB is progressively adding PostgreSQL client compatibility. Some features are unsupported, partially implemented, or behave differently:
user_name@database_name ignores user_name; only database_name is used. Authentication is based on the auth
token
provided via the password field.SpacetimeDB Cloud deployments (without mutual TLS). Other deployments (such
as SpacetimeDB Standalone) do not support SSL/TLS connections.SELECT * FROM st_table) for
introspection. These are not PostgreSQL-compatible, so tools relying on PostgreSQL system catalogs will not work.SpacetimeDB Standalone deployments, specify the port with spacetime start --pg-port <port>. Without this
flag, connections using the PostgreSQL protocol are not enabled.SpacetimeDB Cloud deployments, the port is always 5432.BEGIN TRANSACTION, COMMIT, etc.) are not supported. Each SQL
statement executes in its own transaction context. Client libraries should disable automatic transaction handling.Enum.JSON.Duration is displayed as Interval.Identity, ConnectionId, U8, [U8], Bytes & Hex is displayed as Bytea.To connect to SpacetimeDB using a PostgreSQL client, use the following parameters:
localhost for SpacetimeDB Standalone deploymentsmaincloud.spacetimedb.com for SpacetimeDB Cloud deployments5432 for SpacetimeDB Cloud--pg-port for SpacetimeDB Standaloneauth tokenrequire (only for SpacetimeDB Cloud):::warning
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 })
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]
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.
psqlSpacetimeDB 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"
:::note
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)
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.")
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(())
}