Demonstrating who changed your database policies, and when
Sooner or later almost every team meets the question: this policy granted the wrong thing — who changed it, and when? On Postgres the honest starting answer is that the database does not remember. Catalogs hold the current state of every policy and nothing about the sequence of edits that produced it.
Reconstruction is possible, and better still, avoidable. This page explains why the history goes missing, the practices that build a durable trail yourself, and what RowShield records automatically between scans — with the standing caveat that a technical change record is evidence, not legal or compliance advice, and implies nothing about any framework.
RowShield does not detect this yet. This guide gives you the catalog queries to check it yourself. The nine rules that do ship are listed on the rules index.
Why Postgres does not remember
Data-definition statements are executed and forgotten. CREATE POLICY, ALTER POLICY and DROP POLICY update the system catalogs and move on; unlike table data, no history table accumulates. A year of policy work leaves exactly one artifact per policy: its present definition.
Three channels produce policy changes on a Supabase project, and only one of them leaves a trail by default. Migrations committed to a repository carry authorship and timestamps. Console edits change state immediately and leave nothing behind. Direct sessions — a psql window, a script run by hand — leave traces in server logs only if logging was configured to capture them.
This is why the question arrives at the worst possible moment: the change that matters is usually the one made outside the disciplined path, precisely because it skipped the review that the disciplined path provides. Reconstructing it afterwards means piecing together server logs, memory and guesswork — slow work under pressure, with an incomplete result.
Building a trail yourself
The durable version costs discipline rather than tooling. All schema changes travel through reviewed migrations; console edits are treated as out of bounds in production; every migration lands through a pull request where the diff shows any touched policy line. Git then becomes the primary record, and blame on a migrations directory answers who and when for everything that followed the rule.
Server-side logging adds the net underneath. Statement logging filtered to data-definition commands, or a logging extension along the lines of pgaudit, captures changes arriving from any channel — including the ones that ignored the migration rule. Retention becomes your decision rather than Postgres's, which is both the benefit and the obligation.
Whichever combination you choose, write down where the trail lives and who can read it. A change record nobody can find during an incident functions like no record at all, and a reviewer asking for evidence will not know to look in three separate places unless someone says so plainly beforehand.
What RowShield records automatically
RowShield's drift engine diffs the schema between consecutive scans and reports what moved: policies that appeared, came back, or disappeared; columns and tables added or removed; flags flipped. Each transition carries the scan timestamp, so a quarter of scans reads as a chronological account of the database layer without anyone maintaining a journal.
Findings follow the same vocabulary — created, regressed, resolved — which turns the record into a narrative: a hole opened, was closed, came back after a restore, and closed again. The introspection statements behind every observation are module-level constants over pg_catalog and bucket metadata, exported for inspection; leaked data is summarised by column name and count, never by value.
Stated plainly: this is a technical change record between RowShield scans, not a system of record for compliance purposes. It does not replace migration history, server logs, or whatever retention your obligations require — and it is not legal or compliance advice.
Using the trail during a review
When the question comes — from an incident retrospective, a colleague, or an external reviewer — the useful answer pairs the timeline with the mechanism: what changed, when it was noticed, what the fix was, and why the same class of change is caught earlier now. A transitions record supports exactly that sentence, because created, regressed and resolved entries are already the story in order.
You can see the current state of your own project's policy surface in minutes rather than assembling it from memory: run a free audit at rowshield.dev/audit, or connect the project for scheduled scans and let the drift record accumulate on its own while you do other work.
Frequently asked
- Does Postgres store a history of policy changes?
- No. The system catalogs hold each policy's current definition and no record of previous versions, authors or timestamps. Change history has to come from migrations, statement logging, or an external observer comparing states over time.
- Can RowShield tell me who made a change?
- No. Scans compare states between runs and record what appeared, came back or disappeared, with timestamps — attribution belongs to your migration history and access logs. RowShield supplies the timeline of states, not the identity behind each change.
- Is a change trail enough to satisfy an audit?
- This page cannot answer that — it is not compliance advice, and adequacy depends on your scope, sector and auditor. What a change trail demonstrably does is let you answer who-changed-what-when questions quickly, from dated evidence rather than memory.
Check your project in about ten seconds
Paste a URL. No signup, no writes, nothing stored.
Run the free audit