Skip to content
This project is part of pgEdge Labs and is under active development. APIs and features may change without notice.

Release Notes

All notable changes to pgVolvra are documented in this file. The format follows Keep a Changelog, and pgVolvra uses Semantic Versioning.

[1.0.0-beta3] - 2026-10-26

Added

  • Native object storage for the companion archive. The companion uploads each segment to S3-compatible storage after the segment rotates, comparing the provider's checksum against the hash the manifest already chained before recording the segment as archived. Local disk remains the write path, so every existing durability guarantee is unchanged, and uploaded segments are kept locally so verification still works with no network. One client covers AWS S3, Amazon S3, and Google Cloud Storage through its S3 interoperability endpoint. Both are verified against real buckets. Other S3-compatible stores use the same protocol and are expected to work, but have not been run and are not claimed.
  • The replication slot now waits for the bucket. With object storage configured, the slot does not advance past a segment the bucket has not confirmed, because releasing write-ahead log for data held only on a host that then dies is the failure this tier exists to prevent. A bucket outage retains write-ahead log and is reported; --lag-max remains the ceiling at which a gap is recorded instead.
  • verify and restore read from the bucket when the local archive is absent. Pass an empty --archive to work from object storage alone, which is what a host loss leaves behind.
  • Reporting of a bucket that enforces S3 Object Lock. pgVolvra never sets a retention and offers no flag to; it detects the policy the bucket already enforces and says what that costs. Because a locked object cannot be erased, and volvra.forget is documented for right-to-erasure requests, the conflict is reported rather than documented and forgotten: the companion reports it at startup, volvra.forget warns every time it runs while a retention is in force, volvra.preflight reports the condition, and volvra.companion_status shows the mode and duration.
  • Google Cloud Storage support through its S3 interoperability endpoint, which needs no separate client, verified against a real bucket. GCS does not implement the aws-chunked trailing header that carries a server-side checksum, and an upload asking for one against a Google endpoint fails rather than degrading, so --s3-checksum defaults to auto and selects read-back verification for storage.googleapis.com. Read-back downloads each segment after uploading it to hash it locally, which doubles the traffic and is announced at startup because it is a real cost on a provider that bills egress.
  • volvra.companion_report_immutable, which the companion calls to record the retention. Schema version 2 adds the columns it writes to volvra.companion_checkpoint.

Changed

  • --segment-bytes now defaults to 12 MB rather than 64 MB. An upload larger than about 16 MB is split into parts, and the composite checksum S3 returns for a multipart upload cannot be compared with the manifest hash, so the companion had to download every segment again to verify it. Staying under that threshold roughly halves the traffic archiving costs. Raise it for archives past roughly 700 GB, where more segments mean more manifest uploads.
  • The extension packaging no longer keeps a hand-written list of released versions. extension/build.sh now derives the versions that need an ALTER EXTENSION UPDATE path from the release snapshots in test/releases/, which tools/snapshot-schema.sh writes when a release is cut, and extension/upgrade-from.txt is gone. A release no longer has a step that can be forgotten, and forgetting it used to go unnoticed until the release after next. extension/test.sh now fails if any released version lacks a path to the current one.
  • The upgrade test runs once for every snapshot in test/releases/ rather than only the newest. Straight after a release the newest snapshot is the code under test, so the old behavior proved almost nothing at exactly the point where upgrade coverage matters.

Fixed

  • Archiving to a bucket with S3 Object Lock enabled failed outright. S3 refuses any upload that carries no checksum header on such a bucket, and the manifest upload sent none, so the companion stopped at startup with "Content-MD5 OR x-amz-checksum- HTTP header is required for Put Object requests with Object Lock parameters". A checksum is now always sent. No S3 implementation available as a container image enforces this requirement, so the fault was only visible against a real bucket.
  • A segment larger than about 16 MB is uploaded in parts, and the composite checksum S3 returns for a multipart upload cannot be compared with the manifest hash. The companion fell back to reading the object back and hashing it, which is correct but was silent. Since segments default to 64 MB, that fallback is the normal path on Amazon S3 and it costs an extra download per segment, so it is now reported once per run.
  • The companion test suites waited for the archive manifest before writing test data, but the manifest exists before the replication slot does, so a write issued in that window never reached the stream. Both suites now wait for the slot. This was a test defect, not a product one, but it made a suite report zero archived segments while the trigger tier had captured every row.

  • extension/test.sh no longer fails intermittently on a slow container start. The script waited with pg_isready, which answers yes while the postgres image is still running its init phase against a temporary server, before POSTGRES_DB exists. The run then failed with database "ext" does not exist, which reads as a product failure and is not one. The script now uses volvra_wait_ready from test/lib.sh, as the rest of the suites already did.

[1.0.0-beta2] - 2026-09-21

Fixed

  • TRUNCATE on a covered table with a column named t recorded a column value instead of the row image, and the data could not be recovered. Every statement pgVolvra generates aliases the target table, and those aliases were bare names. PostgreSQL resolves such a reference to a column of that name in preference to the row, so to_jsonb(t) yielded the column. volvra.capture_truncate then stored a scalar where a row image belongs, with a primary key of {"id": null}, and the undo failed with "cannot call jsonb_object_keys on a scalar". Nothing reported a problem until the undo was attempted.
  • A covered table with a column named tgt could not be undone at all: the generated statement failed with "column reference is ambiguous". That failure was at least loud, and no data was lost.
  • Aliases in generated SQL are now quoted ("volvra$row", "volvra$tgt"), which a column name cannot collide with. Columns named src or k were never affected.
  • A table using legacy INHERITS was covered in name only. A row trigger is not inherited, so writes reaching child rows were captured by nothing while volvra.status() reported the parent as covered and volvra.health() reported no problem. Worse, volvra.enable() refused to cover the child, reporting it as "already covered through" the parent, so the gap could not be closed deliberately. volvra._covered_ancestor now follows partition links only, since coverage propagates to partitions and not to inheritance children. volvra.enable warns when a covered parent has uncovered children, and volvra.preflight reports the condition as critical.
  • Redefining a table's PRIMARY KEY is now reported as what it is. Statements are built from the table's current key while captured rows hold the old one, so the lookup matched nothing and the conflict guard blamed a later change that never happened.

Changed

  • volvra.preflight warns when a covered table has its own BEFORE row triggers. An undo writes the captured row back, so those triggers fire and may rewrite it, and the undo reports success either way.

Added

  • volvra.set_warn_changed_rows(n) raises a WARNING when one statement changes more than n rows on a covered table, so a missing WHERE clause announces itself as it runs instead of when somebody notices. Off by default, and off means the statement triggers are not attached at all, so a database that does not use it carries none of its cost.
  • volvra.as_of(table, timestamp) reconstructs a covered table as it stood at a past instant, read-only. It writes nothing, refuses on a table that was never covered rather than returning the present and calling it the past, and warns when a table is not currently capturing because changes made in that gap cannot be known.

[1.0.0-beta1] - 2026-09-15

Added

  • Support for multi-master clusters, through the capture_replicated setting and volvra.set_capture_replicated. PostgreSQL does not fire an ordinary row trigger for rows applied by replication, so a node would otherwise record only what was written to it. The setting defaults to off, which leaves single-node behaviour unchanged, and volvra.preflight warns when a database receives replicated changes without capturing them. Verified on a two-node Spock cluster, where a node reverted a change made on its peer.
  • A critical preflight finding when a pgVolvra table is in a publication or replication set, which would make two nodes write the same history identifiers.
  • Row-level undo for INSERT, UPDATE, and DELETE, selectable by table, time window, transaction, actor, database role, or SQL predicate.
  • Transaction-scoped undo, so a mistaken migration can be reverted across every table the migration touched.
  • Forward replay, the mirror of undo, which reapplies changes in their original direction oldest first. A database restored from a backup that predates changes still held in history can be carried forward over them. Replay shares the conflict guard, the blast-radius cap, the advisory locks and the single transaction with undo, and refuses any row that no longer holds the image captured before its change.
  • Row history with two recorded identities, an application-declared actor and an authenticated database principal.
  • A conflict guard that refuses to overwrite a change made after the mistake, with an option to skip conflicting rows instead.
  • A blast-radius cap on the number of rows a single undo may affect.
  • TRUNCATE capture, with block and allow modes as alternatives.
  • Support for partitioned tables, covered through the parent, so one undo reverts rows in every partition. volvra.cover_partitions attaches TRUNCATE capture to partitions, which PostgreSQL does not propagate on its own, and volvra.maintain reconciles partitions added later.
  • Coverage that survives ALTER TABLE RENAME and SET SCHEMA, because a covered table is identified by its relation identifier rather than by name.
  • Publications created with publish_generated_columns = stored on PostgreSQL 18 and later. Without it, a covered table with a generated column becomes impossible to update once the companion is set up, because REPLICA IDENTITY FULL puts generated columns in the replica identity and PostgreSQL 18 refuses to update a table whose replica identity contains unpublished generated columns.
  • Conflict guards that ignore generated columns. A guard compared a derived value that logical replication does not send, so history restored from an archive conflicted on every row of any table with a generated column, for undo as well as replay.
  • An extension upgrade path. extension/build.sh emits an upgrade script for every version in extension/upgrade-from.txt, so ALTER EXTENSION UPDATE works from any released version.
  • tools/snapshot-schema.sh, which freezes a release's install script as the fixture the next release's upgrade test runs against.
  • Schema-wide coverage with volvra.enable_all, plus coverage gap reporting with volvra.uncovered.
  • Monthly partitioning of the history, with partition-dropping retention and per-table retention policies.
  • A single maintenance function that extends partitions, applies retention, and seals the history.
  • Health, preflight, status, storage, and activity functions, including a publication drift check that reports covered tables the companion publication does not carry.
  • Tamper evidence through a SHA-256 seal chain over ranges of the history, with verification that distinguishes lawful erasure and retention from tampering.
  • A code fingerprint that detects a function altered after install.
  • Per-subject erasure, with redaction by default and hard deletion for when a primary key is itself personal data.
  • Column exclusion, so nominated columns never reach the history.
  • The volvra-companion durable tier, which archives change data to customer-owned storage through a logical replication slot, with archive verification, restore, and a slot-lag safety valve.
  • A command line tool that previews, confirms, and then applies, distributed as a single Go binary with no runtime dependencies. The tool sends every user-supplied value as a bind parameter, exits 2 when preflight or verify finds something wrong, and reads a trailing ago in a time so that --since '10 min ago' works.
  • Optional CREATE EXTENSION packaging for self-hosted users, generated from the same SQL file.

Notes

  • This is a beta. The interfaces are expected to hold, but they may still change before 1.0.0, and upgrading from this release will be supported.
  • pgVolvra requires PostgreSQL 14 or later and no PostgreSQL extensions.
  • Verified against live instances of Supabase, Neon, and pgEdge Cloud. On pgEdge Cloud the durable tier additionally needs a REPLICATION grant. Amazon RDS, Amazon Aurora, and Google Cloud SQL are designed for on the same basis but have not yet been verified.
  • No release signing key is published yet.