Supabase covers HIPAA infrastructure, but RLS and auditing remain your team's responsibility

- Reading
- 6 min
- Code
- 2
A post published by Munaf Khatri on the UnitAutogen blog (October 29, 2026) details, clause by clause, where what Supabase signs in the BAA ends and where the work of whoever writes the RLS policies, audit triggers, and schema functions begins.
Anyone storing PHI (protected health information) in a Supabase project already knows they need to sign the BAA (Business Associate Agreement) and enable High Compliance mode. What fewer people repeat is that this covers only half the problem. That's the argument of a post published on October 29, 2026 by Munaf Khatri on the UnitAutogen blog, a security testing tool for PostgreSQL: the BAA and High Compliance mode protect the platform, not the RLS policies, functions, or audit tables that live inside the project's schema.
For anyone who administers databases, this distinction isn't news in principle, but it tends to vanish in practice once the infrastructure is outsourced. Supabase has a shared-responsibility model that spells this out explicitly, asking the customer to "always enforce Row Level Security (RLS)" on sensitive tables. The sentence is short, but it carries the most labor-intensive part of compliance: no one outside your own team is going to write those policies for you.
What Supabase's BAA Actually Covers
On the hosted plan, Supabase signs the BAA, handles encryption at rest and in transit, and checks a set of project configurations. The platform's HIPAA guide asks the team to enable Point in Time Recovery, enforce SSL, restrict network access, and turn on Postgres connection logging, in addition to requiring MFA on every account with access to the project. Supabase's own Security Advisor flags it when any of these items is turned off.
The advisor and the database linter also flag risky patterns, such as a public table with RLS disabled. That's useful, but it's a configuration check, not a behavior check. Neither one actually executes the policies, so neither can tell you whether a nurse at one clinic can read another clinic's visits, or whether a deleted record left a trace in the audit table.
The Three Questions a HIPAA Audit Asks the Database
The post maps the part left to the team into three questions, each tied to a clause of the US Security Rule:
- Who can reach which patient rows? Access control, under 164.312(a)(1), which requires access to be granted "only to those persons or software programs that have been granted access rights."
- Is every change to that data logged? Audit controls, under 164.312(b).
- Can anyone alter the record afterward? Integrity, under 164.312(c)(1), which concerns protecting PHI against improper alteration or destruction.
The Security Rule also maps two other requirements that round out this picture, without being part of the three questions the audit poses: authentication, under 164.312(d), and periodic reassessment (including whenever the environment changes), under 164.308(a)(8).
In Supabase, almost all of this lives in SQL written by the team itself: RLS policies, SECURITY DEFINER functions, audit triggers. A screenshot of the configured policies doesn't show what the database actually does when an authenticated call triggers one of those functions. It's exactly that gap, between declared policy and executed behavior, that drives the second half of the post.
How UnitAutogen Tries to Automate Evidence Collection
Khatri's own tool, UnitAutogen for PostgreSQL, gained a command dedicated to this. Run from inside the project folder with the local stack up:
supabase start
unitautogen --supabase hipaa --schema publicThe command connects to the local database reported by supabase status, built from the migrations, with no real patient data involved at any point. It generates the tests, creates the needed rows, runs them, and undoes everything with a rollback. The report goes to supabase/tests/seccover/hipaa/public/, and the generated security tests live in supabase/tests/seccover/security/, where supabase test db can already pick them up.
PHI tables are inferred from the schema, and when the inference gets it wrong, the correction goes into a unitautogen.toml file at the project root:
[phi]
tables = ["public.patients", "public.visits"]
not = ["public.countries"]
[audit]
tables = ["audit.change_log"]According to the post, every entry coming from this file is marked in the report as a team declaration, not as an automated finding, which keeps a manual correction from being read as technical evidence.
What the Report Actually Tests
- Access: functions, views, and triggers that run with the owner's privilege (
SECURITY DEFINER) bypass row policies. Each one is called as two different tenants, with data present on both sides, and the result comes back flagged as leaking or containing the leak, along with the table, command, and role involved. - Audit log: for each PHI table and each operation (
insert,update,delete), the report says whether the change left an audit record, classifying the result as logged, conditional, blocked by trigger, or untested. This matters because the connection log Supabase asks you to keep active only records who connected, not which rows changed. - Tampering: for each audit table and each client role, it checks whether records can be edited, deleted, or forged in it. A common way this fails, according to the post, is a
SECURITY DEFINERfunction exposed via.rpc()that writes to the audit table and ends up callable by any authenticated user, writing whatever it wants. - Identity: since the policies depend on
auth.uid()andauth.jwt(), the report checks whether a client session can manipulate what these functions return.
The output comes as an HTML report with a JSON copy, each safeguard marked as evidence or partial, with gaps ranked alongside the change that would fix each one. The file carries a stamp with the database, timestamp, role, tool version, and a SHA-256 hash of each file used in the analysis; editing the report afterward breaks the hash match.
Integrate into the Pipeline, Don't Run It the Night Before an Audit
Most schema changes in Supabase arrive as a migration in a pull request, and Supabase itself already documents a GitHub Action that runs supabase test db. The post suggests adding the hipaa command to the same job and keeping the dated report as a build artifact. In practice, this turns the periodic assessment required by 164.308(a)(8) into something automatic with every schema change, instead of a manual exercise rushed through right before an audit.
What the Report Doesn't Solve
Here the usual skepticism toward any tool that promises to cover compliance applies: the post itself is clear that the report is evidence for an auditor, not certification. It doesn't analyze encryption or transmission, which remain with Supabase, and it doesn't review logs. More importantly, it doesn't judge whether a policy is the right policy. If an RLS rule is poorly designed as a business decision, the report will say the database enforces it correctly, because it does in fact enforce it; the tool tests execution, not intent.
The risk analysis, the written policies, and the BAA itself remain the organization's responsibility, not that of any testing tool. unitautogen hipaa is part of the Enterprise edition of UnitAutogen for PostgreSQL, with a differentiated price tier for early-stage teams and a free trial available to run against a real schema before deciding.
For anyone administering Postgres behind a Supabase project with health data, the practical takeaway from the post is less about this specific tool and more about where the line falls: no managed provider writes your RLS policy, audit trigger, or SECURITY DEFINER function for you. Automating the check on these points with every migration reduces the risk of discovering a hole in the access policy only when the auditor asks.
Translated from the Brazilian Portuguese original · Read the original
PostgreSQL's restore_command confuses network failure with the end of the WAL archive
When the restore command fails, PostgreSQL assumes by definition that the WAL archive ended right there, even when the real cause was network instability. Christophe Pettus shows how this exit code confusion can end a recovery silently, with no error in the log.





