If you store PHI on Supabase, you need a signed BAA and a project set to High Compliance. That takes care of the platform. It doesn't cover your RLS policies, your functions or your audit tables, and a HIPAA review will ask about those too.
Supabase's shared responsibility model says the same. It tells you to "always apply Row Level Security (RLS)" and leaves the access rules on sensitive tables to you.
The part Supabase handles
On the hosted platform, Supabase encrypts data at rest and in transit, signs the BAA and checks your project settings. Its HIPAA guide asks you to turn on Point in Time Recovery, SSL enforcement, network restrictions and Postgres connection logging, and to use MFA on every account that can reach the project. The Security Advisor warns you if any of that gets switched off.
The advisor and the database linter also flag a few risky shapes, like a table in public with RLS turned off. What they don't do is run your policies. They won't tell you whether a nurse at one clinic can read visits from another, or whether a deleted record left a trace in your audit table.
The part you handle
A HIPAA review will ask three things about your database.
- Who can reach which patient rows?
- Is every change to that data recorded?
- Can anyone change that record afterwards?
These map onto the Security Rule. Access control is 164.312(a)(1), which asks that access go "only to those persons or software programs that have been granted access rights". Audit controls are 164.312(b). Integrity, 164.312(c)(1), is about protecting PHI "from improper alteration or destruction". 164.312(d) covers authentication, and 164.308(a)(8) asks you to evaluate all of it from time to time and whenever things change.
On Supabase, nearly all of that lives in SQL you wrote. A screenshot of your policies won't show what the database does when authenticated calls one of your functions.
Getting the evidence from your own project
I built unitautogen hipaa for this part. Run it from your project folder with the local stack up.
supabase start unitautogen --supabase hipaa --schema public It uses the local database from supabase status. That database is built from your migrations, so no patient data is anywhere near it. The tool generates tests and the rows they need, runs them and rolls the rows back. The report lands in supabase/tests/seccover/hipaa/public/, and the security tests it writes go into supabase/tests/seccover/security/, where supabase test db picks them up.
It works out which tables hold PHI from the schema. When it gets one wrong, you fix it in unitautogen.toml next to your supabase/ folder. The report marks every entry that came from that file, so nobody mistakes your declarations for findings.
[phi] tables = ["public.patients", "public.visits"] not = ["public.countries"] [audit] tables = ["audit.change_log"] What it checks
Access. Functions, views and triggers that run as their owner skip your row policies. Each one gets called as two different tenants, with data on both sides, and is reported as holding or leaking, with the table, command and role. Row policies, column grants and personal data slipping out through error messages are covered too.
The audit log. For every PHI table, and for each of insert, update and delete, the report says whether the change left an audit row. Each one comes out as recorded, conditional, cancelled by the trigger, or untested. On Supabase this matters because connection logging, which you're asked to keep on, only says who connected. It says nothing about which rows changed.
Tampering. For each audit table and each client role, can that role edit it, erase it or forge rows in it? One way this goes wrong is a SECURITY DEFINER function exposed through .rpc() that writes to the audit table. Any logged-in user can call it and write whatever they like.
Identity. Your policies trust auth.uid() and auth.jwt(). The report checks whether a client session can change what those return.
All of it goes into one HTML report with a JSON copy. Each safeguard is marked evidence or partial, the gaps are ranked with the change that fixes each one, and the report is stamped with the database, the time, the role, the tool version and a SHA-256 of every file it drew on. If someone edits it afterwards, the hashes stop matching.
Run it when the schema changes
Most Supabase changes arrive as a migration in a pull request. Supabase already documents a GitHub Action that runs supabase test db. Add the hipaa command to the same job and keep the dated report as a build artifact. That covers the evaluation safeguard, and you don't have to scramble before each audit.
What it doesn't do
The report is evidence for your auditor. It doesn't certify anything. It doesn't look at encryption or transmission, which Supabase handles, and it can't review your logs for you. It also can't tell you a policy is the right policy. If a rule is wrong by design, the report will say the database enforces it, because it does. Your risk analysis, your written policies and the BAA itself stay with you.
unitautogen hipaa is in the Enterprise edition of UnitAutogen for PostgreSQL. Early Supabase teams can ask about the startup rate, and there's a free trial if you want to run it on your own schema first.