pgrun
Create, use, and delete disposable PGRun Postgres branches — from the command line or as an MCP tool. One static Go binary, stdlib only, no external dependencies.
A branch is a real, connectable Postgres: copy-on-write off a project's base branch, ready in about the time a normal database takes to boot, gone the moment you delete it or its TTL expires.
Full guide — Safe Copy setup, connecting, limits, CI, troubleshooting: pgrun.dev/docs/branches.
Quickstart
The agent-first path — three commands, then hand the work to Claude Code:
pgrun auth login # opens your browser to authorize; --paste to paste a token instead
pgrun skill install # installs the pgrun-branching skill into ~/.claude/skills
claude # then: "Add an index to users.email and test it on a pgrun branch"
The skill teaches Claude when to create a branch, how to inject its
DATABASE_URL into only the commands that need it, and when to delete it.
Claude does the branching; you never create a branch by hand.
Prefer the CLI directly?
pgrun auth login # opens your browser to authorize; --paste to paste a token instead
pgrun branch create myproject --name scratch --ttl 1h --wait
# DATABASE_URL=postgres://... on stdout, only on ready (exit 0)
psql "$DATABASE_URL" -c 'select 1'
pgrun branch delete myproject scratch
Or, for the common "run one command against a fresh, disposable branch" case — the primitive an agent reaches for most — one line:
pgrun branch exec myproject --create scratch --ttl 1h --delete-after -- \
psql -c 'select 1'
Creates the branch, waits for it, runs the command with DATABASE_URL set
in its environment, deletes the branch afterward (even if the command
fails), and exits with the command's own exit code.
First time? Connect your production database
Branches above fork whatever base a project already has. To branch off a privacy-safe copy of your real production database instead, connect it once — Connect Postgres → Protect Data → Safe Copy — then branch as usual:
pgrun auth login
pgrun project use production # alias: pgrun projects use
pgrun source add --name production --url "$DATABASE_URL" # omit --url for a hidden prompt
pgrun source status production # poll until connected
pgrun source protect production # prints the review table, decides nothing
pgrun source protect production --set public.users.email=fake --approve
pgrun source copy production --wait # the one-time Safe Copy
pgrun branch create production --name dev --wait # branch off the Safe Copy
source protect never activates anything on its own — it prints a table of
every column, resolve the ones marked UNRESOLVED with --set (add
--acknowledge <table>.<column> to copy a sensitive one unmasked), then
--approve yourself once you're satisfied. See Sources below
for the full command table and how secrets are handled.
Install
curl -fsSL https://pgrun.dev/install | sh
The installer downloads the matching release binary from
github.com/pgrundev/pgrun-cli and
verifies its SHA-256. Or install from source with
go install github.com/pgrundev/pgrun-cli/cmd/pgrun@latest, or build directly:
git clone https://github.com/pgrundev/pgrun-cli
cd pgrun-cli && go build -o pgrun ./cmd/pgrun
(brew install pgrundev/tap/pgrun and npx @pgrun/cli are wired in goreleaser
but gated behind tap/npm credentials — curl|sh is the supported beta path.)
Auth
pgrun auth login opens your browser to app.pgrun.dev; click
Authorize and the CLI receives a token minted just for it (named "pgrun
CLI on <hostname>", revocable any time from the Tokens page) — it's never
shown to you. Over SSH, or with --no-browser, it prints a URL and code to
open on any device instead. It never asks for an API URL: --url, then
PGRUN_API_URL, then the saved config, then https://app.pgrun.dev.
--paste keeps the older flow — a direct link to the Tokens page, paste a
token, verified against the API before it's saved. pgrun auth logout
clears the saved config. pgrun auth status prints the URL and a
6-character token fingerprint — never the token.
pgrun auth login
For scripts and CI, skip the browser: set PGRUN_API_TOKEN/PGRUN_API_URL
in the environment, or pgrun auth set --token <TOKEN> [--url <URL>] writes
~/.config/pgrun/config.json at mode 0600 directly (no prompt, no network
call). Precedence (highest first): --url/--token flags on any command →
env → the config file.
Every source subcommand overrides the API base with --api-url instead
of --url — on source add/source update, --url is already the
production connection URL. See Sources.
Commands
--json on create/list/get/delete prints the API's response bytes
verbatim — that's the machine-readable contract, not the human text.
(branch create --wait --json's ready response is the one exception: it
adds database_url and "ready": true on top of the raw body, so an agent
scripting against --json never needs a second call just to learn the
connection string:
{"id":"branch_123","name":"agent-add-users-index","status":"ready","database_url":"postgres://…","ready":true, …}
)
Exit codes
0 success (or, for branch exec, the exec'd command's own exit code) ·
1 operation/API failure · 2 auth/config missing or rejected (with a
pgrun auth login hint; pgrun auth login itself exits 2 when CI is
set — it refuses to open a browser, so use PGRUN_API_TOKEN or auth set
instead) · 64 usage (bad flags/args) · 130 login cancelled with Ctrl+C.
Sources
Connect a real production database, resolve its data-protection policy, then create a one-time privacy-safe copy to branch from — Connect Postgres → Protect Data → Safe Copy:
[<name>] is the Production Database (= project) name — sources are
projects — and falls back to .pgrun/project exactly like a branch
command's [<project>]; set it once with pgrun project use <slug>
(alias: pgrun projects use).
Secrets. On add/update, --url is the production connection URL
(both schemes are accepted — postgres:// and postgresql://): never
printed, never included in --json, never repeated in an error, sent only
in the request body's connection_url field. Prefer the hidden prompt
(pgrun source add --name production, no --url) over typing the URL on a
shared shell — a flag's value is visible to other local processes and lands
in shell history, the prompt is not. Never assign the URL to a non-string
flag (--wait=…, --json=…, --timeout=…) — Go's flag package echoes a
value it cannot parse verbatim, which would print the secret; always pass it
to --url on its own.
Because --url is taken as the connection URL on add/update, the
API-base override on every source subcommand (including those two) is
--api-url, not --url (--token is unchanged). Every other command
family — branch, project, auth — keeps --url as the API base.
A Safe Copy is a one-time copy in this release — refresh/continuous sync
(Phase 6) does not exist yet; sync always reads snapshot_only.
MCP — use pgrun as an agent tool
pgrun mcp serve speaks the Model Context Protocol
over stdio (hand-rolled JSON-RPC 2.0, line-delimited, no SDK) so an agent can
create/list/get/delete branches as tools instead of shelling out. Config is
env-only — an MCP host launches pgrun as a subprocess and passes
credentials through its own env, not flags or the config file:
{
"mcpServers": {
"pgrun": {
"command": "pgrun",
"args": ["mcp", "serve"],
"env": {
"PGRUN_API_URL": "https://<your-pgrun-host>",
"PGRUN_API_TOKEN": "<TOKEN>"
}
}
}
}
(Drop that under mcpServers in Claude Desktop's config, or in a project's
.mcp.json for Claude Code.)
Tools, one per CLI branch subcommand (no source tools yet — connect,
protect, and copy a Production Database from the CLI):
pgrun_create_branch{project,name,ttl?,parent?,wait?=true,timeout_seconds?=300}— waits for ready/failed by default; result JSON includesconnection_url, adatabase_urlalias, and"ready": trueonce ready, so one call is enough to start using the branch.pgrun_list_branches{project}pgrun_get_branch{project,name}pgrun_delete_branch{project,name}
Results are content:[{type:"text",text:<api-json>}]; failures come back as
isError:true with a sanitized message — never a crash, never the token.
Pair it with the skill — MCP/the CLI give an agent the tools, the
pgrun-branching skill gives it the
playbook (when to branch vs. use local Postgres, poll semantics, cleanup
rules, what each error means). pgrun skill install drops it into
~/.claude/skills — the same file is embedded in the binary, so the
installed copy always matches the commands this version understands. See AGENTS.md for the full
agent-facing contract, including the safety rules around production DSNs.
Development
Stdlib only — no external dependencies, offline-buildable.
make build # bin/pgrun
make test # go test ./...
make vet # go vet ./...
make fmt # gofmt -w
Go 1.22+. go test ./..., go vet ./..., and gofmt -l . are expected to
stay clean at all times.
License
MIT — see LICENSE.