Repository avatar
Databases
v0.5.1
active

pgman

io.github.tombaldwin/pgman

PostgreSQL for coding agents: schema, read-only queries, EXPLAIN, masked dumps, confirmed writes.

Documentation

pgman

CI license status: beta

A k9s-style terminal UI for PostgreSQL 10 and newer, wherever it runs: every database in every environment on one screen, guarded queries, safe migrations, masked copies of production, and logs turned back into runnable SQL (Postgres server logs, Hibernate logs and pasted JDBC, offline, with N+1 detection). Teams on AWS or the JVM get extras: RDS and Aurora discovery, Secrets Manager, bastions, and connections from Spring and IntelliJ. Sibling project to ebman.

⚠️ Beta

pgman is beta — expect rough edges and breaking changes. It defaults to read-only connections and routes every write through per-statement safety guards, but don't point it at a production database without reviewing your safety.toml first. Bug reports and feedback very welcome — open an issue.

Install: brew install tombaldwin/tap/pgman or cargo install pgman --locked (more) · try it with no database: pgman --demo.

pgman demo — paste a Hibernate log, ctrl-l to reconstruct queries, N+1 clusters, load + run

Captured from pgman --demo, the synthetic-data mode baked into the binary (no database needed). Regenerate after a code change with vhs demo.tape.

At a glance:

  • Every environment on one screen — E lists each environment's databases (dev, staging, prod…) with replicas under their primary, from RDS, docker or hand-listed connections, without connecting. Enter connects (replicas read-only, the password read from AWS Secrets Manager at that moment, through the bastion when there is one); opt-in probes add versions, migration level, pending patches, lag, sessions and size; pgman env discover proposes the whole file from an AWS account. See docs/environments.md.
  • Applying patches — P plans an environment's pending patches read-only, checks each against your guard rails, and after you confirm runs each in its own transaction; a dry run or a throwaway copy rehearses it first; pgman apply does the same from the command line, and pgman run runs a script that manages its own transactions (docs/run.md). l shows the RDS server's recent errors.
  • Dump and restore — pgman dump staging/ledger runs your own pg_dump through pgman's connection (from the replica, the password read at that moment, through the bastion; nothing secret on a command line or on disk), and pgman restore puts it into a local database, never a protected one. Production is dumped masked — emails, names, phones and free text replaced as they are read, joins intact — and every copy is tracked until it is deleted (pgman copies). See docs/dump-restore.md.
  • For your coding agent — pgman mcp serve gives Claude Code (or any MCP client) read-only tools for queries, lock pre-flights, pending patches, schema lint and environments, plus apply_patches, dump and restore, each confirmed by you. See docs/mcp.md.
  • Connect fast — auto-discovers datasources from Spring application*.yml/.properties, IntelliJ .idea/dataSources.xml, and a committed .pgman/pgman.toml.
  • Logs / pasted code → runnable SQL — Hibernate logs, Postgres/RDS server logs, and pasted JDBC, with N+1 detection — see "Logs and pasted code → runnable SQL" below.
  • Safe by default — read-only connections, statement_timeout, and per-database guard rails on every statement.
  • Edit and page results — u on a result cell edits it in place: a one-row UPDATE by primary key, shown in the same safety confirm as any statement you run. M fetches the next 1000 rows of a SELECT through a server-side cursor.
  • Editor — syntax highlighting, completion, formatting, history, saved queries (:param prompts, rename, search), DBUnit fixture apply + capture.
  • DBA panels — schema browser, EXPLAIN tree, slow queries, active sessions/locks, schema-lint wizard with a config advisor (RDS / Aurora parameter names), result diff.
  • JDBC tap — live app-side queries from any JVM over OpenTelemetry, with hotspots and live N+1 detection. A dedicated Java library adding per-caller, per-pool and per-transaction views is planned.
  • Screenshots — --demo runs the whole tool against a synthetic billing database, and --redact blocks out hosts, accounts and server names on a real one.

Logs and pasted code → runnable SQL

Point it at a Postgres database, then turn logs and pasted code into runnable SQL:

  • Hibernate logs → runnable SQL. Reconstruct executable statements from org.hibernate.SQL lines plus the separately-logged bind parameters. Needs bind-parameter trace logging turned on (org.hibernate.orm.jdbc.bind at TRACE on Hibernate 6, org.hibernate.type.descriptor.sql.BasicBinder at TRACE on Hibernate 5) — without it you still get the statement, just with ? placeholders instead of substituted values.
  • Postgres / RDS server logs → runnable SQL. Reconstruct from log_statement output plus DETAIL: parameters: $1 = … lines (the more reliable source — it needs no application redeploy).
  • Pasted JDBC → runnable SQL. No log needed — a ?-placeholder statement plus a typed parameter list (TYPE:value per line) reconstructs the same way.
  • N+1 detection. Cluster reconstructed queries by shape to surface loop-driven selects.

Three ways in: paste a log into the editor, press ctrl-l (or F8), pick a query — Enter loads it terminated with ;, and a second Enter runs it; skip the paste and launch straight into the reconstructed queries with pgman --log app.log; or, with no log at all, paste a ?-placeholder statement plus its typed parameter list. Walkthrough: docs/logs-to-sql.md.

Run it inside a Spring project and it picks up spring.datasource.* to connect.

Project config (commit this)

Drop a .pgman/pgman.toml at the root of your repo. pgman walks up from the current directory to find it, so launching pgman from any subdirectory of the project works.

# .pgman/pgman.toml — commit this. No passwords here.
# Passwords come from the variable a connection's password_env names.
# PGPASSWORD is only used with --dsn, never for a discovered connection.

[[connections]]
name = "local"
url  = "postgres://postgres@localhost:5432/myapp"

[[connections]]
name = "staging"
url  = "postgres://stg-db.internal:5432/myapp"
user = "app"
password_env = "STAGING_DB_PASSWORD"   # env var holding the password

# Per-database safety overrides. These can only TIGHTEN your personal
# `~/.config/pgman/safety.toml` — a committed file can't relax your guard
# rails. Commit just `[safety.databases.production]` and your own defaults
# still apply everywhere else.
[safety.databases.production]
read_only = true
statement_timeout_ms = 5000

Project connections show up in the startup picker alongside any IntelliJ data sources found in .idea/dataSources.xml. Nothing in that picker connects until you choose it — see Safety.

Safety

pgman connects to production databases. It opens read-only by default, enforces a statement_timeout, classifies every statement, and applies per-database guard rails — e.g. block DROP, confirm TRUNCATE / unqualified DELETE, and wrap DML in a transaction you can roll back. (This is a guard rail, not a replacement for least-privilege database roles — scope the role you connect with.)

Three things worth knowing:

  • Running pgman inside a checkout you did not write is a trust decision. pgman discovers connections from files in the working tree, so nothing it finds there connects without a keypress, PGPASSWORD is only used with --dsn, and a project's [safety] block can only tighten your own guard rails — never relax them. Full trust model in docs/safety-and-privacy.md.
  • TLS: sslmode=require / prefer encrypt the connection but do not verify the server certificate (matching libpq). Use sslmode=verify-full on untrusted networks. sslmode=verify-ca checks the chain but not the hostname, as libpq does.
  • JDBC tap: the --tap-listen / --tap-otlp / --tap-udp listeners are unauthenticated and bind to 127.0.0.1 by default. Only bind a non-loopback address on a trusted/firewalled network. See docs/tap.md.

Install

Homebrew (macOS / Linux):

brew install tombaldwin/tap/pgman

On Homebrew 6 or newer, run brew trust tombaldwin/tap first: Homebrew only loads formulae from taps you have trusted.

It brings libpq, whose pg_dump and pg_restore pgman dump and pgman restore run.

Cargo:

cargo install pgman --locked

pgman dump and pgman restore also need PostgreSQL's client tools: brew install libpq on macOS, apt install postgresql-client on Debian/Ubuntu.

Pre-built binary: download the tarball for your platform (Linux and macOS, x86_64 and arm64) from the releases page, check the *.sha256 next to it, extract, and put pgman on your PATH. Each tarball also carries a build-provenance attestation: gh attestation verify pgman-*.tar.gz --repo tombaldwin/pgman.

Then try it with no database: pgman --demo.

To hack on it, install from a checkout:

cargo install --path . --locked

The binary lands at ~/.cargo/bin/pgman, which is on $PATH if your shell sources ~/.cargo/env (rustup does this for you).

Upgrade

pgman --upgrade

Works in place for a checkout (git pull + cargo install --path .), a crates.io cargo install install, and a Homebrew install. An install made with cargo install --git … isn't one of these: re-run that cargo install --git command to upgrade it. A standalone binary (a downloaded release tarball) has no in-place upgrade — --upgrade prints the releases page instead.

pgman also checks crates.io for a newer release at most once every six hours and shows a header badge when one exists. Turn that off with --no-update-check (or PGMAN_NO_UPDATE_CHECK); it never blocks startup and degrades silently if there's no network.

Testing

cargo fmt --all && cargo clippy --all-targets -- -D warnings && cargo test

See docs/development.md for the integration suite, coverage, fuzzing, and what CI runs on every push.

Docs

Status

Beta. See BACKLOG.md for what's next.

License

Dual-licensed under either of

at your option. Unless you explicitly state otherwise, any contribution you submit for inclusion shall be dual-licensed as above, without additional terms.


Built by Polymorphism Ltd. pgman is one of the tools we build for ourselves and our clients — if you want this kind of internal developer tooling for your team, get in touch.