postgresai 0.17.0-dev.2 → 0.17.0-dev.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -76,6 +76,8 @@ Password input options (in priority order):
76
76
  - `PGAI_MON_PASSWORD` environment variable
77
77
  - if not provided: a strong password is generated automatically
78
78
 
79
+ Monitoring passwords must use printable ASCII characters; non-ASCII passwords are rejected until SASLprep normalization is supported. Role creation and password resets send a SCRAM-SHA-256 verifier instead of the cleartext password, including when the server uses `password_encryption=md5`. Printed SQL redacts the verifier as well. Server statement logging can still record the verifier; treat it as sensitive.
80
+
79
81
  By default, the generated password is printed **only in interactive (TTY) mode**. In non-interactive mode, you must either provide the password explicitly, or opt-in to printing it:
80
82
  - `--print-password` (dangerous in CI logs)
81
83
 
@@ -125,6 +127,42 @@ Notes:
125
127
  - All standard options work with Supabase mode (`--verify`, `--print-sql`, `--skip-optional-permissions`, etc.)
126
128
  - When using `--verify`, the tool checks if all required setup is in place
127
129
 
130
+ ### ClickHouse Managed Postgres
131
+
132
+ Hosts ending in `.pg.clickhouse.cloud` are auto-detected; `--provider clickhouse` forces it.
133
+ The plan is the same superuser plan as self-managed Postgres (`--print-sql` shows it).
134
+
135
+ ```bash
136
+ npx postgresai prepare-db 'postgres://postgres:...@xxx.pg.clickhouse.cloud:5432/postgres?channel_binding=require'
137
+ ```
138
+
139
+ Before any grant runs, one `-- scope:` line lists exactly what the run grants the
140
+ monitoring role, derived from the steps about to run (so it shrinks under `--skip-optional-permissions`
141
+ and is not printed on `--reset-password`, which grants nothing). With `--json` it goes to stderr.
142
+
143
+ `channel_binding=require` in a URI or conninfo string enables SCRAM-SHA-256-PLUS when the server
144
+ offers it, disables the plaintext retry that `sslmode=prefer` would otherwise do, and is rejected
145
+ together with `sslmode=disable`. The mechanism actually negotiated is not enforced by the driver.
146
+
147
+ ### ClickHouse Cloud host metrics
148
+
149
+ For a ClickHouse Managed Postgres target, `mon local-install --db-url` and `mon targets add` also set up
150
+ host metrics (CPU, memory, disk, I/O) from the ClickHouse Cloud Prometheus endpoint when these are set:
151
+
152
+ ```bash
153
+ export CLICKHOUSE_ORG_ID='<org-id>' CLICKHOUSE_KEY_ID='<key-id>' CLICKHOUSE_KEY_SECRET='<key-secret>'
154
+ npx postgresai mon local-install --db-url 'postgres://postgres_ai_mon:...@xxx.pg.clickhouse.cloud:5432/postgres?sslmode=require'
155
+ ```
156
+
157
+ Use an organization API key with the **Basic Service API Reader** role: it is the least privilege that
158
+ works (it can list Postgres services and read `/prometheus`, and cannot change anything or create keys).
159
+ The CLI does not need an Admin key.
160
+
161
+ The key secret is stored in plaintext in `host-metrics/clickhouse-<name>.secret` in the monitoring
162
+ project directory (file 0600, directory 0700), because VictoriaMetrics reads it from a file for basic
163
+ auth. It is never printed or logged. `mon targets remove <name>` deletes it. To rotate the key, re-run
164
+ `mon targets add` with the new key.
165
+
128
166
  ### Verify and password reset
129
167
 
130
168
  Verify that everything is configured as expected (no changes):
@@ -141,6 +179,97 @@ npx postgresai prepare-db postgresql://admin@host:5432/dbname --reset-password -
141
179
 
142
180
  ## Quick start
143
181
 
182
+ ### One command: `pgai connect`
183
+
184
+ ```bash
185
+ pgai connect 'postgresql://postgres:<password>@<host>:5432/postgres'
186
+ ```
187
+
188
+ It signs you in if needed, creates the `postgres_ai_mon` role (with an admin URL; otherwise it
189
+ prints the SQL), provisions monitoring in PostgresAI Cloud, runs the express checkup (the checks
190
+ of `pgai checkup`, as `postgres_ai_mon`) while the box starts and prints its findings (in JSON:
191
+ `checkup`), waits, and prints the dashboard URL. Safe to re-run; a `--clickhouse-key` given on a
192
+ re-run is checked, and a rejected one is `action_required`. For ClickHouse Managed Postgres add `--clickhouse-key <key-id>:<key-secret>`
193
+ (a Basic Service API Reader key) for CPU, memory and disk. `--self-hosted` runs the stack on this
194
+ machine instead. When stdout is not a terminal the result is JSON with `status`, `dashboard_url`
195
+ and `next`. Then: `pgai databases`, `pgai status <name>`, `pgai disconnect <name>`.
196
+
197
+ `pgai init` is the same for a person at a terminal: it signs in, asks for the database URL (and,
198
+ for ClickHouse, the API key; neither is shown as typed), then runs `pgai connect`. Without a
199
+ terminal, or with `--json`, it only points to `pgai connect`.
200
+
201
+ | Option | |
202
+ |---|---|
203
+ | `--provider <provider>` | `clickhouse`, `rds`, `supabase` or `self-managed`; default: detected from the host |
204
+ | `--clickhouse-key <key-id>:<key-secret>` | ClickHouse Cloud API key, for CPU, memory and disk |
205
+ | `--self-hosted` | run the stack on this machine (`mon local-install`) instead of PostgresAI Cloud |
206
+ | `--reset-password` | `postgres_ai_mon` exists and its password is lost: set a new one (see below) |
207
+ | `--wait <minutes>` | how long to wait for the monitoring box; `0` does not wait (default 20) |
208
+ | `-y, --yes` | never prompt (`connect` does not start the browser sign-in; `disconnect` does not ask) |
209
+ | `--json` | JSON output, also on a terminal |
210
+
211
+ | Environment | |
212
+ |---|---|
213
+ | `PGAI_API_KEY` | the API key, instead of signing in (agents, CI) |
214
+ | `PGAI_MON_PASSWORD` | the password of `postgres_ai_mon`. For a new role it is set; for an existing role it is checked by logging in, and never changed. Without it a new role gets a generated password |
215
+ | `PGPASSWORD` | the password for a URL without one. With a `postgres_ai_mon` URL it is the password the monitoring box gets, once the server has checked it |
216
+ | `PGSSLMODE` | the `sslmode` for a URL without one (`sslmode` in the URL wins, as in libpq) |
217
+ | `CLICKHOUSE_KEY_ID` + `CLICKHOUSE_KEY_SECRET` | instead of `--clickhouse-key`; read for ClickHouse hosts only |
218
+
219
+ `postgres_ai_mon` is one role for the whole server, so `connect` never changes the password of an
220
+ existing one. If the role exists and you do not have its password (a reconnect after
221
+ `pgai disconnect`, say), `pgai connect <admin-url> --reset-password` sets a new one
222
+ (`PGAI_MON_PASSWORD`, else generated). It is refused while another database on the same server (the same host name, in this
223
+ organization) is monitored with the role, with `--self-hosted`, and for a database already
224
+ connected. Anything else that logs in as `postgres_ai_mon` (another organization, another host
225
+ name for the same server, a connect running at the same time) needs the new password.
226
+
227
+ When the role exists (an admin URL with `PGAI_MON_PASSWORD`, or the role's own URL), `connect`
228
+ also logs in once as `postgres_ai_mon` with a random password, to learn whether the server checks
229
+ passwords for this host at all. On a server that does, this is one
230
+ `password authentication failed for user "postgres_ai_mon"` line in the server log per run, and it
231
+ counts toward failed-login policies (credcheck, fail2ban). On a server that does not (`trust`),
232
+ `next` says that the password was not checked; a password from `PGPASSWORD` is then not used:
233
+ put it in the URL.
234
+
235
+ A role that is not a superuser creates `postgres_ai_mon` only if it can run the whole
236
+ preparation: `CREATEROLE`, `CREATE` on the database, `pg_stat_statements` already installed, and
237
+ on PostgreSQL 16+ `ADMIN OPTION` on `pg_monitor` and `pg_read_all_stats`. Otherwise the SQL is
238
+ printed and nothing is created.
239
+
240
+ The host and the port are the ones in the URL: `host` or `port` in the query string is refused.
241
+ Of the query string, the monitoring box gets `sslmode`, `channel_binding` and `application_name`.
242
+ Certificate files (`sslrootcert`, `sslcert`, `sslkey`) are used for the connections from this
243
+ machine only; with `sslmode=verify-ca` or `verify-full` and a private CA in `sslrootcert`, `next`
244
+ says that the box has no copy of that CA.
245
+
246
+ A run that fails does not leave such a role behind. The ClickHouse key and the service state are
247
+ checked before the database is touched. If the platform refuses the launch, or a later step of the
248
+ preparation fails, a role that this run created with a generated password is dropped again, so the
249
+ same command can be run again. (After a platform error that is not a refusal, such as a 5xx or a
250
+ timeout, the role stays: a monitoring box may be starting with it.)
251
+
252
+ An admin URL without a database connects to `PGDATABASE`, or else to the database named like the
253
+ user; the name of the connected database (`<host>[:<port>]/<database>`) uses that database.
254
+
255
+ `pgai connect`, `pgai status` and `pgai databases` use the same words:
256
+
257
+ | `status` | Meaning | Exit code |
258
+ |---|---|---|
259
+ | `connected` | monitoring works; `dashboard_url` is set | 0 |
260
+ | `provisioning` | the monitoring box is being set up; check with `pgai status <name>` | 0 |
261
+ | `disconnecting` | a disconnect is in progress (`pgai status`) | 0 |
262
+ | `disconnected` | the result of `pgai disconnect` | 0 |
263
+ | `action_required` | do what `next` says, then re-run | 3 |
264
+ | `failed` | `next` has the reason | 1 |
265
+
266
+ `pgai init` exits with 130 when cancelled at a prompt (Ctrl-C, Ctrl-D).
267
+
268
+ `--self-hosted` starts `mon local-install` with the monitoring URL, the API key and the ClickHouse
269
+ key in its environment (`PGAI_DB_URL`, `PGAI_API_KEY`, `CLICKHOUSE_*`), not in its arguments.
270
+ `mon local-install` reads `PGAI_DB_URL` like `--db-url`, and `PGAI_API_KEY` only together with
271
+ `PGAI_DB_URL`: an exported `PGAI_API_KEY` alone does not change a plain or `--demo` install.
272
+
144
273
  ### Authentication
145
274
 
146
275
  Authenticate via browser to obtain API key:
@@ -250,7 +379,7 @@ postgresai mon health [--wait <sec>] # Check monitoring services health
250
379
 
251
380
  When `--instance-id <uuid>` (or `PGAI_INSTANCE_ID`) is set, `local-install` forwards the id to the platform, which **adopts** the already-provisioned monitoring instance instead of self-registering a duplicate under an auto-created `postgres-ai-monitoring` project. The CLI then persists the adopted instance's real project to `.pgwatch-config`, so checkup reports upload alongside the rest of that instance's health data. Adoption is awaited (with one automatic retry); if it fails, the CLI warns and reports fall back to the default project until you re-run `local-install`. Without the flag, the legacy self-registration path is byte-for-byte unchanged.
252
381
 
253
- `local-install` writes `.env` in the monitoring directory. It preserves existing `REPLICATOR_PASSWORD` and `VM_AUTH_*` values or generates new random ones when missing; `VM_AUTH_USERNAME` defaults to `vmauth` when absent. The replication password is used by the demo PostgreSQL standby replication user, and the VM auth credentials are required before Docker Compose can provision Grafana datasources. If you run `docker compose` directly or maintain `.env` yourself, set both VM auth values before upgrading. For rotation, run `VM_AUTH_PASSWORD="$(openssl rand -base64 18)" ./scripts/rotate-vm-auth.sh` from the monitoring directory so `.env`, `sink-prometheus`, and `grafana` update together.
382
+ `local-install` writes `.env` in the monitoring directory. It preserves existing `REPLICATOR_PASSWORD`, `VM_AUTH_*` and the VictoriaMetrics admin-endpoint keys (`VM_DELETE_AUTH_KEY`, `VM_SNAPSHOT_AUTH_KEY`, `VM_FORCE_MERGE_AUTH_KEY`, `VM_PPROF_AUTH_KEY`, added in 0.17) or generates new random ones when missing. The admin keys live on the `sink-prometheus` command line, so `mon update` and `mon update-config` also recreate that container to apply them; `mon restart` (`docker compose restart`) does not; `VM_AUTH_USERNAME` defaults to `vmauth` when absent. The replication password is used by the demo PostgreSQL standby replication user, and the VM auth credentials are required before Docker Compose can provision Grafana datasources. If you run `docker compose` directly or maintain `.env` yourself, set both VM auth values before upgrading. For rotation, run `VM_AUTH_PASSWORD="$(openssl rand -base64 18)" ./scripts/rotate-vm-auth.sh` from the monitoring directory so `.env`, `sink-prometheus`, and `grafana` update together.
254
383
 
255
384
  #### Monitoring target databases (`mon targets` subgroup)
256
385
  ```bash
@@ -271,6 +400,35 @@ postgresai mon check # System readiness check
271
400
  postgresai mon shell <service> # Open shell to monitoring service
272
401
  ```
273
402
 
403
+ ### PromQL queries (`promql`)
404
+
405
+ Ask a monitoring instance a PromQL question through the platform. The platform
406
+ never connects to the instance: it queues the query as a job, the instance picks
407
+ it up on its next poll, runs it against its own metric store, and posts the
408
+ answer back.
409
+
410
+ ```bash
411
+ # instant query
412
+ pgai promql 'up' --instance <instance-uuid>
413
+
414
+ # range query
415
+ pgai promql 'sum(rate(pgwatch_db_stats_xact_commit[5m]))' \
416
+ --instance <instance-uuid> \
417
+ --range --start 2026-09-21T00:00:00Z --end 2026-09-21T01:00:00Z --step 60
418
+
419
+ # on the box itself, --instance is read from .pgwatch-config
420
+ pgai promql 'up'
421
+ ```
422
+
423
+ `--at <time>` evaluates an instant query at a given moment (ignored with
424
+ `--range`). `--json` prints every point instead of the first and last per series.
425
+ The wait is sized from the instance's poll pacing; `--timeout` overrides it. A
426
+ result too large for the byte budget is trimmed and flagged `truncated`.
427
+
428
+ Requires platform-all !809
429
+ (https://gitlab.com/postgres-ai/platform-all/-/merge_requests/809); until it is
430
+ deployed the command returns a PGRST202 404.
431
+
274
432
  ### MCP server (`mcp` group)
275
433
 
276
434
  ```bash
@@ -299,7 +457,7 @@ required** — the server will not assume an organization. Call `orgs_list` (or
299
457
  run `pgai orgs`) to discover the available ids.
300
458
 
301
459
  Tools exposed:
302
- - `list_issues`: returns the same JSON as `postgresai issues list` (args: `{ org_id, status?, hidden_only?, limit?, offset?, debug? }`).
460
+ - `list_issues`: returns the same JSON as `postgresai issues list` (args: `{ org_id, status?, hidden_only?, limit?, offset?, debug? }`). Unlike the CLI, omitting `status` returns open and closed issues; pass `status: "open"` to match the CLI default.
303
461
  - `view_issue`: view a single issue with its comments (args: `{ issue_id, org_id, debug? }`).
304
462
  - `create_issue`: create a new issue (args: `{ title, description?, org_id, project_id?, labels?, attachments?, is_hidden?, debug? }`).
305
463
  - `update_issue`: update title/description/status/labels/is_hidden (args: `{ issue_id, org_id, title?, description?, status?, labels?, attachments?, is_hidden?, debug? }`).
@@ -307,6 +465,7 @@ Tools exposed:
307
465
  - `update_issue_comment`: update an existing comment (args: `{ comment_id, org_id, content?, attachments?, debug? }`).
308
466
  - `upload_file`: upload a local file and return the storage URL plus a ready-to-paste markdown link (args: `{ path, org_id, debug? }`).
309
467
  - `download_file`: download a file from storage (args: `{ url, org_id, output_path?, debug? }`).
468
+ - `connect_database`: `pgai connect` as a tool (args: `{ database_url, org_id, provider?, clickhouse_key?, debug? }`). Returns the same JSON (`status`: `connected`, `provisioning`, `disconnecting`, `action_required` or `failed`; `dashboard_url`; `next`). It does not wait for the monitoring box: call it again to see the status. `database_url` must carry its password, and only the query parameters `sslmode`, `channel_binding`, `application_name` (and `password`); `PGAI_MON_PASSWORD`, `PGPASSWORD` and `CLICKHOUSE_KEY_ID` + `CLICKHOUSE_KEY_SECRET` of the server process are not used, a TLS failure is not retried in plaintext, and `--self-hosted` is CLI-only.
310
469
 
311
470
  #### `attachments` parameter (issue/comment tools)
312
471
 
@@ -350,7 +509,8 @@ sensitive.
350
509
  ### Issues management (`issues` group)
351
510
 
352
511
  ```bash
353
- postgresai issues list # List issues (shows: id, title, status, created_at; is_hidden only when set)
512
+ postgresai issues list # List OPEN issues (shows: id, title, status, created_at; is_hidden only when set)
513
+ postgresai issues list --status closed # Only closed issues (--status all for both)
354
514
  postgresai issues list --hidden-only # Only hidden issues (PostgresAI staff)
355
515
  postgresai issues view <issueId> # View issue details and comments
356
516
  postgresai issues create --org-id <id> <title> # Create a new issue