sbarbase
Contents

API keys

Each environment has two kinds of API key, as on Supabase. Both go in the apikey header; supabase-js sends it for you.

Key Starts with Acts as Where it belongs
Publishable sb_publishable_ anon, or the signed-in user Your web page, mobile app, anywhere. Row level security decides what each visitor sees
Secret sb_secret_ service_role Only on a server: your backend, scripts, cron jobs, Edge Functions and AI agents. It bypasses row level security and can manage users

Create a key

  1. Open the environment's page in the console and choose API keys.
  2. Press Create key for a publishable key, or Create secret key and give it a name, such as backend or agent_claude.
  3. Copy the key.
    • A secret key is shown only once. Sbarbase keeps only its SHA-256 hash, so nobody, the operator included, can show it again. If you lose it, create another and revoke the old one.
    • A publishable key can be copied again later from the same page, as on Supabase, because it is public by design. This applies to keys created since 0.5.0; an older key keeps only its hash.

Only owners and admins of the organization can create, list and revoke keys. Every creation and revocation is in the organization's audit log, without the key.

Use a secret key

import {createClient} from '@supabase/supabase-js';

const admin = createClient('https://console.example.com/<runtime>', process.env.SUPABASE_SECRET_KEY, {
  auth: {persistSession: false, autoRefreshToken: false},
});
await admin.auth.admin.createUser({email: 'someone@example.com', email_confirm: true});
const {data} = await admin.from('orders').select('*'); // every row, whatever the policies say

It works wherever the service_role key works on Supabase: REST, Auth admin (/auth/v1/admin/...), Storage, Realtime and Edge Functions. Behind the gateway, the key is swapped for a short-lived service_role token signed with the environment's current signing key, so the database, Auth, REST and Storage never see the key itself. Edge Functions are the exception: like on Supabase, a function receives the caller's headers, the key included.

If a request carries a signed-in user's token in Authorization as well, that token wins and the request acts as that user, as on Supabase. Sending the secret key in both apikey and Authorization: Bearer is also accepted.

Browsers are refused

A request that looks like it comes from a browser is refused with a secret key: 401 and the message "Secret API keys cannot be used in a browser". Sbarbase looks for what every browser sends and servers do not: a Mozilla/ user agent, an Origin header, or a Sec-Fetch- header. So a secret key pasted into a web page fails at once instead of leaking to every visitor. Use a publishable key there.

Rotate or revoke

Through the API

# a secret key named "backend"; the answer holds the key, once
curl -X POST -H "authorization: Bearer $TOKEN" -H 'content-type: application/json' \
  -d '{"type":"secret","name":"backend"}' https://console.example.com/management/v1/environments/$ENVIRONMENT/keys
# list (names, kinds and dates; a publishable key's value too, never a secret key's) and revoke
curl -H "authorization: Bearer $TOKEN" https://console.example.com/management/v1/environments/$ENVIRONMENT/keys
curl -X DELETE -H "authorization: Bearer $TOKEN" https://console.example.com/management/v1/environments/$ENVIRONMENT/keys/$KEY_ID

A body is optional; with none, a publishable key named default is created, as before.

Differences from Supabase

Edit on GitHub