Supabase Runbook: Control-Plane Postgres & RLS
The Praxion control plane stores its state in a Postgres database hosted on Supabase. It holds workspaces, telemetry fingerprints, billing units, and the audit chain — never your row data. This runbook documents the security model (forced Row Level Security), the agent-fed pattern, and the operations you may need to perform.
Security model: forced RLS
All nine application tables run with ENABLE + FORCE ROW LEVEL SECURITY. Forcing matters: ENABLE alone lets the table owner bypass RLS. With FORCE, even the owner is filtered, and the service_role is the only credential that can operate through policies:
ALTER TABLE workspaces ENABLE ROW LEVEL SECURITY;
ALTER TABLE workspaces FORCE ROW LEVEL SECURITY;
Policies are scoped to the requesting organization. The Settings screen states it plainly: org_id is the isolation axis Row Level Security acts on, and a session with no organization can see nobody's data. That is why the Clerk org → workspace mapping (see the Clerk runbook) is load-bearing — RLS reads it on every query.
Who writes what
The agent-fed pattern
The agent never talks to Supabase directly. Its batches arrive at the ingest endpoint, are validated and rate-limited, and are written by the worker — application-filtered on purpose. No client-supplied payload can reach a table without passing through the worker's validation, so a compromised agent cannot inject rows into another tenant's namespace.
Migrations
Schema changes are applied as numbered migrations (e.g. mig 0008). Each migration is a transaction; the audit chain is not affected by DDL, but the audit_events table records the migration ID for traceability. Rules of the road:
- Run migrations with a dedicated
migratorrole, never the app role. - Back up before DDL on
semantic,billing, oraudit_events— the tables the compliance story depends on. - Test on a staging project first; Supabase branching makes this cheap.
Common operations
Verify RLS is actually forced
SELECT relname, relrowsecurity, relforcerowsecurity
FROM pg_class
WHERE relrowsecurity;
Every row must show relforcerowsecurity = t. If one does not, apply the FORCE statement above and re-run the check.
Inspect tenant scope of a row
SELECT workspace_id, count(*)
FROM telemetry_fingerprints
GROUP BY workspace_id;
Use this to confirm data is landing under the right workspace after an ingest incident.
Restore from backup
Supabase point-in-time recovery is the supported path. Restore to a staging project, verify the audit chain hash validates end-to-end, then promote. The chain validation is the acceptance test — if it does not validate, do not promote.
Troubleshooting
- RLS "infinite recursion" errors — usually a policy that queries the same table it filters. Check the policy body for self-references.
- Rows visible across workspaces — the session
workspace_idis not being set (login flow), or a table lost itsFORCE. Run the verification query above. - Audit chain breaks after a manual edit — expected. The chain is append-only by design; manual
UPDATEinvalidates it and raises an alert. Restore from backup rather than patching rows.
Isolation you can verify.
Audit the chain, verify the RLS, and sleep well. Questions? Real humans reply within a day.
Contact support →