The Data Model Sanity Bench
Every table in your project holds rules. Rows belong to somebody. Two people cannot share an email. A price is money. The only question is who is enforcing them.
You cannot secure what you did not model. A rule about who may read a row has to compare it to somebody — and if no column names an owner, there is no comparison to make.
one-time · instant download · yours to keep
01 — The problem
The rules are real. Some of them only live in your head.
The tables were generated, not designed
You described a feature and got a table. It had the columns the feature needed and nothing else — no thought about who owns a row, what must stay unique, or whether a price is money or a piece of text that looks like money.
It works, because your code remembers
Every write goes through the one function that sets the owner correctly and checks for duplicates first. That holds exactly until there is a second way to write to the table, which there always is eventually.
And some rules cannot be written at all
The rule you actually want — only the owner may read their own row — needs a column naming the owner. Without one there is nothing to compare against, so the rule cannot be written, cannot be tested, and cannot be turned on later without changing the table first.
02 — What's inside
Four questions per table. None of them an opinion.
What kind of table it is
People's records, or a list you seeded yourself? Plan tiers and country codes have no owner and need none, and the bench will not pretend that is a gap.
Whether a column names an owner
And if so, whether it is a real reference that the database checks, or a copied value — an email, a name, a pasted id — that happened to be correct when it was written.
What the table refuses
Would it accept a duplicate of something you rely on being unique? Are money and dates stored as themselves, or as text the table cannot tell good from bad?
Nobody can hand you a list of your own tables, so this one you enumerate: open your table editor and write down what is there. Ten from a typical first app ship filled in as a worked example — clear them and put your own in.
03 — The standard
Three rules it will not bend for you.
No owner column outranks everything else on the table
A records table with no column naming an owner reads NOTHING TO ENFORCE AGAINST even when uniqueness is enforced and every type is correct. Nothing else can rescue it, because the missing thing is not a rule — it is the thing rules are written against. In the worked example one table is perfect in every other column and still lands there.
A rule you hold up by hand ranks better than one that cannot exist
This is deliberate and it is the opposite of how these usually get ordered. A rule your code enforces can be moved into the table later, in one place, whenever you get to it. A missing owner column cannot be worked around at all — every downstream fix is blocked behind changing the table first.
An answer you could not find is scored as the worst it could have been
Not a guess about what is likely — the worst, because an answer you cannot produce is not evidence. Stated that way it also cannot be gamed: writing cant_find can never get you a better result than telling the truth would have. Where an unfound answer could not have changed the row either way, it is not counted against you and the row says so.
04 — How it works
An hour with your table editor open.
01
Open your table editor and list every table. One row each, including the ones you did not create on purpose.
02
Mark which ones hold people's records and which are lists you seeded. That single column decides whether the rest of the questions even apply.
03
Answer the three remaining questions from what is on the screen. Answer cant_find honestly where you have to.
04
Add the missing owner columns before anything else. Every other fix on those tables is waiting behind that one.
05 — What you'll see
The worked example, live.
Ten tables from a typical first app. T-03 is the one to sit with: uniqueness enforced, types correct, and it still reads NOTHING TO ENFORCE AGAINST. Make every other column on it worse and watch the answer stay exactly the same.
Try it on the worked example
Ten tables from a typical first app. T-03 is perfect in every other column and still reads NOTHING TO ENFORCE AGAINST — make everything else worse and watch the answer not improve.
| Table | Kind | Owner column | Uniqueness | Money & dates | Destination |
|---|---|---|---|---|---|
T-01 User profiles | DATABASE ENFORCES IT the owner is a real reference, what you assume is unique is enforced, and money and dates are stored as themselves | ||||
T-02 Orders and their totals | YOU ENFORCE IT money or dates are stored as plain text or plain numbers, so the table cannot tell you a bad one from a good one | ||||
T-03 Uploaded documents | NOTHING TO ENFORCE AGAINST nothing on this table says who a row belongs to, so no rule about who may read it has anything to compare against | ||||
T-04 Comments on documents | YOU ENFORCE IT the owner is a copied value rather than a real reference - the table will happily accept an owner who does not exist | ||||
T-05 The plan tiers you offer | NO RULES NEEDED this is a reference list, not people's records - there is no owner to name and no rule to write | ||||
T-06 Subscriptions | YOU ENFORCE IT you are relying on something being unique that the table would let you duplicate tomorrow | ||||
T-07 Country and currency codes | NO RULES NEEDED this is a reference list, not people's records - there is no owner to name and no rule to write | ||||
T-08 File upload records | NOTHING TO ENFORCE AGAINST nothing on this table says who a row belongs to, so no rule about who may read it has anything to compare against | ||||
T-09 Team invites | YOU ENFORCE IT you are relying on something being unique that the table would let you duplicate tomorrow | ||||
T-10 Activity log | DATABASE ENFORCES IT the owner is a real reference, what you assume is unique is enforced, and money and dates are stored as themselves |
Verdict
NOTHING TO BUILD ON
Something here has no owner column, so no rule can be written against it.
Fix first T-08 — nothing on this table says who a row belongs to, so no rule about who may read it has anything to compare against
6 of 10
tables that need work
60% of the tables listed
2
no rule can be written against
there is no owner to compare a row to
4
held together by your code remembering
fixable in one place
2
told to leave alone
reference lists, not a gap
None of the four numbers above changes the verdict. Tables that read NO RULES NEEDED are never counted against you.
This is the live engine. 60 rows, the same formulas, in a file you keep Start Here tab that explains every answer in plain words Dashboard with the verdict, the counts, and what to fix first
Get the kit — $6906 — Who it's for
For the person whose tables were written for them.
Buy this if
- Your tables were generated a feature at a time and you have never sat down with all of them.
- You are about to turn on access rules and want to know if they can even be written.
- You have wondered whether two users could end up with the same email.
- You store a price and are not sure whether the database thinks it is money.
Skip it if
- You design schemas for a living. There is nothing here you do not know.
- Your project has no database yet.
- You want something that reads your schema and reports on it — this is a read-off, not a linter.
What this is not
It connects to nothing, runs nothing against your database, and sees no rows. It grades how tables are shaped and never a person. It is not a schema linter, a migration tool, or a security audit, and it is not legal advice — it reads back what you tell it, using rules you can see in the formulas.
07 — Works alongside
Where this sits.
This one is the layer underneath the rest. The three below are the idea it comes from, the rules you write once the shape can hold them, and where to take what you find.
The Browser Line Read-Off
$59The keystone of the same line. This bench is the layer underneath it — where each check runs matters only once the data is shaped so a check can exist.
The Supabase RLS Read-Off
$69The rules written on your tables. Run this bench first — a table with no owner column has nothing for a policy to compare against, so you would be writing rules against a shape that cannot hold them.
Vibe-Coded App Pre-Launch Security Gate
$79The launch-moment go/no-go across six controls. A model you cannot write access rules against is the reason several of them cannot be answered honestly.
08 — Common questions
Before you buy.
Because working and enforced are different things. Every table holds rules — rows belong to somebody, two people cannot share an email, a price is money — and right now some of those rules live in the table and some live in whatever your code happens to do today. The ones living in your code are fine until you add a second way to write to that table, and then they quietly stop being true.
Because a rule you enforce by hand can be moved into the table later, in one place. A missing owner column cannot be worked around at all: a rule saying only the owner may read their own row has to compare that row to somebody, and if no column names an owner there is no comparison to make. You cannot secure what you did not model, and the ordering reflects that.
Nothing stops it being wrong. A real reference points at your users table and the database refuses to accept an owner who does not exist; a copied value is just text that happened to be correct when it was written. Change an email, delete an account, mistype a paste, and the row is now owned by nobody, silently. That row reads YOU ENFORCE IT because you are the only thing keeping the two in step.
No, and the bench says so. A list of plan tiers or country codes has no owner and needs none — demanding structure where there is nothing to structure is inventing work, and a tool that flags everything trains you to flag nothing. Those rows read NO RULES NEEDED and are never counted against you.
No. It connects to nothing, runs nothing against your project, and sees no rows. You open your own table editor, answer four questions per table from what is on the screen, and the spreadsheet does the rest.
This one comes first. That kit checks the rules written on your tables; this one checks whether the tables can carry a rule at all. If a table has no column naming an owner, there is nothing for a policy to compare against — you would be writing rules against a shape that cannot hold them. Run this, fix the owner columns, then go and write the policies.
Get the Read-Off
Find out which of your tables can hold a rule at all.
- One .xlsx — Start Here, Dashboard, Bench
- 60 rows with dropdowns and live formulas
- A worked ten-table example, already filled in
- Offline forever — nothing uploaded, no access granted