Skip to content

fix(recovery): require cluster identity for restore-target isolation - #228

Merged
seonghobae merged 6 commits into
mainfrom
cursor/bc-0141e0e9-f781-4403-8775-4c7570eede78-d5d0
Aug 16, 2026
Merged

seonghobae merged 6 commits into
mainfrom
cursor/bc-0141e0e9-f781-4403-8775-4c7570eede78-d5d0

Conversation

@cursor

@cursor cursor Bot commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

Why this exists

#225 at 3290f75 retargets restore-target isolation to ADR 0022, but the Python seam still treats two different libpq service names as a distinct reviewed identity. Two pg_service.conf sections or DNS aliases can resolve to the same PostgreSQL host, port, database, and cluster. That cannot safely gate pg_restore.

This successor keeps the ADR 0022 number and requires caller-owned pg_control_system().system_identifier evidence for both targets. Distinct names that share one cluster identifier fail closed. The package still does not open a connection, accept a DSN, or execute pg_restore.

Operator action

  1. Choose the live pg_service.conf name and a separate restore-drill name.
  2. Open one caller-owned connection to each service. Run SELECT system_identifier FROM pg_control_system(); on each.
  3. Call verify_postgres_restore_target_isolation(live_service_name=..., restore_service_name=..., live_target_identity=PostgresRestoreTargetIdentity(system_identifier=...), restore_target_identity=...).
  4. Stop when the names match, the identifiers match, or the inputs fail the reviewed grammar. Create a different restore-drill cluster instead of aliasing production.
  5. Treat a return as name-plus-cluster isolation only. It is not proof that restore, RLS, PITR, or a live cluster succeeded.

Evidence

  • RED: batch-prod versus batch-restore-isolated with the same system_identifier fails closed and does not echo names, DSNs, or the identifier.
  • GREEN: the same names with distinct cluster identifiers return.
  • Same-name reuse, DSN/path rejection, subclass rejection, boolean/zero/oversize identifiers, and forged identity records fail closed.
  • Local coverage run --branch --source=pg_llm_batch.postgres_restore_target: 26 passed, 27 stmts / 10 branches / 0 miss / 0 partial = 100%.
  • ruff and interrogate --fail-under 100 clean on the isolation module.
  • Docs: docs/doctoring/postgres-restore-target-isolation.md, docs/adr/0022-postgres-restore-target-isolation.md.

Writer boundary

Does not race #208 backup execution, #212 restore seek, #214 docs overlay, #215 receipt binding, #222 workflow-registry audit, #223 catalog inspection, #219 physical/PITR, or #221 live receipt re-inspection. CHANGELOG/README/ARCHITECTURE stay with those writers. Prefer this head over #225 3290f75 and #224 54acec6.

This slice does not prove restorability, live WAL replay, CSAP, or SOC 2 readiness. Refs #204. Addresses the #225 product-safety review.

Open in Web View Automation 

cursoragent and others added 4 commits August 16, 2026 16:29
Add the RED contract for #204 isolated-target identity: a live
pg_service name and a restore-drill name must be exact distinct
libpq service identities. DSNs, tenant scope, subclasses, and
same-name reuse must fail closed before pg_restore.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Succeed the #204 isolated-target gap without racing #212. Operators
must name a live pg_service and a distinct restore-drill service
before pg_restore. DSNs, tenant scope, and same-name reuse fail
closed. Allocate ADR 0021 so the record does not collide with
#216/#219/#221.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
#222 already files ADR 0021 for the workflow-registry audit. Keep the
isolation seam unchanged and retarget this decision, doctoring, and the
documentation contract to 0022 after a fresh open-writer inventory.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Distinct libpq service names are not cluster isolation. Require
caller-owned pg_control_system() identifiers so two aliases for the
same production cluster fail closed before pg_restore.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
@cursor
cursor Bot requested a review from seonghobae August 16, 2026 16:57
@seonghobae
seonghobae marked this pull request as ready for review August 16, 2026 17:08
@cursor

cursor Bot commented Aug 16, 2026

Copy link
Copy Markdown
Contributor Author

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@seonghobae seonghobae left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed exact head 9cdd7d49082ad6698edb6164eb4f69b2451491a3 against protected main@d2f1e32271910a6db98a0757d67194ddadca4566. The seam closes the distinct-service-name false-isolation gap by requiring exact built-in, bounded PostgreSQL system_identifier evidence for both caller-owned targets and refusing equal service names or equal cluster identifiers with fixed content-free diagnostics. It accepts neither DSNs nor credentials and does not open connections or execute restore work. The contract is appropriately limited to caller-supplied name-plus-cluster identity for the logical restore-drill boundary and does not claim physical/PITR clone isolation or successful restore/RLS. No current review threads exist and I found no source-level must-fix in this bounded slice. Approval applies only to this unchanged head; every then-live exact-head required workflow/check and current ancestry/mergeability must terminal-success before merge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants