Ice Cream Dev / Database audit
Send the catalog. Keep the dashboard.
This shop is the sample. The audit reads tables, constraints, policies, grants, and function definitions. The Supabase SQL editor stays with you. That session is the postgres role, and it can also change billing, auth, and keys.
Customer emails and the Stripe payloads stay on your project. A schema audit does not need them.
Before a contract
This is what I need before I accept an offer. You send a schema-only file. I open it to count the public tables and confirm the price. That look is the size and the price. It is not the audit.
- 1 Schema file In the Upwork thread. Structure only. No rows.
- 2 Table count Up to 15, 16 to 40, or above 40 public tables.
- 3 The price $65, $150, or a price named from the file. Then you send the offer.
Service 1. Opening schema audit. $65.
Up to 15 public tables. The sample shop has 9. This price is the audit and a SQL script, and both happen after the offer is accepted.
Service 2. Larger schema audit. $150.
16 to 40 public tables. The same audit and the same kind of script, after the offer is accepted.
Service 3. Schema audit priced from the file.
Above 40 public tables, or when the file shows more work than the table count. The price is named before the offer. Work past the correction script is a separate offer.
The schema-only file
You generate the file and send it. On Upwork, attach it in the message thread. Files in Messages stay on Upwork, including before anyone is hired.
The file has the structure, the grants, the policies, and the function definitions. It has no rows. Leave the grants in the file. A dump made with --no-privileges removes them, and the grant check cannot be done.
With the Supabase CLI, from a linked project:
supabase db dump --linked -f schema.sql
Or with pg_dump, using the session pooler on port 5432. The connection string stays on your machine. Send schema.sql.
pg_dump "$DATABASE_URL" \
--schema-only \
--schema=public \
--no-owner \
--file=schema.sql
During the contract
The offer is accepted and the money is deposited. The audit starts then. A connection string waits until this point.
- 1 The checks Empty columns, grants to anon and authenticated, and the policies.
- 2 SQL script The correction. The rows stay. The schema gets stricter.
- 3 You run it On your project. The dashboard stays with you.
A read-only role, if one is needed
You create the role in your SQL editor. It can read the catalog. It cannot read table rows, so orders and refund requests stay closed.
create role audit_reader login password 'a-new-password';
grant connect on database postgres to audit_reader;
grant usage on schema public to audit_reader;
Use a new password. The database password from project settings is the postgres role. Leave that one with you.
The connection string uses session mode, port 5432:
postgresql://audit_reader.PROJECT_REF:PASSWORD@aws-0-REGION.pooler.supabase.com:5432/postgres
Send that string after a contract is open, in the Upwork thread. This role sees columns, policies, grants, and function bodies in pg_catalog. information_schema comes back empty, because that view only lists objects the role itself is allowed to use.
What the audit reads
On a restored dump, these three checks are the start. They run against the copy, as a role that can see the catalog.
select table_name, column_name
from information_schema.columns
where table_schema = 'public' and is_nullable = 'YES'
order by 1, 2;
select grantee, table_name, privilege_type
from information_schema.role_table_grants
where table_schema = 'public'
and grantee in ('anon', 'authenticated')
order by 1, 2, 3;
select tablename, policyname, roles::text, cmd
from pg_policies
where schemaname = 'public'
order by 1;
The first list is every column a row can leave empty. The second is what anon and authenticated are allowed to try. The third is the only policies that exist. Where a grant is present and a policy is absent, row level security is the only block.
The same facts are read from pg_catalog when the connection is the read-only role: pg_attribute for columns, pg_class.relacl for grants, pg_policies for policies, and pg_proc for function bodies.
The copy is a Supabase stack, because those checks depend on anon, authenticated, and service_role. A plain Postgres install does not have those roles, and the grant list would not mean the same thing.
What comes back
A SQL script. You run it on your project. The rows stay, including any order that arrived after the copy. The script is the correction.