Skip to main content
A Powabase project’s Postgres database has five user-visible schemas, plus a handful of internal ones (_realtime, supabase_functions, etc.) you’ll occasionally see in pg_namespace. This page explains what each schema contains, who owns it, and which ones are safe to touch. For the AI-schema-specific queryability story, see Querying the ai schema. For RLS, see RLS Model. For migration boundaries, see Migrations.

The five user-visible schemas

SchemaOwnerPurposeSafe to migrate?
publicYouYour application tablesYes
extensionsYouUser-installed Postgres extensionsYes
aiPlatformSources, KBs, agents, runs, sessions, workflowsNo
authGoTrueUser accounts, sessions, OAuth identitiesNo
storageStorage APIBucket and object metadataNo

public — your tables

Empty by default. You add your application tables here: users (or profiles joining to auth.users), posts, orders, etc. RLS is off for new tables in public unless you explicitly enable it (ALTER TABLE my_table ENABLE ROW LEVEL SECURITY). PostgREST exposes public at /rest/v1/* without needing the Accept-Profile header — it’s the default schema for the API.

ai — the AI surface’s state

Where the typed /api/* endpoints store their data. 35+ tables for sources, knowledge_bases, indexed_sources, chunks, embeddings, agents, agent_sessions, agent_runs, workflows, workflow_executions, etc. Full inventory in Querying the ai schema. Don’t migrate ai.*. The platform’s services assume the schema’s invariants — modifying them risks data corruption or service-side breakage. Read freely (RLS lets authenticated SELECT most tables); never schema-change. PostgREST exposes ai at /rest/v1/* with Accept-Profile: ai for reads and Content-Profile: ai for writes. RLS gates which rows each role can touch.

auth — GoTrue’s state

User accounts (auth.users), refresh tokens (auth.refresh_tokens), OAuth identities (auth.identities), MFA factors (auth.mfa_factors), email-change records, recovery tokens. Managed entirely by GoTrue’s own migrations; the project init SQL just creates the schema. Don’t migrate auth.*. GoTrue runs its own schema migrations on startup; conflicting changes break the auth surface. PostgREST does not expose auth — there’s no Accept-Profile that gets you in. Use the typed /auth/v1/* API instead.

storage — Storage API’s state

storage.buckets (one row per bucket), storage.objects (one row per uploaded file), plus a handful of helper tables and functions (storage.foldername(), storage.filename()). Managed by the Storage API. PostgREST exposes storage at /rest/v1/* (it’s in the schemas list). You can read storage.objects to list files via SQL, and RLS policies on storage.objects are how you gate object access in private buckets. Don’t write to storage.objects directly — go through /storage/v1/object/..., which keeps the underlying S3 backend in sync. See Storage policies for the policy patterns.

extensions — Postgres extensions land here

When you CREATE EXTENSION foo, the extension’s objects live in whatever schema the extension specifies, which by Powabase convention is extensions:
CREATE EXTENSION IF NOT EXISTS pg_net SCHEMA extensions;
You can install extensions yourself (from the allowlist — see Extensions) into extensions. The platform pre-installs pg_net and vector here at project provision time.

Internal schemas you’ll see

These exist for platform internals and aren’t intended for user code, but you’ll notice them in pg_namespace listings:
SchemaPurpose
_realtimeRealtime’s per-tenant configuration and replication state
supabase_functionsThe http_request() trigger function plus the audit table for triggered hooks (used by DB webhooks)
realtimeThe realtime.send() and realtime.broadcast_changes() SQL functions
graphql_publicpg_graphql’s mount point (queryable via POST /rest/v1/rpc/graphql)
vaultVault extension’s encrypted secrets storage (preloaded but no platform code uses it)
pgsodium, pg_catalog, information_schema, etc.Standard Postgres internals
You can call functions in realtime and supabase_functions from your own code — that’s why they’re documented above. The rest are internal.

search_path and what schemas resolve

Your Postgres session’s search_path controls which schemas are searched for unqualified table references. The default is "$user", public, extensions — meaning SELECT * FROM users looks in public first, then extensions. To query ai.* from psql without qualifying every table:
SET search_path TO ai, public, extensions;
SELECT * FROM agents;  -- Resolves to ai.agents
PostgREST sets the search_path automatically based on the Accept-Profile / Content-Profile headers — you only think about it for direct SQL connections.

What schema your role can see

Each Postgres role has different USAGE grants on each schema. The defaults that matter:
Rolepublicaiauthstorage
anonUSAGEUSAGE (RLS denies all)USAGE
authenticatedUSAGEUSAGE (RLS-gated)USAGE
service_roleUSAGEUSAGE (RLS bypassed)USAGE
supabase_admin (you in psql / migrations)USAGEUSAGEUSAGEUSAGE
Schema USAGE is just “can the role know this schema exists”; RLS policies on individual tables decide what’s readable. The auth.* schema isn’t granted to anon / authenticated / service_role — they can’t even see it in \dn output, let alone query auth.users.

Next steps

Querying the ai schema

What’s in ai.* and how to query it under RLS.

Extensions

Which extensions are preloaded vs CREATE EXTENSION-able.

RLS Model

The role mapping that drives which schemas / rows each request can see.

Migrations

The schema boundary you should not cross when migrating.