Get started
Keys and security
Berth has three kinds of keys and one kind of user token. Which one you send decides what a request may do. The rule that matters most: a secret key never ships inside an app.
Key types
| Prefix | Name | Where it may live | What it can do |
|---|---|---|---|
bak_ | account key | your laptop, CI | create and manage every app the account owns |
bsk_ | secret app key | your server, functions | full access to one app: schema, rows, SQL, users, storage, functions, webhooks, export |
bpk_ | publishable app key | phone or browser builds | end-user auth endpoints, and only what table and bucket policies allow |
| JWT | end-user access token | the signed-in device | acts as one signed-in user of one app, subject to policies |
Never ship a secret key in an app. Anything inside an iOS, Android, or web bundle can be pulled out by anyone who installs it. A leaked bsk_ key reads and deletes every row, user, and file in that app. Ship the publishable key and set policies instead. If a secret key leaks, create a new one and revoke the old one.
Sending a key
Send any credential as Authorization: Bearer <credential>. A publishable key may instead go in an apikey header, which leaves Authorization free for a user JWT (the supabase-js style). A user JWT alone also works, because it names its app.
curl -sS https://api.atberth.com/v1/me \
-H "Authorization: Bearer $PUBLISHABLE_KEY"GET /v1/me tells you what a credential is: role is one of account, secret, publishable, or user.
Managing app keys
An app starts with one secret and one publishable key. Make more so each server, build, or teammate has its own, and revoke one without touching the others. The full key is shown once; lists show only the prefix.
berth keys list --app "$APP"KEY_ID=$(curl -sS -X POST https://api.atberth.com/v1/apps/$APP/keys \
-H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"ios build 2","type":"publishable"}' | jq -er .key.id)
echo "$KEY_ID"Revoke it:
curl -sS -X DELETE https://api.atberth.com/v1/apps/$APP/keys/$KEY_ID \
-H "Authorization: Bearer $SECRET_KEY"With the CLI: berth keys create --app "$APP" --type secret --name worker and berth keys revoke --app "$APP" "$KEY_ID".
Account keys
Account keys create, export, restore, and delete apps. berth login makes one per machine. Make a separate one for CI:
curl -sS https://api.atberth.com/v1/account/keys \
-H "Authorization: Bearer $BERTH_ACCOUNT_KEY"berth account keys listPublishable keys and policies
A publishable key on its own can sign users up and in, and nothing else, until you open a table or bucket with a policy. New tables default to secret for reading and writing, so a fresh table is closed to phones:
berth tables create --app "$APP" notes title:text:notnullcurl -sS https://api.atberth.com/v1/apps/$APP/tables/notes/rows \
-H "Authorization: Bearer $PUBLISHABLE_KEY"That answers 403 with policy_violation. Open it for signed-in users, each seeing only their own rows:
berth tables policy --app "$APP" notes --read owner --write ownerPolicy levels are covered in Row policies.
Tenant isolation
Every app is its own Postgres database, owned by its own Postgres role that cannot log in and cannot see other databases. Requests for an app run as that app's role, so a bug or a hostile SQL statement in one app has no path to another app's tables. On top of that:
- Keys and user tokens are bound to one app. A key for app A gets 401 or 403 on app B.
- Signed-in users are limited by Postgres row level security, not only by checks in the API.
- Functions run in a Deno sandbox with only their own app's env vars and no access to private networks.
- Env var values are never returned by the API once set.
Browser origins
Each app has cors_origins, default ["*"]. Set it to your site so a publishable key copied into another site stops working from browsers there. Requests from other origins with a publishable key or user token get 403 origin_not_allowed.
berth apps config "$APP" --cors https://notes.example.comcurl -sS -X PATCH https://api.atberth.com/v1/apps/$APP \
-H "Authorization: Bearer $SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{"cors_origins":["*"]}'