postgresai 0.17.0-dev.1 → 0.17.0-dev.3

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,90 @@ 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, waits, and prints the dashboard URL.
190
+ Safe to re-run. For ClickHouse Managed Postgres add `--clickhouse-key <key-id>:<key-secret>`
191
+ (a Basic Service API Reader key) for CPU, memory and disk. `--self-hosted` runs the stack on this
192
+ machine instead. When stdout is not a terminal the result is JSON with `status`, `dashboard_url`
193
+ and `next`. Then: `pgai databases`, `pgai status <name>`, `pgai disconnect <name>`.
194
+
195
+ `pgai init` is the same for a person at a terminal: it signs in, asks for the database URL (and,
196
+ for ClickHouse, the API key, which is not shown as typed), then runs `pgai connect`. Without a
197
+ terminal, or with `--json`, it only points to `pgai connect`.
198
+
199
+ | Option | |
200
+ |---|---|
201
+ | `--provider <provider>` | `clickhouse`, `rds`, `supabase` or `self-managed`; default: detected from the host |
202
+ | `--clickhouse-key <key-id>:<key-secret>` | ClickHouse Cloud API key, for CPU, memory and disk |
203
+ | `--self-hosted` | run the stack on this machine (`mon local-install`) instead of PostgresAI Cloud |
204
+ | `--wait <minutes>` | how long to wait for the monitoring box; `0` does not wait (default 20) |
205
+ | `-y, --yes` | never prompt (`connect` does not start the browser sign-in; `disconnect` does not ask) |
206
+ | `--json` | JSON output, also on a terminal |
207
+
208
+ | Environment | |
209
+ |---|---|
210
+ | `PGAI_API_KEY` | the API key, instead of signing in (agents, CI) |
211
+ | `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 |
212
+ | `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 |
213
+ | `PGSSLMODE` | the `sslmode` for a URL without one (`sslmode` in the URL wins, as in libpq) |
214
+ | `CLICKHOUSE_KEY_ID` + `CLICKHOUSE_KEY_SECRET` | instead of `--clickhouse-key`; read for ClickHouse hosts only |
215
+
216
+ `postgres_ai_mon` is one role for the whole server, so `connect` never changes the password of an
217
+ existing one. If the role exists and you do not have its password, change it explicitly and
218
+ update every monitoring box that uses it:
219
+ `pgai prepare-db <admin-url> --reset-password --password <new-password>`, then
220
+ `PGAI_MON_PASSWORD=<new-password> pgai connect <admin-url>`.
221
+
222
+ When the role exists (an admin URL with `PGAI_MON_PASSWORD`, or the role's own URL), `connect`
223
+ also logs in once as `postgres_ai_mon` with a random password, to learn whether the server checks
224
+ passwords for this host at all. On a server that does, this is one
225
+ `password authentication failed for user "postgres_ai_mon"` line in the server log per run, and it
226
+ counts toward failed-login policies (credcheck, fail2ban). On a server that does not (`trust`),
227
+ `next` says that the password was not checked; a password from `PGPASSWORD` is then not used:
228
+ put it in the URL.
229
+
230
+ A role that is not a superuser creates `postgres_ai_mon` only if it can run the whole
231
+ preparation: `CREATEROLE`, `CREATE` on the database, `pg_stat_statements` already installed, and
232
+ on PostgreSQL 16+ `ADMIN OPTION` on `pg_monitor` and `pg_read_all_stats`. Otherwise the SQL is
233
+ printed and nothing is created.
234
+
235
+ The host and the port are the ones in the URL: `host` or `port` in the query string is refused.
236
+ Of the query string, the monitoring box gets `sslmode`, `channel_binding` and `application_name`.
237
+ Certificate files (`sslrootcert`, `sslcert`, `sslkey`) are used for the connections from this
238
+ machine only; with `sslmode=verify-ca` or `verify-full` and a private CA in `sslrootcert`, `next`
239
+ says that the box has no copy of that CA.
240
+
241
+ A run that fails does not leave such a role behind. The ClickHouse key and the service state are
242
+ checked before the database is touched. If the platform refuses the launch, or a later step of the
243
+ preparation fails, a role that this run created with a generated password is dropped again, so the
244
+ same command can be run again. (After a platform error that is not a refusal, such as a 5xx or a
245
+ timeout, the role stays: a monitoring box may be starting with it.)
246
+
247
+ An admin URL without a database connects to `PGDATABASE`, or else to the database named like the
248
+ user; the name of the connected database (`<host>[:<port>]/<database>`) uses that database.
249
+
250
+ | `status` | Meaning | Exit code |
251
+ |---|---|---|
252
+ | `connected` | monitoring works; `dashboard_url` is set | 0 |
253
+ | `provisioning` | the monitoring box is being set up; check with `pgai status <name>` | 0 |
254
+ | `disconnecting` | a disconnect is in progress (`pgai status`) | 0 |
255
+ | `disconnected` | the result of `pgai disconnect` | 0 |
256
+ | `action_required` | do what `next` says, then re-run | 3 |
257
+ | `failed` | `next` has the reason | 1 |
258
+
259
+ `pgai init` exits with 130 when cancelled at a prompt (Ctrl-C, Ctrl-D).
260
+
261
+ `--self-hosted` starts `mon local-install` with the monitoring URL, the API key and the ClickHouse
262
+ key in its environment (`PGAI_DB_URL`, `PGAI_API_KEY`, `CLICKHOUSE_*`), not in its arguments.
263
+ `mon local-install` reads `PGAI_DB_URL` like `--db-url`, and `PGAI_API_KEY` only together with
264
+ `PGAI_DB_URL`: an exported `PGAI_API_KEY` alone does not change a plain or `--demo` install.
265
+
144
266
  ### Authentication
145
267
 
146
268
  Authenticate via browser to obtain API key:
@@ -250,7 +372,7 @@ postgresai mon health [--wait <sec>] # Check monitoring services health
250
372
 
251
373
  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
374
 
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.
375
+ `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
376
 
255
377
  #### Monitoring target databases (`mon targets` subgroup)
256
378
  ```bash
@@ -271,6 +393,35 @@ postgresai mon check # System readiness check
271
393
  postgresai mon shell <service> # Open shell to monitoring service
272
394
  ```
273
395
 
396
+ ### PromQL queries (`promql`)
397
+
398
+ Ask a monitoring instance a PromQL question through the platform. The platform
399
+ never connects to the instance: it queues the query as a job, the instance picks
400
+ it up on its next poll, runs it against its own metric store, and posts the
401
+ answer back.
402
+
403
+ ```bash
404
+ # instant query
405
+ pgai promql 'up' --instance <instance-uuid>
406
+
407
+ # range query
408
+ pgai promql 'sum(rate(pgwatch_db_stats_xact_commit[5m]))' \
409
+ --instance <instance-uuid> \
410
+ --range --start 2026-09-21T00:00:00Z --end 2026-09-21T01:00:00Z --step 60
411
+
412
+ # on the box itself, --instance is read from .pgwatch-config
413
+ pgai promql 'up'
414
+ ```
415
+
416
+ `--at <time>` evaluates an instant query at a given moment (ignored with
417
+ `--range`). `--json` prints every point instead of the first and last per series.
418
+ The wait is sized from the instance's poll pacing; `--timeout` overrides it. A
419
+ result too large for the byte budget is trimmed and flagged `truncated`.
420
+
421
+ Requires platform-all !809
422
+ (https://gitlab.com/postgres-ai/platform-all/-/merge_requests/809); until it is
423
+ deployed the command returns a PGRST202 404.
424
+
274
425
  ### MCP server (`mcp` group)
275
426
 
276
427
  ```bash
@@ -299,14 +450,15 @@ required** — the server will not assume an organization. Call `orgs_list` (or
299
450
  run `pgai orgs`) to discover the available ids.
300
451
 
301
452
  Tools exposed:
302
- - `list_issues`: returns the same JSON as `postgresai issues list` (args: `{ org_id, status?, hidden_only?, limit?, offset?, debug? }`).
453
+ - `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
454
  - `view_issue`: view a single issue with its comments (args: `{ issue_id, org_id, debug? }`).
304
- - `create_issue`: create a new issue (args: `{ title, description?, org_id, attachments?, debug? }`).
305
- - `update_issue`: update title/description/status/labels (args: `{ issue_id, org_id, title?, description?, status?, labels?, attachments?, debug? }`).
455
+ - `create_issue`: create a new issue (args: `{ title, description?, org_id, project_id?, labels?, attachments?, is_hidden?, debug? }`).
456
+ - `update_issue`: update title/description/status/labels/is_hidden (args: `{ issue_id, org_id, title?, description?, status?, labels?, attachments?, is_hidden?, debug? }`).
306
457
  - `post_issue_comment`: post a comment (args: `{ issue_id, org_id, content?, parent_comment_id?, attachments?, debug? }`).
307
458
  - `update_issue_comment`: update an existing comment (args: `{ comment_id, org_id, content?, attachments?, debug? }`).
308
459
  - `upload_file`: upload a local file and return the storage URL plus a ready-to-paste markdown link (args: `{ path, org_id, debug? }`).
309
460
  - `download_file`: download a file from storage (args: `{ url, org_id, output_path?, debug? }`).
461
+ - `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
462
 
311
463
  #### `attachments` parameter (issue/comment tools)
312
464
 
@@ -350,11 +502,14 @@ sensitive.
350
502
  ### Issues management (`issues` group)
351
503
 
352
504
  ```bash
353
- postgresai issues list # List issues (shows: id, title, status, created_at; is_hidden only when set)
505
+ postgresai issues list # List OPEN issues (shows: id, title, status, created_at; is_hidden only when set)
506
+ postgresai issues list --status closed # Only closed issues (--status all for both)
354
507
  postgresai issues list --hidden-only # Only hidden issues (PostgresAI staff)
355
508
  postgresai issues view <issueId> # View issue details and comments
356
- postgresai issues create --org-id <id> --title <t> # Create a new issue
509
+ postgresai issues create --org-id <id> <title> # Create a new issue
510
+ postgresai issues create --org-id <id> <title> --hidden # Create a hidden issue (PostgresAI staff)
357
511
  postgresai issues update <issueId> [--title ... --status ...]# Update an existing issue
512
+ postgresai issues update <issueId> --hidden|--no-hidden # Hide / unhide an issue (PostgresAI staff)
358
513
  postgresai issues post-comment <issueId> <content> # Post a comment to an issue
359
514
  postgresai issues update-comment <commentId> <content> # Update an existing comment
360
515
  postgresai issues files upload <path> # Upload a file, print URL + markdown
@@ -370,7 +525,11 @@ postgresai issues files download <url> [-o <path>] # Download a file
370
525
  Hidden issues are staff-internal. `issues list` and `issues view` mark them
371
526
  with `is_hidden: true`; the key is omitted entirely otherwise, so ordinary
372
527
  issues look exactly as they always have. `--hidden-only` lists just the hidden
373
- ones, filtered server-side.
528
+ ones, filtered server-side. `issues create --hidden` creates one, and
529
+ `issues update --hidden` / `--no-hidden` hides or unhides an existing issue
530
+ (MCP: `is_hidden` on `create_issue` / `update_issue`). The CLI does no staff
531
+ check of its own: the platform refuses a visibility change for a non-staff
532
+ credential with a plain error, and the CLI prints it and exits non-zero.
374
533
 
375
534
  Staff access is granted per credential, not per person, and a credential that
376
535
  does not qualify simply sees nothing — `--hidden-only` returns an empty list