NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
Go modules · #1457 by repository stars
Last release 1 months ago
06 Sep 2026
Ships on a steady schedule
a new release about every 8 days
Most releases are documented
notes for 21 of 28 stable releases
Nothing withdrawn
no release was ever pulled
1 months old
39 releases · first in 2026
One column per month.
e3834c1 release: sign the checksum with the cosign bundle only; cut 0.8.1
--output-signature / --output-certificate and
requires a bundle path, so the transitional detached .sig/.pem signing
entry failed. Releases now ship the checksums.txt.cosign.bundle only, which
is what install.sh, the README, and the release smoke job already verify
with cosign verify-blob --bundle. 0.8.1 is the 0.8.0 tree plus this fix.v0.8.1 Latest
Latest
Compare
Nothing published for this version
pgbot 0.8.0 — ssh tunnel, BYOK model providers, tune --timeout, Go 1.27
pgbot 0.8.0 — ssh tunnel, BYOK model providers, tune --timeout, Go 1.27
explain and ask (#29, contributed by
@edwardsb). Pick one with PGBOT_AI_PROVIDER or let pgbot detect it from
whichever key is set (OpenAI first, as before); PGBOT_AI_MODEL,
PGBOT_AI_BASE_URL, PGBOT_AI_API_KEY, and PGBOT_AI_REASONING_EFFORT
override the rest, and the existing PGBOT_OPENAI_* / PGBOT_GEMINI_*
settings keep working. Anthropic speaks /v1/messages (default
claude-opus-5), xAI the Responses API (default grok-4.6, sent with
store: false); OpenRouter, Groq, Together, DeepSeek, Mistral, Ollama, vLLM,
and LM Studio go through /chat/completions. The consent prompt now names
the provider, host, and model; a local endpoint is identified as local and
needs no confirmation. Keys still come only from the environment, and every
provider is plain net/http — no new dependencies. The model call gets its
own three-minute budget instead of what is left of collection's.--ssh-tunnel [user@]host[:port] — reach a database through an SSH jump
host (#28, contributed by @DiegoDAF). A global flag (or $PGBOT_SSH_TUNNEL)
for the RDS-in-a-VPC / Postgres-behind-a-bastion case. It is installed as
pgx's dialer rather than an ssh -L forward, so the DSN keeps naming the
real host: sslmode=verify-full and .pgpass still match on it, and no
local port is left open. How the jump host is reached comes from your own
ssh_config (HostName, Port, User, IdentityFile, IdentitiesOnly,
IdentityAgent, StrictHostKeyChecking, UserKnownHostsFile); the agent is
offered before any key on disk; one SSH connection serves the whole run and
is re-dialed once if the transport dies under a long-lived mcp process. A
host key accepted on first sight is recorded in your known_hosts, as ssh
does, so a later change is refused. Two new pure-Go dependencies:
github.com/kevinburke/ssh_config and golang.org/x/crypto.gpt-5.6-terra (was gpt-4o-mini), sent
as a reasoning model with reasoning_effort xhigh and a 32k completion
cap. An existing setup with only OPENAI_API_KEY picks this up without any
other change — set PGBOT_OPENAI_MODEL (or PGBOT_AI_MODEL) to keep the old
model, or PGBOT_AI_REASONING_EFFORT=low to keep the cost down.golang.org/x/crypto v0.56.0 — the first release
clearing the advisories govulncheck reports against the SSH package — needs
Go 1.26 or newer, so go.mod moves from 1.25.13 to go 1.27 with
toolchain go1.27.1. Developers and CI get exactly 1.27.1 through the
default GOTOOLCHAIN=auto; a packager with any 1.27.x can build. The release
pipeline already reads the version from go.mod.pgbot tune --timeout (#26, #30, contributed by @YIKUAIBANZI). tune ran
under a fixed 30s budget with no flag to raise it, so a slow or remote database
died with collect: context deadline exceeded; it now takes the same
--timeout (default 30s) as the other collection commands. The shared
gather path also forwards that budget to the collector, which previously
kept its own 20s+interval cap regardless — so --timeout above ~21s on
indexes, queries, tables, and vacuum now actually extends the run.Nothing published for this version
Nothing published for this version
Nothing published for this version
61f72bb docs(changelog): cut 0.7.2 — report, activity, erd row/html/indexes, audit level
pgbot logs reads the pgAudit trail as its own level. pgAudit writes
its audit records into the server log at LOG severity with an AUDIT:
prefix; they now classify as level audit — pgbot logs --level audit
is the audit-trail reader, completing the pgaudit story next to the
existing posture findings. Scrubbed in --json like everything else.pgbot report — the full inspection as one self-contained HTML page.
The same collection pipeline as inspect (a baseline snapshot is stored as
usual), rendered for a browser: health score, findings ordered by severity
with evidence, remediation, and caveats inline, top queries with shares,
largest tables, unused/redundant indexes and unindexed FKs, the wait
profile, and non-default settings — with click-to-sort columns. The Context
is PII-free by construction and the page makes zero external requests
(pinned by test), so the report is safe to attach to a ticket.pgbot activity — pg_stat_activity for humans. The live client
backends: PID, user@db, app, state (colored), current wait, transaction and
query ages, and the scrubbed SQL. Plain idle sessions are summarized rather
than listed (--all lists them); pgbot's own connections are excluded by
PID; --json emits one scrubbed object per session.pgbot erd --layout row — left-to-right diagram: parents in the left
column, children to the right, edges drawn as dashed ascii (----, |,
+, < into the parent) leaving from each FK's own row.pgbot erd --html — a self-contained interactive diagram file: inline
SVG (dashed edges, arrowheads, pgbot's terminal palette) with inline
pan/zoom script, zero external requests — the schema never leaves the file.pgbot erd shows indexes and database info. Every renderer opens with
the database header (name, server version, table/FK/index counts, size),
and each table box gains an index section below a divider — non-primary
indexes with method, columns, partial-index predicates, and UNIQUE marked.Nothing published for this version
407d78d docs(changelog): cut 0.7.1 — pgbot erd with routed edges
pgbot erd — the schema as an ER diagram, in the terminal. Box-drawn
tables with PK/FK markers and a crow's-foot relationship forest (multi-parent
tables cross-linked, cycles handled), introspected from pg_catalog —
structure only, never data, nothing beyond CONNECT required, and the
connection string never leaves the machine. Every foreign key is
ROUTED as a drawn edge — corner at the FK row, a vertical lane in the left
gutter, crossings as ┼, an arrowhead into the parent — with lane reuse and a
cap so huge schemas degrade to the textual markers. --mermaid emits an
erDiagram pasteable into GitHub or mermaid.live; --schema narrows the scope.3d12bac docs(changelog): cut 0.7.0 — waits + why --duration live diagnosis
pgbot why --duration 10s — live wait diagnosis on top of the offline
history engine. why without the flag is unchanged (offline, no
connection). With it, the waits study becomes a live evidence source
classified through a deterministic first-match cause table — lock contention
(only with a sustained named blocker), lock churn (a possibility, never a
diagnosis), storage/WAL wait (IO alone never claims a missing index),
client/application wait (explicitly not a PostgreSQL problem), CPU
saturation, mixed — behind two gates that outrank every diagnosis:
insufficient evidence (thin sample, partial visibility, poor coverage) and
not-significant (waits on near-zero activity are noise). Wait-rollup history
corroborates by labeled ratio ("Lock waits 8× vs the previous 24h"), never
blended into live percentages, and adds confidence. The report gains an
additive live object (why_schema_version 1.0.0 → 1.1.0); wait counts
fold into the store so each run sharpens the next.
pgbot waits — sampled wait analysis with evidence-gated blockers
(experimental). Samples pg_stat_activity at up to 20 Hz and the lock
graph at 1 Hz for a bounded window (default 10s, --duration, --pid,
--group event|query|session, --json): average active sessions, DB time
by wait class, top wait events, waiting sessions, and blockers — a holder is
named only when observed across ≥3 lock snapshots (or 2 with the victim's
own samples majority-Lock); anything less is transient, never blamed. All
shares are labeled sampled; the only exact numbers are ages read from the
server; lock contention is explicitly reported as NOT evidence of a missing
index. Query text passes the literal scrubber; --json is a separately
versioned document (waits_schema_version 1.0.0). Wait counts fold into the
existing wait_rollups store by default (--no-store opts out). Reuses the
hardened inspect ASH sampler — poll budgets, skip-while-in-flight,
idle-vs-broken accounting — with an extended column set.
Nothing published for this version
a80d322 feat(init): --logs — cover the pgbot logs grant in the setup SQL
pgbot init now covers the pgbot logs grant — commented out by
default (it is one privilege beyond pg_monitor, so opting in stays a
visible act), emitted active with pgbot init --logs. The statement text
is shared with the runtime's missing-grant hint, so the two can never
drift apart.900359a docs(changelog): cut 0.6.2 — installer PATH-shadow warning
install.sh warns when an older pgbot earlier in PATH shadows the fresh
install (a brew or go install copy): the installer printed the new
version while the shell kept running the old binary, so a just-shipped
command looked "missing" minutes after release. The warning names the
shadowing binary, its version, and the fix. (The script is served from
main, so this was live before the tag; the release anchors it.)f2361bf docs(changelog): cut 0.6.1 — logs --follow alias
pgbot logs --follow / -f — a true alias for --live, bound to the
same variable, because tail -f muscle memory deserves to work.Nothing published for this version
deff3df docs(changelog): cut 0.6.0 — pgbot logs + behavioral PgDog detection
pgbot logs — the server log over SQL, typed and self-aware
(experimental). pgbot logs prints the newest 100 entries (--last N to
change, --live to keep following), read through
pg_current_logfile() + pg_read_binary_file() — no agent, no sidecar, no
file access. Entries are parsed from whichever format the server writes
(jsonlog preferred, then csvlog, then stderr with any log_line_prefix) and
typed query / info / warn / error (--level filters). Rotation is
followed; --json emits one scrubbed object per entry (the machine
contract — literals never leave the log). pgbot filters its own footprint
out of the stream — its probe, its polling reads, its connection lines —
because a log tail that reads its own reads is a feedback loop, and
--last 100 means 100 entries you actually wanted. Needs one grant beyond
pg_monitor (printed exactly when missing); without a log collector
(logging_collector=off, the Docker default) it says so and points at
docker logs.pgdog.shard routing hint session-level alongside
a control GUC and reads both back: PgDog consumes pgdog.* hints instead of
forwarding them, so a vanished hint with an intact control can only be PgDog
— on any hostname and port (verified against PgDog v0.1.54). The control
keeps a PgBouncer backend switch from ever reading as a false PgDog.-pooler are no longer labeled "a Neon pooled
endpoint" unless they are on Neon's own domain (#22). PgDog and
self-hosted poolers reuse the -pooler naming convention; those endpoints
still count as a pooler signal but now carry the generic label.9660f4c docs(changelog): cut 0.5.1 — why UX polish from first real-world runs
pgbot why polish from the first real-world runs. Sub-millisecond means
no longer render as "0ms → 0ms" on a real slowdown (precision now scales
with magnitude: 0.04ms → 0.39ms) and tiny per-second rates keep their
significant digits; when the store holds history older than the window,
the too-few-snapshots message says how many more exist and names the
--window widening; and the pick-one database listing carries server
version, provider, and recency, so six databases all named "postgres"
are tellable apart (snapshots deliberately store no host).@pgbot/win32-x64 has
propagated caches the wrapper WITHOUT its platform optionalDependency, and
every retry silently reuses that poisoned tree — which is how the
0.4.2/0.4.3/0.5.0 Windows smokes stayed red for their whole retry budget
while the registry was verifiably fine. The smoke job now gives each attempt
a virgin npm cache, and the wrapper's no-binary error tells real users who
hit the same trap how to retry with a fresh cache.06031bd ci(release): give npm smoke 10 minutes for registry propagation
pgbot why — deterministic root-cause chains from baseline history. The
correlation feature the roadmap promised: per-object time series over the
stored snapshots (each inspect adds one), sustained-shift onset detection,
and explicit mechanism rules connecting a symptom to its cause — "query 42
slowed 3.2× — mean 8ms → 26ms per call · because seq scans on public.orders
surged 0.1 → 50 per second · after the table grew 18%" — with the numbers
and onset times on every hop. v1 ships the query-slowdown rule: interval
mean (ΔTotalMS/ΔCalls, honest where pg_stat_statements' lifetime mean
dilutes fresh regressions) mechanized by a seq-scan surge on a referenced
table, with table-growth and index-dropped antecedents. Temporal discipline
is a hard gate (a cause whose onset follows the symptom never chains);
confidence comes from onset alignment, antecedents, and magnitude, and
anything below 0.5 is worded as a possibility. Fully offline, like diff;
needs ≥3 snapshots and says exactly what to run when it has fewer. --json
emits a separately versioned report (why_schema_version: 1.0.0); the MCP
server gains a matching why tool. Counter resets split series rather than
fabricating rates; missing top-N entries are gaps, never interpolated. The
output explains itself: what was analyzed ("analyzed N queries and M
tables"), how many regressions were found vs shown ("showing the 5 worst of
7"), and a how-to-read legend; pgbot why 10 (or --max-chains) widens the
default 5, and the JSON carries the same scope fields
(analyzed_queries/analyzed_tables/regressions_found).pgaudit_silent (warn) — installed but
pgaudit.log selects no classes, the compliance foot-gun where the audit
trail everyone relies on does not exist; pgaudit_logs_parameters (warn,
risk) — pgaudit.log_parameter=on writes bind parameters (passwords, PII)
into plaintext server logs; pgaudit_double_logging (info) — pgaudit session
logging alongside log_statement=all records every statement twice. Each
ships with a catalogue page (pgbot explain-finding pgaudit_silent),
suppression support, and cluster-wide dedupe under --all-databases.e45afc1 Merge pull request #20 from 10xdev4u-alt/audit-fixes
Preexisting findings the exit code passes under --fail-on-new; Prometheus
label values are escaped exactly once (a database name with a quote or
backslash was double-escaped, changing the exposition bytes); the MCP
diagnose prompt no longer renders the DSN — password included — into
prompt text; archiving_stalled honors archive_timeout (the value is
unit-suffixed — 5min, 1h — so the old Atoi parse was a dead branch and
the threshold stuck at the 1h floor, firing false criticals);
checksum_failures is reported once under --all-databases while
work_mem_low correctly stays per-database; install.sh pins the cosign
signing identity to the release workflow instead of accepting any workflow
in the repo; finding text is truncated by rune, never mid-UTF-8-sequence.--all-databases
run can't lose its schema/events writes; the diagnose prompt drops its
connection_string argument entirely (prompt arguments never reach the
model — the rendered text was the only carrier, and that was the leak) and
directs agents to the server's DATABASE_URL; the cosign identity regexes in
install.sh, release.yml, and the README anchor the workflow filename
(release.yml@) so a similarly-prefixed workflow can't satisfy them; the
Prometheus exposition gains a preexisting label and stops counting
preexisting findings in pgbot_findings_total, matching the exit code; the
--full findings view marks preexisting findings and keeps them out of the
headline counts; the index advisor's query line truncates by rune.8c4983d Add 2-week cooldown to dependabot version upgrade PRs
uses: pgrundev/pgbot@v1 now actually resolves. The composite GitHub
Action moved from .github/actions/pgbot/ to the repository root — the
location the owner/repo@tag syntax (and the Marketplace) requires — and a
floating v1 tag tracks it. release.yml now triggers only on full
vX.Y.Z tags so the major tag can never cut a release by accident.pgbot init — guided setup that never touches the database. Generates
the canonical read-only role SQL (CREATE ROLE … LOGIN, GRANT pg_monitor,
GRANT CONNECT) plus the provider-appropriate pg_stat_statements step —
executable where the extension is preloaded (Supabase, Neon), commented
instructions where preload comes first (RDS, Aurora, Cloud SQL, Azure,
self-hosted). With a connection string it detects the database name and
provider; the output is pipe-safe by contract (every line is a statement, a
-- comment, or blank), so pgbot init | psql "$ADMIN_DSN" is the intended
path — pgbot itself executes nothing. pgbot init --verify connects as the
monitoring role and checks the prerequisites (pg_monitor critical,
pg_stat_statements warn with the provider fix, standby per-node-counter
note), exiting non-zero when the critical one is missing.upload-sarif step runs inside end-users' workflows). Dependabot maintains
the pins and gains a 14-day cooldown for gomod and github-actions version
updates (security advisories are not delayed). Contributed by @lpmi-13 (#14).f7d7580 Merge pull request #13 from pgrundev/fix/issues-8-11
pg_stat_statements installed outside public was detected but unreadable
(#10). Supabase (and any CREATE EXTENSION … SCHEMA x) puts the extension's
objects in extensions; pgbot's probe saw it in pg_extension but every read
used the bare relation name, so queries came back
unavailable: relation "pg_stat_statements" does not exist while the server
capability list still said pg_stat_statements — a silent loss of the report's
highest-value section for a dedicated read-only role whose search_path doesn't
include the schema. The probe now records the namespace of every installed
extension (Capabilities.ExtensionSchemas) and the fixed, allowlisted object
names — the pg_stat_statements view, the pg_stat_statements(showtext) SRF,
pg_stat_statements_info, and hypopg's hypopg_create_index /
hypopg_relation_size / hypopg_reset used by advise — are addressed
schema-qualified and identifier-quoted ("extensions"."pg_stat_statements"),
independent of search_path. When the schema can't be read the bare name is
used, i.e. the previous behaviour. Covered by an integration test that
relocates the extension and runs the read-only role against it.index_invalid no longer overstates failed-build debris as critical write
overhead (#11). A CREATE INDEX CONCURRENTLY that fails during the build
(a duplicate key on a unique build, a timeout, a cancelled session) leaves
indisvalid = false, indisready = false and a 0-byte relation — an index
PostgreSQL ignores on INSERT/UPDATE. pgbot graded every invalid index
critical (impact 85) with the blanket claim "still maintained on every write",
ranking that debris above live operational problems. The schema fingerprint
now carries indisready, indislive, and pg_relation_size for invalid
indexes, and each one is classified: indisready = true → maintained on every
write, never read → critical (unchanged); indisready = false →
failed-build debris, not maintained on writes → warn (impact 45) with
cleanup guidance ("the index you meant to have does not exist"), never a
write-cost claim; indislive = false → being dropped → warn. Evidence lines
carry the state and size (… indisready = false: failed-build debris, NOT maintained on writes (0 B)), the impact estimate says how many are actually
maintained, and the finding page's verify query shows both flags. The
in-progress-build downgrade (warn, confidence 0.5, do-not-drop guard) is
unchanged. Not a JSON contract change: the classification rides on the
existing severity / evidence / impact fields.brew install pgrundev/tap/pgbot works (#8). The README advertised the tap
since 0.3.0, but the pgrundev/homebrew-tap repository was never created and
the release's formula push was gated on a HOMEBREW_TAP_TOKEN that was never
set — so every release stayed green while the documented install failed with
"Repository not found". The tap now exists with a formula for the current
release (macOS Intel/Apple Silicon, Linux x86_64/arm64, SHA-256 pinned to the
signed release archives), and GoReleaser pushes the regenerated formula on
every tag over git+SSH with a deploy key scoped to the tap repo
(HOMEBREW_TAP_DEPLOY_KEY) instead of a personal access token. A new
post-release brew-smoke job brew installs the tag on a fresh macOS runner
and fails the release run if the formula wasn't published, so this can't
silently regress again. Release procedure documented in docs/release.md.npx pgbot → E404 is documented, not a bug to chase (#9). npm's
name-similarity policy blocks creating the bare pgbot package (too close to
got); the wrapper has been @pgbot/cli since 0.3.3. The README now says so
explicitly next to the npx row, and the 0.3.0 release notes that advertised
npx pgbot carry a correction.actions/setup-go v7,
docker/login-action and docker/setup-buildx-action v4). No behaviour
change; govulncheck still reports no vulnerabilities.15c5ef7 fix(lint): restore Finding doc-comment placement; drop deprecated ParseDir
pgbot indexes --correlate, MCP index_code_correlation).
pgbot grades every unused / redundant / invalid index by how the drop can be
proven, and hands an agent exactly what to search for — without ever reading
your repository:
catalog_proven — invalid or redundant/duplicate; provable from the catalog
alone, no code check, no stats-window caveat.needs_code_check — a zero-scan plain btree over bare columns. pgbot emits the
identifiers to grep in every case convention (camelCase, snake_case,
PascalCase, CONSTANT_CASE) plus the load-bearing instruction: search filter
positions only (WHERE / JOIN / ORDER BY / GROUP BY / ORM filters), never SELECT
lists — and how to read a hit vs. a miss.inconclusive — GIN/GiST/BRIN, expression, partial, or a cold window. These
can serve a query shape that simply hasn't run, so they keep "do not DROP INDEX
on this evidence" and are never promoted to actionable by an empty code
search. pgbot never reads the repo and never drops anything.record_index_verdict). An agent records what its
repo search found (found_in_code / not_found_in_code / inconclusive),
stored locally per database. On a later run the same still-unused index carries
the prior verdict forward and notes when the zero-scan window has since grown —
a one-off grep becomes compounding evidence. New index_verdicts store table
only; no existing table changes.REPLICA IDENTITY USING INDEX index shows zero scans on the primary but dropping it breaks logical
replication and UPDATE/DELETE row identity — now excluded alongside PK / unique /
exclusion / FK-backing indexes.finding.safety).
Every finding whose remediation involves a destructive or irreversible action
(DROP INDEX, VACUUM FULL, REINDEX, DROP REPLICATION SLOT, a table rewrite) now
carries machine-actionable guards — {id, kind: prohibition|precondition, action, text, verify} — instead of leaving the warning to free-form prose a summarizing
model could drop. They are emitted deterministically in code and guaranteed in
--json, SARIF, the MCP payloads, and both terminal views. Two guards that
previously existed only in docs pages are now on the finding itself: the
wraparound "don't VACUUM FULL / don't consume XIDs" guard, and the "don't drop a
replication slot a live standby still depends on" guard (whose remediation no
longer nudges toward the drop before the check). pgbot ask / explain reassert
these guards from code, after the model's text, so the model cannot omit them. A
build-failing regression test fails CI if a destructive remediation ships without
a guard. The guards are also carried by SARIF, JUnit (<failure> text), and a
Prometheus destructive="true" label; a test AST-scans the render package and
fails the build if a new output surface ships without carrying them. For a
database with a destructive finding, the default and --full terminal views now
add a ⚠ guard line — clean databases are byte-identical.stale,
age_days), its age is stated in output ("code check is 47 days old — the
repository may have changed since"), and a stale verdict never strengthens. The
strengthened wording reads as corroboration, never authorization (the phrases
"safe to drop" / "confirmed unused" are never generated), and the precondition
guard persists through any verdict. An inconclusive index is never promoted by
any verdict at any window length. The if_not_found caveat now always names
monthly/quarterly/annual jobs a long window still can't see.model.IndexStat gains columns, method, unique, and primary (additive).
JSON contract SchemaVersion → 1.2.0; a 1.1.0 consumer still parses 1.2.0
output unchanged.0b7a374 fix(npm): publish wrapper as @pgbot/cli (bare name blocked by npm)
@pgbot/cli, not pgbot. npm's package-name
similarity policy blocks the bare name pgbot from being created (too close to
the existing got/hubot packages), which failed 0.3.2's publish after the six
platform packages had already gone up. The wrapper now uses the scoped name we
own: install with npx @pgbot/cli inspect "$DATABASE_URL" or
npm i -g @pgbot/cli. Nothing else changes — the installed command is still
pgbot, the six @pgbot/<os>-<arch> binary packages are unchanged, and the
Homebrew formula, install.sh, Docker image, and go install path are
unaffected.f55aad3 docs(changelog): cut 0.3.2 — re-cut of 0.3.1 to land npm (token fix)
npx @pgbot/cli inspect "$DATABASE_URL".7970738 ci(release): post-release smoke — image is public + signature verifies
statement_timeout=15s, etc.) as
the server's non-default parameters; now reads the server's real values via a
transaction-local unpin.aurora_version(), which errored and booked a rollback
on every non-Aurora server each run; now detected from pg_proc.work_mem), so pgbot doesn't report its own temp_bytes.low_cache_hit requires enough block traffic before grading (a thin sample was
flipping the finding and the exit code on noise); vacuum grades "due?" against
the actual autovacuum knobs and per-table reloptions; the real index count is
reported (not the LIMIT-200 scan); idle Client waits aren't counted as
"waiting"; and TPS excludes pgbot's own transactions.npx @pgbot/cli inspect "$DATABASE_URL".Correction (2026-08-19): the npm wrapper is published as @pgbot/cli , not pgbot — npm blocks the bare name (package-name-similarity to got ), so npx p
Correction (2026-08-19): the npm wrapper is published as
@pgbot/cli, notpgbot— npm blocks the bare name (package-name-similarity togot), sonpx pgbotreturnsE404. Usenpx @pgbot/cli inspect "$DATABASE_URL"(available from 0.3.3). See #9.
npx pgbot with no prior install--profile=schema, pgbot lint). Runs only the
findings derivable from the catalog alone — invalid/redundant indexes, unindexed
foreign keys, a narrow identity column, autovacuum disabled on a table — so it's
safe against an empty, freshly-migrated database, where the full profile would
fire unused_indexes and stale_statistics on everything. A schema report says
so in its header and makes no claim about a running database's health.--fail-on-new <base.json>. Compare a run against a base report and act only
on findings the change introduced — new findings, escalated severities, and new
rows inside an existing aggregate (a fourth unindexed FK on top of three).
Pre-existing findings are marked preexisting: true in --json, excluded from
SARIF and the exit code. This is the migration-PR check: schema profile + base
vs. head, only regressions fail. The GitHub Action gains profile and
base-report inputs.int4_identity_column. A sequence-backed int4/serial (or
identity) column wraps at 2.1 billion — int2 at 32767 — regardless of its
current value, after which the next insert errors. Detected structurally, so it
fires on the migration PR while the fix is still free, where the value-based
sequence_exhaustion cannot. Note: this is a new finding ID, so anyone with
a .pgbot.toml will see it for the first time and it will fire on serial primary
keys immediately, some deliberately — scope an [[ignore]] to the bounded tables
you've reasoned about. Its severity is not yet weighted by production table size
(planned), so read it as "will wrap eventually", not "wraps soon".npx @pgbot/cli inspect "$DATABASE_URL" runs with no prior
install. The prebuilt binary ships as a per-platform optionalDependency
(@pgbot/<os>-<arch>), so it lands in the lockfile with an integrity hash,
needs no network beyond the registry, and works with npm ci --ignore-scripts
— no postinstall download. The wrapper passes argv, stdio, signals, and the
exit code through verbatim, published from the release tag with npm provenance.checksums.txt.cosign.bundle), and install.sh verifies it with
cosign verify-blob --bundle — no longer relying on the --certificate /
--signature flags cosign v3 has deprecated. The detached .sig/.pem are kept
this release as a fallback.version: latest no longer 404s. install.sh
treated latest as a literal release tag (pgbot_latest_..._.tar.gz, a 404);
it now resolves latest via the releases API like an empty value, and the
Action passes an empty version rather than the literal string. The Action also
installs into the same ~/.local/bin it adds to PATH instead of disagreeing
with the installer's default.d8eea07 fix: exclude ALL of pgbot's own backends from pg_stat_activity findings
N session(s) idle in transaction with nothing
actually idle, a self-pinned vacuum horizon, connection-saturation slots pgbot
was itself consuming, wait-profile noise, and pgbot listed in its own
connection breakdown). Every pg_stat_activity query now excludes all of
pgbot's own backend PIDs — captured when the pool warms, so the exclusion is
unspoofable (a session can't hide by naming itself pgbot) and never affects a
user service that happens to be named pgbot.PGBOT_INSTALL_DIR is created if it doesn't exist (a custom path
like ~/.local/bin), instead of falling through to an unexpected sudo
prompt.checksums.txt.cosign.bundle) when present, so it no longer depends on the
--certificate / --signature flags cosign v3 has deprecated; it falls back
to the detached certificate + signature when no bundle is published.…govulncheck now runs in CI and reports no vulnerabilities.
pgbot advise): missing-index suggestions, each validated
by the planner with hypopg — nothing is built. Also the MCP suggest_indexes
tool. Requires hypopg + pg_stat_statements + PostgreSQL 16+..pgbot.toml): per-object [[ignore]] rules
(with expiry and dead-rule detection), [severity] remaps, [thresholds]
overrides, and pgbot config check / explain / init. Suppression is always
visible and never hides a critical or affects the exit code silently.docs/findings/<id>.md page for every finding, an
offline pgbot explain-finding <id>, and a by-dimension index.pgbot diff: compare two baseline snapshots offline, honest about the
interval it actually used and about resets/evictions between them.pgbot inspect --all-databases: sweep every non-template database in the
cluster; cluster-wide findings are reported once, not once per database.--fail-on=<severity>, --format=sarif (uploads to the
GitHub Security tab), --format=junit, --format=prometheus (node_exporter
textfile), and a pgrundev/pgbot GitHub Action.--json contracts, published as release assets.system_identifier alone, so snapshots
from different databases on the same server were merged into one series and
their deltas were meaningless. The key now includes the database name.
On upgrade: snapshots written by v0.1.x used the old cluster-wide key and
will not match new per-database runs — those series effectively reset. Old
snapshots are left in place (the system_identifier isn't stored in a snapshot,
so they can't be recomputed); pgbot prints a one-time notice on the first run,
and you can clear the stale series with pgbot baselines prune <fingerprint>.0 clean · 1 warn · 2 critical ·
3 connection/execution failure · 64 usage error. Suppressed findings never
contribute.pg_stat_statements handling.
pg_stat_statements normalizes ordinary queries but stores utility statements
(e.g. CREATE USER … PASSWORD, ALTER ROLE, DO blocks, COPY … FROM PROGRAM)
verbatim. The queries collector trusted that text as already-parameterized and
did not scrub it, so a literal secret in such a statement could appear in a
--json report and, through pgbot explain / ask, be sent to an external
model. All pg_stat_statements text is now scrubbed before it leaves the process.
If you ran a v0.1.x queries/--json/explain/ask and shared the output,
treat any credential in a recent utility statement as exposed and rotate it.$REDACTED$ marker
parsed as an empty capture-group reference: the sensitive span was removed but
came out blank instead of marked. Scrubbing now uses literal replacement and is
covered by a fuzz test.govulncheck now runs in CI and
reports no vulnerabilities.Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Your coding agent can read these notes before it upgrades. Set up the MCP server →