> ## Documentation Index
> Fetch the complete documentation index at: https://xata.io/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# v1.4.0

> pgstream v1.4.0 release notes

**Release Date:** August 12th, 2026

pgstream v1.4.0 is a minor release. It adds a native Prometheus scrape endpoint, granular object type filtering for Postgres-to-Postgres snapshots and DDL replication, and validation of transformation rules against unique indexes before a run starts instead of failing mid-load. Target connection pool limits become configurable, webhook delivery gains retries and idempotency headers, and a DDL parsing bug that silently dropped `RENAME COLUMN` and `DROP COLUMN` changes is fixed. Pipelines with a lossy transformer on a unique-indexed column need attention before upgrading — see the upgrade notes.

## Prometheus Scrape Endpoint

[PR #1008](https://github.com/xataio/pgstream/pull/1008), contributed by [@pauliyobo](https://github.com/pauliyobo) in their first contribution.

`instrumentation.metrics.prometheus.enabled` exposes a `/metrics` endpoint on the existing health server, wired into the OpenTelemetry meter provider independently of the OTLP exporter. Metrics can be scraped directly, with no collector in between:

```yaml theme={null} theme={null}
instrumentation:
  metrics:
    prometheus:
      enabled: true # Defaults to false
      endpoint: "/metrics" # Defaults to /metrics
```

The endpoint returns `404` when disabled.

## Granular Object Type Filtering

[PR #735](https://github.com/xataio/pgstream/pull/735), by [@tsg](https://github.com/tsg).

Both the schema snapshot and DDL replication for Postgres-to-Postgres pipelines can be restricted to a subset of object categories — `tables`, `sequences`, `types`, `indexes`, `functions`, `views`, `triggers` and others, 16 in total — via `include_object_types` (allowlist) or `exclude_object_types` (denylist), independently for snapshot and replication. A DDL statement that touches several object types (for example `CREATE TABLE` with a primary key) is only skipped when every object it touches is excluded.

This does not resolve inter-object dependencies: excluding a category that surviving objects depend on will make the snapshot or DDL replay fail, so use it with a good understanding of your schema.

## Transformation Rules Validated Against Unique Indexes

[PR #1082](https://github.com/xataio/pgstream/pull/1082), [PR #1084](https://github.com/xataio/pgstream/pull/1084), [PR #1085](https://github.com/xataio/pgstream/pull/1085) and [PR #1086](https://github.com/xataio/pgstream/pull/1086), by [@kvch](https://github.com/kvch).

Anonymization is lossy by design: `masking` with `type: id` keeps a 6-character prefix, so every value sharing that prefix collapses to the same masked value. Applied to a column covered by a unique index, this previously surfaced as `duplicate key value violates unique constraint` hours into a data load.

Every transformer is now classified as `preserved` (distinct inputs stay distinct), `not_guaranteed` (random or hashed output, collisions possible) or `lossy` (collides by construction — masking, the greenmask and neosync name and choice generators, `literal_string`). `validate rules` and pipeline startup read `pg_index` for the tables in scope and check the configured rules against unique indexes, primary keys and unique constraints.

With a Postgres target, a `lossy` transformer on a covered column fails at parse time with `transformation rules break a unique index`; with other targets (Kafka, Elasticsearch/OpenSearch, webhooks) the same finding is a warning, since there is no unique index to violate. A `not_guaranteed` transformer always warns. Set `allow_uniqueness_loss: true` on a column rule to keep a lossy transformer anyway.

Related: a `custom` mask configured to mask zero characters (`mask_begin` equal to `mask_end`, or `unmask_begin: 0`) is now rejected at construction rather than silently passing values through unmasked ([PR #1083](https://github.com/xataio/pgstream/pull/1083)) — use `noop` to pass a column through on purpose.

## Configurable Connection Pool Limits

[PR #1048](https://github.com/xataio/pgstream/pull/1048), contributed by [@subotac](https://github.com/subotac) in their first contribution, with [PR #1092](https://github.com/xataio/pgstream/pull/1092) by [@kvch](https://github.com/kvch).

`target.postgres.max_connections` (`PGSTREAM_POSTGRES_WRITER_MAX_CONNECTIONS`) controls the writer pool size, defaulting to 50 and honoring `pool_max_conns` from the target URL when set explicitly.

Previously this setting also sized the schema observer's pool, so a target capped at 90 connections to stay under a 100-connection server limit could still open up to 180. The schema observer is now sized separately, at `min(writer pool, 16)` — its catalog lookups are a startup and DDL burst rather than sustained COPY concurrency — so the process opens at most the configured writer limit plus 16.

## Webhook Delivery

[PR #1080](https://github.com/xataio/pgstream/pull/1080), by [@kvch](https://github.com/kvch).

A failed webhook POST is now retried with configurable backoff (network errors, `429`, `5xx`); other non-2xx responses are treated as permanent and not retried. A delivery that is still failing after retries is no longer checkpointed away — it is logged and dropped so one broken subscriber cannot block delivery to everyone else, and at-least-once semantics apply on restart. New `X-Pgstream-LSN` and `Idempotency-Key` headers let subscribers dedupe redelivered events (both omitted for snapshot events, which share a zero LSN). `DISABLE_RETRIES` now actually disables retries.

## `CREATEROLE` Preflight Check

[PR #1055](https://github.com/xataio/pgstream/pull/1055), contributed by [@lghuy05](https://github.com/lghuy05).

`pgstream check` verifies that the target connection role has `CREATEROLE` (or is a superuser) when `roles_snapshot_mode` is `enabled` or `no_passwords`, catching a missing privilege before the restore fails.

## Bug Fixes

* **`RENAME COLUMN` and `DROP COLUMN` are no longer silently dropped** ([PR #1078](https://github.com/xataio/pgstream/pull/1078)). The regex used to extract column names matched the trailing statement terminator as part of a bare identifier, so `RENAME COLUMN pii_email TO email;` was parsed as renaming to `email;` — the lookup then missed and the DDL event produced an empty schema diff, dropping the change from replication.

## Documentation

* Transformer uniqueness and the unique index check are documented ([PR #1088](https://github.com/xataio/pgstream/pull/1088)).
* The destructive `--reset` snapshot behaviour is documented ([PR #1059](https://github.com/xataio/pgstream/pull/1059)).

## New Contributors

* [@pauliyobo](https://github.com/pauliyobo) in [PR #1008](https://github.com/xataio/pgstream/pull/1008)
* [@subotac](https://github.com/subotac) in [PR #1048](https://github.com/xataio/pgstream/pull/1048)

## Upgrade

⚠️ **Breaking: Postgres-to-Postgres pipelines with a lossy transformer on a unique-indexed column now fail at startup.** This is the intended outcome of the new uniqueness check — it replaces a mid-load `duplicate key value violates unique constraint` failure with an immediate, actionable one — but existing configurations that rely on this combination need either a uniqueness-preserving transformer or `allow_uniqueness_loss: true` on the affected column before upgrading.

⚠️ **Breaking: a zero-width `custom` mask (`masking`) is now rejected at startup.** If you have a `mask_begin`/`mask_end` pair or `unmask_begin: 0` that masks no characters, switch to `noop` or a real mask range.

**Connection pools.** No action needed for most setups. If you were relying on `max_connections` to size the schema observer's pool as well as the writer's, the observer is now capped at `min(writer pool, 16)` regardless — reduce the writer setting if you need a lower total connection count.

## Support

* Issues: [https://github.com/xataio/pgstream/issues](https://github.com/xataio/pgstream/issues)
* Documentation: [https://github.com/xataio/pgstream/tree/main/docs](https://github.com/xataio/pgstream/tree/main/docs)
* Discussions: [https://github.com/xataio/pgstream/discussions](https://github.com/xataio/pgstream/discussions)
