# Supabase plugin

The Supabase plugin lets a Vaubaan Gateway mission use Supabase without handing the agent a credential. Postgres, storage, auth and edge functions of Supabase projects, through the Management API.

- Version: `4.2.0` (package `@vaubaan/plugin-supabase`)
- Status: stable
- Health: not enough reports yet — [what this means](/plugins/health)
- Source: [plugins/supabase](https://github.com/vaubaan/gateway/tree/main/plugins/supabase), catalog [risk-catalog.yaml](https://github.com/vaubaan/gateway/blob/main/plugins/supabase/src/risk-catalog.yaml) version 5 of 2026-10-14
- How to connect an account: [Connect Supabase](/docs/providers/supabase)
- How it is modelled: [handbook](https://github.com/vaubaan/gateway/blob/main/docs/handbook/providers/supabase.md)

One account is your company's Supabase OAuth app authorised on an organisation, or a personal access token; one resource is one project (database, storage, auth and edge functions). Supabase runs one project per environment, so the owner of the account labels each project (staging, prod, …) and the permissions tied to an environment apply to the projects carrying that label.

## Permissions

Each permission is a named risk of the plugin's catalog: a mission holds an explicit list of them, never "all". The severity is computed from the permission's six axes (undo, blast radius, data, cost, visible to, lasts); nothing is decided from it.

| Permission | What it allows | Severity | Why it is a risk |
| --- | --- | --- | --- |
| `supabase.read-data` | Read data and schema | low | SELECT and schema inspection, including \`execute\_sql\`, which runs on the Management API read-only endpoint. Can leak PII held in application tables. Deliberately broad : every Supabase read fires it, and the more specific read permissions (\`read-managed-schemas\`, \`auth.read-users\`, \`storage.signed-url-read\`, \`functions.read\`) co-fire to harden the policy. Alone, it lets \`execute\_sql\` run only a plain query that names no schema Supabase manages. |
| `supabase.read-managed-schemas` | Read the schemas Supabase manages, through SQL | medium (only for `preview`, `staging`, `prod`) | \`execute\_sql\` on what Supabase keeps for itself : \`auth\` (users, identities, sessions, refresh tokens), \`vault\` (secrets and their decrypted view), \`storage\` internals, \`pgsodium\` keys, \`net\` and \`cron\` queues, and the catalog relations that hold password hashes or other sessions' queries. Decided by what the SQL names, not by what the database role may read. Also touched by any query the classifier cannot read with confidence : a statement that is not a plain query, a function outside the list it knows to be inert, dollar quoting, prefixed strings. No read-only preset carries it. |
| `supabase.row-write-one` | Write rows one by one (bounded target) — deprecated, no tool touches it | low (only for `preview`, `staging`, `prod`; deprecated) | Deprecated : matches no call. No tool of this plugin writes rows one by one (\`execute\_sql\` runs on the read-only endpoint, and a migration that writes rows is a bulk write). Holding it grants nothing ; it has no successor. Kept so the id stays resolvable in the accesses and missions that name it. |
| `supabase.bulk-row-write` | Bulk write on a table | high (only for `preview`, `staging`) | Mass INSERT, UPDATE/DELETE without or with a wide WHERE, TRUNCATE, DROP TABLE or COLUMN inside a migration. No automatic rollback, high blast. Not acceptable on prod : a call touching it there is refused. |
| `supabase.migration-write` | Apply a migration (DDL) | medium (only for `preview`, `staging`, `prod`) | CREATE/ALTER TABLE or INDEX. Costly to revert (manual down migration) and can break the applications that read the schema. Touched by every migration ; a migration that also writes rows, drops a table or a column, changes RLS, privileges or extensions needs the matching permission too, and one the classifier cannot read (function body, dynamic SQL) touches all of them. Matches the schema/table-level write strictly (no row segment), so a row write never fires it by accident. |
| `supabase.policy-write` | Change RLS (CREATE/ALTER/DROP POLICY) | high (only for `preview`, `staging`, `prod`) | Critical posture shift : opens or closes data access for every role. One mistake is a public data leak. |
| `supabase.db-role-write` | Create or modify Postgres roles | medium (only for `preview`, `staging`, `prod`) | Credential posture : CREATE / ALTER / DROP ROLE, GRANT, REVOKE, default privileges. Reached through \`apply\_migration\`. |
| `supabase.extension-write` | Enable or disable a Postgres extension | medium (only for `preview`, `staging`, `prod`) | Shifts the available SQL surface. Some extensions (\`pg\_net\`, \`http\`) open outbound exfiltration. Reached through \`apply\_migration\`. |
| `supabase.storage.object-read` | List or download storage objects | low | Reads user files, which may be PII (identity documents, photos, exports). Served through the service key regardless of bucket ACL. |
| `supabase.storage.object-write-one` | Upload or delete one object | low (only for `preview`, `staging`, `prod`) | Mutation of one precise path. Reversible when the agent keeps a copy before overwriting. |
| `supabase.storage.bulk-object-write` | Bulk upload or delete of objects | critical (only for `preview`, `staging`) | Prefix-wide or glob deletes, bucket cascade. Supabase Storage has no versioning by default : a bulk delete is irreversible. Structurally not grantable on prod. |
| `supabase.storage.signed-url-read` | Issue a signed URL | medium | A read on the bucket that may exfiltrate the object to an external recipient through a temporary signed URL. Distinct from a plain list/download : the recipient is chosen by the agent. |
| `supabase.bucket-write` | Create, modify or delete a bucket | critical (only for `preview`, `staging`, `prod`) | Posture shift : a public bucket leaks misclassified private objects ; a delete cascades irreversibly over the objects. Matches the bucket-level write strictly (no object segment). |
| `supabase.storage-config-write` | Change the project storage configuration | low (only for `preview`, `staging`, `prod`) | Quotas, transformations, CDN. Affects the whole project and every application consuming storage. |
| `supabase.auth.read-users` | List or read users | medium (only for `preview`, `staging`, `prod`) | PII : emails, providers, last sign-in, user metadata ; the volume can be large. Through the auth admin API only : a SELECT on \`auth.users\` through \`execute\_sql\` is \`read-managed-schemas\`. |
| `supabase.auth.user-write-one` | Create, modify or delete one user | medium (only for `preview`, `staging`, `prod`) | Affects one identity. Reversible (re-create) except delete, which cascades on the rows the user owns (FK ON DELETE CASCADE). |
| `supabase.auth.bulk-user-write` | Bulk user manipulation | critical (only for `preview`, `staging`) | Mass creation or deletion. Risk of locking real users out. Structurally not grantable on prod. |
| `supabase.auth.generate-link` | Generate a magic link, recovery or invite | low (only for `preview`, `staging`, `prod`) | The returned URL signs in as that user. If it leaks (logs, audit, wrong recipient), account takeover. |
| `supabase.sso-provider-write` | Configure a SAML / OIDC identity provider | medium (only for `preview`, `staging`, `prod`) | Federated identity posture. A wrong configuration lets anyone with an account on that IdP sign in. |
| `supabase.third-party-auth-write` | Configure a third-party auth provider (Firebase, Auth0, …) | medium (only for `preview`, `staging`, `prod`) | Same as SSO : identity posture, federates an external JWT issuer for authentication. |
| `supabase.auth-config-write` | Change the project auth configuration | high (only for `preview`, `staging`, `prod`) | JWT secret, expiries, redirect allow-list, signup policy. The smallest mistake opens or breaks authentication for everyone. |
| `supabase.functions.read` | List or read Edge Functions (metadata and body) | low | The body may contain inline secrets when the author hard-coded a key (a frequent anti-pattern). |
| `supabase.functions.body-write` | Create, modify or delete the code of an Edge Function | medium (only for `preview`, `staging`, `prod`) | Changing the code injects custom behaviour into every invocation. Reversible when the source is in git. Bounded to \`edge-function\[\*\]\` strictly so child writes such as \`function-deployment\` do not fire it. |
| `supabase.functions.deploy` | Deploy a new version of an Edge Function | high (only for `preview`, `staging`, `prod`) | Switches the runtime to the new version. On prod, an immediate change of public behaviour. |
| `supabase.function-secret-write` | Create, modify or delete a function secret | medium (only for `preview`, `staging`, `prod`) | Secrets consumed by Edge Functions at runtime. A delete can break the function immediately ; a create or update can poison it. |
| `supabase.uncategorized-write` | Uncategorized Supabase write | medium (only for `preview`, `staging`, `prod`; the catch-all: touched only by a call no other permission covers) | Catch-all for writes without a dedicated entry. A mission holding it for an environment allows every uncategorized write of the service there ; its limits and “ask me first” bound it. A tool needing only this permission is a catalog bug. |

## Presets

A preset pre-fills the permissions of a mission for one account, with a limit per period, a rate and whether each permission runs freely or asks the mission creator first. The person creating the mission adjusts it; the mission stores the explicit list.

### Read-only analyst

`read-only-analyst` — Query application data and schema, list buckets and objects, on every selected project. No write, no user listing, no SQL on the schemas Supabase manages (auth, vault, storage).

| Permission | What it allows | Limit per period | Rate | Runs |
| --- | --- | --- | --- | --- |
| `supabase.read-data` | Read data and schema | 5000 | 120 per minute | runs freely |
| `supabase.storage.object-read` | List or download storage objects | 1000 | 60 per minute | runs freely |

### Staging developer

`staging-developer` — One mission per application. Day-to-day development on staging-labelled projects — migrations of any kind, RLS, buckets and objects, users one by one, edge functions and their secrets. Reads data, schema, objects and functions on every selected project. No auth, storage or SSO configuration, no signed URL, nothing written outside staging.

| Permission | What it allows | Limit per period | Rate | Runs |
| --- | --- | --- | --- | --- |
| `supabase.read-data` | Read data and schema | 10000 | 120 per minute | runs freely |
| `supabase.storage.object-read` | List or download storage objects | 2000 | 60 per minute | runs freely |
| `supabase.functions.read` | List or read Edge Functions (metadata and body) | 1000 | 60 per minute | runs freely |
| `supabase[staging].auth.read-users` |  | 500 | 30 per minute | runs freely |
| `supabase[staging].bulk-row-write` |  | 200 | 20 per minute | runs freely |
| `supabase[staging].migration-write` |  | 200 | 10 per minute | runs freely |
| `supabase[staging].policy-write` |  | 100 | 10 per minute | runs freely |
| `supabase[staging].db-role-write` |  | 100 | 10 per minute | runs freely |
| `supabase[staging].extension-write` |  | 100 | 10 per minute | runs freely |
| `supabase[staging].bucket-write` |  | 50 | 10 per minute | runs freely |
| `supabase[staging].storage.object-write-one` |  | 1000 | 60 per minute | runs freely |
| `supabase[staging].storage.bulk-object-write` |  | 50 | 10 per minute | runs freely |
| `supabase[staging].auth.user-write-one` |  | 500 | 30 per minute | runs freely |
| `supabase[staging].auth.generate-link` |  | 100 | 10 per minute | runs freely |
| `supabase[staging].functions.body-write` |  | 500 | 30 per minute | runs freely |
| `supabase[staging].functions.deploy` |  | 200 | 10 per minute | runs freely |
| `supabase[staging].function-secret-write` |  | 50 | 10 per minute | runs freely |

### Migration operator

`migration-operator` — Apply migrations. Any migration on staging-labelled projects. On prod, only plain schema changes (create or alter tables and indexes) and RLS changes, each asking first; a prod migration that writes rows, drops a table or a column, grants privileges, enables an extension or defines a function is refused.

| Permission | What it allows | Limit per period | Rate | Runs |
| --- | --- | --- | --- | --- |
| `supabase.read-data` | Read data and schema | 2000 | 60 per minute | runs freely |
| `supabase[staging].migration-write` |  | 100 | 10 per minute | runs freely |
| `supabase[staging].bulk-row-write` |  | 50 | 10 per minute | runs freely |
| `supabase[staging].policy-write` |  | 50 | 10 per minute | runs freely |
| `supabase[staging].db-role-write` |  | 50 | 10 per minute | runs freely |
| `supabase[staging].extension-write` |  | 50 | 10 per minute | runs freely |
| `supabase[prod].migration-write` |  | 20 | 2 per minute | ask me first |
| `supabase[prod].policy-write` |  | 10 | 2 per minute | ask me first |

## Tools

| Tool | What it does |
| --- | --- |
| `supabase__get_project` | Read the project |
| `supabase__get_anon_key` | Read the API URL and the anonymous key |
| `supabase__list_tables` | List tables |
| `supabase__list_migrations` | List applied migrations |
| `supabase__execute_sql` | Run a read-only SQL query |
| `supabase__get_advisors` | Read the advisors |
| `supabase__apply_migration` | Apply a database migration |
| `supabase__list_buckets` | List storage buckets |
| `supabase__get_storage_config` | Read the storage configuration |
| `supabase__update_storage_config` | Change the storage configuration |
| `supabase__get_auth_config` | Read the sign-in configuration |
| `supabase__update_auth_config` | Change the sign-in configuration |
| `supabase__list_sso_providers` | List SSO providers |
| `supabase__get_sso_provider` | Read an SSO provider |
| `supabase__create_sso_provider` | Register an SSO provider |
| `supabase__update_sso_provider` | Change an SSO provider |
| `supabase__delete_sso_provider` | Delete an SSO provider |
| `supabase__list_third_party_auth` | List third-party sign-in providers |
| `supabase__get_third_party_auth` | Read a third-party sign-in provider |
| `supabase__create_third_party_auth` | Register a third-party sign-in provider |
| `supabase__delete_third_party_auth` | Delete a third-party sign-in provider |
| `supabase__list_functions` | List edge functions |
| `supabase__get_function` | Read an edge function |
| `supabase__get_function_body` | Read the source of an edge function |
| `supabase__create_function` | Create an edge function |
| `supabase__update_function` | Change an edge function |
| `supabase__delete_function` | Delete an edge function |
| `supabase__deploy_function` | Deploy an edge function |
| `supabase__list_function_secrets` | List the names of function secrets |
| `supabase__create_function_secrets` | Set function secrets |
| `supabase__delete_function_secrets` | Delete function secrets |
| `supabase__list_users` | List users |
| `supabase__get_user` | Read a user |
| `supabase__create_user` | Create a user |
| `supabase__update_user` | Change a user |
| `supabase__delete_user` | Delete a user |
| `supabase__generate_link` | Generate a one-time sign-in link |
| `supabase__create_bucket` | Create a storage bucket |
| `supabase__update_bucket` | Change a storage bucket |
| `supabase__delete_bucket` | Delete a storage bucket |
| `supabase__list_objects` | List the objects of a bucket |
| `supabase__upload_object` | Upload an object |
| `supabase__delete_object` | Delete an object |
| `supabase__create_signed_url` | Create a signed URL for an object |

## Health

Health: not enough reports yet. A plugin version is deemed validated by the community with at least 200 reports and 90 % of them successful, over the last 90 days, as reported by the instances whose operators turned community telemetry on. It is a community signal, not a security guarantee.
