pg-proto 0.2.1

Session-typed PostgreSQL wire protocol
Documentation

pg-proto

CI docs.rs crates.io MSRV License: MIT

pg-proto is an asynchronous Rust implementation of the PostgreSQL frontend/backend wire protocol designed for proxies, poolers, gateways, drivers, and protocol-aware test infrastructure.

Its distinguishing feature is that PostgreSQL's connection state machine is represented in Rust's type system. Operations consume a Conn<Transport, Phase, Cleanliness> and return a connection in its next phase. Code which tries to issue a query during COPY IN, execute before binding, send a startup packet while TLS negotiation is pending, or release a dirty connection to a pool does not compile.

Most PostgreSQL protocol libraries decode messages but retain the session phase in a runtime enum. pg-proto is useful when protocol correctness is part of the architecture rather than merely an implementation detail: the legal next operations are visible in function signatures, illegal compositions are rejected at compile time, and proxy policy can still inspect, replace, or reject complete typed messages.

What it provides

  • Direction-parameterised frontend and backend codecs. Ambiguous tags such as S and E cannot be decoded in the wrong direction.
  • Typed pre-startup handling for SSLRequest, GSSENCRequest, CancelRequest, and StartupMessage, including transport-changing rustls upgrades.
  • Independent client-facing and upstream authentication sessions, including cleartext, MD5, SCRAM-SHA-256, and SCRAM-SHA-256-PLUS with channel binding.
  • Simple and extended query sessions, pipelining, error draining, function calls, COPY IN/OUT/BOTH, and physical replication framing.
  • Lossless, reconstructable Parse, Bind, Describe, Execute, RowDescription, and DataRow values for SQL and result rewriting.
  • A demultiplexer for asynchronous notices, notifications, and parameter status updates without polluting the causal session type.
  • Positionally tagged notices and transaction/parameter evidence for pooling decisions.
  • Connection-branded prepared statements and portals with name rewriting.
  • Exact typestate erasure and checked re-entry at storage and pool boundaries.
  • A protocol grammar macro which emits typestates, their duals, a runtime FSM for differential testing, and railroad diagrams embedded in rustdoc.

The crate owns protocol representation and ordering. Applications retain control of listeners, credentials, authorisation, SQL transformation, routing, pooling, cancellation storage, telemetry, and failure policy.

Why use it?

PostgreSQL infrastructure tends to fail at phase boundaries rather than while decoding an individual frame. A pooler may return a connection while it is still in a transaction, a proxy may forward Query while a COPY exchange is active, or an extended-query error path may forget to discard messages until Sync. Typestate makes these transitions explicit and turns many such bugs into type errors.

The phase index is orthogonal to connection cleanliness. A connection can be protocol-ready but unsuitable for unconditional pool release because of an open transaction, changed GUC, prepared statement, portal, LISTEN, or advisory lock. Only Conn<_, Ready, Pristine> exposes unconditional release.

What can be built with it?

The bounded intermediary pipeline example shows ordered forwarding, local interception, and backpressure without proxy-owned message queues.

  • A TLS-terminating PostgreSQL proxy which authenticates each side independently and inspects plaintext SQL and result rows.
  • A transaction or session pooler whose release policy consumes explicit protocol and cleanliness evidence.
  • A SQL firewall, audit gateway, query rewriter, or column-encryption proxy.
  • A sharding/router layer which rewrites prepared-statement and portal names.
  • A logical or physical replication relay with typed COPY-BOTH half-closes.
  • A PostgreSQL-compatible server, mock backend, recorder, replay tool, or protocol conformance harness.
  • A driver or administrative client which benefits from compile-time sequencing.

Usage

The generated grammar witnesses make the sequencing model easy to see. Every method consumes the previous phase; uncommenting an operation which is illegal in the current phase produces a compiler error.

use pg_proto::grammar::frontend::Session;

let ready = Session::new();
let building = ready.begin_extended().parse().bind().execute();
let ready = building.sync().ready();

// `building.query()` would not compile: Query is unavailable during an
// extended-query pipeline, which must leave through Sync.
let _terminated = ready.terminate();

A proxy can inspect or replace a typed message before reconstructing its checked wire frame:

use std::convert::Infallible;

use bytes::Bytes;
use pg_proto::{
    codec::{FrontendMessage, Parse},
    intermediary::Intermediary,
};

let mut proxy = Intermediary::new((), ());
let message = FrontendMessage::Parse(Parse {
    statement: Bytes::from_static(b"report"),
    query: Bytes::from_static(b"select email from customers"),
    parameter_types: vec![],
});

let rewritten = proxy
    .inspect(message, |(), (), message| {
        let FrontendMessage::Parse(mut parse) = message else {
            unreachable!("the caller selected a Parse message")
        };
        parse.query = Bytes::from_static(
            b"select decrypt_email(email) from customers where active",
        );
        Ok::<_, Infallible>(FrontendMessage::Parse(parse))
    })
    .unwrap();

let frame = rewritten.to_frame()?;
# Ok::<(), std::io::Error>(())

For a complete networked example, see the TLS-terminating SQL logging proxy. The companion protocol logging proxy prints every decoded message in both directions. More focused examples live in the examples/ directory, including message rewriting and the neutral proxy composition boundary.

Rustdoc entry points

Build the same documentation locally with:

cargo doc --workspace --no-deps --open

Supported PostgreSQL versions

PostgreSQL 14, 15, 16, 17, and 18 are supported. Each version runs the same live suite against its official Alpine image. PostgreSQL 14–17 negotiate a requested protocol 3.2 startup down to 3.0; PostgreSQL 18 reports protocol 3.2. Both behaviours are covered explicitly.

Run a selected version locally with a Docker-compatible runtime:

PG_PROTO_POSTGRES_VERSION=18 \
  cargo test --test postgres_container -- --ignored

See SUPPORTED_VERSIONS.md for the tested protocol matrix.

Known limitations

  • The API is pre-1.0 and may change as it is integrated into a production proxy.
  • Kerberos V5, GSSAPI, SSPI, and GSS token exchanges are represented by the protocol API, but the crate does not ship platform credential-provider engines. GSS encryption negotiation is modelled; a production GSSENC transport adapter remains application work.
  • Pool scheduling, routing, SQL parsing, policy, credential storage, certificate provisioning, and cancellation-key persistence are intentionally not included.
  • Rust is affine rather than linear: callers can abandon a session by dropping it. Conn is #[must_use] and has a debug-only drop bomb, but release builds cannot make deliberate connection abandonment impossible.
  • Unknown future PostgreSQL message tags are rejected by the typed codec until their direction and semantics are added.
  • Formal multiparty verification is not provided. Client and server roles are dual generated APIs with differential runtime-FSM testing, not a machine-checked proof of a complete three-party proxy.

Security assumptions and downstream responsibilities are documented in SECURITY.md. The audited proxy capability boundary is in PROXY_COMPATIBILITY.md, and migration from a runtime-enum implementation is covered by MIGRATION.md. Contribution instructions and community expectations are in CONTRIBUTING.md and CODE_OF_CONDUCT.md.

Verification

The ordinary suite includes unit, fixture, property-style differential, compile-fail, and documentation tests:

cargo test --workspace

Live PostgreSQL tests require a Docker-compatible runtime:

cargo test --test postgres_container -- --ignored

Licence

Licensed under the MIT License.