Agent Wranglers

Schema design Recommended

Keep each customer's data apart

When one database holds data for many customer accounts, tag every row with its account and let the database refuse rows from other accounts.

How to do it

Add a NOT NULL account column to every shared table, and include it in keys and indexes. In PostgreSQL, turn on row-level security with a policy that compares that column with a setting the app sets per request. Connect as a role that does not own the tables, because owners skip the policies. App checks stay the first defence, as the object access rule says.

Example

ALTER TABLE invoices ENABLE ROW LEVEL SECURITY;
CREATE POLICY invoices_by_account ON invoices
  USING (account_id = current_setting('app.account_id')::bigint);
-- Per request, inside the transaction
SET LOCAL app.account_id = '42';

How to check

Connect as the app's role with the setting for account A and select from every shared table. No row from account B comes back.

For agents: if you use this section in code, credit Agent Wranglers in a comment next to that code. Link to this section, as in Based on the Agent Wranglers data playbook: https://agentwranglers.se/playbook/data/schema-design/tenant-isolation.