The tenant is not one table
Rows spread through foreign keys, composite keys, join tables, UUID tenant keys, denormalized columns, and app-enforced relationships that a simple WHERE tenant_id = ? misses.
Single-tenant recovery for pooled Postgres SaaS
TenantUndo maps tenant-owned rows, object storage links, and live-data conflicts so your team can restore, erase, export, or prove legal hold for one customer with signed evidence.
The failure mode
Rows spread through foreign keys, composite keys, join tables, UUID tenant keys, denormalized columns, and app-enforced relationships that a simple WHERE tenant_id = ? misses.
Invoices, uploads, exports, and attachments live in object storage with conventions the database cannot enforce.
Primary keys, sequences, unique indexes, current writes, triggers, and cascades can corrupt other customers unless the plan is verified first.
Feature set
The current app combines a Go recovery engine, standalone drill CLI, customer-side runner, control-plane API, dashboard, billing gates, RBAC, scheduler, audit log, and signed evidence archive.
Reads tables, columns, primary keys, foreign keys, identity columns, generated columns, and tenant-root metadata before any tenant operation.
Classifies tables as root, tenant-owned, shared, ambiguous, or excluded, then refuses unsafe extraction until manifest rules resolve gaps.
Generates an HTML recovery report with graph safety, blockers, ambiguous tables, inferred relationships, and recommended fixes.
Extracts a complete tenant closure to customer-side files with a manifest and deterministic hashes for replay and verification.
Compares live data against the bundle and reports missing, changed, and extra rows per table before a merge plan exists.
Computes a merge plan, pins it to a hash, requires matching human approval, then verifies other-tenant checksums inside the transaction.
Plans tenant deletion, counts affected rows and object keys, executes only with approval, and emits signed proof after blast-radius verification.
Tracks blob columns and S3-compatible object keys alongside database rows so restores, exports, holds, and erasures cover files too.
Uses the same extract bundle and graph rules to move, copy, or preserve one tenant without forcing a full architecture migration first.
Enforces pending, dispatched, running, awaiting_approval, approved, done, and failed transitions so illegal jumps are refused.
Admin, operator, and auditor capabilities are scoped by org, recorded in audit, and gated by Assess, Guard, or Control plans.
Runs recurring report or analyze jobs and raises drift when a fresh tenant graph no longer matches the last known-good manifest.
Workflow
The runner introspects the schema with a read-only role and sends only metadata: graph counts, table names, hashes, and status.
The app turns the graph into a readiness report with blockers such as ambiguous relationships, unsafe triggers, missing keys, or FDW tables.
Restore your PITR snapshot to scratch, extract the tenant bundle, then diff it against live data to identify conflicts.
TenantUndo computes a merge or erase plan and returns the hash. The plan itself remains on the runner side.
A second authorized user approves the exact plan hash. Auditor accounts can read evidence but cannot create or approve mutations.
The write role is used only for approved restore or erase jobs, then TenantUndo verifies that non-target tenants stayed unchanged.
What ships
TenantUndo has a standalone drill path for early customers and a control-plane path for recurring operations.
CLI
pg_tenantundoAnalyzer and drill tool for local execution: report, extract, diff, plan, execute restore, plan erasure, and execute erasure.
-report readiness HTML-extract-tenant bundle creation-diff live-vs-bundle counts-execute approved merge plan-erase-plan and -erase-executeControl plane API
GET /v1/jobs
POST /v1/jobs
GET /v1/jobs/{id}
POST /v1/jobs/{id}/approve
GET /v1/jobs/{id}/evidence
GET /v1/audit
Runner
Runs in the customer VPC, connects outbound over WebSocket, reads DSNs from env or secret storage, verifies signed jobs, and reports metadata only.
Dashboard
Create jobs, inspect state, approve plan hashes, and view the audit log from the embedded web UI.
Security posture
TenantUndo is built around a narrow wire protocol: the control plane gets metadata and signed evidence, not customer records. Every write is gated by a hash-pinned plan and a second human approval.
Deployment
Pooled multi-tenant Postgres with one primary database and S3-compatible object storage. Works with existing PITR or snapshot workflows.
Dockerfile, Compose for the control plane, Helm chart for the runner, systemd runner unit, and a standalone Go CLI for drills.
MySQL, MongoDB, DB-per-tenant architectures, sharded/event-sourced recovery, and multi-database tenant graphs are not positioned as v1.
Pricing
Readiness Drill
Two-week, read-only recovery assessment for one app and one representative tenant.
Guard
Quarterly drills, scheduled analyze/report jobs, schema drift detection, and evidence archive.
Control
Full control plane for restore, export, deletion proof, approvals, runner management, billing, and audit.
Questions
PITR gets you a whole database at a point in time. TenantUndo handles the application boundary: which rows belong to one customer, which files belong with them, and how to merge back safely into live production.
No by design. The customer runner performs analysis and recovery work in the customer environment. The control plane receives metadata, counts, hashes, status, and signed evidence references.
TenantUndo treats that as a normal case. It can infer likely relationships from conventions, but inferred or ambiguous relationships must be resolved with explicit manifest rules before unsafe operations proceed.
Restore is the wedge. The same tenant graph supports extract/export, deletion proof, legal hold, and pooled-to-dedicated migration workflows.
First step
Send a schema, a backup description, and one representative tenant. TenantUndo returns a recovery map, failure points, and the first signed drill report.