Tap a local PostgreSQL connection and decode its wire traffic.
tapgres reassembles each connection and decodes it with the
pgwire protocol layer. It has two traffic
sources, selected with --mode, and an optional interactive view with --tui:
pcap(default): passively captures traffic on a port with libpcap. Cleartext only — if SSL/GSS is negotiated and accepted the stream goes opaque and decoding stops. A refused negotiation keeps decoding in cleartext.mitm: runs a local TLS-terminating proxy so you can decode encrypted sessions too. Point your client at the proxy; it decrypts the client leg, decodes in the middle, and forwards to the real server.--tui: render either source as an interactive, scrollable full-screen view instead of line-oriented stdout.
F→B is the client (frontend) → server (backend); B→F is the reverse.
=== new connection 127.0.0.1:40005 -> 127.0.0.1:55432 (port 55432) ===
[F→B] SSLRequest: (awaiting server reply)
[B→F] SslResponse: refuse (continuing in cleartext)
[F→B] Startup: protocol 3.0 user=pgtest, database=postgres
[F→B] Query: SELECT id, name FROM users
[B→F] RowDescription: id(oid=23, text), name(oid=25, text)
[B→F] DataRow: { id=1, name='alice' }
[B→F] CommandComplete: SELECT 1
[B→F] ReadyForQuery: txn=idle
tapgres -p 5432 # monitor port 5432 on loopback (default)
tapgres -p 5432 -i eth0 # capture on a specific interface
tapgres -p 5432 -i any # capture on all interfaces
Capturing requires privileges (CAP_NET_RAW or root):
sudo setcap cap_net_raw+ep $(which tapgres)The proxy terminates TLS on the client leg and re-encrypts (or goes
cleartext) on the upstream leg. The decoded output is identical to pcap
mode, but it works against clients that require SSL (sslmode=require).
TLS (client trusts tapgres CA) TLS or cleartext
psql ───────────────────────────────► tapgres ──────────────────► postgres
▲ decodes here ▲
-
Start the proxy against your server:
tapgres --mode mitm --listen 127.0.0.1:15432 --upstream 127.0.0.1:5432On first run it generates a CA + server certificate (under
$XDG_CONFIG_HOME/tapgres, or~/.config/tapgres) and prints where to find the CA. Bring your own cert with--tls-cert/--tls-keyif you prefer. -
Make the client trust the CA and point it at the proxy. For libpq/psql:
cp ~/.config/tapgres/ca.crt ~/.postgresql/root.crt psql "host=127.0.0.1 port=15432 user=… sslmode=require sslrootcert=~/.postgresql/root.crt"
The auto-generated leaf is valid for
localhost,127.0.0.1and::1.
The upstream leg auto-negotiates TLS (it sends an SSLRequest and honors the
server's reply), so it works whether the server is cleartext or TLS. Pass
--no-upstream-tls to force a cleartext upstream. The proxy does not verify
the upstream certificate — it assumes a local, operator-controlled server.
GSS encryption is refused (the client falls back); cancel requests are relayed verbatim.
Add --tui to either mode for a full-screen, scrollable view instead of
line-oriented stdout. The chosen source runs in a background thread and feeds
the TUI on the main thread:
tapgres --tui # pcap source, interactive view
tapgres --mode mitm --tui # TLS proxy source, interactive view
Keybindings:
| Key | Action |
|---|---|
q / Ctrl-C |
quit |
j/k, arrows, PgUp/PgDn |
scroll |
g / G |
top / bottom |
f |
toggle follow (auto-tail) |
w |
toggle line wrap |
c |
clear |
The direction symbol is highlighted in a high-contrast colour ([F→B] cyan,
[B→F] magenta) and the packet name is bold; warnings are red and connection
notices yellow. The packet view has a green border. --tui with pcap still
needs capture privileges.
# Nix
nix build && ./result/bin/tapgres --help
# Cargo (libpcap must be installed, e.g. libpcap-dev on Debian/Ubuntu)
cargo install --path .nix develop # Rust toolchain + libpcap + PostgreSQL 18
cargo testMIT. See LICENSE.
